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. However, if reject() is called (due to the user pressing cancel or closing the dialog) while the thread is still running I get an error:
QThread: Destroyed while thread is still running
What I'd like to happen is for the loop in the worker to break early, then do some cleanup in the background (e.g. clear some queues, emit a signal). I've managed to do this with my own "cancel" function, but how do I get it to play nicely with reject() (and all the many ways it could be called)? I don't want the dialog to block waiting for the cleanup - it should just keep running in the background, then exit gracefully.
See sample code below which exhibits the problem. Any help would be greatly appreciated.
#!/usr/bin/env python
from PyQt4 import QtCore, QtGui
import sys
import time
class Worker(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
def process(self):
# dummy worker process
for n in range(0, 10):
print 'process {}'.format(n)
time.sleep(0.5)
self.finished.emit()
finished = QtCore.pyqtSignal()
class Dialog(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
self.init_ui()
def init_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)
self.show()
self.raise_()
def run(self):
# start the worker thread
self.thread = QtCore.QThread()
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()
def main():
app = QtGui.QApplication(sys.argv)
dlg = Dialog()
ret = dlg.exec_()
if __name__ == '__main__':
main()