QScrollArea 中禁用的 QToolButtons 阻止滚动(PySide)

Disabled QToolButtons inside QScrollArea prevent scrolling (PySide)

我希望能够在 QScrollArea 中使用鼠标滚动,无论鼠标是否位于禁用的 QToolButton 上方。文档提到禁用的小部件不会将鼠标事件传播到 parents,我发现我需要安装事件过滤器来解决我遇到的这个问题。这是我设法做到的程度。我认为事件过滤器是在检测到车轮事件时安装的,但我不确定下一步应该做什么:

from PySide import QtGui
from PySide.QtCore import *
import sys

app = QtGui.QApplication(sys.argv)

wid = QtGui.QWidget()
wid.resize(200, 200)

scroll = QtGui.QScrollArea()
scroll.setWidget(wid)

layout = QtGui.QVBoxLayout()
wid.setLayout(layout)

class installEvent(QObject):
    def eventFilter(self, i_obj, i_event):

        if i_event.type() == QEvent.Wheel:
            print "QEvent: " + str(i_event.type())


buttonEnabled = QtGui.QToolButton()
layout.addWidget(buttonEnabled)


buttonDisabled = QtGui.QToolButton()
buttonDisabled.setEnabled(False)
layout.addWidget(buttonDisabled)

buttonDisabled.installEventFilter(installEvent(buttonDisabled))

scroll.show()

sys.exit(app.exec_())

捕获到正确的事件后,您需要将事件 post 发送到父窗口小部件。我以前这样做过:

def eventFilter(self, obj, event):
    if obj and not obj.isEnabled() and event.type() == QEvent.Wheel:
        newEvent = QWheelEvent(obj.mapToParent(event.pos()), event.globalPos(),
                               event.delta(), event.buttons(),
                               event.modifiers(), event.orientation())
        QApplication.instance().postEvent(obj.parent(), newEvent)
        return True

    return QObject.eventFilter(self, obj, event)

此事件过滤器仅在禁用按钮且事件类型为 QEvent.Wheel 时才执行某些操作。否则它会实现 eventFilter.

的默认行为

if 语句中的登录首先将坐标映射到父窗口小部件,然后使用与原始事件相同的参数(坐标映射除外)构造新的 QWEheelEvent。最后一部分是获取 QApplication 的一个实例并使用 postEvent 方法将事件发送到 Qt 事件循环,Qt 将在其中将事件分发到适当的小部件(禁用的父级)按钮)。

我猜想面向对象的编程风格在 PySide 和解决此类问题时是合适的。如果将来 PySide 初学者偶然发现这个答案,这里是关于如何让它工作的完整示例:

from PySide import QtGui
from PySide.QtCore import *
import sys

# http://zetcode.com/gui/pysidetutorial/firstprograms/

class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):

        self.scroll = QtGui.QScrollArea()
        self.scroll.setWidget(self)

        self.layout = QtGui.QVBoxLayout()
        self.setLayout(self.layout)

        self.btn = QtGui.QToolButton()
        self.btn.move(50, 50)
        self.btn.setEnabled(False)
        self.btn.installEventFilter(self)

        self.layout.addWidget(self.btn)
        self.setGeometry(300, 300, 250, 150)   
        self.scroll.show()

    def eventFilter(self, obj, event):
        if obj and not obj.isEnabled() and event.type() == QEvent.Wheel:
            newEvent = QtGui.QWheelEvent(obj.mapToParent(event.pos()), event.globalPos(),
                                   event.delta(), event.buttons(),
                                   event.modifiers(), event.orientation())
            QtGui.QApplication.instance().postEvent(obj.parent(), newEvent)
            print "QEvent: " + str(event.type())
            return True

        return QObject.eventFilter(self, obj, event) 

def main():

    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

我的情况是这样的:

  • QScrollArea 小部件中的 QPushButton 很少
  • 当我在活动的按钮上滚动而不是非活动的按钮时,滚动有效。

我在实现中所做的是:

  • 在我的所有按钮上安装父级 class (QMainWindow) 的事件过滤器,同时将它们添加到布局
  • 在 QMainWindow 中实现了对 eventFilter 的覆盖 class
  • 过滤 QPushButtons 和 WheelEvents
  • 将 wheelEvent 转换为新的 WheelEvent 并将其发布到我的应用程序实例的主事件循环中。

如果有人正在寻找 C++ 等效代码来防止上述行为(您可以根据需要调整代码):

bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
{
    if (obj->metaObject()->className() == QString("QPushButton") && ev->type() == QEvent::Wheel){
        QWheelEvent *wev = static_cast<QWheelEvent*>(ev);
        QPushButton *btn = static_cast<QPushButton*>(obj);
        if(btn->isEnabled() == false){
            QWheelEvent newEvent = QWheelEvent(btn->mapToParent(wev->pos()), wev->globalPos(),
                                   wev->delta(), wev->buttons(),
                                   wev->modifiers(), wev->orientation());
            qApp->sendEvent(obj->parent(), &newEvent);
            return true;
        }
    }
    return QMainWindow::eventFilter(obj,ev);
}