如何在QTableWidget水平滚动时重定位自定义QHeaderView
解决自定义表头跨列标签随水平滚动移动的问题
你的问题出在没有监听表头的滚动事件,导致滚动时未更新标签位置。以下是修复后的代码,关键改动有两处:
- 重写
scrollContentsBy方法:表头滚动时触发该方法,调用adjustPositions更新标签位置 - 修复重复创建标签的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
相关产品推荐
相关产品推荐

