PyQt6窗口无法随QTableView缩小自动调整尺寸问题求助
解决PyQt6窗口无法随内部部件缩小自动适配的问题
问题概述
窗口包含嵌套布局与部件,核心问题是QTableView随模型行数变化时:
- 行数增加,窗口能自动适配变大;
- 行数减少,QTableView高度缩小,但容器窗口保持之前的最大尺寸,其他部件被拉伸填充。
需要实现窗口无需手动计算尺寸,就能随内容自动缩小的效果。
核心原因
Qt窗口默认不会自动收缩到小于其历史最小尺寸,且当内部部件尺寸变化后,布局不会主动触发窗口重新计算最优尺寸。同时直接给QTableView设置setFixedHeight会限制布局对部件尺寸的动态调整能力。
解决方案
- 配置布局尺寸约束:给相关布局设置尺寸约束,让布局能同时限制窗口的最小和最大尺寸。
- 动态调整表格尺寸而非固定高度:用
setMaximumHeight和setMinimumHeight替代setFixedHeight,保留布局对表格尺寸的调整空间。 - 触发窗口重新计算尺寸:在表格内容更新后,让窗口重新计算并应用最优尺寸。
修改后的完整代码
import sys from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QFrame, QLabel, QComboBox, \ QTableView, QHeaderView, QPushButton, QLayout class MainWindow(QMainWindow): def __init__(self): super().__init__() """Set up main window definition - fixed width of 450, with an internal vertical layout.""" self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.layout = QHBoxLayout() self.central_widget.setLayout(self.layout) self.setFixedWidth(450) self.sub_layout = QVBoxLayout() # 给主布局设置尺寸约束,确保整体生效 self.sub_layout.setSizeConstraint(QLayout.SizeConstraint.SetMinAndMaxSize) self.layout.addLayout(self.sub_layout) """populate vertical layout""" self.top_section = TopSection() self.sub_layout.addWidget(self.top_section) """separator frame for visibility""" sep = QFrame() sep.setFrameShape(QFrame.Shape.HLine) self.sub_layout.addWidget(sep) self.bottom_section = BottomSection() self.sub_layout.addWidget(self.bottom_section) class TopSection(QWidget): def __init__(self): super().__init__() self.layout = QHBoxLayout() self.setLayout(self.layout) """in the real application, this frame is replaced with nested layouts and widgets""" example_frame = QFrame() example_frame.setFrameShape(QFrame.Shape.Box) example_frame.setFrameShadow(QFrame.Shadow.Plain) example_frame.setFixedHeight(200) example_frame.setFixedWidth(250) self.layout.addWidget(example_frame) class BottomSection(QWidget): def __init__(self): super().__init__() self.layout = QVBoxLayout() self.setLayout(self.layout) # 设置布局尺寸约束,让布局控制窗口最小/最大尺寸 self.layout.setSizeConstraint(QLayout.SizeConstraint.SetMinAndMaxSize) """Label Header""" label = QLabel("Lorem ipsum dolor sit amet...") self.layout.addWidget(label) """QComboBox for selecting main list item and simulating state changes""" combo = QComboBox() self.layout.addWidget(combo) combo.addItem("Show 2 rows") combo.addItem("Show 10 rows") """QTableView for showing attributes of main list item""" self.table = QTableView() """hide headers for cleaner look""" self.table.verticalHeader().setVisible(False) self.table.horizontalHeader().setVisible(False) self.table.setFixedWidth(250) self.layout.addWidget(self.table) table_model = MyTableModel() self.table.setModel(table_model) """QPushButton for parity with actual implementation layout""" button = QPushButton("This does nothing") self.layout.addWidget(button) """simulate initial state""" self.make_rows(2) self.resize_table() """simulate state change driver""" combo.currentIndexChanged.connect(self.simulate_state_change) def make_rows(self, row_count: int): self.table.model().clear() for i in range(row_count): self.table.model().insertRow(i) self.table.model().setData(self.table.model().index(i, 0), i) self.table.model().setData(self.table.model().index(i, 1), -i) self.table.setRowHeight(i, 25) def resize_table(self): self.table.setColumnWidth(0, 40) self.table.setColumnWidth(1, 210) """cap the table height at 6 rows high, but let it be smaller than 6 rows high if needed""" height = min(150, self.table.model().rowCount() * 25) # 用最大/最小高度替代固定高度,保留布局调整空间 self.table.setMaximumHeight(height) self.table.setMinimumHeight(height) self.table.update() def simulate_state_change(self): self.make_rows(10 if self.table.model().rowCount() == 2 else 2) self.resize_table() # 触发窗口重新计算并应用最优尺寸 self.window().resize(self.window().sizeHint()) class MyTableModel(QAbstractTableModel): def __init__(self): super().__init__() self.records = [] def rowCount(self, parent=QModelIndex()): return len(self.records) def columnCount(self, parent=QModelIndex()): return 2 def insertRow(self, row, parent=QModelIndex()): self.beginInsertRows(parent, row, row) self.records.insert(row, [0, 0]) self.endInsertRows() def data(self, index, role=Qt.ItemDataRole.DisplayRole): if not index.isValid(): return None if role != Qt.ItemDataRole.DisplayRole: return None return self.records[index.row()][index.column()] def setData(self, index, value, role=Qt.ItemDataRole.EditRole): if not index.isValid(): return False if role == Qt.ItemDataRole.EditRole: self.records[index.row()][index.column()] = value self.dataChanged.emit(index, index, [role]) def clear(self): self.beginResetModel() self.records = [] self.endResetModel() # emits modelReset() app = QApplication([]) window = MainWindow() window.show() sys.exit(app.exec())
关键修改说明
- 给
MainWindow的sub_layout和BottomSection的layout都设置了SetMinAndMaxSize约束,让布局能根据内容动态调整窗口的最小和最大尺寸。 - 将
setFixedHeight替换为setMaximumHeight和setMinimumHeight,既固定了表格的目标高度,又不阻止布局对整体尺寸的计算。 - 在
simulate_state_change末尾调用self.window().resize(self.window().sizeHint()),强制窗口重新计算并应用当前内容的最优尺寸,触发收缩。
内容的提问来源于stack exchange,提问作者mikstravaganza
相关产品推荐
相关产品推荐

