缩放视图时如何保持QGraphicsItem的大小和位置?

时间:2023-02-02 00:11:29

I have some QGraphicsItems in the QGraphicsScene which should keep the same size and position when scaling. I've tried QGraphicsItem::ItemIgnoresTransformations but it turns out that the items get wrong positions. Below is a sample code:

我在QGraphicsScene中有一些QGraphicsItems,它们在缩放时应保持相同的大小和位置。我已经尝试过QGraphicsItem :: ItemIgnoresTransformations但事实证明这些项目的位置错误。以下是示例代码:

I have subclassed QGraphicsView like this:

我已经将QGraphicsView子类化为:

class Graphics : public QGraphicsView
{
public:
    Graphics();
    QGraphicsScene *scene;
    QGraphicsRectItem *rect;
    QGraphicsRectItem *rect2;

protected:
    void wheelEvent(QWheelEvent *event);
};

And in its constructor:

在它的构造函数中:

Graphics::Graphics()
{
    scene = new QGraphicsScene;
    rect = new QGraphicsRectItem(100,100,50,50);
    rect2 = new QGraphicsRectItem(-100,-100,50,50);
    scene->addLine(0,200,200,0);

    rect->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
    scene->addItem(rect);
    scene->addItem(rect2);

    setScene(scene);
    scene->addRect(scene->itemsBoundingRect());
}

The wheelEvent virtual function:

wheelEvent虚函数:

void Graphics::wheelEvent(QWheelEvent *event)
{
    if(event->delta() < 0)
        scale(1.0/2.0, 1.0/2.0);
    else
        scale(2, 2);
    scene->addRect(scene->itemsBoundingRect());

    qDebug() << rect->transform();
    qDebug() << rect->boundingRect();
    qDebug() << rect2->transform();
    qDebug() << rect2->boundingRect();
}

orginal view looks like this: 1

原始视图如下所示:1

take the line as road and rect aside as a symbol. When zoomed out, the rect maintain its size but jumps out of the scene: 2

把这条线作为道路和矩形作为一个符号。缩小时,rect保持其大小但跳出场景:2

which should be that topleft of rect to middle of line. I'm also confused with debug info showing that the boundingRect and transform stays the same, which seems that nothing has changed! What causes the problem and is there any way to solve it? Could someone help? Thank you!

这应该是直线到线的中间。我也对调试信息感到困惑,显示boundingRect和transform保持不变,似乎没有任何改变!导致问题的原因是什么方法可以解决?有人可以帮忙吗?谢谢!

2 个解决方案

#1


1  

Sorry for delay, now I've solved the problem myself.

抱歉延迟,现在我自己解决了这个问题。

I found QGraphicsItem::ItemIgnoresTransformations only works when the point you want stick to is at (0,0) in item's coordinate. You need also update boundingRect manually in this way. Nevertheless, the best solution I've found is subclass QGraphicsItem and set matrix in paint() according to world matrix. Below is my code .

我发现QGraphicsItem :: ItemIgnoresTransformations仅在您想要粘贴的点位于项目坐标中的(0,0)时有效。您还需要以这种方式手动更新boundingRect。然而,我发现的最佳解决方案是子类QGraphicsItem,并根据世界矩阵在paint()中设置矩阵。以下是我的代码。

QMatrix stableMatrix(const QMatrix &matrix, const QPointF &p)
{
    QMatrix newMatrix = matrix;

    qreal scaleX, scaleY;
    scaleX = newMatrix.m11();
    scaleY = newMatrix.m22();
    newMatrix.scale(1.0/scaleX, 1.0/scaleY);

    qreal offsetX, offsetY;
    offsetX = p.x()*(scaleX-1.0);
    offsetY = p.y()*(scaleY-1.0);
    newMatrix.translate(offsetX, offsetY);

    return newMatrix;
}

And the paint function:

和油漆功能:

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                   QWidget *widget)
{
     QPointF p(left, top);
     painter->setMatrix(stableMatrix(painter->worldMatrix(), p));
     painter->drawRect(left, top, width, height);
}

The second argument of stableMatrix is sticked point, in my sample code it's top-left of the item. You can change it to your preference. It works really fine! Hope this post help :)

stableMatrix的第二个参数是固定点,在我的示例代码中它是项目的左上角。您可以根据自己的喜好进行更改。它工作得很好!希望这篇文章帮助:)

#2


0  

The solution to this is even simpler.

对此的解决方案甚至更简单。

QGraphicsItem::ItemIgnoresTransformations

的QGraphicsItem :: ItemIgnoresTransformations

The item ignores inherited transformations (i.e., its position is still anchored to its parent, but the parent or view rotation, zoom or shear transformations are ignored). [...]

该项忽略继承的转换(即,其位置仍锚定到其父级,但忽略父级或视图的旋转,缩放或剪切转换)。 [...]

And that's the key! Item ignores all transformations, but is still bound to its parent. So you need two items: a parent item that will keep the relative position (without any flags set) and a child item that will do the drawing (with QGraphicsItem::ItemIgnoresTransformations flag set) at parent's (0,0) point.

这就是关键! Item忽略所有转换,但仍绑定到其父级。所以你需要两个项目:一个保持相对位置的父项(没有设置任何标志)和一个将在父项(0,0)点进行绘图(设置QGraphicsItem :: ItemIgnoresTransformations标志)的子项。

Here is some working code of a crosshair that have constant size and rotation, while keeping the relative position to its parent:

下面是一些十字准线的工作代码,它具有恒定的大小和旋转,同时保持与其父级的相对位置:

#include <QGraphicsItem>
#include <QPainter>

class CrossHair : public QGraphicsItem
{
private:
    class CrossHairImpl : public QGraphicsItem
    {
    public:
        CrossHairImpl (qreal len, QGraphicsItem *parent = nullptr)
            : QGraphicsItem(parent), m_len(len)
        {
            setFlag(QGraphicsItem::ItemIgnoresTransformations);
        }

        QRectF boundingRect (void) const override
        {
            return QRectF(-m_len, -m_len, m_len*2, m_len*2);
        }

        void paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
        {
            painter->setPen(QPen(Qt::red, 1));
            painter->drawLine(0, -m_len, 0, m_len);
            painter->drawLine(-m_len, 0, m_len, 0);
        }

    private:
        qreal m_len;
    };

public:
    CrossHair (qreal x, qreal y, qreal len, QGraphicsItem *parent = nullptr)
        : QGraphicsItem(parent), m_impl(len, this)  // <-- IMPORTANT!!!
    {
        setPos(x, y);
    }

    QRectF boundingRect (void) const override
    {
        return QRectF();
    }

    void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
    {
        // empty
    }

private:
    CrossHairImpl m_impl;
};

#1


1  

Sorry for delay, now I've solved the problem myself.

抱歉延迟,现在我自己解决了这个问题。

I found QGraphicsItem::ItemIgnoresTransformations only works when the point you want stick to is at (0,0) in item's coordinate. You need also update boundingRect manually in this way. Nevertheless, the best solution I've found is subclass QGraphicsItem and set matrix in paint() according to world matrix. Below is my code .

我发现QGraphicsItem :: ItemIgnoresTransformations仅在您想要粘贴的点位于项目坐标中的(0,0)时有效。您还需要以这种方式手动更新boundingRect。然而,我发现的最佳解决方案是子类QGraphicsItem,并根据世界矩阵在paint()中设置矩阵。以下是我的代码。

QMatrix stableMatrix(const QMatrix &matrix, const QPointF &p)
{
    QMatrix newMatrix = matrix;

    qreal scaleX, scaleY;
    scaleX = newMatrix.m11();
    scaleY = newMatrix.m22();
    newMatrix.scale(1.0/scaleX, 1.0/scaleY);

    qreal offsetX, offsetY;
    offsetX = p.x()*(scaleX-1.0);
    offsetY = p.y()*(scaleY-1.0);
    newMatrix.translate(offsetX, offsetY);

    return newMatrix;
}

And the paint function:

和油漆功能:

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                   QWidget *widget)
{
     QPointF p(left, top);
     painter->setMatrix(stableMatrix(painter->worldMatrix(), p));
     painter->drawRect(left, top, width, height);
}

The second argument of stableMatrix is sticked point, in my sample code it's top-left of the item. You can change it to your preference. It works really fine! Hope this post help :)

stableMatrix的第二个参数是固定点,在我的示例代码中它是项目的左上角。您可以根据自己的喜好进行更改。它工作得很好!希望这篇文章帮助:)

#2


0  

The solution to this is even simpler.

对此的解决方案甚至更简单。

QGraphicsItem::ItemIgnoresTransformations

的QGraphicsItem :: ItemIgnoresTransformations

The item ignores inherited transformations (i.e., its position is still anchored to its parent, but the parent or view rotation, zoom or shear transformations are ignored). [...]

该项忽略继承的转换(即,其位置仍锚定到其父级,但忽略父级或视图的旋转,缩放或剪切转换)。 [...]

And that's the key! Item ignores all transformations, but is still bound to its parent. So you need two items: a parent item that will keep the relative position (without any flags set) and a child item that will do the drawing (with QGraphicsItem::ItemIgnoresTransformations flag set) at parent's (0,0) point.

这就是关键! Item忽略所有转换,但仍绑定到其父级。所以你需要两个项目:一个保持相对位置的父项(没有设置任何标志)和一个将在父项(0,0)点进行绘图(设置QGraphicsItem :: ItemIgnoresTransformations标志)的子项。

Here is some working code of a crosshair that have constant size and rotation, while keeping the relative position to its parent:

下面是一些十字准线的工作代码,它具有恒定的大小和旋转,同时保持与其父级的相对位置:

#include <QGraphicsItem>
#include <QPainter>

class CrossHair : public QGraphicsItem
{
private:
    class CrossHairImpl : public QGraphicsItem
    {
    public:
        CrossHairImpl (qreal len, QGraphicsItem *parent = nullptr)
            : QGraphicsItem(parent), m_len(len)
        {
            setFlag(QGraphicsItem::ItemIgnoresTransformations);
        }

        QRectF boundingRect (void) const override
        {
            return QRectF(-m_len, -m_len, m_len*2, m_len*2);
        }

        void paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
        {
            painter->setPen(QPen(Qt::red, 1));
            painter->drawLine(0, -m_len, 0, m_len);
            painter->drawLine(-m_len, 0, m_len, 0);
        }

    private:
        qreal m_len;
    };

public:
    CrossHair (qreal x, qreal y, qreal len, QGraphicsItem *parent = nullptr)
        : QGraphicsItem(parent), m_impl(len, this)  // <-- IMPORTANT!!!
    {
        setPos(x, y);
    }

    QRectF boundingRect (void) const override
    {
        return QRectF();
    }

    void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
    {
        // empty
    }

private:
    CrossHairImpl m_impl;
};