pytest-qt
pytest-qt copied to clipboard
BUG: QTest Press/Click not Working
Referencing this SO issue, I am experiencing the same problem, even with setMouseTracking
enabled (in windows
mode, not headless
). It appears as if mousePress
and mouseClick
don't properly release mouse buttons.
MRE
To get hovering to function, I am forced to use mousePress
after moves when I shouldn't have to.
def test_menubar_toolbar_hover_triggers_statusbar_messages(app: MainApp, qtbot: QtBot) -> None:
"""Test for correct status bar messages when items are hovered.
For example, when the user clicks 'File' in the menubar and hovers over 'New', the
statusbar message should read 'Create a new project...'. This test currently does not
pass
Args:
app (MainApp): (fixture) Qt main application
qtbot (QtBot): (fixture) Bot that imitates user interaction
"""
window = app.view
menubar = window.menubar
toolbar = window.toolbar
statusbar = window.statusbar
file_menu = window.file_menu
new_action = window.new_action
new_button = toolbar.widgetForAction(new_action)
qtbot.addWidget(menubar)
qtbot.addWidget(file_menu)
qtbot.addWidget(toolbar)
qtbot.addWidget(new_button)
menubar.setMouseTracking(True)
file_menu.setMouseTracking(True)
toolbar.setMouseTracking(True)
new_button.setMouseTracking(True)
file_rect = menubar.actionGeometry(file_menu.menuAction())
new_rect = file_menu.actionGeometry(new_action)
def check_status():
assert statusbar.currentMessage() == 'Create a new project...'
# Assert - Precondition
assert statusbar.currentMessage() == ''
# Act - Menubar
qtbot.wait(10) # In non-headless mode, give time for previous test to finish
qtbot.mouseMove(menubar, file_rect.center())
qtbot.mouseClick(menubar, QtCore.Qt.LeftButton, pos=file_rect.center())
qtbot.mouseMove(file_menu, new_rect.center())
# Assert - Menubar
qtbot.waitUntil(check_status)
# Act - Toolbar
qtbot.wait(10) # In non-headless mode, give time for previous test to finish
qtbot.mouseMove(new_button)
# Assert - Toolbar
qtbot.waitUntil(check_status)
Solution
pyqtgraph
has implemented a solution that works: write custom mouse movement methods instead of wrapping QTest
:
# Alternative: replace qt_api with qtpy
# from qtpy import QtCore, QtGui, QtTest, QtWidgets
def mousePress(widget, pos, button, modifier=None):
if isinstance(widget, qt_api.QtWidgets.QGraphicsView):
widget = widget.viewport()
if modifier is None:
modifier = qt_api.QtCore.Qt.KeyboardModifier.NoModifier
event = qt_api.QtGui.QMouseEvent(qt_api.QtCore.QEvent.Type.MouseButtonPress, pos, button, qt_api.QtCore.Qt.MouseButton.NoButton, modifier)
qt_api.QtWidgets.QApplication.sendEvent(widget, event)
def mouseRelease(widget, pos, button, modifier=None):
if isinstance(widget, qt_api.QtWidgets.QGraphicsView):
widget = widget.viewport()
if modifier is None:
modifier = qt_api.QtCore.Qt.KeyboardModifier.NoModifier
event = qt_api.QtGui.QMouseEvent(qt_api.QtCore.QEvent.Type.MouseButtonRelease, pos, button, qt_api.QtCore.Qt.MouseButton.NoButton, modifier)
qt_api.QtWidgets.QApplication.sendEvent(widget, event)
def mouseMove(widget, pos, buttons=None, modifier=None):
if isinstance(widget, qt_api.QtWidgets.QGraphicsView):
widget = widget.viewport()
if modifier is None:
modifier = qt_api.QtCore.Qt.KeyboardModifier.NoModifier
if buttons is None:
buttons = qt_api.QtCore.Qt.MouseButton.NoButton
event = qt_api.QtGui.QMouseEvent(qt_api.QtCore.QEvent.Type.MouseMove, pos, qt_api.QtCore.Qt.MouseButton.NoButton, buttons, modifier)
qt_api.QtWidgets.QApplication.sendEvent(widget, event)
def mouseDrag(widget, pos1, pos2, button, modifier=None):
mouseMove(widget, pos1)
mousePress(widget, pos1, button, modifier)
mouseMove(widget, pos2, button, modifier)
mouseRelease(widget, pos2, button, modifier)
def mouseClick(widget, pos, button, modifier=None):
mouseMove(widget, pos)
mousePress(widget, pos, button, modifier)
mouseRelease(widget, pos, button, modifier)
Reviewing:
- Qt6 QTest Namespace
- Qt5 QTest Namespace
- PySide2.QtTest Note
- PySide2 QTest Namespace
- PySide6.QtTest Note
- PySide6 QTest Namespace
and the available QWidget
virtual functions (Python)/protected functions (C++) in
it is evident that
-
QWidget
objects do not supportmouseClick
(thatmouseClick
can be called without breaking seems an accident) -
mouseClick
is only available for C++ (Qt5/Qt6) inQTest
- Many methods (namely all mouse and key events) were not bound from C++ when making
PyQt5
,PySide2
,PyQt6
, andPySide6
All of the above (plus the referenced SO post by pyqtgraph) give me strong reason to suggest:
- No longer use
QTest.mouseClick()
(minimal change) - Switch from
QTest
mouse and key methods toQtGui
mouse and key events (as it is unlikely they will continue to work properly and/or be provided in python bindings of Qt in the future).