Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Can QTableWidget header text be vertical, but cell text horizontal?



  • I've got some QTableWidgets in my PyQt5 application where the cell content is short, but some of the cell headers are necessarily quite long.

    Ideally, I would like to be able to render the header text vertically, but only in those columns that need it (most of the columns are fine), but leave all the actual table cell context in the standard horizontal orientation.

    Is this something that Qt supports, or which can be done in PyQt5? I've read conflicting answers online about whether it's actually possible.

    My current test code is below. It doesn't work, but I don't understand why, and whether it will ever work. If I comment out the setItemDelegate line then it works as a standard QTableWidget, but as soon as I attempt to add the delegate, it starts crashing.

    import sys
    
    from PyQt5 import QtGui, QtCore
    from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem, QGridLayout, QFrame, QDialog, \
        QStyledItemDelegate
    
    
    class VerticalTextDelegate(QStyledItemDelegate):
        def __init__(self, parent):
            super(VerticalTextDelegate, self).__init__()
    
        def paint(self, painter, option, index):
            optionCopy = QtGui.QStyleOptionViewItem(option)
            rectCenter = QtCore.QPointF(QtCore.QRectF(option.rect).center())
            painter.save()
            painter.translate(rectCenter.x(), rectCenter.y())
            painter.rotate(-90.0)
            painter.translate(-rectCenter.x(), -rectCenter.y())
            optionCopy.rect = painter.worldTransform().mapRect(option.rect)
    
            # Call the base class implementation
            super(VerticalTextDelegate, self).paint(painter, optionCopy, index)
    
            painter.restore()
    
        def sizeHint(self, option, index):
            val = QtGui.QSize(self.sizeHint(option, index))
            return QtGui.QSize(val.height(), val.width())
    
    
    class VerticalWidgetDialog(QDialog):
        def __init__(self, windowTitle):
            super().__init__()
            self.setWindowTitle(windowTitle)
            self.layout = QGridLayout(self)
    
            self.table = QTableWidget()
            rowCount, columnCount = 8, 6
            self.table.setRowCount(rowCount)
            self.table.setColumnCount(columnCount)
    
            for c in range(0, columnCount):
                self.table.setHorizontalHeaderItem(c, QTableWidgetItem("Header" + str(c)))
    
            for r in range(0, rowCount):
                for c in range(0, columnCount):
                    text = str(r) + ":" + str(c)
                    self.table.setItem(r, c, QTableWidgetItem(text))
            self.table.setItemDelegateForColumn(2, VerticalTextDelegate(self))
    
            self.innerLayout = QGridLayout()
            self.innerLayoutFrame = QFrame()
            self.innerLayoutFrame.setLayout(self.innerLayout)
            self.innerLayout.addWidget(self.table)
            self.layout.addWidget(self.innerLayoutFrame, 0, 0)
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        windowTitle = "Table header test"
        testDialog = VerticalWidgetDialog(windowTitle)
        testDialog.show()
        sys.exit(app.exec_())
    

    The expected result I get without the setItemDelegate line:
    Test QTableWidget with dummy data

    but all attempts to make any of the header text vertical has so far failed.


  • Lifetime Qt Champion

    Hi
    The QHeaderView doesn't support Delegates sadly.
    But you can subclass it and override
    https://doc.qt.io/qt-5/qheaderview.html#paintSection
    and paint the rotated text there.
    Im sorry i don't have any samples for python.



  • OK thanks, I'll look into the subclassing and see if it gives me any success.


Log in to reply