使用 QAction 切换启动和停止线程

Start and Stop thread using QAction toggled

我目前正在尝试构建一个 PyQt5 应用程序,它应该由主 GUI 组成,并且在后台应该有一个不同的线程应该在无限循环中测量某些东西。我想使用 QAction 或 Checkbox 启动和停止该线程。

也就是说,当我按下复选框并且状态为真时,线程应该启动,如果我再次单击它,它应该停止。

现在最好的实现方式是什么?

目前我正在使用这样的工作线程:

class Worker(QtCore.QObject):
    def __init__(self):
        super(Worker, self).__init__()
        self._isRunning = True

    def task(self):
        if not self._isRunning:
            self._isRunning = True
        while self._isRunning:
            time.sleep(0.5)
            ... measure ...

    def stop(self):
        self._isRunning = False

然后在主线程中使其成为 运行:

self.thread = QtCore.QThread()
self.thread.start()
self.worker = Worker()
self.worker.moveToThread(self.thread)

self.btn_start.clicked.connect(self.worker.task)
self.btn_stopped.clicked.connect(lambda: self.worker.stop())

到目前为止这有效。但我并不确定这是最好的方法,如果我可以按照描述的方式使用复选框来做同样的事情,我也会更喜欢它。

就目前而言,您发布的代码不是多线程的。这是因为 worker.task() 是由主线程中的代码启动的,所以它也会 运行 在主线程中。您需要使用工作线程的 started 信号来启动任务,并使用工作线程上的自定义信号来退出线程。

下面的演示脚本应该可以解决这些问题:

import sys, time
from PyQt5 import QtCore, QtWidgets

class Worker(QtCore.QObject):
    finished = QtCore.pyqtSignal()
    messageSent = QtCore.pyqtSignal(str)

    def __init__(self):
        super(Worker, self).__init__()
        self._isRunning = False

    def task(self):
        print('WKR thread:', QtCore.QThread.currentThread())
        self._isRunning = True
        count = 0
        while self._isRunning:
            time.sleep(0.5)
            count += 1
            self.messageSent.emit('count: %s' % count)
        self.finished.emit()

    def stop(self):
        self._isRunning = False

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtWidgets.QCheckBox('Test', self)
        self.button.toggled.connect(self.handleButton)
        self.label = QtWidgets.QLabel(self)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.thread = QtCore.QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.task)
        self.worker.finished.connect(self.thread.quit)
        self.worker.messageSent.connect(self.label.setText)

    def handleButton(self, checked=False):
        print('GUI thread:', QtCore.QThread.currentThread())
        if checked:
            self.label.clear()
            self.thread.start()
        else:
            self.worker.stop()

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(800, 150, 200, 50)
    window.show()
    sys.exit(app.exec_())