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

PyQt6窗口无法随QTableView缩小自动调整尺寸问题求助

解决PyQt6窗口无法随内部部件缩小自动适配的问题

问题概述

窗口包含嵌套布局与部件,核心问题是QTableView随模型行数变化时:

  • 行数增加,窗口能自动适配变大;
  • 行数减少,QTableView高度缩小,但容器窗口保持之前的最大尺寸,其他部件被拉伸填充。
    需要实现窗口无需手动计算尺寸,就能随内容自动缩小的效果。

核心原因

Qt窗口默认不会自动收缩到小于其历史最小尺寸,且当内部部件尺寸变化后,布局不会主动触发窗口重新计算最优尺寸。同时直接给QTableView设置setFixedHeight会限制布局对部件尺寸的动态调整能力。

解决方案

  1. 配置布局尺寸约束:给相关布局设置尺寸约束,让布局能同时限制窗口的最小和最大尺寸。
  2. 动态调整表格尺寸而非固定高度:用setMaximumHeight和setMinimumHeight替代setFixedHeight,保留布局对表格尺寸的调整空间。
  3. 触发窗口重新计算尺寸:在表格内容更新后,让窗口重新计算并应用最优尺寸。

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 13:55:57