Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: QTest Press/Click not Working #428

Open
adam-grant-hendry opened this issue Jun 4, 2022 · 1 comment
Open

BUG: QTest Press/Click not Working #428

adam-grant-hendry opened this issue Jun 4, 2022 · 1 comment

Comments

@adam-grant-hendry
Copy link

adam-grant-hendry commented Jun 4, 2022

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)
@adam-grant-hendry
Copy link
Author

adam-grant-hendry commented Jun 6, 2022

Reviewing:

  1. Qt6 QTest Namespace
  2. Qt5 QTest Namespace
  3. PySide2.QtTest Note
  4. PySide2 QTest Namespace
  5. PySide6.QtTest Note
  6. PySide6 QTest Namespace

and the available QWidget virtual functions (Python)/protected functions (C++) in

  1. Qt5
  2. Qt6
  3. PySide2
  4. PySide6

it is evident that

  1. QWidget objects do not support mouseClick (that mouseClick can be called without breaking seems an accident)
  2. mouseClick is only available for C++ (Qt5/Qt6) in QTest
  3. 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:

  1. No longer use QTest.mouseClick() (minimal change)
  2. Switch from QTest mouse and key methods to QtGui 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).

adam-grant-hendry pushed a commit to adam-grant-hendry/pytest-qt that referenced this issue Jun 6, 2022
`QTest` mouse actions `mouseClick` and `mousePress` do not release
the mouse, causing `qtbot` to fail tests. Replace with `QtGui` mouse
events.

Fixes Issue pytest-dev#428
adam-grant-hendry pushed a commit to adam-grant-hendry/pytest-qt that referenced this issue Jun 6, 2022
`QTest` mouse actions `mouseClick` and `mousePress` do not release
the mouse, causing `qtbot` to fail tests. Replace with `QtGui` mouse
events.

Fixes Issue pytest-dev#428
adam-grant-hendry pushed a commit to adam-grant-hendry/pytest-qt that referenced this issue Jun 6, 2022
`QTest` mouse actions `mouseClick` and `mousePress` do not release
the mouse, causing `qtbot` to fail tests. Replace with `QtGui` mouse
events.

Fixes Issue pytest-dev#428
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant