如何为PyQt5 QTableView表头设置两行不同字体的文本?
实现QTableView表头两行不同样式文本
问题背景
当前运行代码后的表头仅能显示普通换行文本,无法区分字号与粗细;期望实现水平表头每个单元格显示两行居中文本:上行使用较大字号,下行使用较小字号且为粗体。
尝试过HTML和QCSS样式,但Qt表头不支持该方式,需通过实现QStyledItemDelegate并绑定到QHeaderView来实现。此前代码错误地将Delegate设置给了表格列(仅修改数据单元格),未作用于表头。
解决方案
核心步骤:
- 自定义
QStyledItemDelegate,在paint方法中手动绘制两行不同样式的文本 - 将自定义Delegate绑定到表格的水平表头(而非表格列)
- 调整表头高度以适配两行文本显示
以下是修改后的完整代码:
# base of example from https://www.pythonguis.com/tutorials/qtableview-modelviews-numpy-pandas/ import sys from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import QStyledItemDelegate coltxts = { 0: "STANDARD_COLUMN_DESCRIPTION", 1: "ADVANCED_COLUMN_DESCRIPTION", 2: "VERBOSE_COLUMN_DESCRIPTION", } class TableModel(QtCore.QAbstractTableModel): def __init__(self, data): super(TableModel, self).__init__() self._data = data def data(self, index, role): if role == Qt.DisplayRole: return self._data[index.row()][index.column()] def headerData(self, section, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal: if role == Qt.DisplayRole: # 返回元组存储两行文本内容 return ("Column {}".format(section), coltxts[section]) return super().headerData(section, orientation, role) def rowCount(self, index): return len(self._data) def columnCount(self, index): return len(self._data[0]) class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.table = QtWidgets.QTableView() data = [ [4, 9, 2], [1, 0, 0], [3, 5, 0], [3, 3, 2], [7, 8, 9], ] self.model = TableModel(data) self.table.setModel(self.model) # 给水平表头设置自定义Delegate header_delegate = TwoLineHeaderDelegate(self.table) self.table.horizontalHeader().setItemDelegate(header_delegate) # 设置表头高度以适配两行文本 self.table.horizontalHeader().setMinimumHeight(60) # 自动调整列宽 QTimer.singleShot(10, self.table.resizeColumnsToContents) self.setCentralWidget(self.table) class TwoLineHeaderDelegate(QStyledItemDelegate): def paint(self, painter, option, index): # 获取表头的两行文本内容 header_text = index.data(Qt.DisplayRole) if not isinstance(header_text, tuple) or len(header_text) !=2: super().paint(painter, option, index) return top_text, bottom_text = header_text # 保存 painter 状态,避免影响后续绘制 painter.save() # 设置绘制区域为表头单元格范围 painter.setClipRect(option.rect) # 绘制背景(保持原表头样式) if option.state & QtWidgets.QStyle.State_Selected: painter.fillRect(option.rect, option.palette.highlight()) # ------------------- 绘制上行文本(大字号) ------------------- top_font = painter.font() top_font.setPointSize(top_font.pointSize() + 2) # 增大字号 painter.setFont(top_font) # 计算上行文本的绘制位置(居中) top_rect = QtCore.QRect(option.rect) top_rect.setHeight(option.rect.height() // 2) painter.drawText(top_rect, Qt.AlignCenter, top_text) # ------------------- 绘制下行文本(小字号+粗体) ------------------- bottom_font = painter.font() bottom_font.setPointSize(bottom_font.pointSize() - 2) # 减小字号 bottom_font.setBold(True) # 设置粗体 painter.setFont(bottom_font) # 计算下行文本的绘制位置(居中) bottom_rect = QtCore.QRect(option.rect) bottom_rect.setTop(option.rect.top() + option.rect.height() // 2) painter.drawText(bottom_rect, Qt.AlignCenter, bottom_text) # 恢复 painter 状态 painter.restore() def sizeHint(self, option, index): # 自定义表头单元格的大小提示,确保能放下两行文本 hint = super().sizeHint(option, index) hint.setHeight(max(hint.height(), 60)) return hint app=QtWidgets.QApplication(sys.argv) window=MainWindow() window.show() app.exec_()
代码说明
- Model修改:
headerData返回包含两行文本的元组,方便Delegate获取内容 - 自定义Delegate:
- 在
paint方法中分别绘制两行文本,设置不同的字体属性(字号、粗细) - 手动计算每行文本的绘制区域,确保居中对齐
- 保留原表头的选中状态绘制逻辑,保持样式一致性
- 在
- 表头绑定:通过
self.table.horizontalHeader().setItemDelegate()将Delegate绑定到水平表头 - 尺寸调整:设置表头最小高度,确保两行文本完全显示
内容的提问来源于stack exchange,提问作者sdbbs
相关产品推荐
相关产品推荐

