Я пытаюсь подвести мышь к "кнопке" File
в строке меню. В моей программе pytestqt.mouseMove
перемещает мышь не в то место (в настоящее время она щелкает рядом с заголовком окна).
Настройка
OS: Windows 10 Professional x64-bit, Build 1909
Python: 3.8.10 x64-bit
PyQt: 5.15. 4
pytest-qt: 4.0.2
IDE: 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_menubar_click(app: MainApp, qtbot: QtBot) -> None:
# Arrange
file_menu = app.view.file_menu
file_menu.setMouseTracking(True)
qtbot.addWidget(file_menu)
# Act
qtbot.wait(1000)
qtbot.mouseMove(file_menu)
qtbot.wait(5000)
# Assert
assert False
Проблема:
Мышь перемещается не в то место:
Мне нужно, чтобы мышь переместилась к кнопке File
, чтобы я мог щелкнуть ее. Как мне этого добиться?
Блестяще еще раз! У вас есть книга или ресурс, который вы можете порекомендовать для лучшего изучения Qt в Python? Я читал «Создание приложений с графическим интерфейсом с помощью Python и Qt 5» Мартина Фитцпатрика, «Современный PyQt» Джошуа Уильямса и «Начало PyQt» Джошуа М. Уиллмана, но ни в одном из них нет подробной информации. Похоже, вы очень хорошо разбираетесь в Qt. Вы случайно не написали книгу?
@A.Hendry Вам просто нужно прочитать документы Qt, там четко указано, что представляет собой каждый элемент и как они взаимодействуют.
Дополняющий вопрос. Я пытаюсь прочитать документы, чтобы понять, как имитировать щелчок мыши, открывающий меню.
qtbot.mouseClick(menubar, qt_api.QtCore.Qt.LeftButton)
не работает. Как мне это сделать?@A.Hendry Я думаю, вы знаете, что если вы передадите позицию, то будет использоваться центр виджета (в данном случае строка меню), с другой стороны, QMenu не отображается мгновенно, поэтому дайте ему задержку:
qtbot.mouseClick(menubar, QtCore.Qt.LeftButton, pos=rect.center())
qtbot.wait(3000)
@eyllansc facepalm! Я забыл аргумент позиции! Спасибо!! Вы супер полезны!
Извините, последний вопрос. Я знаю, что отнимаю у тебя много времени. Как мне теперь (1) перейти к пункту меню «Файл», который появляется под «кнопкой «Файл», и (2) щелкнуть по нему? Я куплю тебе еще две чашки кофе :)
@ A.Hendry Вы хотите нажать «Новый проект ...»?
Да сэр. Я хочу нажать «Новый проект...» и убедиться, что он делает то, что должен.
@A.Hendry Ниже приведен код: gist.github.com/eyllanesc/ded349044bf43dd79f8c43acb049b263, я удалю его через 1 или 2 дня.
@eyllansc А, это имеет смысл. Просто промойте и повторите тот же код, что и для строки меню. Спасибо!
Не могли бы вы добавить суть ответа, чтобы другие тоже могли извлечь из этого пользу?
@ A.Hendry Нет, так как это не вопрос вашего поста.
Понял и не переживай. Еще раз спасибо за вашу помощь!
@eyllansc Поскольку вы уже столкнулись с проблемой, хотите ли вы, чтобы я поместил вопросы в этих комментариях как отдельный вопрос SO, чтобы я мог дать вам баллы за ответ?
@ A.Hendry Нет, если у вас есть время, вы публикуете вопрос и публикуете ответ. Баллы банальные и с моей репутацией меньше.