设置 ItemIgnoresTransformations 标志时如何将 QGraphicsItem 定位在父级右上角?

问题描述 投票:0回答:1

我正在尝试在 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的分辨率而变化。如果正确,文本的右边缘应该完全位于图像的右边缘。

qt qgraphicsview qgraphicsscene qgraphicstextitem qgraphicspixmapitem
1个回答
0
投票

问题是用于放置文本项的坐标系是像素图项的坐标系。

它的原点在相对于像素图项目的逻辑坐标中总是相同的,因此它的“左”总是正确的:它的第一个字母总是几乎在图像的相同位置对齐(根据字体和字符)。

由于文本项忽略变换,因此看起来文本被“移动”,但这只是一种“视错觉”:如果图像不改变大小,但文本项被缩放,也会发生同样的情况,这本质上是同一件事(这两种转换是不兼容的)。

对此有两种可能的解决方案。

QGraphicsPixmapItem 子类

此方法使用项目的

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()
    (不执行任何操作);
  • 添加子QGraphicsSimpleTextItem;
  • 将文本项的
    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())
© www.soinside.com 2019 - 2024. All rights reserved.