鼠标点击会穿透Qwidgets到pyside应用吗?

2 投票
1 回答
1606 浏览
提问于 2025-04-18 08:54

我正在尝试在我的图形界面(gui)中检测鼠标点击,下面的代码可以检测到在一个Qwidget层级上的鼠标点击。

import sys
from PySide import QtGui, QtCore

class MouseDetector(QtCore.QObject):
    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            print 'mouse pressed', obj
        return super(MouseDetector, self).eventFilter(obj, event)

class MainWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        layout = QtGui.QHBoxLayout()
        layout.addWidget(QtGui.QLabel('this is a label'))
        layout.addWidget(QtGui.QPushButton('Button'))

        self.setLayout(layout)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)  
    mouseFilter = MouseDetector()
    app.installEventFilter(mouseFilter)

    main = MainWindow()
    main.show()

    sys.exit(app.exec_())

但是,如果我在一个Qwidget里面再嵌套一个Qwidget,然后再嵌套一个Qwidget,鼠标点击就无法穿透到应用程序的其他部分。

更让人困惑的是,当我在最里面的那个小部件上安装事件过滤器时,鼠标点击依然无法被检测到。

# Widget x.1 is embedded in Widget X

#    -----------------Widget x.1-----------------------
#    |                                                |
#    |                                                |
#    | ----------------------  ---------------------| |
#    | |                    |  |                    | |
#    | |   Widget  x.1.1 |  |  |   Widget x.1.2     |
#    | |                    |  |                    | |
#    | ----------------------  ---------------------| |
#    |                                                |
#    --------------------------------------------------

我是不是在解决这个问题的方式上搞错了?任何建议都非常感谢。

1 个回答

1

这是Qt中鼠标事件传播的正常表现。

QLabel和QPushButton默认会接收鼠标事件,这意味着它们会处理这些事件,而不让事件继续传递给它们的父级。如果你想让事件继续传递,可以继承你的QWidget,并重写鼠标交互的方法,不去处理这些事件,这样它们就会继续传播。你也可以在所有东西上面放一个透明的控件来捕捉鼠标事件。

另外,你还可以使用事件过滤器,这样也能实现。我运行了程序,MouseDetector运行得很好(Windows 7,Python 2.7,PySide 1.2.2):

mouse pressed <PySide.QtGui.QPushButton object at 0x00000000038A0A88>
mouse pressed <PySide.QtGui.QLabel object at 0x00000000038A0908>
mouse pressed <__main__.MainWindow object at 0x00000000038A0888>

在所有控件上面放一个捕捉鼠标事件的QWidget是很简单的。例如:

from PySide import QtCore, QtGui

class ClickableWidget(QtGui.QWidget):

    clicked = QtCore.Signal(QtGui.QMouseEvent)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def mousePressEvent(self, event):
        self.clicked.emit(event)
        event.ignore()

def beep():
    print('button clicked')

def dup():
    print('catcher clicked')

app = QtGui.QApplication([])
window = QtGui.QWidget()
button = QtGui.QPushButton('Click me', window)
button.clicked.connect(beep)
catcher = ClickableWidget(window)
catcher.clicked.connect(dup)
catcher.raise_()

window.show()

app.exec_()

在这里,按钮是可见的,但无法点击,因为另一个(默认是透明的)QWidget在它上面(raise_())。实际上,即使不发出信号,只要在按钮上面放一个控件,就能捕捉到所有的鼠标事件,因为这些事件只会传递给父控件(而这个捕捉控件并不是按钮的子控件)。另一种方法(忽略事件)就有点复杂了。你可以参考Qt - 顶层控件的键盘和鼠标事件透明性?来了解更多。

撰写回答