如何在 PyQt 中使用 QTest 访问/处理 QMessageBox 实例?

问题描述 投票:0回答:1

前言:我看过this非常相似的帖子,以及其他一些帖子。不幸的是,PyQt6 绑定似乎不能很好地映射到 PySide / Qt(C++) 库,因此我无法使用这些问题中的解决方案。

我正在为我在 PyQt6 中构建的应用程序构建一个测试库。我的测试经常需要与 QMessageBox 交互,但是,每当 QMessageBox 生成时,任何 QTest 函数的执行就会停止。当 QMessageBox 生成时,使用静态函数 API (

msg = QMessageBox.critical(...)
) 以及基于属性的 API 中的
show()
exec()
(
msg = QMessageBox()
msg.setText(...)
msg.show()),整个程序似乎会被阻塞。 
)。我只在主线程中生成它们,因为从 QThread 中这样做似乎会导致未定义的行为。

我不知道如何访问活动的 QMessageBox。我的所有小部件都存储在布局中,布局使用访问布局内多个对象的方法,例如:

#for context, "repack" refers to 'packing' files into an archive
class RepackTray(QGridLayout):
    def __init__(self):
        super().__init__()
        self.inputDirPath = QLineEdit()
        self.repackButton = QPushButton("Start!")
        self.repackButton.clicked.connect(lambda:self.repackTargetDir(self.repackButton))
    
    def repackTargetDir(self, button: QPushButton):
        sourceDir = self.inputDirPath.text()
        ...
        #pre-repack validation logic, for example, can also spawn QMessageBoxes like so:
        if not valid(sourceDir):
             msg = QMessageBox.critical(None, "Error!", "Directory is not valid!")
        #multiple QMessageBoxes can be spawned at once in a success case
     
        #actual repack call takes place in a QThread, which returns 
        #results in a signal connected to handleRepackResultInfo()

    def handleRepackResultInfo(self, result):
        #specifically, I'm trying to click the default button on this QMessageBox
        #neither method works, obviously used separately
        #method 1 - self.messageBox doesn't exist to the tester
        self.messageBox = QMessageBox()
        self.messageBox.setText(result.text)
        self.messageBox.exec()
        #method 2 - test code can't access the QMessageBox later
        msg = QMessageBox.information(None, "Title", result.text)

我用来自动化测试的函数如下所示:

app = QApplication(sys.argv)

def clientGUILib()
    ...
    #defines self.repackPanel as the RepackTray instance, works in other functions
    ...
    def start_repack(self):
        QTest.mouseClick(self.repackPanel.repackButton, Qt.MouseButton.LeftButton)
        #repack works properly, but then QMessageBox appears
        #Now what?

我似乎无法弄清楚如何从这里与 QMessageBox 交互。

  • 使用方法1,python无法识别属性self.repackpanel.messageBox
  • 使用方法2,我还没有找到一种方法来实际获取活动的QMessageBox
    • 我尝试过app.activeModalWidget()、app.activePopupWidget()、app.activeWindow()、app.focusWidget()、app.topLevelWidgets();在 QMessageBox 关闭之前,它们都不会触发。
  • 我什至尝试使用外部库在鼠标捕捉到默认按钮或按 Enter 键后单击。这些没有任何效果,但我不确定是否是因为它们发射得太快 - 我无法使用
    QWait()
    来延迟它们,因为在
    QMessageBox
    关闭之前它似乎不起作用,并且
    time.sleep()
    不起作用,因为它还会暂停 Qt 执行和渲染。

我确信我做的很多事情都是错误的,因为我对 PyQt/Qt 没有太多经验,而且 PyQt 文档也不是那么好。如果我的代码设置方式使得自动化变得不可行,那么流程应该是什么样子的一些指示将非常受欢迎。即使只是通过对话框的笨拙方法(就像我尝试使用鼠标/键盘自动化库)在这一点上也会有所帮助 - 测试自动化是很好的东西,但如果不可能的话我可以没有它。谢谢!

编辑 2:删除了编辑 1,因为它对于理解问题没有太大帮助。 主要问题是,当主事件循环生成 QMessageBox 时,任何代码的执行都会冻结。我不确定如何更改应用程序代码以允许我的测试代码在 QMessageBox

 处于活动状态时运行;然而,我需要做的就是在每个选项上单击“确定”(并验证文本,如果可能的话)。无论我实际上如何创建和关闭这些框,当主事件循环由于 QMessageBox 而被阻止时,我实际上似乎无法执行
任何 Qt 代码,并且无限的 while 循环会导致应用程序挂起。

qt pyqt automated-tests pyqt6
1个回答
0
投票
通过查找

类似问题(使用 PyQt5 和不同的测试框架)解决了我自己的问题。这个想法是安排对获取 activewindow()

 的函数的调用,然后如果该窗口是 
QMessageBox
,请单击默认按钮。

对于后代,这是对我有用的测试代码:

#Within testing class declaration def close_active_modal(self): w = QApplication.activeWindow() if isinstance(w, QMessageBox): closeBtn = w.defaultButton() QTest.mouseClick(closeBtn, Qt.MouseButton.LeftButton) def start_repack(self, delay_time=1): #Must be scheduled to occur AFTER the messagebox appears threading.Timer(delay_time, self.close_active_modal).start() QTest.mouseClick(self.repackPanel.repackButton, Qt.MouseButton.LeftButton)
    
© www.soinside.com 2019 - 2024. All rights reserved.