PAYATHON 2026

PyQt5 中如何让 QTableWidget 的所有单元格随窗口缩放并保持可见?

数据阿明

可以为水平和垂直表头设置 QHeaderView.Stretch,再通过 setMinimumSectionSize() 降低列宽和行高的最小限制。使用 Qt.ScrollBarAlwaysOff 隐藏滚动条。

这样行列可以随表格的可用空间自动调整。窗口过小或行列过多时,无法同时保证所有单元格可见、文字完整且清晰可读。

为什么原来的设置没有达到预期?

原代码只设置了水平表头:

self.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)

这会让设置过的列宽随可用宽度调整,但行高不会随可用高度调整。要显示全部行,还需要设置垂直表头。

QHeaderView 的 minimumSectionSize 属性限制每个 section 的最小尺寸:水平表头限制列宽,垂直表头限制行高。默认限制可能与字体和样式有关。

你观察到的 58 可以先从这个属性排查,但不能据此认定所有环境的最小列宽都是 58:

print(self.horizontalHeader().minimumSectionSize())
print(self.verticalHeader().minimumSectionSize())

将调整模式改为 QHeaderView.Fixed 不会自动解除最小尺寸限制。Stretch 模式会自动分配尺寸,也不适合同时用 setColumnWidth() 手动控制列宽。

同时设置行列伸缩和最小尺寸

下面的 PyQt5 示例保留原来的窗口布局方式,展示尺寸适配设置:

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
    QHeaderView,
    QHBoxLayout,
    QMainWindow,
    QTableWidget,
    QWidget,
)


class ProductsTable(QTableWidget):
    def __init__(self, table_rows, table_columns):
        super().__init__()
        self.setRowCount(table_rows)
        self.setColumnCount(table_columns)

        horizontal_header = self.horizontalHeader()
        vertical_header = self.verticalHeader()

        # 允许列宽和行高缩小到 1 像素。
        horizontal_header.setMinimumSectionSize(1)
        vertical_header.setMinimumSectionSize(1)

        # 分别根据可用宽度和高度自动分配尺寸。
        horizontal_header.setSectionResizeMode(QHeaderView.Stretch)
        vertical_header.setSectionResizeMode(QHeaderView.Stretch)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)


class MainWindow(QMainWindow):
    def __init__(self, table_rows, table_columns):
        super().__init__()
        self.setWindowTitle("My App")

        table = ProductsTable(table_rows, table_columns)
        layout = QHBoxLayout()
        layout.addWidget(table)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

这里的 1 表示允许的最小尺寸,并不会直接把所有单元格设成 1 像素。Stretch 会根据表格的可用空间分配实际尺寸,窗口大小变化时也会重新调整,通常不需要重写 resizeEvent()。

原来着色、拖放等与尺寸无关的逻辑可以继续保留。

为什么使用滚动条策略?

原代码通过以下方式隐藏滚动条:

self.verticalScrollBar().setVisible(False)
self.horizontalScrollBar().setVisible(False)

对于 QTableWidget,用滚动条策略表达“始终不显示”更合适:

self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

隐藏滚动条不会压缩单元格。行高和列宽的调整仍依赖两个表头的 Stretch 模式和最小尺寸设置;空间不足时,只隐藏滚动条仍可能导致内容被裁切。

全部可见有哪些限制?

单元格显示在 table.viewport() 中,它的尺寸不包含表头和边框。可以在窗口显示后检查:

print(table.viewport().width(), table.viewport().height())

假设有 C 列、R 行,最小列宽为 w_min、最小行高为 h_min,至少需要满足:

viewport_width >= C * w_min
viewport_height >= R * h_min

即使满足这些条件,极小的单元格也可能只剩网格线,文字无法完整显示。如果主要用途是查看颜色分布,可以考虑隐藏表头或关闭网格,给概览留出更多空间:

self.horizontalHeader().hide()
self.verticalHeader().hide()
self.setShowGrid(False)

这些设置可以按需选用。如果必须看清每个单元格中的文字,就需要保留合理的最小尺寸,并在空间不足时允许滚动,或者限制窗口继续缩小。

备注:内容仅供参考。