我有一个QDialog根据这里给出的结构创建一个QThread来做一些工作,同时保持UI响应:How To Really, Truly Use QThreads; The Full Explanation。但是,如果在线程仍在运行时调用reject()(由于用户按下取消或关闭对话框),我会收到错误:
QThread:在线程仍在运行时被销毁
我想要发生的是工作中的循环提前中断,然后在后台进行一些清理(例如清除一些队列,发出信号)。我已经设法用我自己的“取消”功能来做到这一点,但是如何让它与reject()(以及它可以调用的所有方法)很好地配合?我不希望对话框阻止等待清理 - 它应该继续在后台运行,然后优雅地退出。
请参阅下面显示问题的示例代码。任何帮助将不胜感激。
#!/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()
答案 0 :(得分:8)
您的问题是:在关闭对话框或按下取消按钮后,Python释放self.thread
,而Qt线程仍在运行。
要避免这种情况,您可以为该线程指定父级。例如,
def run(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()
然后它将由Qt而不是PyQt拥有,因此在由Qt优雅终止之前,GC不会收集它。 实际上,这种方法只是让Qt不抱怨并且不能完全解决问题。
要正常终止线程,常见的方法是使用一个标志来通知worker函数停止 例如:
class Worker(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
def process(self):
# dummy worker process
self.flag = False
for n in range(0, 10):
if self.flag:
print 'stop'
break
print 'process {}'.format(n)
time.sleep(0.5)
self.finished.emit()
finished = QtCore.pyqtSignal()
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
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)
QtCore.QObject.connect(self, QtCore.SIGNAL('rejected()'), self.stop_worker)
self.show()
self.raise_()
def stop_worker(self):
print 'stop'
self.worker.flag = True
def run(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()