PySide6中如何让QGraphicsSimpleTextItem在父项居中且不受视图变换影响?
解决方案
方法1:使用ItemIgnoresTransformations并动态更新位置
设置ItemIgnoresTransformations后,文本项的坐标系统会切换到视图坐标系(而非父项的局部坐标系),直接用父项局部坐标计算中心会失效,需要通过坐标转换同步位置,同时可添加裁剪控制文本显示范围。
代码实现
from PySide6.QtWidgets import QGraphicsSimpleTextItem, QGraphicsItem from PySide6.QtCore import Qt, QPointF, QRectF from PySide6.QtGui import QPainter, QFont, QColor class FixedCenterTextItem(QGraphicsSimpleTextItem): def __init__(self, text, parent=None): super().__init__(text, parent) self.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self._font = QFont("Arial", 12) self.setFont(self._font) self.update_text_position() def update_text_position(self): if not self.parentItem(): return # 获取父项局部坐标系中心,转换为视图坐标系坐标 parent_center = self.parentItem().boundingRect().center() view_pos = self.parentItem().mapToScene(parent_center) self_pos = self.mapFromScene(view_pos) # 偏移文本自身尺寸的一半,实现真正居中 text_rect = self.boundingRect() self.setPos(self_pos - QPointF(text_rect.width()/2, text_rect.height()/2)) def itemChange(self, change, value): # 监听父项几何/变换变化,实时更新位置 if change in (QGraphicsItem.ItemParentChange, QGraphicsItem.ItemParentHasChanged): self.update_text_position() elif change == QGraphicsItem.ItemSceneHasChanged: self.update_text_position() return super().itemChange(change, value) def paint(self, painter, option, widget=None): # 添加裁剪,隐藏超出父项范围的文本 if self.parentItem(): parent_rect = self.parentItem().boundingRect() clip_rect = self.mapFromParent(parent_rect).boundingRect() painter.setClipRect(clip_rect) painter.setPen(QColor("purple")) painter.setFont(self._font) super().paint(painter, option, widget)
方法2:重写paint方法,手动控制变换和位置
如果不想依赖ItemIgnoresTransformations,可在paint阶段暂时重置 painter 变换,同时通过坐标转换计算正确的绘制位置,添加裁剪逻辑。
代码实现
from PySide6.QtWidgets import QGraphicsSimpleTextItem, QGraphicsItem from PySide6.QtCore import Qt, QPointF, QRectF from PySide6.QtGui import QPainter, QFont, QColor, QTransform class FixedCenterTextItem(QGraphicsSimpleTextItem): def __init__(self, text, parent=None): super().__init__(text, parent) self._font = QFont("Arial", 12) self.setFont(self._font) def paint(self, painter, option, widget=None): if not self.parentItem(): super().paint(painter, option, widget) return # 保存原始变换,后续恢复 original_transform = painter.worldTransform() # 将父项局部中心转换为视图坐标系位置 parent_center = self.parentItem().boundingRect().center() scene_center = self.parentItem().mapToScene(parent_center) view_center = widget.mapFromScene(scene_center) if widget else scene_center # 重置painter变换,让文本不受视图缩放影响 painter.setTransform(QTransform()) # 计算文本居中绘制的偏移位置 text_rect = self.boundingRect() draw_pos = QPointF(view_center.x() - text_rect.width()/2, view_center.y() - text_rect.height()/2) # 添加裁剪,隐藏超出父项范围的文本 parent_scene_rect = self.parentItem().mapToScene(self.parentItem().boundingRect()).boundingRect() parent_view_rect = widget.mapFromScene(parent_scene_rect) if widget else parent_scene_rect painter.setClipRect(parent_view_rect) # 绘制文本 painter.setPen(QColor("purple")) painter.setFont(self._font) painter.drawText(draw_pos, self.text()) # 恢复原始变换 painter.setTransform(original_transform)
关键注意事项
ItemIgnoresTransformations会让项脱离父项变换系统,必须通过mapToScene/mapFromScene完成坐标转换- 文本居中需考虑自身
boundingRect,偏移一半尺寸才能实现真正的中心对齐 - 裁剪功能需要将父项边界转换到对应坐标系(视图/局部),才能正确隐藏超出部分
- 使用
fitInView时,视图变换会影响场景坐标,必须依赖坐标转换同步位置
内容的提问来源于stack exchange,提问作者hollarob
相关产品推荐
相关产品推荐

