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

PyQt:QGraphicsScene内嵌QWidget无法自动缩小问题咨询

根本原因

将QWidget通过QGraphicsScene::addWidget()嵌入场景时,Qt会自动创建一个QGraphicsProxyWidget代理对象托管该控件,这层代理默认的尺寸调整逻辑为仅允许尺寸扩张,拦截自动收缩请求:

  • 代理会缓存托管QWidget的历史最大尺寸,当内部布局因子控件减少触发缩小请求时,代理不会同步更新自身的boundingRect,也不会把收缩请求传递给托管的QWidget
  • 这和QScrollArea的实现逻辑完全不同:QScrollArea直接将托管控件作为视口的子控件,走标准Qt布局的双向尺寸调整流程,没有代理层的尺寸缓存限制,因此可以正常收缩
  • 这也是观察到itemsBoundingRect()只增不减的核心原因:新增按钮时的扩张请求会被代理放行,控件尺寸正常变大;删除按钮时的收缩请求被代理拦截,控件始终保持历史最大尺寸。
修复方案

需要在子控件删除、布局完成重算后,主动重置托管控件和代理的尺寸,步骤如下:

  • 保存addWidget()返回的QGraphicsProxyWidget对象,不要使用匿名调用
  • 子控件删除后,等待事件循环完成布局重算,再主动触发控件尺寸调整和代理尺寸同步

修正后的可运行代码如下:

import sys
from PyQt6 import QtCore
from PyQt6.QtWidgets import (QMainWindow, QApplication, QWidget, QVBoxLayout,
                             QPushButton, QGraphicsView, QGraphicsScene, QSizePolicy)
from PyQt6.QtGui import QBrush

class Window(QMainWindow): 
    def __init__(self):
        super(Window,self).__init__()
        self.setGeometry(300,300,1000,1000)

        
app = QApplication(sys.argv)
window = Window()
app.setStyle('Fusion')
widget = QWidget()
widget.setStyleSheet("background:red")
widget.setSizePolicy(QSizePolicy.Policy.Minimum,QSizePolicy.Policy.Minimum)

vbox = QVBoxLayout()
proxy = None
gs = None

def resize_proxy():
    # 按布局最小尺寸调整控件
    widget.adjustSize()
    # 同步代理尺寸
    proxy.setGeometry(widget.geometry())
    # 同步场景尺寸
    gs.setSceneRect(gs.itemsBoundingRect())
    print(gs.itemsBoundingRect())

def add_btn():
    btn = QPushButton("x")
    btn.setFixedHeight(40)
    vbox.addWidget(btn)
    btn.clicked.connect(lambda: [btn.deleteLater(), QtCore.QTimer.singleShot(0, resize_proxy)])
    print(gs.itemsBoundingRect())

btn = QPushButton("add")
btn.clicked.connect(add_btn)
vbox.addWidget(btn)

init_btn = QPushButton("x")
init_btn.setFixedHeight(40)
vbox.addWidget(init_btn)
init_btn.clicked.connect(lambda: [init_btn.deleteLater(), QtCore.QTimer.singleShot(0, resize_proxy)])

gv = QGraphicsView()
gv.setSizePolicy(QSizePolicy.Policy.Minimum,QSizePolicy.Policy.Minimum)
gs = QGraphicsScene()
gs.setBackgroundBrush(QBrush(QtCore.Qt.GlobalColor.blue))

# 保存代理对象引用
proxy = gs.addWidget(widget)
gv.setScene(gs)

widget.setLayout(vbox)
window.setCentralWidget(gv)
window.show()
sys.exit(app.exec())

代码中使用QTimer.singleShot(0, resize_proxy)是因为deleteLater()不会立即销毁控件,需要等下一轮事件循环才会完成删除、触发布局重算,此时读取的布局尺寸才是删除子控件后的准确值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:27:26