Qt: having selected items appear on top in QGraphicsScene
Asked Answered
S

5

7

So i have a QGraphicsScene with various items. Some of the can take the same coordinates in the scene. When I display the scene the ones displayed on top are the ones which were added last. Is there a way to make a certain item always appear on top or even better make it reappear on top during the program (rather than deleting it and adding it again)?. I've been thinking about drawing everything beforehand and then using setVisible() to show only the items I want to, but since I want to add new things online it seems to be a bit problematic.

Sisterhood answered 15/6, 2014 at 15:38 Comment(0)
A
15

You can use QGraphicsItem::setZValue ( qreal z ) to set the Z-value of the item. The Z value decides the stacking order of sibling items. An item with a higher Z value will always be drawn on top of another item with a lower Z value. The default Z value is zero. So you can set it to a higher value to bring it on top :

item->setZValue(1);

or bring it to bottom of other items :

item->setZValue(-1);
Aristocratic answered 15/6, 2014 at 17:1 Comment(2)
Is 1 the highest possible Z-value? Because if it isn't than @Leslie 's answer would be the correct (though bad performance-wise) one.Biogen
@Biogen Z-value could be more than 1. The logic for managing z orders is application dependent and could be done in many ways.Aristocratic
B
4

After doing some research I think that @Leslie 's answer is in a way more correct than @Nejat 's. However it is very inefficient if you have a lot of items and you have to do that check very often.

I my case I have a QGraphicsTextItem that shows the coordinates of the cursor inside my scene.

void QCVN_SceneView::mouseMoveEvent(QMouseEvent *event)
{
  QPointF cursorPoint = mapToScene(event->pos());
//  cout << "Event pos: x=" << event->pos().x() << ", y=" << event->pos().y() << endl;
//  cout << "Event pos (in scene): x=" << cursorPoint.x() << ", y=" << cursorPoint.y() << endl;

  QString coords = QString("%1, %2")
                          .arg(cursorPoint.x())
                          .arg(cursorPoint.y());

  cursorSceneCoords->setPlainText(coords);
  cursorSceneCoords->setPos(cursorPoint);

  QGraphicsView::mouseMoveEvent(event);
}

Obviously if I have 10000000 items in the scene using setZValue(1) will not do the trick unless all items are with a z-value under 1. Iterating through the whole list of items is also a bad way of doing it.

I gave it a little thought and it struck me once I remembered that setZValue() uses a qreal argument. Depending on the system it can be a double or a float however this is not important. We can simply use std::numeric_limits to get the very, very top value possible. Of course we have to limit all our items in the scene (except the desired once that we want on top) to have a maximum z-value of maximum possible value minus 1 (or 0.1 or whatever). So if you do

YOUR_GRAPHICS_ITEM.setZValue(std::numeric_limits<qreal>::max());

the item will always be on top of the rest.

Biogen answered 6/3, 2016 at 22:38 Comment(0)
M
2

This question is a little confusing. The subject mentions wanting items on top when selected, while the description talks about more permanent ordering concerns (creation and such). I'm adding this answer to clarify possible solutions for those coming here due to the subject:

  • For permanent order depth management setZValue is the way to go. For example, a node view with group backdrops where backdrops should always be behind all other items, the zValue can be set to -100.
  • For temporary ordering, such as selections, where you don't want to mess with the permanent ordering (which should be reserved for user-set or application-specific order state needs), you can use stackBefore. This will change the order of items within the same zDepth. So, when items are selected you could run through all items and adjust the order and you don't have to worry about trying to reset zDepth back to what it was before when done (I suppose you could still want to restore, but at least this way the concerns are separate from zDepth). Note, FWIW, I find the stackBefore ordering of sibling to feel backwards.
Milton answered 18/3, 2018 at 17:10 Comment(0)
M
1

Check this

http://www.qtcentre.org/threads/5428-help-with-ZValue!!

// Find largest Z
qreal maxZ = 0;
foreach (QGraphicsItem *item, 
        clickedItem>collidingItems(Qt::IntersectsItemBoundingRect))
    maxZ = qMax(maxZ, item->zValue());

// Assign new Z
clickedItem->setZValue(maxZ + some);
Mutism answered 7/11, 2015 at 10:9 Comment(0)
A
0

https://github.com/sonichy/HTYPaint2/blob/master/mainwindow.cpp#L775

void MainWindow::on_action_layerTop_triggered()
{
    if(scene->selectedItems().size() > 0){
        QList<QGraphicsItem*> collidingItems = scene->collidingItems(scene->selectedItems().first());
        for(int i=0; i<collidingItems.size(); i++){
            collidingItems.at(i)->stackBefore(scene->selectedItems().first());
        }
        scene->update();
    }
}
Autoroute answered 26/11, 2019 at 4:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.