使用 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_())
我目前正在尝试构建一个 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_())