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

How to reposition custom QHeaderview when QTableWidget is scrolled horizontally

提问人:mr_js 提问时间:8/4/2023 更新时间:8/4/2023 访问量:25

问:

我正在使用基于 QHeaderView 的自定义标题来创建跨越多个列的列标签。列标签本身就是 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):
        QHeaderView.__init__(self, orientation, parent=None)
        self.sectionResized.connect(self.handleSectionResized)
        self.top_text = top_text
        self.top_labels = list()
        self.setFixedHeight(20)

    def handleSectionResized(self, i):
        # print('Resizing') 
        self.adjustPositions()


    def showEvent(self, event):
        ncol = self.count()
        ntop = len(self.top_text)
        for i in range(ntop):
            text, start_col, span = self.top_text[i]
            print(self.top_text[i])
            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()
        return super().showEvent(event)

        
    def adjustPositions(self):
        ntop = len(self.top_text)
        for i in range(ntop):
            text, start_col, span = self.top_text[i]
            label = self.top_labels[i]
            span_size = self.getSpanSize(start_col, span)
            geom = QRect(
                self.sectionViewportPosition(start_col),
                0, #place at top
                span_size, 
                self.height(),
            )
            label.setGeometry(geom)

    def getSpanSize(self, index, span):
        span_size = 0
        for i in range(index, index+span):
            span_size += self.sectionSize(i)
        return span_size


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()) #uncomment to see column headers moving correctly
        self.header = CustomHeader(Qt.Horizontal, top_labels, self)
        self.setHorizontalHeader(self.header)

    def create_label_info(self, symbol, child_list):
        top_labels = list()
        top_labels.append((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(DebugWindow, self).__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())
pyqt5 qtablewidget pyside6 qheaderview

评论

0赞 musicamante 8/5/2023
你为什么要这么做?如果要提供部分跨越,这不是一个好方法,更不用说在 中创建标签是错误的,因为每次显示标题,您都会创建新标签,包括在最小化和恢复窗口之后。如果您对保留默认样式外观不感兴趣,只需覆盖 paintSection() 即可。showEvent()
0赞 mr_js 8/5/2023
这不仅仅是为了提供部分跨越。我还将其用于分层标签。
0赞 musicamante 8/6/2023
不过,问题是一样的,你不应该使用QLabel。覆盖。paintSection()
0赞 mr_js 8/6/2023
我不完全确定它是否适合我的用例。我不仅需要跨部分的标签,还需要特定部分的单个标签。您知道使用 paintSection() 是否可以做到这一点吗?
0赞 musicamante 8/6/2023
是的,是的,你只需要以一种聪明的方式去做。的参数是给定逻辑节索引所覆盖的矩形,因此,对于单个标签,只需在该矩形内绘制文本,对于跨列,计算包含跨区部分的矩形(使用 和 ),并在该新矩形内绘制文本。rectpaintSection()sectionPosition()sectionSize()

答: 暂无答案