如何在PyQt中添加或移除QWidget且不影响其他控件尺寸
PyQt实现底部可显隐TextEdit且不改动上层控件尺寸的方案
这一需求完全可以实现,你原有代码中按钮出现缩放的核心原因是所有上层控件都使用了垂直方向可扩展的尺寸策略,且布局未做拉伸权重分配,窗口高度变化时布局会自动拉伸所有可扩展控件。
修改核心逻辑
- 调整上层控件的垂直尺寸策略为固定模式,避免被布局拉伸
- 给顶层布局的不同区域设置拉伸系数,仅允许TextEdit区域分配额外拉伸空间
- 简化冗余布局结构,避免嵌套容器带来的布局计算异常
完整可运行修改后代码
from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import ( QApplication, QComboBox, QFrame, QGridLayout, QHBoxLayout, QSizePolicy, QTextEdit, QToolButton, QVBoxLayout, QWidget, QMainWindow ) import sys class AppWindow(QMainWindow): def __init__(self, main_widget): super(AppWindow, self).__init__() self.main_widget = main_widget self.setCentralWidget(self.main_widget) class AppWidget(QWidget): def __init__(self, panels=[]): super(AppWidget, self).__init__() self.panels = panels self.toolbar_frame = QFrame(self) self.toolbar_frame_layout = QHBoxLayout(self.toolbar_frame) self.toolbar_frame_layout.addStretch() self.log_button = QToolButton(self.toolbar_frame) self.log_button.setText('Toggle Log') self.toolbar_frame_layout.addWidget(self.log_button) self.toolbar_frame.setLayout(self.toolbar_frame_layout) self.project_frame = QFrame(self) self.project_frame_layout = QHBoxLayout(self.project_frame) self.project_dropdown = QComboBox(self.project_frame) self.project_dropdown.setMinimumSize(20, 0) self.project_refresh = QToolButton(self.project_frame) self.project_refresh.setText('Refresh') self.project_frame_layout.addWidget(self.project_dropdown) self.project_frame_layout.addWidget(self.project_refresh) self.project_frame.setLayout(self.project_frame_layout) self.panel_frame = QFrame(self) self.panel_frame_layout = QVBoxLayout(self.panel_frame) for panel in panels: self.panel_frame_layout.addWidget(panel) self.panel_frame.setLayout(self.panel_frame_layout) self.bottom_frame = QFrame(self) self.bottom_frame_layout = QHBoxLayout(self.bottom_frame) self.bottom_frame_layout.addStretch() self.sg_button = QToolButton() self.sg_button.setText('Extra Stuff') self.bottom_frame_layout.addWidget(self.sg_button) self.bottom_frame.setLayout(self.bottom_frame_layout) self.log = QTextEdit() self.log_widget = QFrame(self) self.log_frame_layout = QHBoxLayout(self.log_widget) self.log_frame_layout.addWidget(self.log) self.log_widget.setLayout(self.log_frame_layout) # 顶层布局设置拉伸系数,仅日志区域可分配拉伸空间 self.total_layout = QVBoxLayout(self) self.total_layout.addWidget(self.toolbar_frame, 0) self.total_layout.addWidget(self.project_frame, 0) self.total_layout.addWidget(self.panel_frame, 0) self.total_layout.addWidget(self.bottom_frame, 0) self.total_layout.addWidget(self.log_widget, 1) self.setLayout(self.total_layout) self.log_button.clicked.connect(self.toggle_log) # 初始化隐藏日志区域 self.log_widget.hide() def toggle_log(self): self.log_widget.setVisible(not self.log_widget.isVisible()) # 自动调整窗口尺寸,仅改动日志相关的底部区域 self.window().adjustSize() class AppPanel(QWidget): def __init__(self, sections=[]): super(AppPanel, self).__init__() # 垂直方向设为固定尺寸策略,不会被拉伸 self.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Fixed ) self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.sections = sections for section in self.sections: self.layout.addWidget(section) class AppSection(QWidget): def __init__(self, buttons=[]): super(AppSection, self).__init__() # 垂直方向设为固定尺寸策略,不会被拉伸 self.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Fixed ) self.buttons = buttons self.layout = QGridLayout() for i, button in enumerate(self.buttons): col = i % 2 row = i // 2 self.layout.addWidget(button, row, col) self.setLayout(self.layout) class AppButton(QToolButton): def __init__(self, text=''): super(AppButton, self).__init__() self.setText(text) self.setFocusPolicy(Qt.NoFocus) self.setIconSize(QSize(50, 50)) self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # 垂直方向设为固定尺寸策略,高度不会变动 self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) if __name__ == '__main__': app = QApplication(sys.argv) app_buttons = [AppButton(text='APPS ' + str(i)) for i in range(5)] custom_btns = [AppButton(text='Custom ' + str(i)) for i in range(5)] app_section = AppSection(buttons=app_buttons) custom_section = AppSection(buttons=custom_btns) panels = [AppPanel(sections=[app_section, custom_section])] ex = AppWidget(panels=panels) lw = AppWindow(main_widget=ex) lw.show() app.exec_()
效果说明
修改后点击Toggle Log按钮时:
- 隐藏TextEdit时,窗口直接裁剪掉底部日志区域,上层所有按钮、工具栏的尺寸、位置完全不变
- 显示TextEdit时,窗口仅向下扩展日志区域,上层控件无任何尺寸变化
- 若用户手动拖拽放大窗口,额外的空间只会分配给TextEdit区域,不会影响上层控件尺寸
内容的提问来源于stack exchange,提问作者Snuh
相关产品推荐
相关产品推荐

