You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用pytest-qt测试Qt菜单?触发QAction遇AttributeError

Qt菜单Action的pytest-qt测试解决方案

问题场景

基于Qt实现的MainWindow类通过create_menu_bar方法创建了菜单,包含顶级Action(sheet_one)和下拉菜单(sheet_five_menu),但使用pytest-qt模拟点击时多次触发AttributeError,尝试findChild、直接访问属性、调用create_menu_bar后操作等方法均失败。

错误原因分析

  1. findChild查找失败:创建Action时未设置objectName,Qt的findChild默认按对象名称(而非显示文本)查找,因此返回None。
  2. 直接访问属性失败:sheet_one是create_menu_bar方法内的局部变量,未注册为MainWindow的实例属性。
  3. 调用create_menu_bar后操作失败:该方法无返回值(默认返回None),因此menu变量为None,不存在trigger方法。

正确测试方法

方法一:给Action设置objectName,用findChild查找

修改原代码,给需要测试的Action添加唯一objectName:

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

    def create_menu_bar(self):
        sheet_one = QtWidgets.QAction("sheet_one", self)
        sheet_one.setObjectName("action_sheet_one")  # 添加对象名称
        sheet_one.triggered.connect(lambda: self.load_df(sheet_name="sheet one"))

        # 下拉菜单的Action同理设置objectName
        sheet_five_one = QtWidgets.QAction("sheet five one", self)
        sheet_five_one.setObjectName("action_sheet_five_one")
        sheet_five_two = QtWidgets.QAction("sheet five two", self)
        sheet_five_two.setObjectName("action_sheet_five_two")

        menu = self.menuBar()
        menu.addAction(sheet_one)
        sheet_five_menu = menu.addMenu("&sheet five")
        sheet_five_menu.addAction(sheet_five_one)
        sheet_five_menu.addAction(sheet_five_two)

测试代码中通过findChild定位并触发Action:

def test_menu_actions(qtbot):
    window = MainWindow()
    qtbot.addWidget(window)

    # 触发顶级菜单的sheet_one
    sheet_one_action = window.findChild(QtWidgets.QAction, "action_sheet_one")
    assert sheet_one_action is not None
    sheet_one_action.trigger()
    # 可添加断言验证load_df是否被正确调用(如结合unittest.mock)

    # 触发下拉菜单中的sheet_five_one
    sheet_five_one_action = window.findChild(QtWidgets.QAction, "action_sheet_five_one")
    assert sheet_five_one_action is not None
    sheet_five_one_action.trigger()

方法二:遍历菜单树查找Action(无需修改原代码)

通过递归遍历菜单栏的Action和子菜单,按显示文本定位目标:

def test_menu_actions_by_text(qtbot):
    window = MainWindow()
    qtbot.addWidget(window)

    # 递归查找指定文本的Action
    def find_action_by_text(menu, target_text):
        for action in menu.actions():
            if action.text() == target_text:
                return action
            # 检查是否为子菜单,递归查找
            sub_menu = action.menu()
            if sub_menu:
                found_action = find_action_by_text(sub_menu, target_text)
                if found_action:
                    return found_action
        return None

    # 触发顶级菜单的sheet_one
    sheet_one_action = find_action_by_text(window.menuBar(), "sheet_one")
    assert sheet_one_action is not None
    sheet_one_action.trigger()

    # 触发下拉菜单中的sheet_five_one
    sheet_five_one_action = find_action_by_text(window.menuBar(), "sheet five one")
    assert sheet_five_one_action is not None
    sheet_five_one_action.trigger()

方法三:将Action设为实例属性(直接访问)

修改原代码,把需要测试的Action注册为MainWindow的实例属性:

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

    def create_menu_bar(self):
        # 将Action设为实例属性
        self.sheet_one = QtWidgets.QAction("sheet_one", self)
        self.sheet_one.triggered.connect(lambda: self.load_df(sheet_name="sheet one"))

        self.sheet_five_one = QtWidgets.QAction("sheet five one", self)
        self.sheet_five_two = QtWidgets.QAction("sheet five two", self)

        menu = self.menuBar()
        menu.addAction(self.sheet_one)
        sheet_five_menu = menu.addMenu("&sheet five")
        sheet_five_menu.addAction(self.sheet_five_one)
        sheet_five_menu.addAction(self.sheet_five_two)

测试代码直接访问实例属性触发Action:

def test_menu_actions_direct(qtbot):
    window = MainWindow()
    qtbot.addWidget(window)

    window.sheet_one.trigger()
    window.sheet_five_one.trigger()

内容的提问来源于stack exchange,提问作者Willy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:10:34