【问题标题】:How to receive hover events for child widgets?如何接收子小部件的悬停事件?
【发布时间】:2020-09-25 05:30:21
【问题描述】:

我有一个 QWidget 包含另一个(子)小部件,我想为其处理 hoverEnterEventhoverLeaveEventThe documentation 提到

当鼠标光标移入、移出或移入小部件时,如果小部件具有Qt::WA_Hover 属性,则会发生鼠标事件。

所以我尝试通过设置此属性并实现相应的事件处理程序来接收悬停事件:

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout

class TestWidget(QWidget):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        layout.addWidget(TestLabel('Test 1'))
        layout.addWidget(TestLabel('Test 2'))
        self.setLayout(layout)
        self.setAttribute(Qt.WA_Hover)

class TestLabel(QLabel):
    def __init__(self, text):
        super().__init__(text)
        self.setAttribute(Qt.WA_Hover)

    def hoverEnterEvent(self, event):  # this is never invoked
        print(f'{self.text()} hover enter')

    def hoverLeaveEvent(self, event):  # this is never invoked
        print(f'{self.text()} hover leave')

    def mousePressEvent(self, event):
        print(f'{self.text()} mouse press')

app = QApplication([])
window = TestWidget()
window.show()
sys.exit(app.exec_())

但它似乎不起作用,没有收到悬停事件。另一方面,mousePressEvent 确实有效。

此外,我还尝试了以下方法:

  • 为所有小部件设置self.setMouseTracking(True)
  • TestWidget 包装在QMainWindow 中(尽管这不是我想要为实际应用做的),
  • 在父窗口小部件和event.accept() 上实现事件处理程序(尽管据我了解,事件是从内向外传播的,所以这不是必需的)。

如何在我的自定义 QWidget 上接收悬停事件?

【问题讨论】:

    标签: python pyqt pyqt5 qlabel


    【解决方案1】:

    QWidget 和 QLabel 一样没有hoverEnterEventhoverLeaveEvent 方法,这些方法来自QGraphicsItem,因此您的代码不起作用。

    如果你想监听类型的悬停事件你必须重写event()方法:

    import sys
    from PyQt5.QtCore import Qt, QEvent
    from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
    
    
    class TestWidget(QWidget):
        def __init__(self):
            super().__init__()
            layout = QVBoxLayout(self)
            layout.addWidget(TestLabel("Test 1"))
            layout.addWidget(TestLabel("Test 2"))
    
    
    class TestLabel(QLabel):
        def __init__(self, text):
            super().__init__(text)
            self.setAttribute(Qt.WA_Hover)
    
        def event(self, event):
            if event.type() == QEvent.HoverEnter:
                print("enter")
            elif event.type() == QEvent.HoverLeave:
                print("leave")
            return super().event(event)
    
    
    def main():
        app = QApplication(sys.argv)
        window = TestWidget()
        window.show()
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

    • 谢谢,你知道为什么QWidget 不实现这些事件处理程序吗?通过event然后手动检查事件类型感觉很麻烦。
    • @a_guest mmm,我想(因为答案只有 Qt 的开发人员知道)因为绝大多数 QWidgets 不使用它,它会不必要地浪费内存。 Qt 尝试优化以使二进制文件不重。
    • @eyllanesc 看起来在 QWidget 中有处理悬停的方法,名为 enterEventleaveEvent。见Shilo's answer
    【解决方案2】:

    你知道你可以使用 QWidget 的 enterEvent 和 leaveEvent 来做到这一点吗?您需要做的就是更改方法名称。您甚至不需要在标签上设置 Hover 属性。

    from PyQt5.QtWidgets import QApplication, QGridLayout, QLabel, QWidget
    
    
    class Window(QWidget):
        def __init__(self, parent=None):
            super(Window, self).__init__(parent)
            layout = QGridLayout()
            self.label = MyLabel(self)
            layout.addWidget(self.label)
            self.setLayout(layout)
            text = "hover label"
            self.label.setText(text)
    
    
    class MyLabel(QLabel):
        def __init__(self, parent=None):
            super(MyLabel, self).__init__(parent)
            self.setParent(parent)
    
        def enterEvent(self, event):
            self.prev_text = self.text()
            self.setText('hovering')
    
        def leaveEvent(self, event):
            self.setText(self.prev_text)
    
    
    if __name__ == "__main__":
        app = QApplication([])
        w = Window()
        w.show()
        app.exit(app.exec_())
    

    【讨论】:

    • 这似乎是一个不错的解决方案。我只是想知道为什么要创建这些单独的enterEventleaveEvent?它只是一个命名约定,一个用于 QWidget,另一个用于 QGraphicsItem?还是在这些事件的工作方式方面存在更根本的区别(与hoverEnterEventhoverLeaveEvent 相比)?阅读the documentation 似乎enterEventleaveEvent 正是我正在寻找的。此外,据我了解,WA_Hover 会导致元素在悬停时重新绘制。
    • @a_guest 据我所知,hoverEnterenterEvent 的行为几乎相同。我知道的唯一区别是HoverEnterEvent 调用update。我认为这是为了确保安排重绘,但我并不肯定。老实说,我不确定WA_Hover 究竟做了什么。文档说它强制对进入/离开事件进行重新绘制,但没有说明它是如何实现的,或者何时有必要。我唯一一次使用WA_Hover 是在我覆盖了paintEvent 的时候,但是如果我在进入/离开事件中调用了update,那可能就没有必要了`
    • 我对文档中的悬停事件重绘有同样的理解。在我的应用程序中,我想在用户将鼠标悬停在另一个小部件上时突出显示一个小部件,因此我实际上不需要对悬停的小部件进行此更新/重绘。所以你的解决方案是我最终选择的解决方案。但是,由于该问题明确询问如何接收小部件的悬停事件,因此我将其他答案保留为已接受。无论如何,我感谢您对这些专用事件处理程序的回答和解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 2021-02-20
    • 1970-01-01
    相关资源
    最近更新 更多