如何实现QFrame点击伸缩时内部指定布局不发生垂直尺寸调整?
问题根因
- 没有维护展开/收缩的状态标记,点击后只会单向缩小不会回弹
- 直接修改QFrame的geometry会被内部布局的尺寸策略覆盖,动画过程会出现布局抖动
- 内容区域的显隐没有和动画联动,收缩到头部高度后内容还会被布局强行撑开
修复后的实现代码
from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * class AppDemo(QWidget): def __init__(self): super().__init__() self.resize(400, 800) # 维护展开状态标记 self.is_expanded = True self.frame = QFrame() self.frame.setStyleSheet("background: green; border-radius: 8px;") # 给frame设置尺寸策略,水平可以扩展,垂直根据内容适配 self.frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.frame_layout = QVBoxLayout() self.frame_layout.setContentsMargins(0,0,0,0) self.frame_layout.setSpacing(0) self.frame.setLayout(self.frame_layout) self.header_layout = QHBoxLayout() self.header_label = QLabel("Marvin sollte Programmieren lernen!") # 给头部设置固定高度,收缩后frame高度就是头部高度 self.header_label.setFixedHeight(40) self.header_layout.addWidget(self.header_label) self.content_layout = QVBoxLayout() self.content_frame = QFrame() self.content_frame.setStyleSheet("background: red; border-radius: 8px;") # 内容垂直固定高度,水平自适应 self.content_frame.setFixedHeight(500) self.content_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.content_layout.addWidget(self.content_frame) self.frame_layout.addLayout(self.header_layout) self.frame_layout.addLayout(self.content_layout) self.main_layout = QVBoxLayout() self.main_layout.addWidget(self.frame) # 底部加 stretch 保证frame永远靠顶部 self.main_layout.addStretch() self.setLayout(self.main_layout) self.frame.mouseReleaseEvent = lambda _:self.toggle_frame() def toggle_frame(self): current_height = self.frame.height() # 收缩后的高度就是头部的高度 header_height = self.header_label.height() # 展开后的高度是头部+内容高度 expanded_height = header_height + self.content_frame.height() self.animation = QPropertyAnimation(self.frame, b"maximumHeight") self.animation.setDuration(500) self.animation.setStartValue(current_height) if self.is_expanded: # 收缩到头部高度 self.animation.setEndValue(header_height) else: # 展开到完整高度 self.animation.setEndValue(expanded_height) self.animation.setEasingCurve(QEasingCurve.OutCubic) self.animation.start() # 切换状态 self.is_expanded = not self.is_expanded if __name__ == "__main__": app = QApplication([]) demo = AppDemo() demo.show() app.exec_()
关键修改说明
- 新增
is_expanded状态标记,点击时切换展开/收缩两个状态,支持反复点击交互 - 改用
maximumHeight作为动画属性,避免布局对frame尺寸的强制约束,动画过程无抖动更流畅 - 明确设置各组件的尺寸策略:内容区域水平自适应、垂直固定高度,完全符合需求
- 布局的边距和间距设为0,避免展开收缩时出现多余的空白间隙
- 主布局底部增加拉伸项,保证frame始终停靠在容器顶部,交互效果符合预期
内容的提问来源于stack exchange,提问作者Mergen Studios
相关产品推荐
相关产品推荐

