Python 在QDialog reject()上优雅地终止QThread

Python 在QDialog reject()上优雅地终止QThread,python,multithreading,qt,pyqt,Python,Multithreading,Qt,Pyqt,我有一个QDialog,它创建一个QThread来执行一些工作,同时根据这里给出的结构保持UI的响应性:。但是,如果在线程仍在运行时调用reject()(由于用户按下cancel或关闭对话框),我会得到一个错误: QThread:在线程仍在运行时销毁 我希望工作者中的循环尽早中断,然后在后台进行一些清理(例如,清除一些队列,发出信号)。我已经用自己的“cancel”函数实现了这一点,但是如何让它与reject()配合使用(以及调用它的所有方法)?我不想让对话框在等待清理时阻塞——它应该在后台继续

我有一个QDialog,它创建一个QThread来执行一些工作,同时根据这里给出的结构保持UI的响应性:。但是,如果在线程仍在运行时调用reject()(由于用户按下cancel或关闭对话框),我会得到一个错误:

QThread:在线程仍在运行时销毁

我希望工作者中的循环尽早中断,然后在后台进行一些清理(例如,清除一些队列,发出信号)。我已经用自己的“cancel”函数实现了这一点,但是如何让它与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()

您的问题是:
self.thread
在关闭对话框或按下取消按钮后被Python释放,而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()

使用此方法是否可以让主应用程序在退出之前等待任何线程清理?我正在一个更大的应用程序(QGIS)中作为插件运行代码。@snorfalorpagus:我更新了我的帖子。我认为使用标志是停止线程的最安全的方法。
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()