PyQt5向运行中线程发射信号修改参数不生效问题
问题根因
你的代码有两处核心错误,直接导致参数更新逻辑完全不生效:
Worker类的update方法是实例方法,但定义时漏写了第一个参数self。跨线程信号触发槽函数时,会把传入的字符串参数错误填充到self的位置,直接触发参数不匹配的运行时错误,槽函数根本跑不起来,查看控制台的报错输出就能发现这个问题。- 工作线程的事件循环被阻塞了。跨线程信号默认用队列连接机制:信号发出后,槽函数的调用请求会被投递到Worker所在工作线程的事件队列里,等线程调度事件时才会执行。但你在
run里写了while 1 + time.sleep(1)的阻塞逻辑,线程全程被死循环和sleep占死,根本没机会处理事件队列里的更新请求,update方法永远不会被调用。
另外你现在写的while 1是无限死循环,循环外的self.finished.emit()永远执行不到,线程也没法正常退出。
修复方法
- 给
update方法补上self参数,推荐用QMutexLocker自动管理互斥锁,避免手动加解锁遗漏导致死锁 - 不要用阻塞式的sleep+死循环占满线程,把定时打印逻辑交给Qt的事件循环调度,保证事件能被正常处理
- 增加线程退出逻辑,避免无限死循环导致线程无法回收、内存泄漏
修复后的可运行代码如下:
import sys from PyQt5.QtCore import Qt, QMutex, QMutexLocker, QObject, QThread, pyqtSignal, QTimer, QEventLoop from PyQt5.QtWidgets import ( QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, ) class Worker(QObject): finished = pyqtSignal() def __init__(self, parent=None): super(Worker, self).__init__(parent) self.mutex = QMutex() self.stringToDisplay = 'Holà' self.event_loop = None def update(self, newStringToDisplay): # 互斥锁自动管理,离开作用域自动解锁 with QMutexLocker(self.mutex): self.stringToDisplay = newStringToDisplay def _log_string(self): with QMutexLocker(self.mutex): print(self.stringToDisplay) def stop(self): # 退出事件循环,终止线程任务 if self.event_loop and self.event_loop.isRunning(): self.event_loop.quit() def run(self): # 用定时器实现1秒间隔打印,不阻塞事件循环 print_timer = QTimer(self) print_timer.timeout.connect(self._log_string) print_timer.start(1000) # 启动工作线程的本地事件循环,处理跨线程信号、定时器事件 self.event_loop = QEventLoop() self.event_loop.exec() # 事件循环退出后清理资源 print_timer.stop() self.event_loop = None self.finished.emit() class Window(QMainWindow): updateSignal = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self.thread = None self.worker = None self.setupUi() def setupUi(self): self.setWindowTitle("GUI") self.resize(300, 150) self.button2runThread = QPushButton('Click to start the thread', self) self.button2runThread.clicked.connect(self.runThread) self.button2updateParams = QPushButton('Click to update parameter in the running thread', self) self.button2updateParams.clicked.connect(self.updateThread) layout = QVBoxLayout() layout.addWidget(self.button2runThread) layout.addWidget(self.button2updateParams) self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.centralWidget.setLayout(layout) def updateThread(self): if self.worker and self.thread.isRunning(): self.updateSignal.emit('My new string') def runThread(self): # 避免重复点击创建多个冗余线程 if self.thread and self.thread.isRunning(): return 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.thread.finished.connect(self.thread.deleteLater) # 窗口关闭时自动停止线程,避免残留 self.destroyed.connect(self.worker.stop) self.destroyed.connect(self.thread.quit) self.updateSignal.connect(self.worker.update) self.thread.start() if __name__ == '__main__': app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec())
运行后先点击启动线程按钮,控制台会每秒打印一次Holà,再点击更新参数按钮,后续打印的内容就会切换为My new string。
内容的提问来源于stack exchange,提问作者ocha67
相关产品推荐
相关产品推荐

