如何从 GUI 停止 QThread [英] How to stop a QThread from the GUI

查看:63
本文介绍了如何从 GUI 停止 QThread的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我之前发布的上一个问题的后续问题.问题是如何在使用不继承 Qthread 的推荐方法时从 GUI 停止(终止|退出|退出)QThread,而是创建 QObject,然后将其移动到 QThread.下面是一个工作示例.我可以启动 GUI 和 Qthread,我可以让后者更新 GUI.然而,我无法阻止它.我为 qthread 尝试了几种方法(quit()、exit(),甚至 terminate())都无济于事.非常感谢帮助.

完整代码如下:

导入时间,sys从 PyQt4.QtCore 导入 *从 PyQt4.QtGui 导入 *类 SimulRunner(QObject):'管理模拟的对象'stepIncreased = pyqtSignal(int, name = 'stepIncreased')def __init__(self):super(SimulRunner, self).__init__()self._step = 0self._isRunning = Trueself._maxSteps = 20def longRunning(self):而 self._step <self._maxSteps 和 self._isRunning == True:self._step += 1self.stepIncreased.emit(self._step)时间.睡眠(0.1)定义停止(自我):self._isRunning = False类 SimulationUi(QDialog):'PyQt 接口'def __init__(self):super(SimulationUi, self).__init__()self.goButton = QPushButton('Go')self.stopButton = QPushButton('停止')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) # 也试过 exit() 和 terminate()# 还尝试了以下(没有奏效)# 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)如果 __name__ == '__main__':app = QApplication(sys.argv)simul = SimulationUi()模拟显示()sys.exit(app.exec_())

解决方案

我发现我原来的问题实际上是两个问题合二为一:为了阻止主线程的次要线程,您需要做两件事:

>

  1. 能够从主线程下行到次线程

  2. 发送适当的信号以停止线程

我无法解决 (2),但我想出了如何解决 (1),这为我提供了解决原始问题的方法.而不是停止线程,我可以停止线程的处理(longRunning() 方法)

问题是辅助线程只有在运行自己的事件循环时才能响应信号.常规 Qthread(这是我的代码使用的)没有.不过,将 QThread 子类化为该效果很容易:

class MyThread(QThread):定义运行(自我):self.exec_()

并在我的代码中使用了 self.simulThread = MyThread() 而不是原始的 self.simulThread = Qthread().这确保了辅助线程运行事件循环.但这还不够.longRunning() 方法需要有机会实际处理来自主线程的事件.借助 this SO answer 我发现在 longRunning() 方法中简单添加了一个 QApplication.processEvent()给了次要线程这样的机会.我现在可以停止在辅助线程中执行的处理,即使我还没有想出如何停止线程本身.

结束.我的 longRunning 方法现在看起来像这样:

def longRunning(self):而 self._step <self._maxSteps 和 self._isRunning == True:self._step += 1self.stepIncreased.emit(self._step)时间.睡眠(0.1)QApplication.processEvents()

我的 GUI 线程有这三行来完成这项工作(除了上面列出的 QThread 子类):

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

欢迎评论!

This is a follow up question to a previous one I posted earlier. The problem is how to stop (terminate|quit|exit) a QThread from the GUI when using the recommended method of NOT subclassing Qthread, but rather vreating a QObject and then moving it to a QThread. Below if a working example. I can start the GUI and the Qthread and I can have the latter update the GUI. However, I cannot stop it. I tried several methods for qthread (quit(), exit(), and even terminate()) to no avail. Help greatly appreciated.

Here is the complete code:

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

解决方案

I found out that my original question was actually two questions in one: in order to stop a secondary thread from the main one, you need two things:

  1. Be able to communicate from the main thread down to the secondary thread

  2. Send the proper signal to stop the thread

I haven't been able to solve (2), but I figured out how to solve (1), which gave me a workaround to my original problem. Instead of stopping the thread, I can stop the thread's processing (the longRunning() method)

The problem is that a a secondary thread can only respond to signals if it runs its own event loop. A regular Qthread (which is what my code used) does not. It is easy enough, though, to subclass QThread to that effect:

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

and used self.simulThread = MyThread() in my code instead of the original self.simulThread = Qthread(). This ensures that the secondary thread runs an event loop. That was not enough, though. The longRunning() method needs to have a chance to actually process the event coming down from the main thread. With the help of this SO answer I figured out that the simple addition of a QApplication.processEvent() in the longRunning() method gave the secondary thread such a chance. I can now stop the processing carried out in the secondary thread, even though I haven't figured out how to stop the thread itself.

To wrap up. My longRunning method now looks like this:

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

and my GUI thread has these three lines that do the job (in addition to the QThread subclass listed above):

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

Comments are welcome!

这篇关于如何从 GUI 停止 QThread的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆