Python 如何从 GUI 停止 QThread
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16246796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to stop a QThread from the GUI
提问by stefano
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.
这是我之前发布的上一个问题的后续问题。问题是当使用推荐的不继承 Qthread 的方法时,如何从 GUI 停止(终止|退出|退出)QThread,而是创建 QObject,然后将其移动到 QThread。下面是一个工作示例。我可以启动 GUI 和 Qthread,我可以让后者更新 GUI。然而,我无法阻止它。我为 qthread 尝试了几种方法(quit()、exit(),甚至 terminate())都无济于事。非常感谢帮助。
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_())
采纳答案by stefano
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 downto 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)
我无法解决 (2),但我想出了如何解决 (1),这给了我解决原始问题的方法。而不是停止线程,我可以停止线程的处理(longRunning()方法)
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:
问题是辅助线程只有在运行自己的事件循环时才能响应信号。常规 Qthread(这是我的代码使用的)没有。但是,将 QThread 子类化为该效果很容易:
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 answerI 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 processingcarried out in the secondary thread, even though I haven't figured out how to stop the thread itself.
并self.simulThread = MyThread()在我的代码中使用而不是原始self.simulThread = Qthread(). 这确保了辅助线程运行事件循环。但这还不够。该longRunning()方法需要有机会实际处理来自主线程的事件。的帮助下这个SO回答我想通了,简单的增加了一个QApplication.processEvent()在longRunning()方法给辅助线程这样的机会。我现在可以停止在辅助线程中执行的处理,即使我还没有想出如何停止线程本身。
To wrap up. My longRunning method now looks like this:
结束。我的 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()
and my GUI thread has these three lines that do the job (in addition to the QThread subclass listed above):
我的 GUI 线程有这三行来完成这项工作(除了上面列出的 QThread 子类):
self.simulThread = MyThread()
self.simulRunner.moveToThread(self.simulThread)
self.stopButton.clicked.connect(self.simulRunner.stop)
Comments are welcome!
欢迎评论!
回答by maggie
I know its long ago but i just stumbled over the same problem.
我很久以前就知道了,但我只是偶然发现了同样的问题。
I have been also searching for an appropriate way to do this. Finally it was easy. When exiting the application the task needs to be stopped and the thread needs to be stopped calling its quit method. See stop_thread method on bottom. And you need to wait for the thread to finish. Otherwise you will get QThread: Destroyed while thread is still running' message at exit.
我也一直在寻找一种合适的方法来做到这一点。最后很容易。退出应用程序时,需要停止任务,并且需要停止调用其退出方法的线程。请参阅底部的 stop_thread 方法。你需要等待线程完成。否则,您将在退出时收到QThread: Destroyed while thread is still running' 消息。
(I also changed my code to use pyside)
(我也把我的代码改成使用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_())

