You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QStyledItemDelegate文本对齐问题:如何修正单元格文本偏移?

QStyledItemDelegate单元格上色后文本偏移的解决办法

问题描述

使用QStyledItemDelegate为特定单元格上色后,单元格内文本大幅偏移至左上区域。尝试用对齐标志仅能小幅缓解,文本仍偏左;使用translate方法会移动整个单元格而非仅文本。需要实现仅调整文本位置,同时保持单元格背景高亮的效果。

原问题代码

import sys

import pandas as pd
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Qt
from PySide6.QtGui import QPen, QBrush, QColor
from PySide6.QtWidgets import QStyledItemDelegate


class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, data):
        super(TableModel, self).__init__()
        self._dfDisplay = data
        self._data = data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            value = self._data[index.column()][index.row()]
            return str(value)

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, index):
        return self._data.shape[1]

    def headerData(self, col, orientation, role):
        if orientation == Qt.Orientation.Horizontal:
            if role == Qt.ItemDataRole.DisplayRole:
                return str(self._dfDisplay.columns[col])
        return None


class MyDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        super().paint(painter, option, index)
        painter.setPen(QPen(Qt.GlobalColor.black, 3))

        # Highlight target cells
        if index.siblingAtColumn(0).data() in ["GOAT"]:
            if index.column() > 0:
                # Set fill color
                brush = QBrush(QColor('#90EE90'))
                brush.setStyle(Qt.SolidPattern)
                painter.setBrush(brush)
                # Set outline color
                painter.setPen(Qt.GlobalColor.white)
                painter.drawRect(option.rect)
                # Draw text
                painter.setPen(Qt.GlobalColor.black)
                painter.translate(1, 0)
                painter.drawText(option.rect,Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, str(index.data()))


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.table = QtWidgets.QTableView()
        self.delegate = MyDelegate()
        self.table.setItemDelegate(self.delegate)

        # 原代码中CustomHeaderView未定义,此处注释处理
        # header_view = CustomHeaderView(QtCore.Qt.Orientation.Horizontal)
        # self.table.setHorizontalHeader(header_view)
        data = pd.DataFrame([["GOAT", "Giraffe", "Potatoe", "Another"],
                             [77, 33, 111111, 233],
                             [50, 70, 89, 100000]
                             ])
        print(data)

        self.model = TableModel(data)
        self.table.setModel(self.model)
        self.setCentralWidget(self.table)


app = QtWidgets.QApplication(sys.argv)

window = MainWindow()
window.show()
app.exec()

问题分析

原代码的核心问题:

  1. 先调用super().paint()已经完成了文本和单元格的绘制,之后再次手动绘制矩形和文本,导致内容重叠且位置偏移。
  2. 手动绘制文本时未考虑Qt样式的内边距(padding),直接使用option.rect绘制,导致文本紧贴单元格边缘。
  3. translate是对整个画家坐标系的偏移,会影响后续所有绘制操作,而非仅当前文本。

解决方案

方案一:修改Option再调用父类绘制(推荐)

通过修改QStyleOptionViewItem的背景色,让父类的paint()方法自动处理文本对齐和边距,无需手动绘制文本和矩形:

class MyDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        # 判断是否需要高亮目标单元格
        if index.siblingAtColumn(0).data() == "GOAT" and index.column() > 0:
            # 设置单元格背景色
            option.backgroundBrush = QBrush(QColor('#90EE90'))
            # 可选:添加选中状态的视觉反馈
            option.state |= Qt.StateFlag.State_Selected
        
        # 调用父类方法完成所有标准绘制流程
        super().paint(painter, option, index)

方案二:手动绘制但处理内边距

如果必须自定义绘制细节,需要保存画家状态,调整绘制区域为扣除内边距的内容区域,避免重复绘制:

class MyDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        # 保存画家初始状态,避免影响后续绘制
        painter.save()

        # 判断是否需要高亮
        if index.siblingAtColumn(0).data() == "GOAT" and index.column() > 0:
            # 绘制背景矩形
            brush = QBrush(QColor('#90EE90'))
            painter.setBrush(brush)
            painter.setPen(QPen(Qt.GlobalColor.white))
            painter.drawRect(option.rect)

            # 获取扣除内边距的内容绘制区域
            style = option.widget.style() if option.widget else QtWidgets.QApplication.style()
            content_rect = style.subElementRect(QtWidgets.QStyle.SubElement.SE_ItemViewItemText, option, option.widget)
            
            # 在内容区域内绘制文本,保持对齐
            painter.setPen(QPen(Qt.GlobalColor.black))
            painter.drawText(content_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, str(index.data()))
        else:
            # 非高亮单元格直接使用父类绘制逻辑
            super().paint(painter, option, index)
        
        # 恢复画家初始状态
        painter.restore()

说明

  • 方案一利用Qt内置样式系统,自动处理文本对齐、内边距和各种交互状态(选中、悬停等),代码简洁且兼容性更强。
  • 方案二更灵活,适合需要高度自定义绘制效果的场景,但需要手动处理样式内边距和状态逻辑。

内容的提问来源于stack exchange,提问作者David Frick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:41:25