如何在PyQt5 Python中自动换行QTableWidget的标题内容

问题描述

我正在PyQt5所在的QTableWidget工作。它有一个标题列,我想自动换行。下面是表格的样子:

enter image description here

我们可以看到,像Maximum Variation Coefficient这样的标头标签有3个字,因此占用了过多的列宽。如何将单词包装在标题中。

下面是代码

import sys

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *


# Main Window
class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'PyQt5 - QTableWidget'
        self.left = 0
        self.top = 0
        self.width = 300
        self.height = 200

        self.setwindowTitle(self.title)
        self.setGeometry(self.left,self.top,self.width,self.height)

        self.createTable()

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)
        self.setLayout(self.layout)

        # Show window
        self.show()

    # Create table
    def createTable(self):
        self.tableWidget = QTableWidget()

        # Row count
        self.tableWidget.setRowCount(3)

        # Column count
        self.tableWidget.setColumnCount(2)

        self.tableWidget.setHorizontalHeaderLabels(["Maximum Variation Coefficient","Maximum Variation Coefficient"])
        self.tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizetoContents)

        self.tableWidget.setItem(0,QTableWidgetItem("3.44"))
        self.tableWidget.setItem(0,1,QTableWidgetItem("5.3"))
        self.tableWidget.setItem(1,QTableWidgetItem("4.6"))
        self.tableWidget.setItem(1,QTableWidgetItem("1.2"))
        self.tableWidget.setItem(2,QTableWidgetItem("2.2"))
        self.tableWidget.setItem(2,QTableWidgetItem("4.4"))

        # Table will fit the screen horizontally
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        self.tableWidget.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

我尝试添加self.tableWidget.setWordWrap(True),但这并没有做任何更改。任何人都可以提供一些好的解决方案。请帮忙。谢谢

编辑:

也尝试过这个:

self.tableWidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignHCenter | Qt.Alignment(QtCore.Qt.TextWordWrap))

但是没有用

解决方法

为了实现所需的功能,必须设置自己的标头并继续以下两个假设:

  • 如果列的 width 不足,则标题必须根据节内容提供正确的大小提示 height
  • 文本对齐方式必须包含QtCore.Qt.TextWordWrap标志,以便绘画者知道它可以包装文本;

第一点需要继承QHeaderView的子类并重新实现sectionSizeFromContents()

class WrapHeader(QtWidgets.QHeaderView):
    def sectionSizeFromContents(self,logicalIndex):
        # get the size returned by the default implementation
        size = super().sectionSizeFromContents(logicalIndex)
        if self.model():
            if size.width() > self.sectionSize(logicalIndex):
                text = self.model().headerData(logicalIndex,self.orientation(),QtCore.Qt.DisplayRole)
                if not text:
                    return size
                # in case the display role is numeric (for example,when header 
                # labels are not defined yet),convert it to a string; 
                text = str(text)

                option = QtWidgets.QStyleOptionHeader()
                self.initStyleOption(option)
                alignment = self.model().headerData(logicalIndex,QtCore.Qt.TextAlignmentRole)
                if alignment is None:
                    alignment = option.textAlignment

                # get the default style margin for header text and create a 
                # possible rectangle using the current section size,then use
                # QFontMetrics to get the required rectangle for the wrapped text
                margin = self.style().pixelMetric(
                    QtWidgets.QStyle.PM_HeaderMargin,option,self)
                maxWidth = self.sectionSize(logicalIndex) - margin * 2
                rect = option.fontMetrics.boundingRect(
                    QtCore.QRect(0,maxWidth,10000),alignment | QtCore.Qt.TextWordWrap,text)

                # add vertical margins to the resulting height
                height = rect.height() + margin * 2
                if height >= size.height():
                    # if the height is bigger than the one provided by the base
                    # implementation,return a new size based on the text rect
                    return QtCore.QSize(rect.width(),height)
        return size


class App(QWidget):
    # ...
    def createTable(self):
        self.tableWidget = QTableWidget()
        self.tableWidget.setHorizontalHeader(
            WrapHeader(QtCore.Qt.Horizontal,self.tableWidget))
        # ...

然后,要设置自动换行标志,有两个选项:

  1. 使用每个现有列的setHeaderData()在基础模型上设置对齐标记:
        # ...
        model = self.tableWidget.model()
        default = self.tableWidget.horizontalHeader().defaultAlignment()
        default |= QtCore.Qt.TextWordWrap
        for col in range(self.tableWidget.columnCount()):
            alignment = model.headerData(
                col,QtCore.Qt.Horizontal,QtCore.Qt.TextAlignmentRole)
            if alignment:
                alignment |= QtCore.Qt.TextWordWrap
            else:
                alignment = default
            model.setHeaderData(
                col,alignment,QtCore.Qt.TextAlignmentRole)
  1. 通过在选项上应用标志,使用QProxyStyle覆盖标题的绘制:
# ...

class ProxyStyle(QtWidgets.QProxyStyle):
    def drawControl(self,control,painter,widget=None):
        if control in (self.CE_Header,self.CE_HeaderLabel):
            option.textAlignment |= QtCore.Qt.TextWordWrap
        super().drawControl(control,widget)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle(ProxyStyle())
    ex = App()
    sys.exit(app.exec_())

最后,请考虑将setSectionResizeModeResizeToContentsStretch一起使用,将始终导致表尝试使用标头所需的空间第一次显示时。