PySide6中调用QThread的start()不显示按钮,run()却正常?
为什么QThread调用start()不显示按钮,直接调用run()却正常?
问题描述
测试代码在QThread中创建按钮,直接调用run()方法时按钮可以正常显示,但调用start()启动线程时,按钮却无法显示。代码如下:
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget from PySide6.QtCore import QThread from functools import partial class TestButton(QPushButton): def __init__(self): super().__init__() self.index = 0 class BtnThread(QThread): def __init__(self, parent): super().__init__() self.parent = parent def run(self): for i in range(100): pbtn = TestButton() pbtn.index = i #pbtn.clicked.connect(lambda :self.slotfunc(i)) pbtn.clicked.connect(partial(self.slotfunc, i)) self.parent.vlayout.addWidget(pbtn) def slotfunc(self, i): print(str(i) + " pressed") class MainWindow(QMainWindow): def __init__(self): super().__init__() self.vlayout = QVBoxLayout() btnthread = BtnThread(self) #btnthread.start() # <-- Run / Thread btnthread.run() self.basicWidget = QWidget() self.basicWidget.setLayout(self.vlayout) self.setCentralWidget(self.basicWidget) self.show() app = QApplication() window = MainWindow() window.show() app.exec()
原因分析
核心原因是Qt的UI组件必须在主线程(QApplication所在线程)中创建和操作,这是Qt明确的线程安全规则:
- 直接调用
btnthread.run()时,并没有启动新线程,只是在主线程中同步执行了run方法的代码,所有按钮都在主线程创建,符合规则,因此能正常显示。 - 调用
btnthread.start()时,QThread会启动一个独立的子线程,run方法在子线程内执行。此时在子线程中创建QPushButton、调用布局的addWidget方法,完全违反了Qt的线程安全要求——UI组件的初始化、渲染和操作必须在其所属的UI线程(主线程)中进行,子线程操作UI会导致组件无法正确初始化,自然无法显示。
正确解决方案
子线程不能直接操作UI,应通过信号槽机制实现线程间通信:子线程发送信号通知主线程,主线程接收信号后再执行UI操作。修改后的代码如下:
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget from PySide6.QtCore import QThread, Signal from functools import partial class TestButton(QPushButton): def __init__(self): super().__init__() self.index = 0 class BtnThread(QThread): # 定义信号,传递按钮索引参数 add_button_signal = Signal(int) def __init__(self): super().__init__() def run(self): for i in range(100): # 发送信号,触发主线程创建按钮 self.add_button_signal.emit(i) def slotfunc(self, i): print(str(i) + " pressed") class MainWindow(QMainWindow): def __init__(self): super().__init__() self.vlayout = QVBoxLayout() self.basicWidget = QWidget() self.basicWidget.setLayout(self.vlayout) self.setCentralWidget(self.basicWidget) btnthread = BtnThread() # 连接子线程信号到主线程的槽函数 btnthread.add_button_signal.connect(self.add_button) btnthread.start() self.show() def add_button(self, index): # 在主线程中创建按钮并添加到布局 pbtn = TestButton() pbtn.index = index pbtn.clicked.connect(partial(self.slotfunc, index)) self.vlayout.addWidget(pbtn) def slotfunc(self, i): print(str(i) + " pressed") app = QApplication() window = MainWindow() window.show() app.exec()
内容的提问来源于stack exchange,提问作者zerom
相关产品推荐
相关产品推荐

