Fixed QGraphicsItem position, without changing behaviour of other QGraphicsItems in scene
Asked Answered
B

1

5

This question is related to: Forcing QGraphicsItem To Stay Put

I'd like to have a QGraphicsItem on a fixed location when moving around in the scene.

The suggested solution is to override the void paintEvent(QPaintEvent*) of the sub-classed QGraphicsView.

void MyGraphicsView::paintEvent(QPaintEvent*) {
  QPointF scenePos = mapToScene(0,0); // map viewport's top-left corner to scene
  myItem->setPos(scenePos);
}

However, the problem is that I want everything else in the scene to stay intact, i.e. if I zoom or move I want all other QGraphicsItems to behave as default.

One poor way of solving this is to call void QGraphicsView::paintEvent(QPaintEvent*) from within void MyGraphicsView::paintEvent(QPaintEvent*).

void MyGraphicsView::paintEvent(QPaintEvent* event) {
  QGraphicsView::paintEvent(event);

  QPointF scenePos = mapToScene(0,0); // map viewport's top-left corner to scene
  myItem->setPos(scenePos);
}

However, this adds a flickering behaviour to my_item since it's positioned first using QGraphicsView::paintEvent(event); and then using the added code

QPointF scenePos = mapToScene(0,0); // map viewport's top-left corner to scene
myItem->setPos(scenePos);

The question is, do I have to re-implement void MyGraphicsView::paintEvent(QPaintEvent*) from scratch and write code for both the desired behaviour of myItem and the default behaviour of all other QGraphicsItems, or is there an easier way to do this?

Thank you.

Boucher answered 28/6, 2013 at 20:35 Comment(4)
Why don't you call paintEvent() after you call setPos() ?Cavallaro
@Cavallaro It will have an even worse effect. Showing traces of the item all over the screen.Boucher
Could you use the QGraphicsItem::itemChange function and see if you get some notifications there? Maybe QGraphicsItem::ItemScenePositionHasChanged is the one you need. You have to enable the QGraphicsItem::ItemSendsScenePositionChanges flag for that to work.Embolic
see also #18411750Educe
H
8

I think this is what you are looking for:

http://qt-project.org/doc/qt-4.8/qgraphicsitem.html#setFlag

QGraphicsItem::ItemIgnoresTransformations

Description from the docs:

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). This flag is useful for keeping text label items horizontal and unscaled, so they will still be readable if the view is transformed. When set, the item's view geometry and scene geometry will be maintained separately. You must call deviceTransform() to map coordinates and detect collisions in the view. By default, this flag is disabled. This flag was introduced in Qt 4.3. Note: With this flag set you can still scale the item itself, and that scale transformation will influence the item's children.

You may also want to parent everything that does pan around to something else. Then, you move or scale or rotate a single graphics group to affect everything except your "un-transformable" objects.

https://qt-project.org/doc/qt-4.8/graphicsview.html#the-graphics-view-coordinate-system

https://qt-project.org/doc/qt-4.8/painting-transformations.html (a cool example, though it doesn't show this feature really)

http://qt-project.org/doc/qt-4.8/demos-chip.html (great example of using QGraphicsView)

Hope that helps.

EDIT:

Example showing how you can achieve a static layer using parenting:

main.cpp

#include <QApplication>
#include "mygraphicsview.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyGraphicsView w;
    w.show();

    return a.exec();
}

mygraphicsview.h

#ifndef MYGRAPHICSVIEW_H
#define MYGRAPHICSVIEW_H

#include <QGraphicsView>
#include <QGraphicsItemGroup>
#include <QMouseEvent>

class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    MyGraphicsView(QWidget *parent = 0);
    ~MyGraphicsView();

public slots:
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
private:
    bool down;
    QPointF m_last_pos;

    QGraphicsItemGroup * m_group;
};

#endif // MYGRAPHICSVIEW_H

mygraphicsview.cpp

#include "mygraphicsview.h"

#include <QGraphicsItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsTextItem>

MyGraphicsView::MyGraphicsView(QWidget *parent)
    : QGraphicsView(parent)
{
    down = false;
    this->setScene(new QGraphicsScene);

    // Anything not added to the "group" will stay put
    this->scene()->addEllipse(20, 20, 50, 50);
    this->scene()->addEllipse(180, 180, 50, 50);
    this->scene()->addText("Click and drag with the mouse to move only the tiny dots.");

    // This group will receive all transformations
    m_group = new QGraphicsItemGroup;
    for(int r = 0; r < 20; r ++)
    {
        for(int c = 0; c < 20; c++)
        {
            if(c % 5 == 0 && r % 5 == 0)
            {
                QGraphicsTextItem * txt = new QGraphicsTextItem(QString::number(r) + "," + QString::number(c));
                m_group->addToGroup(txt);
                txt->setPos(r*100, c*100);
            }
            m_group->addToGroup(new QGraphicsEllipseItem(r *100, c*100, 5, 5));
        }
    }

    this->scene()->addItem(m_group);
}

MyGraphicsView::~MyGraphicsView()
{

}

void MyGraphicsView::mousePressEvent(QMouseEvent *event)
{
    m_last_pos = mapToScene(event->pos());
    down = true;
}

void MyGraphicsView::mouseReleaseEvent(QMouseEvent *)
{
    down = false;
}

void MyGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    if(down)
    {
        QPointF temp = mapToScene(event->pos());

        QPointF delta = temp - m_last_pos;
        m_last_pos = temp;

        // Apply transformation to the group, not the scene!
        m_group->translate(delta.x(), delta.y());
    }
}
Horn answered 29/6, 2013 at 0:9 Comment(4)
Sorry, ItemIgnoresTransformations is already set. This won't help against moving around in the scene.Boucher
Have you tried parenting everything else to another QGraphicsItemGroup, and then applying translations to that instead of to the scene?Horn
That would still have to make me implement void MyGraphicsView::paintEvent(QPaintEvent*) from scratch, right?Boucher
I'll throw together an example in the next few minutes. If you do the parenting right, you shouldn't need to.Horn

© 2022 - 2024 — McMap. All rights reserved.