2

I have a QGraphicsItem "p" with 4 children a, b, c and d, inserted in that order.

#include <QtWidgets/QApplication>
#include <QtWidgets/QGraphicsScene>
#include <QtWidgets/QGraphicsItem>
#include <QtWidgets/QGraphicsView>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QGraphicsScene scene(0, 0, 200, 200);
    QGraphicsView view(&scene);

    QGraphicsItem* p = new QGraphicsRectItem(nullptr);
    scene.addItem(p);
    QGraphicsRectItem* a = new QGraphicsRectItem( 0,  0, 40, 40, p);
    QGraphicsRectItem* b = new QGraphicsRectItem(10, 10, 40, 40, p);
    QGraphicsRectItem* c = new QGraphicsRectItem(20, 20, 40, 40, p);
    QGraphicsRectItem* d = new QGraphicsRectItem(30, 30, 40, 40, p);

    // cosmetic
    p->moveBy(40, 40);
    a->setBrush(Qt::blue);
    b->setBrush(Qt::red);
    c->setBrush(Qt::yellow);
    d->setBrush(Qt::green);

    view.show();

    return app.exec();
}

I want to put item a between c and d like this:

enter image description here

So basically I use stackBefore and do:

a->stackBefore(d);

But it doesn't work. So I took a look at QGraphicsItem's code and it seems like if the item is already stacked before (immediately or not) it will not move:

// Only move items with the same Z value, and that need moving.
int siblingIndex = sibling->d_ptr->siblingIndex;
int myIndex = d_ptr->siblingIndex;
if (myIndex >= siblingIndex) {

I can either do:

b->stackBefore(a);
c->stackBefore(a);

which move all elements under a. or:

a->setParentItem(nullptr);
a->setParentItem(p);
a->stackBefore(d);

which remove a, and re-insert it on top so I can use stackBefore.

Both solutions look no very efficient. The first one, does not scale and the second one lack semantics.

Is there an elegant way to achieve this ?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
VSA
  • 43
  • 1
  • 5
  • Thanks for the question, this explains why my `stackBefore()` calls weren't working, which I never investigated properly (went with Z order instead). :-) Looks like we really need a `sortBefore()` function to handle this. It is disappointing that there is nothing in the API since a lot of that info is already in private parts and would be easier to handle internally. – Maxim Paperno Sep 11 '19 at 20:28

1 Answers1

0

I would assign an explicit Z value for each graphics item:

int z = 0;
a->setBrush(Qt::blue);
a->setZValue(++z);
b->setBrush(Qt::red);
b->setZValue(++z);
c->setBrush(Qt::yellow);
c->setZValue(++z);
d->setBrush(Qt::green);
d->setZValue(++z);

And then, before using stackBefore(), change the Z value of the moving item:

a->setZValue(d->zValue());
a->stackBefore(d);
Former contributor
  • 2,466
  • 2
  • 10
  • 15
  • By the way, I would prefer not using stackBefore() at all, and instead design my graphics scene using explicit X, Y and Z coordinates for each graphics item. Z is qreal, so you have many possibilities when raising or sinking items among layers. – Former contributor Sep 11 '19 at 11:13
  • I was thinking of zValue, but I find it too hard to maintain in my application (many removals / insertions). In your example the stackBefore is useless as a is immediately before d. I think I'll just go with the chain stackBefore for now – VSA Sep 11 '19 at 12:16