24

我在 QScrollArea 中有一个带有几个 QSpinBox 对象的控件。在滚动区域中滚动时一切正常,除非鼠标恰好位于其中一个 QSpinBoxes 上。然后 QSpinBox 窃取焦点,并且滚轮事件操纵旋转框值而不是滚动滚动区域。

我不想完全禁用使用鼠标滚轮来操作 QSpinBox,但我只希望它在用户明确单击或进入 QSpinBox 时发生。有没有办法防止 QSpinBox 从 QScrollArea 窃取焦点?

正如在下面对答案的评论中所说,设置 Qt::StrongFocus 确实会阻止焦点矩形出现在控件上,但是它仍然会窃取鼠标滚轮并调整旋转框中的值并阻止 QScrollArea 滚动。与 Qt::ClickFocus 相同。

4

7 回答 7

17

为了解决这个问题,我们需要关心以下件事:

  1. 旋转框不能通过使用鼠标滚轮获得焦点。这可以通过将焦点策略设置为 来完成Qt::StrongFocus
  2. 旋转框必须仅在它已经具有焦点时才接受轮事件。这可以通过QWidget::wheelEventQSpinBox子类中重新实现来完成。

MySpinBox实现此功能的类的完整代码:

class MySpinBox : public QSpinBox {

    Q_OBJECT

public:

    MySpinBox(QWidget *parent = 0) : QSpinBox(parent) {
        setFocusPolicy(Qt::StrongFocus);
    }

protected:

    virtual void wheelEvent(QWheelEvent *event) {
        if (!hasFocus()) {
            event->ignore();
        } else {
            QSpinBox::wheelEvent(event);
        }
    }
};

就是这样。请注意,如果您不想创建新的QSpinBox子类,那么您也可以使用事件过滤器来解决这个问题。

于 2013-10-15T13:40:27.340 回答
16

尝试Qt::WheelFocus从 spinbox' 中删除QWidget::focusPolicy

spin->setFocusPolicy( Qt::StrongFocus );

此外,您需要防止轮事件到达旋转框。您可以使用事件过滤器来做到这一点:

explicit Widget( QWidget * parent=0 )
    : QWidget( parent )
{
    // setup ...
    Q_FOREACH( QSpinBox * sp, findChildren<QSpinBox*>() ) {
        sp->installEventFilter( this );
        sp->setFocusPolicy( Qt::StrongFocus );
    }

}

/* reimp */ bool eventFilter( QObject * o, QEvent * e ) {
    if ( e->type() == QEvent::Wheel &&
         qobject_cast<QAbstractSpinBox*>( o ) )
    {
        e->ignore();
        return true;
    }
    return QWidget::eventFilter( o, e );
}

为了完整起见,格兰特林伯格进行了编辑,因为这让我完成了 90% 的工作:

除了上面 mmutz 所说的,我还需要做一些其他的事情。我必须创建一个 QSpinBox 的子类并实现focusInEvent(QFocusEvent*)focusOutEvent(QFocusEvent*). 基本上,在 a 上focusInEvent,我将 Focus Policy 更改为Qt::WheelFocus,在focusOutEvent我将其更改回Qt::StrongFocus.

void MySpinBox::focusInEvent(QFocusEvent*)
{
     setFocusPolicy(Qt::WheelFocus);
}

void MySpinBox::focusOutEvent(QFocusEvent*)
{
     setFocusPolicy(Qt::StrongFocus);
}

此外,事件过滤器类中的 eventFilter 方法实现会根据 spinbox 子类的当前焦点策略更改其行为:

bool eventFilter(QObject *o, QEvent *e)
{
    if(e->type() == QEvent::Wheel &&
       qobject_cast<QAbstractSpinBox*>(o))
    {
        if(qobject_cast<QAbstractSpinBox*>(o)->focusPolicy() == Qt::WheelFocus)
        {
            e->accept();
            return false;
        }
        else
        {
            e->ignore();
            return true;
        }
    }
    return QWidget::eventFilter(o, e);
}
于 2011-04-28T16:25:03.730 回答
12

我的解决方案尝试。易于使用,无需子类化。

首先,我创建了一个新的辅助类:

#include <QObject>

class MouseWheelWidgetAdjustmentGuard : public QObject
{
public:
    explicit MouseWheelWidgetAdjustmentGuard(QObject *parent);

protected:
    bool eventFilter(QObject* o, QEvent* e) override;
};

#include <QEvent>
#include <QWidget>

MouseWheelWidgetAdjustmentGuard::MouseWheelWidgetAdjustmentGuard(QObject *parent) : QObject(parent)
{
}

bool MouseWheelWidgetAdjustmentGuard::eventFilter(QObject *o, QEvent *e)
{
    const QWidget* widget = static_cast<QWidget*>(o);
    if (e->type() == QEvent::Wheel && widget && !widget->hasFocus())
    {
        e->ignore();
        return true;
    }

    return QObject::eventFilter(o, e);
}

StrongFocus然后我在运行时或在 Qt Designer中将有问题的小部件的焦点策略设置为。然后我安装我的事件过滤器:

ui.comboBox->installEventFilter(new MouseWheelWidgetAdjustmentGuard(ui.comboBox));

完毕。当MouseWheelWidgetAdjustmentGuard父对象 - 组合框 - 被销毁时,将自动删除。

于 2016-05-26T12:22:58.543 回答
5

只是为了扩展,您可以使用 eventFilter 来执行此操作,而不需要派生一个新的 QMySpinBox 类型类:

bool eventFilter(QObject *obj, QEvent *event)
{
    QAbstractSpinBox* spinBox = qobject_cast<QAbstractSpinBox*>(obj);
    if(spinBox)
    {
        if(event->type() == QEvent::Wheel)
        {
            if(spinBox->focusPolicy() == Qt::WheelFocus)
            {
                event->accept();
                return false;
            }
            else
            {
                event->ignore();
                return true;
            }
        }
        else if(event->type() == QEvent::FocusIn)
        {
            spinBox->setFocusPolicy(Qt::WheelFocus);
        }
        else if(event->type() == QEvent::FocusOut)
        {
            spinBox->setFocusPolicy(Qt::StrongFocus);
        }
    }
    return QObject::eventFilter(obj, event);
}
于 2013-07-15T16:42:56.270 回答
2

在这篇文章的帮助下,我们为Python/PySide 制作了一个解决方案。如果有人偶然发现这一点。就像我们做的那样:]

class HumbleSpinBox(QtWidgets.QDoubleSpinBox):
    def __init__(self, *args):
        super(HumbleSpinBox, self).__init__(*args)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)

    def focusInEvent(self, event):
        self.setFocusPolicy(QtCore.Qt.WheelFocus)
        super(HumbleSpinBox, self).focusInEvent(event)

    def focusOutEvent(self, event):
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        super(HumbleSpinBox, self).focusOutEvent(event)

    def wheelEvent(self, event):
        if self.hasFocus():
            return super(HumbleSpinBox, self).wheelEvent(event)
        else:
            event.ignore()
于 2018-08-29T09:19:02.007 回答
2

这是我的紫罗兰色长颈鹿答案的 Python PyQt5 端口:


def preventAnnoyingSpinboxScrollBehaviour(self, control: QAbstractSpinBox) -> None:
    control.setFocusPolicy(Qt.StrongFocus)
    control.installEventFilter(self.MouseWheelWidgetAdjustmentGuard(control))

class MouseWheelWidgetAdjustmentGuard(QObject):
    def __init__(self, parent: QObject):
        super().__init__(parent)

    def eventFilter(self, o: QObject, e: QEvent) -> bool:
        widget: QWidget = o
        if e.type() == QEvent.Wheel and not widget.hasFocus():
            e.ignore()
            return True
        return super().eventFilter(o, e)

于 2020-04-17T14:57:59.590 回答
0

只是为了帮助有需要的人,它缺少一个小细节:

call focusInEvent and focusOutEvent from QSpinBox :

    void MySpinBox::focusInEvent(QFocusEvent* pEvent)
    {
        setFocusPolicy(Qt::WheelFocus);
        QSpinBox::focusInEvent(pEvent);
    }

    void MySpinBox::focusOutEvent(QFocusEvent*)
    {
        setFocusPolicy(Qt::StrongFocus);
        QSpinBox::focusOutEvent(pEvent);
    }
于 2013-09-05T13:39:39.003 回答