在 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的主要内容,如果未能解决你的问题,请参考以下文章