PyQt6 不调用任何悬停在框架上的事件

PyQt6 Isn't calling any events for hovering over a frame

我的目标是检测用户何时悬停或停止悬停在帧上,但每当我尝试使用 eventFilter 检测时,都没有事件 运行 表明这一点。 hoverEnter、hoverLeave 和 hoverMouseMove 的事件 ID 是 127、128 和 129,但是如果您 运行 代码,您会发现它们根本就没有出现。这是失败的代码:

import sys
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *


class MainApp(QWidget):
    def __init__(self):
        super().__init__()
              
        self.setWindowTitle("Test Window")
        self.resize(300, 200)
        
        self.outerLayout = QHBoxLayout()
        self.outerLayout.setContentsMargins(50, 50, 50, 50)
        
        self.frame = QFrame()
        self.frame.setStyleSheet("background-color: lightblue;")
        self.innerLayout = QHBoxLayout(self.frame)
        self.label = QLabel(self.frame)
        self.label.setText("Example Frame")

        self.innerLayout.addWidget(self.label)
        self.outerLayout.addWidget(self.frame)
        
        self.setLayout(self.outerLayout)
    
    def eventFilter(self, obj, event):
        if event.type() == 127:
            print("hovered")
        elif event.type() == 128:
            print("no longer hovered")
        elif event.type() == 129:
            print("hover move event")
        print(event.type())
        return True

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainApp()
    window.installEventFilter(window)
    window.show()
    sys.exit(app.exec())

我的最终目标是能够检测 QFrame 何时被点击。我在想我会尝试通过检查鼠标点击来做到这一点,如果鼠标悬停在框架上,则触发该功能。

首先需要注意的是clicked不是一个事件而是一个信号。当按钮接收到 MouseButtonRelease 事件时,将发出按钮单击信号。

在这个答案中,我将至少展示以下方法来实现 QFrame 中的点击信号。

  • 覆盖 mouseReleaseEvent

    import sys
    
    from PyQt6.QtCore import pyqtSignal, pyqtSlot
    from PyQt6.QtWidgets import QApplication, QFrame, QHBoxLayout, QLabel, QWidget
    
    
    class Frame(QFrame):
        clicked = pyqtSignal()
    
        def mouseReleaseEvent(self, event):
            super().mouseReleaseEvent(event)
            self.clicked.emit()
    
    
    class MainApp(QWidget):
        def __init__(self):
            super().__init__()
    
            self.setWindowTitle("Test Window")
            self.resize(300, 200)
    
            self.outerLayout = QHBoxLayout(self)
            self.outerLayout.setContentsMargins(50, 50, 50, 50)
    
            self.frame = Frame()
            self.frame.setStyleSheet("background-color: lightblue;")
    
            self.label = QLabel(text="Example Frame")
    
            self.innerLayout = QHBoxLayout(self.frame)
            self.innerLayout.addWidget(self.label)
    
            self.outerLayout.addWidget(self.frame)
    
            self.frame.clicked.connect(self.handle_clicked)
    
        @pyqtSlot()
        def handle_clicked(self):
            print("frame clicked")
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainApp()
        window.show()
        sys.exit(app.exec())
    
  • 使用事件过滤器:

    import sys
    
    from PyQt6.QtCore import QEvent
    from PyQt6.QtWidgets import QApplication, QFrame, QHBoxLayout, QLabel, QWidget
    
    
    class MainApp(QWidget):
        def __init__(self):
            super().__init__()
    
            self.setWindowTitle("Test Window")
            self.resize(300, 200)
    
            self.outerLayout = QHBoxLayout(self)
            self.outerLayout.setContentsMargins(50, 50, 50, 50)
    
            self.frame = QFrame()
            self.frame.setStyleSheet("background-color: lightblue;")
    
            self.label = QLabel(text="Example Frame")
    
            self.innerLayout = QHBoxLayout(self.frame)
            self.innerLayout.addWidget(self.label)
    
            self.outerLayout.addWidget(self.frame)
    
            self.frame.installEventFilter(self)
            # for move mouse
            # self.frame.setMouseTracking(True)
    
        def eventFilter(self, obj, event):
            if obj is self.frame:
                if event.type() == QEvent.Type.MouseButtonPress:
                    print("press")
                # for move mouse
                # elif event.type() == QEvent.Type.MouseMove:
                #    print("move")
                elif event.type() == QEvent.Type.MouseButtonRelease:
                    print("released")
            return super().eventFilter(obj, event)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainApp()
        window.show()
        sys.exit(app.exec())
    

加上

O 尝试的很大一部分错误是,通过执行 window.installEventFilter(window) 它只监听来自 window 本身而不是来自 QFrame 的事件。解决方案是将 QFrame 事件发送到 class window.frame.installEventFilter(window).

另一方面,不要使用数字代码,而是使用枚举,因为它们更具可读性。

另一方面,对于鼠标事件,必须启用 Qt::WA_Hover 属性(阅读 docs 了解更多信息)

import sys

from PyQt6.QtCore import QEvent, Qt
from PyQt6.QtWidgets import QApplication, QFrame, QHBoxLayout, QLabel, QWidget


class MainApp(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Test Window")
        self.resize(300, 200)

        self.outerLayout = QHBoxLayout(self)
        self.outerLayout.setContentsMargins(50, 50, 50, 50)

        self.frame = QFrame()
        self.frame.setStyleSheet("background-color: lightblue;")

        self.label = QLabel(text="Example Frame")

        self.innerLayout = QHBoxLayout(self.frame)
        self.innerLayout.addWidget(self.label)

        self.outerLayout.addWidget(self.frame)

        self.frame.setAttribute(Qt.WidgetAttribute.WA_Hover)
        self.frame.installEventFilter(self)

    def eventFilter(self, obj, event):
        if obj is self.frame:
            if event.type() == QEvent.Type.HoverEnter:
                print("enter")
            elif event.type() == QEvent.Type.HoverMove:
                print("move")
            elif event.type() == QEvent.Type.HoverLeave:
                print("leave")
        return super().eventFilter(obj, event)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainApp()
    window.show()
    sys.exit(app.exec())