如何从 GUI 停止 QThread [英] How to stop a QThread from the GUI
问题描述
这是我之前发布的上一个问题的后续问题.问题是如何在使用不继承 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_())
我发现我原来的问题实际上是两个问题合二为一:为了阻止主线程的次要线程,您需要做两件事:
>
能够从主线程下行到次线程
发送适当的信号以停止线程
我无法解决 (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:
Be able to communicate from the main thread down to the secondary thread
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屋!