在PyQt应用程序中终止长时间运行的Python命令

时间:2018-01-15 12:22:45

标签: python multithreading pyqt multiprocessing terminate

我有一个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_()

在主窗口中,我可以通过单击按钮启动任务。然后弹出模态对话框并开始任务。如果GUI被阻止是可以的(我知道我可以通过将任务放在一个单独的工作线程中来防止冻结GUI线程,但这不是重点)。关键的是,我希望能够击中"取消"按钮来终止任务。或者,由于长时间运行的任务总是Python命令,我也可以使用Ctrl + C来终止任务。

我无法改变长时间运行的Python命令:即,我无法将其分解成小块,并将状态变量与线程结合使用,有时会提示。替代方法(按Ctrl + C)也不起作用,因为PyQt似乎没有注册它(即使Python解释器应该在它运行任务时)。

1 个答案:

答案 0 :(得分:1)

最简单的方法是使用multiprocessing。这将允许您同时运行任务(或任务组)并随时终止处理。但是,请务必阅读programming guidelines以了解如何有效使用该模块。特别是,尽管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_()