You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 03:23:28