如何实现QTableWidget表头随滚动条滚动而非固定显示?
在PySide6中实现QTableWidget表头随滚动条同步滚动
默认情况下,QTableWidget的水平和垂直表头会固定显示,不会随滚动条滚动。如果想要让表头随内容一起滚动,不再始终保持可见,可通过以下方式实现。
当前效果

期望效果

原始示例代码
import string import random from typing import Optional from PySide6.QtCore import Qt from PySide6.QtWidgets import QTableWidget, QWidget, QAbstractItemView, QHeaderView, QTableWidgetItem, QHBoxLayout, \ QApplication class DataTable(QTableWidget): def __init__(self, parent: Optional[QWidget] = None): super(DataTable, self).__init__(parent) self.setContextMenuPolicy(Qt.CustomContextMenu) self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setTextElideMode(Qt.ElideMiddle) for j in range(20): if self.columnCount() <= j: self.insertColumn(j) for i in range(20): if self.rowCount() <= i: self.insertRow(i) self.setItem(i, j, QTableWidgetItem('Hello')) word = ''.join(random.choice(string.ascii_letters) for _ in range(6)) self.setHorizontalHeaderLabels([str(word) for i in range(self.columnCount())]) self.setVerticalHeaderLabels([str(word) for i in range(self.rowCount())]) if __name__ == '__main__': app = QApplication() widget = QWidget() widget.resize(400, 600) layout = QHBoxLayout(widget) layout.addWidget(DataTable()) widget.show() app.exec()
实现方案
核心思路是将表头的父控件改为表格的视口,并同步表头与内容区域的滚动动作:
1. 修改表头父控件
在DataTable的__init__方法中,将水平和垂直表头的父对象设置为表格的视口,让表头成为视口的子控件,从而随视口滚动:
# 将水平表头父控件设为视口 self.horizontalHeader().setParent(self.viewport()) # 将垂直表头父控件设为视口 self.verticalHeader().setParent(self.viewport())
2. 同步滚动动作
重写scrollContentsBy方法,在内容滚动时同步表头的滚动偏移量:
def scrollContentsBy(self, dx: int, dy: int) -> None: super().scrollContentsBy(dx, dy) # 同步水平表头滚动 self.horizontalHeader().scroll(dx, 0) # 同步垂直表头滚动 self.verticalHeader().scroll(0, dy)
完整修改后的代码
import string import random from typing import Optional from PySide6.QtCore import Qt from PySide6.QtWidgets import QTableWidget, QWidget, QAbstractItemView, QHeaderView, QTableWidgetItem, QHBoxLayout, \ QApplication class DataTable(QTableWidget): def __init__(self, parent: Optional[QWidget] = None): super(DataTable, self).__init__(parent) self.setContextMenuPolicy(Qt.CustomContextMenu) self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setTextElideMode(Qt.ElideMiddle) # 修改表头父控件为视口 self.horizontalHeader().setParent(self.viewport()) self.verticalHeader().setParent(self.viewport()) for j in range(20): if self.columnCount() <= j: self.insertColumn(j) for i in range(20): if self.rowCount() <= i: self.insertRow(i) self.setItem(i, j, QTableWidgetItem('Hello')) word = ''.join(random.choice(string.ascii_letters) for _ in range(6)) self.setHorizontalHeaderLabels([str(word) for i in range(self.columnCount())]) self.setVerticalHeaderLabels([str(word) for i in range(self.rowCount())]) def scrollContentsBy(self, dx: int, dy: int) -> None: super().scrollContentsBy(dx, dy) # 同步表头滚动偏移量 self.horizontalHeader().scroll(dx, 0) self.verticalHeader().scroll(0, dy) if __name__ == '__main__': app = QApplication() widget = QWidget() widget.resize(400, 600) layout = QHBoxLayout(widget) layout.addWidget(DataTable()) widget.show() app.exec()
原理说明
- QTableWidget默认将表头放在自身容器下,而非视口内,因此表头不会随视口滚动;修改父控件为视口后,表头会随视口的滚动而移动。
- 重写
scrollContentsBy方法,确保内容滚动时表头的滚动偏移量与内容完全一致,实现同步滚动效果。
内容的提问来源于stack exchange,提问作者laurapons
相关产品推荐
相关产品推荐

