Fixed QGraphicsItem position, without changing beh

2019-02-26 17:50发布

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.

1条回答
做自己的国王
2楼-- · 2019-02-26 17:55

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());
    }
}
查看更多
登录 后发表回答