我正在尝试在 QGraphicsPixMapItem 的右上角显示文本。我希望文本在屏幕上保持恒定的大小,而不管图像的大小如何,因此我在文本上使用 ItemIgnoresTransformations 标志。但是,当我这样做时,我无法弄清楚如何在 QGraphicsPixMapItems 坐标系中获取文本项的正确宽度。
以下是我目前的尝试:
QGraphicsPixmapItem *pixMapItem = m_GraphicsScene->addPixmap(QPixmap(imagePath));
m_GraphicsScene->setSceneRect(pixMapItem->boundingRect());
m_GraphicsView->FitViewToScene();
QGraphicsSimpleTextItem *nameTextItem = new QGraphicsSimpleTextItem("Item Name", pixMapItem);
QGraphicsSimpleTextItem *priceTextItem = new QGraphicsSimpleTextItem("$0.00", pixMapItem);
nameTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations);
priceTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations);
QFont f = QFont();
f.setPointSize(30);
nameTextItem->setFont(f);
priceTextItem->setFont(f);
priceTextItem->setPos(pixMapItem->boundingRect().width() - priceTextItem->boundingRect().width(), 0);
我认为错误在于减去priceTextItem的宽度,因为相对位置根据pixmapitem的分辨率而变化。如果正确,文本的右边缘应该完全位于图像的右边缘。
问题是用于放置文本项的坐标系是像素图项的坐标系。
它的原点在相对于像素图项目的逻辑坐标中总是相同的,因此它的“左”总是正确的:它的第一个字母总是几乎在图像的相同位置对齐(根据字体和字符)。
由于文本项忽略变换,因此看起来文本被“移动”,但这只是一种“视错觉”:如果图像不改变大小,但文本项被缩放,也会发生同样的情况,这本质上是同一件事(这两种转换是不兼容的)。
对此有两种可能的解决方案。
此方法使用项目的
paint()
的覆盖,这是了解项目实际转换的最简单方法。
实际上,我们:
paint()
;transform()
;我无法真正用 C++ 编写,但下面的 python 示例应该足够清晰,可以作为某种形式的伪代码运行:
class PixmapItem(QGraphicsPixmapItem):
def __init__(self, pixmap):
super().__init__(pixmap)
self.textItem = QGraphicsSimpleTextItem('Hello world!', self)
self.textItem.setFlag(QGraphicsItem.ItemIgnoresTransformations)
def paint(self, qp, opt, widget=None):
super().paint(qp, opt, widget)
scale = qp.transform().m11()
self.textItem.setX(
self.boundingRect().right()
- self.textItem.boundingRect().width() / scale
)
在这种情况下,我们使用“中间项”方法。这个概念是使用一个空的 QGraphicsItem 作为像素图项的子项,它将忽略转换,然后将文本项添加为它自己的子项。
该容器将被放置在父级(像素图)的右边缘,因此无论进行何种变换,其原点始终是绝对的。然后,实际的 QGraphicsSimpleTextItem 将被创建为该容器的子级,并且其
x
将是其边界矩形宽度的负数。由于文本项将从父级继承“无变换”,因此其坐标系将相同,并且与该父级的位置一致;由于父级的原点相对放置在像素图项边界矩形上,因此文本最终显示在所需的位置。
可以使用用作“代理容器”的 QGraphicsRectItem 来实现上述内容:技巧是使用 null rect(无大小),这显然会导致无法绘制。
如上,一个基本的Python示例:
pmItem = scene.addPixmap(QPixmap(path))
container = QGraphicsRectItem(0, 0, 0, 0, pmItem)
container.setX(pmItem.boundingRect().right())
container.setFlag(QGraphicsItem.ItemIgnoresTransformations)
textItem = QGraphicsSimpleTextItem('Hello world!', container)
textItem.setX(-textItem.boundingRect().width())
提供可重用性的另一种选择是创建一个 QGraphicsItem 子类,然后我们将:
boundingRect()
(返回空的QRectF)和paint()
(不执行任何操作);x
设置为其边界矩形的负宽度;x
设置在其边界矩形的右侧;class AlignedTextItem(QGraphicsItem):
def __init__(self, parent):
super().__init__(parent)
self.setX(parent.boundingRect().right())
self.setFlag(QGraphicsItem.ItemIgnoresTransformations)
self.textItem = QGraphicsSimpleTextItem('Hello world!', self)
self.textItem.setX(-self.textItem.boundingRect().width())
def boundingRect(self):
return QRectF()
def paint(self, qp, opt, widget=None):
pass
class PixmapItem(QGraphicsPixmapItem):
def __init__(self, pixmap):
super().__init__(pixmap)
self.alignedTextItem = AlignedTextItem(self)
# this is not a real override...
def setPixmap(self, pixmap):
super().setPixmap(pixmap)
self.alignedTextItem.setX(self.boundingRect().right())