【问题标题】:How to interrupt a script execution on a QThread in Python PyQt?如何在 Python PyQt 中中断 QThread 上的脚本执行?
【发布时间】:2018-08-11 18:07:37
【问题描述】:

我在做什么:

我正在制作一个 PyQt 应用程序,它允许用户从他们的机器中选择一个脚本文件,然后该应用程序使用 exec() 在单独的 QThread 上执行它,然后向他们显示结果。我已经实现了所有这些,现在我正在尝试添加一个“停止执行”按钮。

问题:

我无法中断脚本执行,只要用户按下“停止执行”按钮,就会发生这种情况。我无法停止正在执行脚本的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 的问题,但在我的情况下它们都不起作用。这是我尝试过的:

  1. 从 GUI 调用 thread.quit()(在脚本执行结束后杀死线程 - 与 wait() 相同)
  2. 从对象引发SystemExit(脚本执行结束后退出整个应用程序)
  3. 从 GUI 调用 thread.terminate()(按下“停止执行”按钮时应用程序崩溃)
  4. 使用终止标志变量(不适用于我的情况,因为 run() 不是基于循环的)

那么,有没有其他解决方案可以在按下按钮时停止exec() 或立即终止线程?

【问题讨论】:

  • 如果任意代码块没有明确提供中断机制,就无法终止它。毕竟,代码可能只是while True: print('spam') - 这将永远阻塞。多线程在这里是错误的方法 - 您需要使用多处理。
  • @ekhumoro 如果我使用多处理,我可以中断执行吗?
  • 是的,你可以直接杀死它,就像你想要一个单独的进程一样。

标签: python python-3.x pyqt pyqt5 qthread


【解决方案1】:

感谢@ekhumoro 关于使用多处理而不是多线程的提示,我能够找到解决方案。

我使用QProcess 执行脚本,然后在单击“停止执行”按钮时调用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 进程,这正是我想要的。

【讨论】:

    【解决方案2】:

    检查下一个代码,也许它可以帮助你:

    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()   
    

    【讨论】:

    • 这使用了thread.terminate(),我已经尝试过并提到过。它对我不起作用,它会强制关闭应用程序。
    猜你喜欢
    • 2014-03-10
    • 1970-01-01
    • 2020-03-23
    • 2016-07-17
    • 2013-12-27
    • 2010-12-28
    • 1970-01-01
    相关资源
    最近更新 更多