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

PyQt5中如何在应用启动时调整QStackedWidget所有子项大小

解决QStackedWidget启动时未触发所有子页面尺寸调整的问题

这个问题其实是Qt对QStackedWidget的优化逻辑导致的:它只会对当前显示的页面进行布局计算和尺寸调整,隐藏的页面默认不会收到Resize或Show事件——毕竟Qt不会去渲染不可见的部件,这是合理的性能优化,但确实会影响到你这种需要提前获取尺寸的场景(比如视频帧绘制、仪表盘布局)。

你之前用threading.Timer的方案报错,是因为PyQt要求所有UI操作必须在主线程执行,而threading.Timer会在新线程里触发代码,导致跨线程操作UI部件,这是严格禁止的。下面给你两种靠谱的解决方案:

方案一:主线程内临时切换所有页面(推荐)

利用Qt的QTimer.singleShot(0),在UI事件循环启动后(确保窗口尺寸已经确定),临时切换到每个页面一次,触发它们的尺寸调整事件,最后切回初始页面。这种方式最贴近真实的用户操作,能确保页面的布局完全更新。

修改你的Window类,添加初始化方法:

class Window(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.stacked_widget = QtWidgets.QStackedWidget()
        layout = QtWidgets.QVBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        layout.addWidget(self.stacked_widget)
        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        widget_1 = Main(self.stacked_widget)
        widget_2 = Page(self.stacked_widget, 1)
        widget_3 = Page(self.stacked_widget, 2)
        widget_4 = Page(self.stacked_widget, 3)
        wid_list = [widget_1, widget_2, widget_3, widget_4]
        for wid in wid_list:
            self.stacked_widget.addWidget(wid)
        self.showMaximized()
        # 用Qt定时器在主线程触发页面初始化(0延迟表示事件循环空闲时执行)
        QtCore.QTimer.singleShot(0, self.init_pages)
        
    def init_pages(self):
        # 保存当前页面索引,之后要切回来
        current_idx = self.stacked_widget.currentIndex()
        # 遍历所有页面,切换并强制处理事件
        for idx in range(self.stacked_widget.count()):
            self.stacked_widget.setCurrentIndex(idx)
            # 强制Qt处理当前页面的所有 pending 事件(比如Resize、Show)
            QtWidgets.QApplication.processEvents()
        # 切回初始页面
        self.stacked_widget.setCurrentIndex(current_idx)

方案二:手动发送尺寸事件

如果不想切换页面,可以直接给每个隐藏页面手动发送Show和Resize事件,模拟页面被显示和调整大小的过程:

在Window类的__init__最后添加这段代码(showMaximized()之后):

# 用定时器确保窗口尺寸已稳定
QtCore.QTimer.singleShot(0, self.send_size_events)

# 定义发送事件的方法
def send_size_events(self):
    # 获取stacked_widget的最终尺寸
    stacked_size = self.stacked_widget.size()
    # 这里要确保能拿到所有页面的列表,或者直接遍历stacked_widget的子部件
    for idx in range(self.stacked_widget.count()):
        wid = self.stacked_widget.widget(idx)
        # 发送Show事件
        show_event = QtCore.QShowEvent()
        QtCore.QCoreApplication.sendEvent(wid, show_event)
        # 发送Resize事件
        resize_event = QtCore.QResizeEvent(stacked_size, QtCore.QSize())
        QtCore.QCoreApplication.sendEvent(wid, resize_event)

这种方式更高效,不需要切换页面,用QTimer.singleShot(0)包裹能避免窗口还没完成布局就获取尺寸的问题。

为什么你的Timer方案会报错?

threading.Timer是Python标准库的多线程定时器,它会在一个新的非UI线程里执行hello方法,而PyQt的所有UI操作(比如setCurrentIndex)必须在主线程执行,否则就会抛出QObject::setParent: Cannot set parent, new parent is in a different thread的错误。Qt自己的QTimer是基于事件循环的,所有回调都会在主线程执行,所以完全没问题。

内容的提问来源于stack exchange,提问作者Voldemort

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:34:29