如何在Python PyQt中中断QThread上的脚本执行?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Python PyQt中中断QThread上的脚本执行?相关的知识,希望对你有一定的参考价值。

What I'm Doing:

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

The Problem:

我无法中断脚本执行,只要用户按下“停止执行”按钮就会执行该脚本执行。我无法阻止QObject执行脚本或终止托管该对象的QThread的任务。

My Code:

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

My Attempts:

关于阻止exec()QThread有很多问题,但在我的情况下它们都不起作用。这是我尝试过的:

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

那么,是否还有其他解决方案可以在按下按钮时停止exec()或杀死线程?

答案

感谢@ 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过程,这正是我所寻找的。

另一答案

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

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

以上是关于如何在Python PyQt中中断QThread上的脚本执行?的主要内容,如果未能解决你的问题,请参考以下文章

PyQt:如何终止可重用的 QThread

python pyqt5 QThread

PyQt5 OpenCV 网络摄像头使用 QThread

如何在 PyQT 中使用 QThread

为啥在 macOS 上使用 QThread 时 PyQt 应用程序崩溃或挂起?

QThread 似乎没有启动; PyQt5,Python 2.7.9