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

使用pytest-qt的QtBot.waitSignal()测试QDialog失败问题排查

问题:使用pytest-qt的waitSignal测试QDialog时超时失败

我在用pytest-qt的waitSignal()方法测试QDialog时遇到了超时问题,以下是最小复现代码:

import sys
from PyQt6.QtWidgets import (
    QApplication, QDialog, QGridLayout, QPushButton
)

def get_dialog(app):
    dialog = QDialog()
    dialog.resize(300,200)
    dialog.setWindowTitle("Test dialog")
    layout = QGridLayout()
    button1 = QPushButton("&Ok", dialog)
    def ok_pressed():
         dialog.done(0)
    button1.clicked.connect(ok_pressed)
    layout.addWidget(button1, 0, 0)
    button2 = QPushButton("&Cancel", dialog)
    def cancel_pressed():
         dialog.done(1)
    button2.clicked.connect(cancel_pressed)
    layout.addWidget(button2, 0, 1)
    dialog.setLayout(layout)
    dialog.open()
    return dialog, button1

def main():
    app = QApplication(sys.argv)
    get_dialog(app)
    app.exec()

def test_dialog(qtbot, qapp):
    app = qapp
    dialog, button1 = get_dialog(app)
    button1.click()
    with qtbot.waitSignal(dialog.finished, timeout=2000):
        pass
    assert True

if __name__ == '__main__':
    main()

运行测试后返回超时错误:

$ pytest t.py
============================================================================ test session starts ============================================================================
platform linux -- Python 3.10.4, pytest-7.3.2, pluggy-1.0.0
PyQt6 6.5.1 -- Qt runtime 6.5.0 -- Qt compiled 6.5.1
rootdir: /home/hakon/test/python/pytest-qt/qdialog-cancel/pre
plugins: mock-3.11.1, qt-4.2.0, anyio-3.7.1
collected 1 item                                                                                                                                                            

t.py F                                                                                                                                                               [100%]

================================================================================= FAILURES ==================================================================================
________________________________________________________________________________ test_dialog ________________________________________________________________________________

qtbot = <pytestqt.qtbot.QtBot object at 0x7fea78d47a30>, qapp = <PyQt6.QtWidgets.QApplication object at 0x7fea78d5c1f0>

    def test_dialog(qtbot, qapp):
        app = qapp
        dialog, button1 = get_dialog(app)
        button1.click()
>       with qtbot.waitSignal(dialog.finished, timeout=2000):
E       pytestqt.exceptions.TimeoutError: Signal finished(int) not emitted after 2000 ms

t.py:36: TimeoutError
--------------------------------------------------------------------------- Captured Qt messages ----------------------------------------------------------------------------
QtWarningMsg: Could not connect "org.freedesktop.IBus" to globalEngineChanged(QString)
========================================================================== short test summary info ==========================================================================
FAILED t.py::test_dialog - pytestqt.exceptions.TimeoutError: Signal finished(int) not emitted after 2000 ms
============================================================================= 1 failed in 2.02s =============================================================================

改用waitUntil()方法则可以正常运行:

def test_dialog(qtbot, qapp):
    app = qapp
    dialog, button1 = get_dialog(app)
    dialog_done = False
    def dialog_done_cb():
        nonlocal dialog_done
        dialog_done = True
    dialog.finished.connect(dialog_done_cb)
    button1.click()
    qtbot.waitUntil(lambda: dialog_done)
    assert True

问题原因与解决方法

问题出在调用顺序错误:你先触发了按钮点击(此时dialog.finished信号已经发出),之后才进入waitSignal()上下文开始监听信号——信号早已发送完毕,waitSignal()自然捕获不到,最终超时。

waitSignal()的正确用法是先启动信号监听,再执行触发信号的操作。修正后的测试代码如下:

def test_dialog(qtbot, qapp):
    app = qapp
    dialog, button1 = get_dialog(app)
    # 先进入waitSignal上下文监听信号,再触发按钮点击
    with qtbot.waitSignal(dialog.finished, timeout=2000):
        button1.click()
    assert True

这样修改后,waitSignal()会先注册信号监听器,再执行按钮点击操作触发dialog.finished信号,就能正常捕获到信号,不会出现超时错误。

内容的提问来源于stack exchange,提问作者Håkon Hægland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 08:35:53