pytest-dev / pytest-dev/pytest-qt

BUG: QTest Press/Click not Working

Open
#428 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
455
Forks
72
Avg merge
12h 26m
Merged PRs (30d)
1

Description

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)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing test_menubar_toolbar_hover_triggers_statusbar_messages with the provided MRE in windows mode, then inspect the existing QTest-backed mouse interaction entry points. Compare their press, release, move, and click behavior with the pyqtgraph ui_testing.py reference. Done means hover status messages work without the extra mousePress workaround and existing interaction tests remain passing.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
desktop, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.