14

これは、以前に投稿した前の質問のフォローアップの質問です。問題は、Qthread をサブクラス化するのではなく、QObject を作成して QThread に移動するという推奨される方法を使用する場合に、GUI から QThread を停止 (終了 | 終了 | 終了) する方法です。以下は実際の例です。GUI と Qthread を開始し、Qthread に GUI を更新させることができます。しかし、私はそれを止めることはできません。qthread のいくつかのメソッド (quit()、exit()、さらには terminate()) を試してみましたが、役に立ちませんでした。大変助かります。

完全なコードは次のとおりです。

import time, sys
from PyQt4.QtCore  import *
from PyQt4.QtGui import * 

class SimulRunner(QObject):
    'Object managing the simulation'

    stepIncreased = pyqtSignal(int, name = 'stepIncreased')
    def __init__(self):
        super(SimulRunner, self).__init__()
        self._step = 0
        self._isRunning = True
        self._maxSteps = 20

    def longRunning(self):
        while self._step  < self._maxSteps  and self._isRunning == True:
            self._step += 1
            self.stepIncreased.emit(self._step)
            time.sleep(0.1)

    def stop(self):
        self._isRunning = False

class SimulationUi(QDialog):
    'PyQt interface'

    def __init__(self):
        super(SimulationUi, self).__init__()

        self.goButton = QPushButton('Go')
        self.stopButton = QPushButton('Stop')
        self.currentStep = QSpinBox()

        self.layout = QHBoxLayout()
        self.layout.addWidget(self.goButton)
        self.layout.addWidget(self.stopButton)
        self.layout.addWidget(self.currentStep)
        self.setLayout(self.layout)

        self.simulRunner = SimulRunner()
        self.simulThread = QThread()
        self.simulRunner.moveToThread(self.simulThread)
        self.simulRunner.stepIncreased.connect(self.currentStep.setValue)


        self.stopButton.clicked.connect(simulThread.qui)  # also tried exit() and terminate()
        # also tried the following (didn't work)
        # self.stopButton.clicked.connect(self.simulRunner.stop)
        self.goButton.clicked.connect(self.simulThread.start)
        self.simulThread.started.connect(self.simulRunner.longRunning)
        self.simulRunner.stepIncreased.connect(self.current.step.setValue)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    simul = SimulationUi()
    simul.show()
    sys.exit(app.exec_())
4

3 に答える 3

19

私はずっと前にそれを知っていますが、同じ問題に遭遇しました。

また、これを行うための適切な方法を探しています。最後に、それは簡単でした。アプリケーションを終了するときは、タスクを停止する必要があり、スレッドはその quit メソッドの呼び出しを停止する必要があります。下部の stop_thread メソッドを参照してください。そして、スレッドが終了するまで待つ必要があります。そうしないと、 exit で QThread: Destroyed while thread is still running というメッセージが表示されます

(また、pyside を使用するようにコードを変更しました)

import time, sys
from PySide.QtCore  import *
from PySide.QtGui import *

class Worker(QObject):
    'Object managing the simulation'

    stepIncreased = Signal(int)

    def __init__(self):
        super(Worker, self).__init__()
        self._step = 0
        self._isRunning = True
        self._maxSteps = 20

    def task(self):
        if not self._isRunning:
            self._isRunning = True
            self._step = 0

        while self._step  < self._maxSteps  and self._isRunning == True:
            self._step += 1
            self.stepIncreased.emit(self._step)
            time.sleep(0.1)

        print "finished..."

    def stop(self):
        self._isRunning = False


class SimulationUi(QDialog):
    def __init__(self):
        super(SimulationUi, self).__init__()

        self.btnStart = QPushButton('Start')
        self.btnStop = QPushButton('Stop')
        self.currentStep = QSpinBox()

        self.layout = QHBoxLayout()
        self.layout.addWidget(self.btnStart)
        self.layout.addWidget(self.btnStop)
        self.layout.addWidget(self.currentStep)
        self.setLayout(self.layout)

        self.thread = QThread()
        self.thread.start()

        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.worker.stepIncreased.connect(self.currentStep.setValue)

        self.btnStop.clicked.connect(lambda: self.worker.stop())
        self.btnStart.clicked.connect(self.worker.task)

        self.finished.connect(self.stop_thread)

    def stop_thread(self):
        self.worker.stop()
        self.thread.quit()
        self.thread.wait()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    simul = SimulationUi()
    simul.show()
    sys.exit(app.exec_())
于 2015-08-21T10:36:53.343 に答える
5

私の元の質問は、実際には 2 つの質問を 1 つにまとめたものであることがわかりました。メインのスレッドからセカンダリ スレッドを停止するには、次の 2 つのものが必要です。

  1. メインスレッドからセカンダリスレッドまで通信できる

  2. 適切なシグナルを送信してスレッドを停止する

(2) は解決できませんでしたが、(1) の解決方法はわかりました。これにより、元の問題の回避策が得られました。スレッドを停止する代わりに、スレッドの処理(longRunning()メソッド)を停止できます。

問題は、セカンダリ スレッドが独自のイベント ループを実行する場合にのみ、シグナルに応答できることです。通常の Qthread (私のコードが使用したもの) はそうではありません。ただし、その効果のために QThread をサブクラス化するのは簡単です。

class MyThread(QThread):
    def run(self):
        self.exec_()

self.simulThread = MyThread()オリジナルの代わりに私のコードで使用されますself.simulThread = Qthread()。これにより、セカンダリ スレッドがイベント ループを実行することが保証されます。しかし、それだけでは十分ではありませんでした。メソッドにはlongRunning()、メイン スレッドからのイベントを実際に処理する機会が必要です。this SO answerの助けを借りQApplication.processEvent()て、メソッドに a を追加するだけでlongRunning()、セカンダリスレッドにそのようなチャンスが与えられることがわかりました。スレッド自体を停止する方法がわかりませんが、セカンダリ スレッドで実行されている処理を停止できるようになりました。

まとめます。私の longRunning メソッドは次のようになります。

def longRunning(self):
    while self._step  < self._maxSteps  and self._isRunning == True:
        self._step += 1
        self.stepIncreased.emit(self._step)
        time.sleep(0.1)
        QApplication.processEvents() 

私のGUIスレッドには、上記のQThreadサブクラスに加えて、次の3行があります。

    self.simulThread = MyThread()
    self.simulRunner.moveToThread(self.simulThread)
    self.stopButton.clicked.connect(self.simulRunner.stop)

コメント大歓迎です!

于 2013-04-29T15:31:46.777 に答える