PyQt5自定义可折叠QToolButton子控件重叠布局问题求助
我发现你遇到的问题根源在于content_area的尺寸策略设置和动画高度计算逻辑,当子控件换成QToolButton时,这些设置无法适配它的尺寸特性,导致重叠。下面是修复后的完整代码,我会把关键修改点标出来:
from PyQt5 import QtCore, QtGui, QtWidgets class CollapsibleBox(QtWidgets.QWidget): def __init__(self, title="", parent=None): super(CollapsibleBox, self).__init__(parent) self.toggle_button = QtWidgets.QToolButton(text=title, checkable=True, checked=False) self.toggle_button.setStyleSheet("QToolButton {background-color:#dadada; border: none; padding: 4px;}") self.toggle_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.toggle_button.setArrowType(QtCore.Qt.ArrowType.RightArrow) self.toggle_button.pressed.connect(self.on_pressed) self.toggle_animation = QtCore.QParallelAnimationGroup(self) # 修改点1:调整content_area的垂直尺寸策略为Preferred,允许根据内容自动调整高度 self.content_area = QtWidgets.QScrollArea(maximumHeight=0, minimumHeight=0) self.content_area.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) self.content_area.setFrameShape(QtWidgets.QFrame.NoFrame) self.content_area.setWidgetResizable(True) # 新增:确保内容区域能自适应子控件 lay = QtWidgets.QVBoxLayout(self) lay.setSpacing(0) lay.setContentsMargins(0, 0, 0, 0) lay.addWidget(self.toggle_button) lay.addWidget(self.content_area) self.toggle_animation.addAnimation(QtCore.QPropertyAnimation(self, b"minimumHeight")) self.toggle_animation.addAnimation(QtCore.QPropertyAnimation(self, b"maximumHeight")) self.toggle_animation.addAnimation(QtCore.QPropertyAnimation(self.content_area, b"maximumHeight")) @QtCore.pyqtSlot() def on_pressed(self): checked = self.toggle_button.isChecked() self.toggle_button.setArrowType(QtCore.Qt.ArrowType.DownArrow if checked else QtCore.Qt.ArrowType.RightArrow) # 修正箭头逻辑:展开时箭头向下 self.toggle_animation.setDirection(QtCore.QAbstractAnimation.Forward if checked else QtCore.Qt.AbstractAnimation.Backward) self.toggle_animation.start() def setContentLayout(self, layout): # 确保内容布局的margin和spacing为0,避免额外间隙 layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) # 安全删除旧布局 if self.content_area.layout(): QtWidgets.QWidget().setLayout(self.content_area.layout()) self.content_area.setLayout(layout) # 修改点2:用父按钮的sizeHint计算折叠高度,更准确 collapsed_height = self.toggle_button.sizeHint().height() content_height = layout.sizeHint().height() # 更新动画参数 for i in range(self.toggle_animation.animationCount() - 1): animation = self.toggle_animation.animationAt(i) animation.setDuration(500) animation.setStartValue(collapsed_height) animation.setEndValue(collapsed_height + content_height) content_animation = self.toggle_animation.animationAt(self.toggle_animation.animationCount() - 1) content_animation.setDuration(500) content_animation.setStartValue(0) content_animation.setEndValue(content_height) if __name__ == '__main__': import sys import random app = QtWidgets.QApplication(sys.argv) w = QtWidgets.QMainWindow() w.setCentralWidget(QtWidgets.QWidget()) dock = QtWidgets.QDockWidget("Collapsible Demo") w.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock) scroll = QtWidgets.QScrollArea() dock.setWidget(scroll) content = QtWidgets.QWidget() content.setStyleSheet("background-color:white") scroll.setWidget(content) scroll.setWidgetResizable(True) vlay = QtWidgets.QVBoxLayout(content) towerGeometry = CollapsibleBox("Tower Geometry") vlay.addWidget(towerGeometry) layoutForTowerGeometry = QtWidgets.QVBoxLayout() # 替换为QToolButton测试 section = QtWidgets.QToolButton(text="Sections") section.setStyleSheet("QToolButton {background-color: #dadada; border: none; padding: 4px;}") section.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) layoutForTowerGeometry.addWidget(section) flanges = CollapsibleBox("Flanges") layoutForGeometryParameters = QtWidgets.QVBoxLayout() geometryParameters = QtWidgets.QToolButton(text="Geometry Parameters") geometryParameters.setStyleSheet("QToolButton {background-color: #dadada; border: none; padding: 4px;}") geometryParameters.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) layoutForGeometryParameters.addWidget(geometryParameters) guidanceInfo = QtWidgets.QToolButton(text="Guidance Info") guidanceInfo.setStyleSheet("QToolButton {background-color: #dadada; border: none; padding: 4px;}") guidanceInfo.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) layoutForGeometryParameters.addWidget(guidanceInfo) flanges.setContentLayout(layoutForGeometryParameters) layoutForTowerGeometry.addWidget(flanges) cans = QtWidgets.QToolButton(text="Cans") cans.setStyleSheet("QToolButton {background-color: #dadada; border: none; padding: 4px;}") cans.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) layoutForTowerGeometry.addWidget(cans) towerGeometry.setContentLayout(layoutForTowerGeometry) vlay.addStretch() w.resize(640, 480) w.show() sys.exit(app.exec_())
关键修改说明:
- 调整内容区域尺寸策略:把
content_area的垂直sizePolicy从Fixed改为Preferred,并开启setWidgetResizable(True),确保它能根据子控件的尺寸自动调整高度,避免固定高度导致的重叠。 - 修正折叠高度计算:改用
self.toggle_button.sizeHint().height()计算折叠状态的高度,比原来基于整个控件sizeHint的方式更准确,不会包含内容区域的冗余高度。 - 统一控件样式:给所有QToolButton设置了一致的styleSheet(去掉边框、统一padding),避免默认样式带来的尺寸差异,同时让显示效果和原来的QPushButton一致。
- 修正箭头逻辑:原来的箭头切换逻辑搞反了,现在展开时箭头向下,收起时向右,符合用户直觉。
- 安全处理旧布局:删除旧布局时用了更安全的方式,避免内存泄漏。
现在你可以替换原来的代码,测试QToolButton作为子项的情况,应该不会再出现重叠问题了。
内容的提问来源于stack exchange,提问作者Shafiqul Islam
相关产品推荐
相关产品推荐

