`pytest-qt`函数`mouseMove()`不工作

Posted

技术标签:

【中文标题】`pytest-qt`函数`mouseMove()`不工作【英文标题】:`pytest-qt` Function `mouseMove()` Not Working 【发布时间】:2021-08-07 23:55:02 【问题描述】:

已向here、here、here、here 和 here 提出了这个问题,并且在 Qt5 中显然仍然存在一个错误,注意到here。到目前为止,我没有找到任何解决我的问题的方法。

我正在尝试测试当我的鼠标悬停在工具栏按钮上时是否显示正确的状态栏消息。

设置

操作系统: Windows 10 Professional x64 位,Build 1909Python: 3.8.10 x64 位PyQt: 5.15.4pytest-qt: 4.0.2IDE: VSCode 1.59.0

项目目录

gui/
├───gui/
│   │   main.py
│   │   __init__.py
│   │   
│   ├───controller/
│   │       controller.py
│   │       __init__.py
│   │
│   ├───model/
│   │      model.py
│   │       __init__.py
│   │
│   └───view/
│           view.py
│            __init__.py
├───resources/
│   │    __init__.py
│   │   
│   └───icons
│       │   main.ico
│       │   __init__.py
│       │   
│       └───toolbar
│               new.png
│               __init__.py
└───tests/
    │   conftest.py
    │   __init__.py
    │
    └───unit_tests
            test_view.py
            __init__.py

代码

gui/main.py:

from PyQt5.QtWidgets import QApplication

from gui.controller.controller import Controller
from gui.model.model import Model
from gui.view.view import View


class MainApp:
    def __init__(self) -> None:
        self.controller = Controller()
        self.model = self.controller.model
        self.view = self.controller.view

    def show(self) -> None:
        self.view.showMaximized()


if __name__ == "__main__":
    app = QApplication([])
    root = MainApp()
    root.show()
    app.exec_()

gui/view.py:

from typing import Any

from PyQt5.QtCore import QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QFrame, QGridLayout, QStatusBar, QToolBar, QWidget
from pyvistaqt import MainWindow

from resources.icons import toolbar


class View(MainWindow):
    def __init__(
        self, controller, parent: QWidget = None, *args: Any, **kwargs: Any
    ) -> None:
        super().__init__(parent, *args, **kwargs)
        self.controller = controller

        # Set the window name
        self.setWindowTitle("GUI Demo")

        # Create the container frame
        self.container = QFrame()

        # Create the layout
        self.layout = QGridLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Set the layout
        self.container.setLayout(self.layout)
        self.setCentralWidget(self.container)

        # Create and position widgets
        self._create_actions()
        self._create_menubar()
        self._create_toolbar()
        self._create_statusbar()

    def _create_actions(self):
        self.new_icon = QIcon(toolbar.NEW_ICO)

        self.new_action = QAction(self.new_icon, "&New Project...", self)
        self.new_action.setStatusTip("Create a new project...")

    def _create_menubar(self):
        self.menubar = self.menuBar()

        self.file_menu = self.menubar.addMenu("&File")

        self.file_menu.addAction(self.new_action)

    def _create_toolbar(self):
        self.toolbar = QToolBar("Main Toolbar")
        self.toolbar.setIconSize(QSize(16, 16))

        self.addToolBar(self.toolbar)

        self.toolbar.addAction(self.new_action)

    def _create_statusbar(self):
        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)

gui/model.py:

from typing import Any


class Model(object):
    def __init__(self, controller, *args: Any, **kwargs: Any):
        self.controller = controller

gui/controller.py:

from typing import Any

from gui.model.model import Model
from gui.view.view import View


class Controller(object):
    def __init__(self, *args: Any, **kwargs: Any):
        self.model = Model(controller=self, *args, **kwargs)
        self.view = View(controller=self, *args, **kwargs)

resources/icons/toolbar/__init__.py:

import importlib.resources as rsrc

from resources.icons import toolbar

with rsrc.path(toolbar, "__init__.py") as path:
    NEW_ICO = str((path.parent / "new.png").resolve())

test/conftest.py:

from typing import Any, Callable, Generator, List, Sequence, Union

import pytest
import pytestqt
from pytestqt.qtbot import QtBot
from gui.main import MainApp
from PyQt5 import QtCore

pytest_plugins: Union[str, Sequence[str]] = ["pytestqt.qtbot",]
"""A ``pytest`` global variable that registers plugins for use in testing."""


@pytest.fixture(autouse=True)
def clear_settings() -> Generator[None, None, None]:
    yield
    QtCore.QSettings().clear()


@pytest.fixture
def app(qtbot: QtBot) -> Generator[MainApp, None, None]:
    # Setup
    root = MainApp()
    root.show()
    qtbot.addWidget(root.view)

    # Run
    yield root

    # Teardown - None

test/unit_tests/test_view.py:

import time

from PyQt5 import QtCore, QtWidgets
import pytest
from pytestqt import qt_compat
from pytestqt.qt_compat import qt_api
from pytestqt.qtbot import QtBot

from gui.main import MainApp


def test_toolbar_newbutton_hover(app: MainApp, qapp: QtBot, qtbot: QtBot):
    # Arrange
    new_button = app.view.toolbar.widgetForAction(app.view.new_action)
    new_button.setMouseTracking(True)

    qtbot.addWidget(new_button)

    # Act
    qtbot.mouseMove(new_button)
    qapp.processEvents()
    time.sleep(5)  # See if statusbar message appears

    # Assert
    assert app.view.statusbar.currentMessage() == "Create a new project..."

问题:

状态栏消息永远不会更新,鼠标有时只会移动到工具栏按钮。我不知道如何让这个测试通过。

【问题讨论】:

【参考方案1】:

您的代码有 2 个问题:

不应使用 time.sleep,因为它会阻塞 GUI。 您已将鼠标放在按钮的中心,但您没有移动它。
def test_toolbar_newbutton_hover(app: MainApp, qapp: QtBot, qtbot: QtBot):
    new_button = app.view.toolbar.widgetForAction(app.view.new_action)
    new_button.setMouseTracking(True)

    qtbot.addWidget(new_button)

    # Act
    qtbot.wait(1000)
    qtbot.mouseMove(new_button)
    qtbot.wait(1000)
    qtbot.mouseMove(new_button, new_button.rect().bottomRight() - QtCore.QPoint(10, 10))

    # Assert
    assert app.view.statusbar.currentMessage() == "Create a new project..."

【讨论】:

太棒了!谢谢!事实上,即使我删除了最后一个mouseMove,这仍然有效。我相信time.sleep 阻止 GUI 是第一个问题,第二个问题是我没有等待足够长的时间让 ToolTip 和 StatusBar 更新。谢谢!

以上是关于`pytest-qt`函数`mouseMove()`不工作的主要内容,如果未能解决你的问题,请参考以下文章

在 CircleCI 上运行 pytest-qt

如何使用 pytest-qt 自动拖动鼠标?

为啥在使用 pytest-qt 进行测试时出现致命的 Python 错误?

如何使用 pytest-qt 鼠标单击在 QTableWidget 中选择一个项目?

如何在pytest-qt中单击按钮后检查正确打开窗口

如何在 pytest-qt 中处理模态对话框而不模拟对话框