1

with pycharm i created a small gui with pyqt5 and want to test with pytest and qtbot.

I have a problem to click a menu_entry within the menu and I could need a hint.

I looked at this very helpful post: `pytestqt.mouseMove` to menubar menu widget moves to wrong place in the comments they spoke about the problem to click within in the menubar an entry. (gist.github.com/eyllanesc/ded349044bf43dd79f8c43acb049b263) But the solution code isn't available anymore.

menu 
`-> menu_entry1
`-> menu_entry2

Currently the test code looks like this:

def test_menubar_click(app, qtbot) -> None:
    app.show()
    file_menu = app.ui.menuECIES
    menubar = app.ui.menubar
    qtbot.add_widget(menubar)

    action_rect = menubar.actionGeometry(file_menu.menuAction())
    qtbot.wait(1000)
    qtbot.mouseMove(menubar, action_rect.center())
    qtbot.wait(1000)
    qtbot.mouseClick(menubar, QtCore.Qt.LeftButton, pos=action_rect.center())
    qtbot.wait(1000)
    qtbot.mouseMove(file_menu, action_rect.center())
    qtbot.wait(1000)
    qtbot.mouseClick(file_menu, QtCore.Qt.LeftButton)
    qtbot.wait(1000)

Added Code for minimal, reproducible Example:

main.py

import sys

from qtpy import QtWidgets

from ui.mainwindow import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setWindowTitle("Test Tool by TTM - v0.1")

        self.shortcut_exit = QtWidgets.QShortcut("Alt+Q", self)
        self.shortcut_exit.activated.connect(self.close)
        self.ui.actionGenerate_new.triggered.connect(self.print_message)

    def print_message(self):
        print("Menu generate new clicked")
# Hauptprogramm

def main_GUI():
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    return app, window

if __name__ == "__main__":
    app, window = main_GUI()
    rc = app.exec_()
    print("App end is exit code {}".format(rc))
    sys.exit(rc)

ui/mainwindow.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'ui/mainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 20))
        self.menubar.setObjectName("menubar")
        self.menuECIES = QtWidgets.QMenu(self.menubar)
        self.menuECIES.setObjectName("menuECIES")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionGenerate_new = QtWidgets.QAction(MainWindow)
        self.actionGenerate_new.setObjectName("actionGenerate_new")
        self.actionExit = QtWidgets.QAction(MainWindow)
        self.actionExit.setObjectName("actionExit")
        self.menuECIES.addAction(self.actionGenerate_new)
        self.menuECIES.addSeparator()
        self.menuECIES.addAction(self.actionExit)
        self.menubar.addAction(self.menuECIES.menuAction())

        self.retranslateUi(MainWindow)
        self.actionExit.triggered.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.menuECIES.setTitle(_translate("MainWindow", "ECIE&S"))
        self.actionGenerate_new.setText(_translate("MainWindow", "Generate new"))
        self.actionExit.setText(_translate("MainWindow", "E&xit!"))

test_main.py

import os
import sys

import pytest
from PyQt5 import QtCore, QtGui, QtTest, QtWidgets
from pytestqt.plugin import QtBot

from main import MainWindow

@pytest.fixture
def app(qtbot):
    window = MainWindow()
    qtbot.addWidget(window)
    return window

def test_menubar_click(app, qtbot) -> None:
    # Arrange
    app.show()
    file_menu = app.ui.menuECIES
    menubar = app.ui.menubar
    qtbot.add_widget(menubar)
    # Act
    action_rect = menubar.actionGeometry(file_menu.menuAction())
    qtbot.wait(1000)
    qtbot.mouseMove(menubar, action_rect.center())
    qtbot.wait(1000)
    qtbot.mouseClick(menubar, QtCore.Qt.LeftButton, pos=action_rect.center())
    qtbot.wait(1000)
    qtbot.mouseMove(file_menu, action_rect.center())
    qtbot.wait(1000)

thomas.tm
  • 19
  • 4

1 Answers1

1

TL;DR

  1. Upgrade to PyQt6 or PySide6 (mouseMove in PyQt5 has a bug)
  2. You need to grab the submenu item geometries and click those.

Answer

This is the author of the post and solution to

pytestqt.mouseMove to menubar menu widget moves to wrong place

I updated the answer because what was originally provided was not fully accurate. Please see the new answer. This should help you.

However, one thing you are missing is that you need to grab the geometry from each submenu item:

action_rect = menubar.actionGeometry(file_menu.menuAction())

This is actually the "menuECIES" button geometry on your menubar, but not any of the submenu items beneath it. You need to get the geometry for actionGenerate_new and actionExit.

actionGenerate_new_rect = file_menu.actionGeometry(actionGenerate_new)
actionExit_rect = file_menu.actionGeometry(actionExit)

and then click those.

Your test function would look like this:

def test_menubar_click(app, qtbot) -> None:
    # Arrange
    app.show()
    file_menu = app.ui.menuECIES
    menubar = app.ui.menubar
    actionGenerate_new = app.ui.actionGenerate_new
    actionExit = app.ui.actionExit

    qtbot.add_widget(menubar)

    file_rect = menubar.actionGeometry(file_menu.menuAction())
    actionGenerate_new_rect = file_menu.actionGeometry(actionGenerate_new)
    actionExit_rect = file_menu.actionGeometry(actionExit)

    # Act
    qtbot.wait(1000)
    qtbot.mouseMove(menubar, file_rect.center())
    qtbot.wait(1000)
    qtbot.mouseClick(menubar, QtCore.Qt.LeftButton, pos=file_rect.center())
    qtbot.wait(1000)
    qtbot.mouseMove(file_menu, actionGenerate_new_rect.center())
    qtbot.wait(1000)
    qtbot.mouseMove(file_menu, actionExit_rect.center())
    qtbot.wait(1000)

You're not asserting anything though, so the test will pass without doing anything.

adam.hendry
  • 4,458
  • 5
  • 24
  • 51