PyQt4: How to pause a Thread until a signal is emitted?
Question: 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.


Answer: 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/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.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 time
    
    class 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.

