前言:我看过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 交互。
QWait()
来延迟它们,因为在 QMessageBox
关闭之前它似乎不起作用,并且 time.sleep()
不起作用,因为它还会暂停 Qt 执行和渲染。我确信我做的很多事情都是错误的,因为我对 PyQt/Qt 没有太多经验,而且 PyQt 文档也不是那么好。如果我的代码设置方式使得自动化变得不可行,那么流程应该是什么样子的一些指示将非常受欢迎。即使只是通过对话框的笨拙方法(就像我尝试使用鼠标/键盘自动化库)在这一点上也会有所帮助 - 测试自动化是很好的东西,但如果不可能的话我可以没有它。谢谢!
编辑 2:删除了编辑 1,因为它对于理解问题没有太大帮助。 主要问题是,当主事件循环生成 QMessageBox 时,任何代码的执行都会冻结。我不确定如何更改应用程序代码以允许我的测试代码在 QMessageBox
处于活动状态时运行;然而,我需要做的就是在每个选项上单击“确定”(并验证文本,如果可能的话)。无论我实际上如何创建和关闭这些框,当主事件循环由于 QMessageBox 而被阻止时,我实际上似乎无法执行任何 Qt 代码,并且无限的 while 循环会导致应用程序挂起。
类似问题(使用 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)