如何从GUI停止QThread

时间:2013-04-26 23:50:00

标签: python pyqt pyqt4 qthread

这是我之前发布的前一个问题的后续问题。 问题是当使用NOT子类化Qthread的推荐方法时,如何从GUI停止(终止|退出)QThread,而是使用QObject然后将其移动到QThread。下面是一个工作实例。我可以启动GUI和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_())

3 个答案:

答案 0 :(得分:8)

我很久以前就知道了,但我偶然发现了同样的问题。

我一直在寻找合适的方法来做到这一点。最后很容易。退出应用程序时,需要停止任务,并且需要停止调用其退出方法的线程。请参见底部的stop_thread方法。你需要等待线程完成。否则你将得到 QThread:在线程仍在运行时被销毁&#39;退出时的消息

(我也改变了我的代码以使用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_())

答案 1 :(得分:3)

我发现我的原始问题实际上是两个问题:为了阻止主线程中的辅助线程,你需要两件事:

  1. 能够从主线程向下与第二线程进行通信

  2. 发送正确的信号以停止线程

  3. 我无法解决(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()方法中简单添加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子类):

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

    欢迎评论!

答案 2 :(得分:0)

您可以通过调用exit()或quit()来停止线程。在极端情况下,您可能需要强制终止执行线程。但是,这样做是危险且不鼓励的。请阅读terminate()和setTerminationEnabled()的文档以获取详细信息。

src:https://doc.qt.io/qtforpython/PySide2/QtCore/QThread.html