0

I am using a custom header based on QHeaderView to create column labels that span multiple columns. The column labels are themselves QLabel objects. I can correctly resize the column labels when a column is resized. However, it is unclear how to reposition the labels when the table is horizontally scrolled. Currently the custom header labels remain stationary. Below is a working example:

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())
mr_js
  • 949
  • 12
  • 29
  • Why are you doing this? If it's to provide section spanning, that's not a good way to do so, not mentioning the fact that creating the labels in the `showEvent()` is *wrong*, since you're creating new label *every time* the header is shown, including after minimizing and restoring the window. If you are not interested in keeping the default style look, just override [`paintSection()`](https://doc.qt.io/qt-5/qheaderview.html#paintSection). – musicamante Aug 04 '23 at 20:48
  • It's not just to provide section spanning. I also use it for hierarchical labels. – mr_js Aug 05 '23 at 08:40
  • Still, the problem is the same, you should not use QLabel. Override `paintSection()`. – musicamante Aug 05 '23 at 17:34
  • I'm not entirely sure it would fit my use case. I need not only labels that span sections but individual labels for certain section. Do you know if this is possible using paintSection()? – mr_js Aug 05 '23 at 17:37
  • Yes, it is, you just need to do it in a smart way. The `rect` argument of `paintSection()` is the rectangle covered by the given logical section index, so, for individual labels, just draw text within that rectangle, for spanning columns compute the rectangle that contains the spanned sections (using `sectionPosition()` and `sectionSize()`) and draw the text within that new rectangle. – musicamante Aug 05 '23 at 17:48

0 Answers0