在 PyQt 应用程序中终止一个 long-运行 Python 命令
Terminate a long-running Python command within a PyQt application
我有一个 PyQt GUI,我用它在 Python 中开始长 运行 计算。这是一个最小的例子:
import sys
import time
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QDialog,
QVBoxLayout, QPushButton, QDialogButtonBox)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
button = QPushButton("Start", self)
button.clicked.connect(self.long_task)
self.setGeometry(300, 300, 300, 200)
self.show()
def long_task(self):
dialog = QDialog(self)
vbox = QVBoxLayout(dialog)
label = QLabel("Running...")
button = QDialogButtonBox(QDialogButtonBox.Cancel)
vbox.addWidget(label)
vbox.addWidget(button)
dialog.open()
time.sleep(10) # long task, external function
dialog.close()
app = QApplication(sys.argv)
main = MainWindow()
app.exec_()
在主 window 中,我可以通过单击按钮来启动任务。然后弹出一个模式对话框,任务开始。如果 GUI 被阻塞也没关系(我知道我可以通过将任务放在一个单独的工作线程中来防止冻结 GUI 线程,但这不是重点)。至关重要的是,我希望能够点击 "Cancel" 按钮来终止任务。或者,由于长 运行 任务始终是 Python 命令,我也可以忍受使用 Ctrl+C 终止任务。
我无法更改 long-运行 Python 命令:即我无法将其分解成小块并结合使用状态变量和线程,正如有时建议的那样。替代方案(按 Ctrl+C)也不起作用,因为 PyQt 似乎没有注册它(即使 Python 解释器应该在它是 运行 任务时)。
最简单的方法是使用 multiprocessing. This will allow you to run a task (or group of tasks) concurrently and terminate processing at any time. However, make sure you read the programming guidelines to understand how to use the module effectively. In particular, although the terminate 方法适用于自包含任务,它不应该与使用共享资源的多个任务一起使用。
这是一个基于您的示例的简单演示:
import sys
import time
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QDialog,
QVBoxLayout, QPushButton, QDialogButtonBox)
from multiprocessing import Pool
def long_task():
for x in range(10):
print('long task:', x)
time.sleep(1)
return 'finished'
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
button = QPushButton("Start", self)
button.clicked.connect(self.long_task)
self.setGeometry(300, 300, 300, 200)
self.show()
def long_task(self):
dialog = QDialog(self)
vbox = QVBoxLayout(dialog)
label = QLabel("Running...")
button = QDialogButtonBox(QDialogButtonBox.Cancel)
button.rejected.connect(dialog.close)
vbox.addWidget(label)
vbox.addWidget(button)
def callback(msg):
print(msg)
dialog.accept()
pool.apply_async(long_task, callback=callback)
if dialog.exec_() == QDialog.Rejected:
pool.terminate()
print('terminated')
app = QApplication(sys.argv)
main = MainWindow()
app.exec_()
我有一个 PyQt GUI,我用它在 Python 中开始长 运行 计算。这是一个最小的例子:
import sys
import time
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QDialog,
QVBoxLayout, QPushButton, QDialogButtonBox)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
button = QPushButton("Start", self)
button.clicked.connect(self.long_task)
self.setGeometry(300, 300, 300, 200)
self.show()
def long_task(self):
dialog = QDialog(self)
vbox = QVBoxLayout(dialog)
label = QLabel("Running...")
button = QDialogButtonBox(QDialogButtonBox.Cancel)
vbox.addWidget(label)
vbox.addWidget(button)
dialog.open()
time.sleep(10) # long task, external function
dialog.close()
app = QApplication(sys.argv)
main = MainWindow()
app.exec_()
在主 window 中,我可以通过单击按钮来启动任务。然后弹出一个模式对话框,任务开始。如果 GUI 被阻塞也没关系(我知道我可以通过将任务放在一个单独的工作线程中来防止冻结 GUI 线程,但这不是重点)。至关重要的是,我希望能够点击 "Cancel" 按钮来终止任务。或者,由于长 运行 任务始终是 Python 命令,我也可以忍受使用 Ctrl+C 终止任务。
我无法更改 long-运行 Python 命令:即我无法将其分解成小块并结合使用状态变量和线程,正如有时建议的那样。替代方案(按 Ctrl+C)也不起作用,因为 PyQt 似乎没有注册它(即使 Python 解释器应该在它是 运行 任务时)。
最简单的方法是使用 multiprocessing. This will allow you to run a task (or group of tasks) concurrently and terminate processing at any time. However, make sure you read the programming guidelines to understand how to use the module effectively. In particular, although the terminate 方法适用于自包含任务,它不应该与使用共享资源的多个任务一起使用。
这是一个基于您的示例的简单演示:
import sys
import time
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QDialog,
QVBoxLayout, QPushButton, QDialogButtonBox)
from multiprocessing import Pool
def long_task():
for x in range(10):
print('long task:', x)
time.sleep(1)
return 'finished'
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
button = QPushButton("Start", self)
button.clicked.connect(self.long_task)
self.setGeometry(300, 300, 300, 200)
self.show()
def long_task(self):
dialog = QDialog(self)
vbox = QVBoxLayout(dialog)
label = QLabel("Running...")
button = QDialogButtonBox(QDialogButtonBox.Cancel)
button.rejected.connect(dialog.close)
vbox.addWidget(label)
vbox.addWidget(button)
def callback(msg):
print(msg)
dialog.accept()
pool.apply_async(long_task, callback=callback)
if dialog.exec_() == QDialog.Rejected:
pool.terminate()
print('terminated')
app = QApplication(sys.argv)
main = MainWindow()
app.exec_()