I'm trying to establish a Python test methodology whereby a QApplication is
launched (and its event loop executed) in a secondary thread, while the
primary thread performs some "test scenario".
Summary: I'm unable to set this up, and it isn't clear why this isn't

Details: please consider the attached test file (executed with `pytest -vs

In this minimal test case, the actual test function body is empty. A
"fixture" function is used to set up the system, i.e. launch a thread to
construct the application showing only a "quit" button.

Consider these cases:

0.  run the above command, wait for the test passing being reported on the
console, then click the "quit" button. This appears to be working as

1. run the above command, but click the "quit" button immediately. Thus
results in the error message `QObject::killTimer: Timers cannot be stopped
from another thread`,
followed by a segmentation fault

2. rather than just "yielding" to the test function, yield the application
object (so the test function can interact with it). No matter when you
click the "quit" button, the result is the same as in 1.

3. call `h.app.quit()` after he `yield` (i.e. as soon as the test function
completes). Result: the application becomes unresponsive, but doesn't stop

Any idea why the above happens ?

Many thanks for any help you can offer !


      ...ich hab' noch einen Koffer in Berlin...
import threading
import time
import sys
import pytest

class Helper:
    def __init__(self):
        self.app = None

    def launch(self):
        from PyQt6.QtCore import QSize, Qt
        from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton

        class MainWindow(QMainWindow):
            def __init__(self):

                self.setWindowTitle("Test App")
                button = QPushButton("quit")

        self.app = QApplication(sys.argv)
        window = MainWindow()
        print('starting event loop')
        print('event loop done')

def application():
    h = Helper()
    thread = threading.Thread(target=h.launch)
    print('setup done')
    # case 0: wait for test to pass, then click "quit" button. test works as expected.
    # case 1: click "quit" button prior to test passing. test segfaults
    # case 2: replace the next line by "yield h.app". test segfaults
    # case 3: uncomment. test segfaults
    # h.app.quit()
    print('teardown done')

def test_app(application):
    print('running scenario')
Interest mailing list

Reply via email to