Search code examples
pythonmultithreadingpyqt4interruptmessagebox

PyQt4: How to pause a Thread until a signal is emitted?


I have the following pyqtmain.py:

#!/usr/bin/python3
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from pyqtMeasThread import *


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        self.qt_app = QApplication(sys.argv)
        QMainWindow.__init__(self, parent)

        buttonWidget = QWidget()
        rsltLabel = QLabel("Result:")
        self.rsltFiled = QLineEdit()
        self.buttonStart = QPushButton("Start")

        verticalLayout = QVBoxLayout(buttonWidget)
        verticalLayout.addWidget(rsltLabel)
        verticalLayout.addWidget(self.rsltFiled)
        verticalLayout.addWidget(self.buttonStart)

        butDW = QDockWidget("Control", self)
        butDW.setWidget(buttonWidget)
        self.addDockWidget(Qt.LeftDockWidgetArea, butDW)

        self.mthread = QThread()  # New thread to run the Measurement Engine
        self.worker = MeasurementEngine()  # Measurement Engine Object

        self.worker.moveToThread(self.mthread)
        self.mthread.finished.connect(self.worker.deleteLater)  # Cleanup after thread finished

        self.worker.measure_msg.connect(self.showRslt)

        self.buttonStart.clicked.connect(self.worker.run)

        # Everything configured, start the worker thread.
        self.mthread.start()

    def run(self):
        """ Show the window and start the event loop """
        self.show()
        self.qt_app.exec_()  # Start event loop

    @pyqtSlot(str)
    def showRslt(self, mystr):
        self.rsltFiled.setText(mystr)


def main():
    win = MainWindow()
    win.run()


if __name__ == '__main__':
    main()

And another thread script performing the actual measurement:

from PyQt4.QtCore import *
import time

class MeasurementEngine(QObject):
    measure_msg = pyqtSignal(str)
    def __init__(self):
        QObject.__init__(self)  # Don't forget to call base class constructor

    @pyqtSlot()
    def run(self):
        self.measure_msg.emit('phase1')
        time.sleep(2) # here I would like to make it as an interrupt
        self.measure_msg.emit('phase2')

What this code does now is that after the Start button is pressed, the function run in the thread will be executed. However, actually in the function run, there are two phases of the measurement. Right now I used an time delay.

But what I would like to implement actually is that after the 'phase1' measurement is done. A message box will be popped up, and at the same time, the thread will be paused/held. Until the user closed the message box, then the thread function will be resumed.


Solution

  • You can't display a QDialog from within a QThread. All GUI related stuff must be done in the GUI thread (the one that created the QApplication object). What you could do is to use 2 QThread:

    • 1st: perform phase1. You can connect the finished signal of this QThread to a slot in the QMainWindow that will display the popup (using QDialog.exec_() so it will be modal).
    • 2nd: perform phase2. You create the QThread after the popup shown here above has been closed.