4
votes

I have a QWidget containing another (child) widget for which I'd like to process hoverEnterEvent and hoverLeaveEvent. The documentation mentions that

Mouse events occur when a mouse cursor is moved into, out of, or within a widget, and if the widget has the Qt::WA_Hover attribute.

So I tried to receive the hover events by setting this attribute and implementing the corresponding event handlers:

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_())

However it doesn't seem to work, no hover events are received. The mousePressEvent on the other hand does work.

In addition I tried also the following things:

  • Set self.setMouseTracking(True) for all widgets,
  • Wrap the TestWidget in a QMainWindow (though that's not what I want to do for the real application),
  • Implement event handlers on parent widgets and event.accept() (though as I understand it, events propagate from inside out, so this shouldn't be required).

How can I receive hover events on my custom QWidgets?

2

2 Answers

4
votes

The QWidget like the QLabel do not have the hoverEnterEvent and hoverLeaveEvent methods, those methods are from the QGraphicsItem so your code doesn't work.

If you want to listen to the hover events of the type you must override the event() method:

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()
2
votes

Did you know that you can do this with QWidget's enterEvent and leaveEvent? All you need to do is change the method names. You won't even need to set the Hover attribute on the label.

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_())