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

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.

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.