Skip to content Skip to sidebar Skip to footer

Terminating Qthread Gracefully On Qdialog Reject()

I have a QDialog which creates a QThread to do some work while keeping the UI responsive, based on the structure given here: How To Really, Truly Use QThreads; The Full Explanation

Solution 1:

Your problem is: self.thread is freed by Python after the dialog is closed or the cancel button is pressed, while Qt thread is still running.

To avoid such situation, you can designate a parent to that thread. For example,

defrun(self):
        # start the worker thread
        self.thread = QtCore.QThread(self)
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        QtCore.QObject.connect(self.thread, QtCore.SIGNAL('started()'), self.worker.process)
        QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.thread.quit)
        QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.worker.deleteLater)
        QtCore.QObject.connect(self.thread, QtCore.SIGNAL('finished()'), self.thread.deleteLater)
        self.thread.start()

Then it will be owned by Qt instead of PyQt and hence won't be collected by GC before it is terminated by Qt gracefully. Actually, this method just lets Qt not complain and doesn't solve the problem completely.

To terminate a thread gracefully, the common approach is using a flag to inform the worker function to stop. For example:

classWorker(QtCore.QObject):
    def__init__(self):
        QtCore.QObject.__init__(self)

    defprocess(self):
        # dummy worker process
        self.flag = Falsefor n inrange(0, 10):
            if self.flag:
                print'stop'breakprint'process {}'.format(n)
            time.sleep(0.5)
        self.finished.emit()

    finished = QtCore.pyqtSignal()

classDialog(QtGui.QDialog):
    def__init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)
        self.init_ui()

    definit_ui(self):
        self.layout = QtGui.QVBoxLayout(self)
        self.btn_run = QtGui.QPushButton('Run', self)
        self.layout.addWidget(self.btn_run)
        self.btn_cancel = QtGui.QPushButton('Cancel', self)
        self.layout.addWidget(self.btn_cancel)

        QtCore.QObject.connect(self.btn_run, QtCore.SIGNAL('clicked()'), self.run)
        QtCore.QObject.connect(self.btn_cancel, QtCore.SIGNAL('clicked()'), self.reject)

        QtCore.QObject.connect(self, QtCore.SIGNAL('rejected()'), self.stop_worker)

        self.show()
        self.raise_()

    defstop_worker(self):
        print'stop'
        self.worker.flag = Truedefrun(self):
        # start the worker thread
        self.thread = QtCore.QThread(self)
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        QtCore.QObject.connect(self.thread, QtCore.SIGNAL('started()'), self.worker.process)
        QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.thread.quit)
        QtCore.QObject.connect(self.worker, QtCore.SIGNAL('finished()'), self.worker.deleteLater)
        QtCore.QObject.connect(self.thread, QtCore.SIGNAL('finished()'), self.thread.deleteLater)
        self.thread.start()

Post a Comment for "Terminating Qthread Gracefully On Qdialog Reject()"