My goal is to detect when a user hovers or stops hovering over a frame, but whenever I try to detect that with an eventFilter, there are just no events that get run that show that. The event IDs for hoverEnter, hoverLeave, and hoverMouseMove are 127, 128, and 129, but if you run the code, you'll see that they just don't come up. Here is the code that fails:
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())
My end goal here is to be able to detect when a QFrame is clicked. I was thinking I would try to do that by checking for mouse clicks, and if the mouse is hovering over the frame, trigger the function.
First of all it should be noted that clicked is not an event but a signal. The button clicked signal is emitted when the button receives the MouseButtonRelease event.
In this answer I will show at least the following methods to implement the clicked signal in the QFrame.
Override 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())
Use a eventFilter:
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())
A big part of the error of the O attempt is that by doing window.installEventFilter(window)
it is only listening for events from the window itself and not from the QFrame. The solution is to send the QFrame events to the class window.frame.installEventFilter(window)
.
On the other hand, do not use numerical codes but the enumerations since they are more readable.
On the other hand, for the mouse event, the Qt::WA_Hover
attribute must be enabled(Read the docs for more information)
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())