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
相关产品推荐
相关产品推荐

