在 PyQt5 中取消 QThread

Posted

技术标签:

【中文标题】在 PyQt5 中取消 QThread【英文标题】:Cancel QThread in PyQt5 【发布时间】:2021-12-03 11:29:10 【问题描述】:

我在 PyQt5 中有一个 GUI,它启动一个从串行端口读取的 QThread。当线程读取所有数据时,它确实退出了,但是我希望能够在单击停止按钮时停止它。我怎么做?这是基本代码:

    # ...
    class Worker(QObject):
        finished = pyqtSignal()
        progress = pyqtSignal(list)

    def __init__(self):
        QObject.__init__(self)
        self._Reader = Reader()
        self._Reader.progress = self.progress
        self._Reader.finished = self.finished
    def run(self):
        self._Reader.read()
    
    class Ui(QtWidgets.QMainWindow):
        # ...
        def startClicked(self):
            self.thread = QThread()
            self.worker = Worker()
            self.worker.moveToThread(self.thread)

            self.thread.started.connect(self.worker.run)
            self.worker.finished.connect(self.thread.quit)
            self.worker.finished.connect(self.worker.deleteLater)
            self.worker.finished.connect(self.workerFinished)
            self.thread.finished.connect(self.thread.deleteLater)
            self.worker.progress.connect(self.reportProgress)
            self.thread.start()
        def stopClicked(self):
            # How do I stop the thread?
            pass


【问题讨论】:

请提供minimal reproducible example 【参考方案1】:

在管理线程时,您可以执行此操作,如此处文档中的状态:https://doc.qt.io/qt-5/qthread.html

您可以通过调用 exit() 或 quit() 来停止线程。

https://doc.qt.io/qt-5/qthread.html#exit

退出:

告诉线程的事件循环以返回码退出。

调用此函数后,线程离开事件循环并从对 QEventLoop::exec() 的调用中返回。 QEventLoop::exec() 函数返回 returnCode。

按照惯例,returnCode 为 0 表示成功,任何非零值表示错误。

https://doc.qt.io/qt-5/qthread.html#quit

退出:

告诉线程的事件循环退出并返回代码 0(成功)。相当于调用 QThread::exit(0)。

如果线程没有事件循环,这个函数什么也不做。

【讨论】:

【参考方案2】:

我假设您在某些数据处理循环中读取数据。如果这个假设是错误的,那么下面的假设当然是不成立的。

您不能直接从主线程调用辅助线程的quit() 并期望辅助线程会立即处理并退出线程。原因是线程忙于在数据处理循环中读取数据。所以需要在辅助线程中打断数据处理循环,让事件循环空闲。

(顺便说一句。不要将数据处理循环与事件循环混淆。数据处理循环是您自己编写的用于从端口读取数据的循环。事件循环是Qt在您调用@后自动创建的循环987654322@,它正在辅助线程中处理事件、信号和槽。当您的数据处理循环正在运行时,此事件循环被阻塞。)

为了打破数据处理循环,你需要做两件事:

    从主线程调用QThread::requestInterruption(),作为对某些“Abort”按钮被按下的响应(不用担心线程安全,请求中断是线程安全/原子的) 在辅助线程的循环中,您需要定期检查QThread::isInterruptionRequested(),如果返回true,则break 循环并发出worker 的finished() 信号

一旦从辅助线程中的数据处理循环中断,辅助线程中的事件循环就可以用于处理从主线程发送的信号。

我可以在您的代码中看到 worker 的 finished() 信号连接到 QThread::quit()。所以从辅助线程发出finished()(在你从数据处理循环中中断之后)将调用线程的quit(),它将由辅助线程的事件循环(现在是空闲的)处理,它会退出事件循环,随后线程,如果您正确连接了所有内容,它将删除工作人员和线程。 (虽然我没有检查你的这部分代码)

【讨论】:

以上是关于在 PyQt5 中取消 QThread的主要内容,如果未能解决你的问题,请参考以下文章

pyqt5-003

pyqt5-003

PyQt5:具有相等列宽的布局按钮

使用 PyQt5 在 QLabel 中绘制图像

PyQt5——QSS

PyQt5 - 我如何在 QDateEdit 上禁用周末