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

如何在QTableWidget水平滚动时重定位自定义QHeaderView

解决自定义表头跨列标签随水平滚动移动的问题

你的问题出在没有监听表头的滚动事件,导致滚动时未更新标签位置。以下是修复后的代码,关键改动有两处:

  1. 重写scrollContentsBy方法:表头滚动时触发该方法,调用adjustPositions更新标签位置
  2. 修复重复创建标签的bug:原代码每次显示表头都会新建QLabel,导致标签叠加

修复后的完整代码

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
COL_LEN = 4

class CustomHeader(QHeaderView):
    def __init__(self, orientation, top_text, parent=None):
        super().__init__(orientation, parent)
        self.sectionResized.connect(self.handleSectionResized)
        self.sectionMoved.connect(self.adjustPositions)  # 列移动时同步更新位置
        self.top_text = top_text
        self.top_labels = []
        self.setFixedHeight(20)

    def handleSectionResized(self, i):
        self.adjustPositions()

    # 重写滚动方法,滚动时更新标签位置
    def scrollContentsBy(self, dx, dy):
        super().scrollContentsBy(dx, dy)
        self.adjustPositions()

    def showEvent(self, event):
        # 仅在标签未创建时初始化,避免重复创建
        if not self.top_labels:
            for text, start_col, span in self.top_text:
                label = QLabel(text, self) 
                label.setStyleSheet("font-weight:bold;background-color: gray;border-right: 1px solid white;")
                label.setAlignment(Qt.AlignCenter)
                self.top_labels.append(label)
                label.show()
            self.adjustPositions()
        super().showEvent(event)

    def adjustPositions(self):
        for idx, (text, start_col, span) in enumerate(self.top_text):
            label = self.top_labels[idx]
            span_size = sum(self.sectionSize(i) for i in range(start_col, start_col + span))
            geom = QRect(
                self.sectionViewportPosition(start_col),
                0,
                span_size, 
                self.height(),
            )
            label.setGeometry(geom)

class MyTable(QTableWidget):
    def __init__(self, nrow, child_list, symbol, parent=None):
        nchild = len(child_list)
        ncol = (COL_LEN+1)*nchild + 1
        super().__init__(nrow, ncol, parent=parent)
        top_labels = self.create_label_info(symbol, child_list)
        self.verticalHeader().hide()
        self.setHorizontalHeaderLabels([''] * self.columnCount())
        self.header = CustomHeader(Qt.Horizontal, top_labels, self)
        self.setHorizontalHeader(self.header)

    def create_label_info(self, symbol, child_list):
        top_labels = [(symbol, 0, 1)]
        n = 2
        for e in child_list :
            top_labels.append((e, n, COL_LEN))
            n += COL_LEN + 1
        return top_labels

class DebugWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.table = MyTable(10,  ['Child 1', 'Child 2'], 'Class') 
        layout = QVBoxLayout()
        layout.addWidget(self.table)
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DebugWindow()
    window.show()
    sys.exit(app.exec())

关键改动说明

  • scrollContentsBy重写:当表头水平滚动时,该方法会被触发,在父类完成滚动操作后立即更新标签位置,确保标签跟随列同步移动。
  • 标签创建逻辑优化:增加if not self.top_labels:判断,避免每次显示窗口都重复创建QLabel,防止控件叠加和资源浪费。
  • 新增sectionMoved信号连接:如果用户拖动列调整顺序,标签位置也会同步更新。

现在你的跨列表头标签会在水平滚动、列宽调整、列顺序改变时都保持正确的位置了。

内容的提问来源于stack exchange,提问作者mr_js

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 03:06:04