6

ここに示す構造に基づいて、UI の応答性を維持しながら作業を行う QThread を作成する QDialog があります。完全な説明。ただし、スレッドがまだ実行されている間に 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()
4

1 に答える 1

10

あなたの問題は次のとおりself.threadです。ダイアログが閉じられた後、またはキャンセルボタンが押された後、Qtスレッドがまだ実行されている間にPythonによって解放されます。

このような状況を回避するために、そのスレッドに親を指定できます。例えば、


    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()

その後、PyQt ではなく Qt によって所有されるため、Qt によって正常に終了される前に GC によって収集されることはありません。実際、この方法では Qt が文句を言わないようにするだけで、問題を完全に解決することはできません。

スレッドを正常に終了するための一般的な方法は、フラグを使用してワーカー関数に停止を通知することです。
例えば:

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()
于 2013-04-14T22:59:45.057 に答える