如何在 Python PyQt 中中断 QThread 上的脚本执行?
How to interrupt a script execution on a QThread in Python PyQt?
我在做什么:
我正在制作一个 PyQt 应用程序,允许用户从他们的机器 select 一个脚本文件,然后该应用程序使用 exec()
在单独的 QThread
上执行它,然后向他们展示结果。我已经实现了所有这些,现在我正在尝试添加一个 "Stop Executing" 按钮。
问题:
我无法中断脚本执行,这应该在用户按下 "Stop Executing" 按钮时发生。我无法停止执行脚本的 QObject
任务或终止托管对象的 QThread
。
我的代码:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
class Execute(QObject):
def __init__(self, script):
super().__init__()
self.script = script
def run(self):
exec(open(self.script).read())
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.thread = QThread()
self.test = Execute(user_script)
self.test.moveToThread(self.thread)
self.thread.started.connect(self.test.run)
self.thread.start()
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
# Somehow stop script execution
我的尝试:
有很多关于停止 exec()
或 QThread
的问题,但其中 none 对我有效。这是我尝试过的:
- 从 GUI 调用
thread.quit()
(在脚本执行结束后终止线程 - 与 wait()
相同)
- 从对象中引发
SystemExit
(脚本执行结束后退出整个应用程序)
- 从 GUI 调用
thread.terminate()
(按下 "Stop Executing" 按钮时应用崩溃)
- 使用终止标志变量(不适用于我的情况,因为
run()
不是基于循环的)
那么,是否有任何其他解决方案可以在按下按钮时立即停止 exec()
或终止线程?
查看下一段代码,也许对您有帮助:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
from PyQt5 import Qt #+
class WorkThread(Qt.QThread):
threadSignal = Qt.pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self, *args, **kwargs):
c = 0
while True:
Qt.QThread.msleep(100)
c += 1
self.threadSignal.emit(c)
class MsgBox(Qt.QDialog):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.label = Qt.QLabel("")
layout.addWidget(self.label)
close_btn = Qt.QPushButton("Close")
layout.addWidget(close_btn)
close_btn.clicked.connect(self.close)
self.setGeometry(900, 65, 400, 80)
self.setWindowTitle('MsgBox from WorkThread')
class GUI(Qt.QWidget): #(QMainWindow):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.btn = Qt.QPushButton("Start thread.")
layout.addWidget(self.btn)
self.btn.clicked.connect(self.startExecuting)
self.msg = MsgBox()
self.thread = None
# Lots of irrelevant code here ...
# Called when "Start/Stop Executing" button is pressed
def startExecuting(self, user_script):
if self.thread is None:
self.thread = WorkThread()
self.thread.threadSignal.connect(self.on_threadSignal)
self.thread.start()
self.btn.setText("Stop thread")
else:
self.thread.terminate()
self.thread = None
self.btn.setText("Start thread")
def on_threadSignal(self, value):
self.msg.label.setText(str(value))
if not self.msg.isVisible():
self.msg.show()
if __name__ == '__main__':
app = Qt.QApplication([])
mw = GUI()
mw.show()
app.exec()
感谢 @ekhumoro 关于使用多处理而不是多线程的提示,我找到了解决方案。
我使用了QProcess
来执行脚本,然后在单击"Stop Executing"按钮时调用了process.kill()
。像这样:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QProcess
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.process = QProcess()
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.start("python", ["-u", user_script])
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
self.process.kill()
这会在不中断 GUI 进程的情况下立即停止脚本执行,这正是我想要的。
我在做什么:
我正在制作一个 PyQt 应用程序,允许用户从他们的机器 select 一个脚本文件,然后该应用程序使用 exec()
在单独的 QThread
上执行它,然后向他们展示结果。我已经实现了所有这些,现在我正在尝试添加一个 "Stop Executing" 按钮。
问题:
我无法中断脚本执行,这应该在用户按下 "Stop Executing" 按钮时发生。我无法停止执行脚本的 QObject
任务或终止托管对象的 QThread
。
我的代码:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
class Execute(QObject):
def __init__(self, script):
super().__init__()
self.script = script
def run(self):
exec(open(self.script).read())
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.thread = QThread()
self.test = Execute(user_script)
self.test.moveToThread(self.thread)
self.thread.started.connect(self.test.run)
self.thread.start()
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
# Somehow stop script execution
我的尝试:
有很多关于停止 exec()
或 QThread
的问题,但其中 none 对我有效。这是我尝试过的:
- 从 GUI 调用
thread.quit()
(在脚本执行结束后终止线程 - 与wait()
相同) - 从对象中引发
SystemExit
(脚本执行结束后退出整个应用程序) - 从 GUI 调用
thread.terminate()
(按下 "Stop Executing" 按钮时应用崩溃) - 使用终止标志变量(不适用于我的情况,因为
run()
不是基于循环的)
那么,是否有任何其他解决方案可以在按下按钮时立即停止 exec()
或终止线程?
查看下一段代码,也许对您有帮助:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
from PyQt5 import Qt #+
class WorkThread(Qt.QThread):
threadSignal = Qt.pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self, *args, **kwargs):
c = 0
while True:
Qt.QThread.msleep(100)
c += 1
self.threadSignal.emit(c)
class MsgBox(Qt.QDialog):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.label = Qt.QLabel("")
layout.addWidget(self.label)
close_btn = Qt.QPushButton("Close")
layout.addWidget(close_btn)
close_btn.clicked.connect(self.close)
self.setGeometry(900, 65, 400, 80)
self.setWindowTitle('MsgBox from WorkThread')
class GUI(Qt.QWidget): #(QMainWindow):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.btn = Qt.QPushButton("Start thread.")
layout.addWidget(self.btn)
self.btn.clicked.connect(self.startExecuting)
self.msg = MsgBox()
self.thread = None
# Lots of irrelevant code here ...
# Called when "Start/Stop Executing" button is pressed
def startExecuting(self, user_script):
if self.thread is None:
self.thread = WorkThread()
self.thread.threadSignal.connect(self.on_threadSignal)
self.thread.start()
self.btn.setText("Stop thread")
else:
self.thread.terminate()
self.thread = None
self.btn.setText("Start thread")
def on_threadSignal(self, value):
self.msg.label.setText(str(value))
if not self.msg.isVisible():
self.msg.show()
if __name__ == '__main__':
app = Qt.QApplication([])
mw = GUI()
mw.show()
app.exec()
感谢 @ekhumoro 关于使用多处理而不是多线程的提示,我找到了解决方案。
我使用了QProcess
来执行脚本,然后在单击"Stop Executing"按钮时调用了process.kill()
。像这样:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QProcess
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.process = QProcess()
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.start("python", ["-u", user_script])
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
self.process.kill()
这会在不中断 GUI 进程的情况下立即停止脚本执行,这正是我想要的。