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

QTableView中QStyledItemDelegate绘制的星级控件错位问题

QTableView中QStyledItemDelegate绘制的星级控件错位问题

嘿,我来帮你搞定这个星级控件错位的问题!你已经精准找到了问题根源——option.rect的坐标是单元格在QTableView视口内的局部坐标,但QWidget.render()默认把传入的点当成是相对于父窗口(也就是整个QTableView)的全局坐标,所以才会被表头和窗口边框的偏移带跑。

给你几个解决思路,从简单修复到优化改进都有:

快速修复:调整Painter坐标

只需要修改你的StarRatingDelegate的paint方法,先把painter的原点平移到单元格左上角,再渲染控件到(0,0)位置就可以了:

class StarRatingDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        
    def paint(self, painter, option, index):
        rating = index.data()
        star_rating_widget = StarRatingWidget(10, self.parent())
        star_rating_widget.set_rating(rating)
        
        # 保存painter当前状态,避免影响后续绘制
        painter.save()
        # 把painter原点移到单元格左上角
        painter.translate(option.rect.topLeft())
        # 现在渲染到(0,0)就是单元格的左上角了
        star_rating_widget.render(painter, QPoint(0, 0))
        painter.restore()

性能优化:复用星级控件

你现在每次paint都新建一个StarRatingWidget,对于50行的表格来说还好,但数据多了会很耗性能。可以把控件作为Delegate的成员变量复用:

class StarRatingDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 提前创建一次控件,后续复用
        self.star_widget = StarRatingWidget(10, parent)
        
    def paint(self, painter, option, index):
        rating = index.data()
        self.star_widget.set_rating(rating)
        
        painter.save()
        painter.translate(option.rect.topLeft())
        self.star_widget.render(painter, QPoint(0, 0))
        painter.restore()

更优方案:直接用QPainter绘制星星

其实没必要通过Widget来渲染,直接用QPainter画星星效率更高,还能灵活控制对齐方式(比如居中),彻底避免坐标问题:

from PySide6.QtGui import QFont, Qt

class StarRatingDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.star_size = 12  # 星星的字体大小
        self.star_spacing = 2  # 星星之间的间距
        
    def paint(self, painter, option, index):
        rating = index.data() or 0
        # 计算星星区域的总宽度,方便居中
        total_star_width = 5 * self.star_size + 4 * self.star_spacing
        # 水平居中的起始X坐标
        start_x = option.rect.left() + (option.rect.width() - total_star_width) // 2
        # 垂直居中的起始Y坐标(文字绘制是基线对齐,所以要加上字体大小)
        start_y = option.rect.top() + (option.rect.height() + self.star_size) // 2
        
        painter.save()
        # 设置星星的字体
        star_font = QFont(painter.font().family(), self.star_size)
        painter.setFont(star_font)
        
        for i in range(5):
            current_x = start_x + i * (self.star_size + self.star_spacing)
            star_char = '★' if i < rating else '☆'
            # 已选中的星星用黄色,未选中用灰色
            star_color = Qt.yellow if i < rating else Qt.gray
            painter.setPen(star_color)
            painter.drawText(QPoint(current_x, start_y), star_char)
            
        painter.restore()
        
    def sizeHint(self, option, index):
        # 告诉表格这个列需要的最小宽度和高度
        total_width = 5 * self.star_size + 4 * self.star_spacing
        return QSize(total_width, self.star_size)

额外小修复:修正你的MainWindow代码

顺便提一下,你代码里有两个小错误,可能影响表格的显示效果:

  • self.list.horizontalHeader().setStretchLastSection = True 应该改成 self.list.horizontalHeader().setStretchLastSection(True)(这是方法调用,不是属性赋值)
  • self.list.show_grid = False 应该改成 self.list.setShowGrid(False)

这样调整后,星级控件就会乖乖待在单元格里啦!

备注:内容来源于stack exchange,提问作者Mr. Clear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 12:34:33