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

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


Use a QWaitCondition from the QtCore module. Using a mutex lock, you set the background thread to wait/sleep until the foreground thread wakes it back up. Then it will continue doing its work from there.

#!/usr/bin/python3import sysfrom 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.mutex = QMutex()        self.cond = QWaitCondition()        self.mthread = QThread()  # New thread to run the Measurement Engine        self.worker = MeasurementEngine(self.mutex, self.cond)  # 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    # since this is a slot, it will always get run in the event loop in the main thread    @pyqtSlot(str)    def showRslt(self, mystr):        self.rsltFiled.setText(mystr)        msgBox = QMessageBox(parent=self)        msgBox.setText("Close this dialog to continue to Phase 2.")        msgBox.exec_()        self.cond.wakeAll()def main():    win = MainWindow()    win.run()if __name__ == '__main__':    main()

And:

from PyQt4.QtCore import *import timeclass MeasurementEngine(QObject):    measure_msg = pyqtSignal(str)    def __init__(self, mutex, cond):        QObject.__init__(self)  # Don't forget to call base class constructor        self.mtx = mutex        self.cond = cond    @pyqtSlot()    def run(self):        # NOTE: do work for phase 1 here        self.measure_msg.emit('phase1')        self.mtx.lock()        try:            self.cond.wait(self.mtx)            # NOTE: do work for phase 2 here            self.measure_msg.emit('phase2')        finally:            self.mtx.unlock()

Your timing is a little bit off in all this though. You create the app and start the thread before you even show your window. Thus, the message box will pop up before the main window even pops up. To get the right sequence of events, you should start your thread as part of the run method of your MainWindow, after you have already made the main window visible. If you want the wait condition to be separate from the setting of the messages, you may need a separate signal and slot to deal with that.


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.


Your thread can emit a signal to the main window to show the dialog.If you don't want to close the thread while the dialog is open, the thread could enter a while loop for waiting. In the while loop it can continuously check a variable which the main thread can set to true after the dialog is finished.This might not be the cleanest solution, but it should work.

To clarify my answer a bit, I added some pseudo code. What you have to care about is how you share the dialog_closed variable. You could e.g. use a member variable of the thread class.

Thread:emit_signaldialog_closed = Falsewhile not dialog_closed:   passgo_on_with_processingMainThread:def SignalRecieved():   open_dialog   dialog_closed = True