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

如何使用QTest模拟触发PySide2菜单栏Tool下的Save保存功能

问题描述

我正尝试使用QTest模拟保存功能(菜单栏Tool分类下的Save选项),但暂时没有找到可行的实现思路,相关代码如下:

主程序代码

#!/usr/bin/python
from PySide2.QtCore import Qt, QEvent
from PySide2.QtWidgets import QApplication
from PySide2.QtGui import (QTextCharFormat, QIcon, QKeySequence,
                           QBrush, QColor, QTextDocument, QFont,
                           QTextCursor, QTextBlockFormat, QFontDatabase)
from PySide2.QtWidgets import (QPlainTextEdit, QSizePolicy, QApplication, QLabel, QGridLayout, QMessageBox, QToolBar, QTextEdit, QCheckBox, QAction,
                               QTableWidget, QTableWidgetItem, QHeaderView, QMenu,
                               QWidget)
from PySide2.QtCore import Qt, QSize, QRegExp, QFile, QTextStream, QRect
from PySide2.QtWidgets import (QMainWindow, QVBoxLayout,
                               QPlainTextEdit, QGridLayout, QGroupBox,
                               QListWidget, QHBoxLayout, QLabel, QLineEdit,
                               QMenuBar, QPushButton, QMessageBox, QDialog,
                               QTextEdit, QVBoxLayout, QWidget, QFormLayout,
                               QCheckBox, QDialogButtonBox,
                               QTableWidget, QTableWidgetItem, QHeaderView)
import sys


class Window(QMainWindow):
    def __init__(self, app):
        super().__init__()


    def create_widget(self):
        self.create_menu()
        self.plain_textedit = QPlainTextEdit()
        self.plain_textedit.setMouseTracking(True)
        self.plain_textedit.viewport().installEventFilter(self)
        main_layout = QVBoxLayout()
        main_layout.addWidget(self.toolbar)
        main_layout.addWidget(self.plain_textedit)
        self.window = QWidget()
        self.window.setLayout(main_layout)
        self.setCentralWidget(self.window)


    def create_menu(self):
        """
        create menu bar into window
        """
        self.toolbar = QToolBar()
        self.toolbar.setStyleSheet("QToolBar {background: #ff8000}")
        self.addToolBar(Qt.TopToolBarArea, self.toolbar)
        menu = QMenu("&Tool", self)
        self.menuBar().addMenu(menu)
        self.actionSave = QAction(QIcon.fromTheme('document-save'),
                                  "&Save", self, shortcut=QKeySequence(Qt.CTRL+Qt.Key_W),
                                  triggered=self.save_data, enabled=True)
        self.toolbar.addAction(self.actionSave)
        menu.addAction(self.actionSave)
        menu.addSeparator()
    
    def save_data(self):
        pass
        
        
    def eventFilter(self, obj, event):
        if obj is self.plain_textedit.viewport():
            print ("plain_1")
        return super().eventFilter(obj, event)
       

def main():
    app = QApplication([])
    window = Window(app)
    window.create_widget()
    window.show()
    app.exec_()


if __name__ == "__main__":
    sys.exit(main())

界面示意图

界面示意图

测试代码

#!/usr/bin/python 
import os
env = os.environ
import unittest
import logging
from PySide2.QtWidgets import QApplication
from PySide2.QtTest import QTest
from PySide2.QtCore import Qt, QEvent
from debug2 import Window

class test(unittest.TestCase):
    def setUp(self):
        self.app = QApplication.instance()
        if self.app is None:
            self.app = QApplication([])

        self.window = Window(self.app)
        self.window.create_widget()
        self.window.show()
        #to open gui and helpful for debug
        #self.app.exec_()

    def tearDown(self):
        self.app.deleteLater()

    def test_save_data(self):
        actions = self.window.menuBar().actions()
        for action in actions:
            sub_commands = action.menu().actions()
            for sub_command in sub_commands:
                if sub_command.text() == "&Save":
                    print (dir(QTest))
                    QTest.mouseClick(self.window.window, Qt.LeftButton)
                #print (sub_command, sub_command.objectName(), sub_command.text())

if __name__ == '__main__':
    unittest.main()
解决方案

共有三种常用实现方案,可根据测试需求选择:

方案1:直接触发Save动作信号(最简洁)

菜单、工具栏、快捷键触发保存最终都会调用QAction的trigger()信号,直接调用该方法即可触发save_data,不需要模拟UI点击,测试逻辑稳定不易受界面布局影响。
修改后的test_save_data代码:

def test_save_data(self):
    save_action = None
    # 查找Save对应的QAction
    for action in self.window.menuBar().actions():
        if action.text() == "&Tool":
            for sub_action in action.menu().actions():
                if sub_action.text() == "&Save":
                    save_action = sub_action
                    break
            break
    # 校验是否找到目标动作
    self.assertIsNotNone(save_action, "未找到Tool菜单下的Save选项")
    # 触发保存动作
    save_action.trigger()
    # 此处可添加自定义断言,校验save_data执行后的逻辑是否符合预期

方案2:模拟快捷键触发

代码中已经给Save选项绑定了Ctrl+W快捷键,直接模拟用户按键即可触发,更贴近真实操作场景:

def test_save_data(self):
    # 模拟按下Ctrl+W快捷键
    QTest.keyClick(self.window, Qt.Key_W, Qt.ControlModifier)
    # 后续断言逻辑

方案3:模拟用户点击菜单流程

如果需要完全复现用户点击菜单栏→点击Tool→点击Save的完整操作,可使用该方案:

def test_save_data(self):
    # 查找Tool菜单动作
    tool_action = None
    for action in self.window.menuBar().actions():
        if action.text() == "&Tool":
            tool_action = action
            break
    self.assertIsNotNone(tool_action, "未找到Tool菜单")
    # 点击菜单栏上的Tool选项展开菜单
    tool_rect = self.window.menuBar().actionGeometry(tool_action)
    QTest.mouseClick(self.window.menuBar(), Qt.LeftButton, Qt.NoModifier, tool_rect.center())
    # 查找Save选项
    save_action = None
    for sub_action in tool_action.menu().actions():
        if sub_action.text() == "&Save":
            save_action = sub_action
            break
    self.assertIsNotNone(save_action, "未找到Save选项")
    # 点击展开菜单里的Save选项
    save_rect = tool_action.menu().actionGeometry(save_action)
    QTest.mouseClick(tool_action.menu(), Qt.LeftButton, Qt.NoModifier, save_rect.center())
    # 后续断言逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 17:06:01