PyQt5中QTimer启动后无法执行update_label函数的问题求助
PyQt5 QTimer在QRunnable中无法触发timeout的问题解决
问题根源
- QRunnable线程生命周期问题:QRunnable的
run方法执行完毕后,线程会被线程池立即回收,QTimer刚启动就失去了运行上下文,根本没机会触发timeout信号。 - 事件循环缺失:QTimer依赖Qt的事件循环才能工作,但QRunnable所在的子线程默认没有启动事件循环,即便
run方法不结束,Timer也无法正常触发回调。
解决方案
方案一:用QThread替代QRunnable(推荐)
QThread自带事件循环,适合长期运行的定时任务,代码结构更稳定:
from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import pyqtSlot, QThread, pyqtSignal class WorkerSignal(QtCore.QObject): latest_number = pyqtSignal(str) class Updater(QThread): def __init__(self): super(Updater, self).__init__() self.update_progress = WorkerSignal() print("started") def run(self): self.timer = QtCore.QTimer() self.timer.setInterval(2000) self.timer.timeout.connect(self.update_label) self.timer.start() # QThread的exec_()会启动事件循环,保持线程持续运行 self.exec_() def update_label(self): provider = configfile.provider number = str(nmbr.latest_numer(provider)) self.update_progress.latest_number.emit(number) print("update label started") class MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setObjectName("MainWindow") self.resize(1300, 768) self.setMinimumSize(1300,768) _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("Bot", "Bot")) self.number = QtWidgets.QLabel(self) self.number.setGeometry(QtCore.QRect(1100, 10, 300, 20)) self.number.setObjectName("number") self.show() self.updater = Updater() self.updater.update_progress.latest_number.connect(self.update_number) self.updater.start() @pyqtSlot(str) def update_number(self, val): self.number.setText(f"Current block: {val}")
方案二:在QRunnable中手动启动事件循环
如果一定要用QRunnable,需要在run方法里启动事件循环,让线程保持运行:
from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import pyqtSlot, QRunnable, QEventLoop class WorkerSignal(QtCore.QObject): latest_number = pyqtSignal(str) class Updater(QRunnable): def __init__(self): super(Updater, self).__init__() self.update_progress = WorkerSignal() self.is_running = True print("started") def run(self): self.timer = QtCore.QTimer() self.timer.setInterval(2000) self.timer.timeout.connect(self.update_label) self.timer.start() # 启动事件循环,持续处理事件直到停止 loop = QEventLoop() while self.is_running: loop.processEvents() self.timer.stop() def update_label(self): provider = configfile.provider number = str(nmbr.latest_numer(provider)) self.update_progress.latest_number.emit(number) print("update label started") # MainWindow部分与原代码一致,注意需添加停止Updater的逻辑
额外提示
如果获取latest_numer的操作不耗时,完全可以直接在主线程用QTimer,无需额外开线程:
class MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) # ... 其他初始化代码 ... self.timer = QtCore.QTimer() self.timer.setInterval(2000) self.timer.timeout.connect(self.update_number_directly) self.timer.start() def update_number_directly(self): provider = configfile.provider number = str(nmbr.latest_numer(provider)) self.number.setText(f"Current block: {number}")
内容的提问来源于stack exchange,提问作者Marcello De Paola
相关产品推荐
相关产品推荐

