Qgraphicsscene setParentItem update childrens position?
-
So i have a qgraphicsscene and in the mouserelease event im adding all selected qgraphicsitems to another qgraphicsrectitem called "GroupSelectionRect". Im making the children itself unmovable and unselecteable but the parent item stays movable. I move the parent item and click somewhere else. In the mousepress event when clicked somwhere else i remove the parent from the children.
EVERYTHING works fine till here. But than i select the items again and they get added to the "GroupSelectionRect" they have been removed from. At this point the childitems aswell as the "GroupSelectionRect" jumps to an random position in the qgraphicsscene and i couldn't figure out WHY and how to fix that?Here is my code:
mousepress (removing from group if not clicked on groupitem:void CustomGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { if(GroupSelectionRect && (GroupSelectionRect->boundingRect().adjusted(-8, -8, 8, 8).contains(GroupSelectionRect->mapFromScene(event->scenePos())))) { // Der Mausklick erfolgte innerhalb des Bereichs von GroupSelectionRect // Führen Sie Ihre Aktionen hier aus qDebug() << "AUF das GruppenRect geklcikt"; clickedOnGroupRect = true; }else if(GroupSelectionRect && !GroupSelectionRect->boundingRect().adjusted(-8, -8, 8, 8).contains(GroupSelectionRect->mapFromScene(event->scenePos()))) { clickedOnGroupRect = false; if(GroupSelectionRect && !GroupSelectionRect->childItems().isEmpty()){ foreach (QGraphicsItem *item, GroupSelectionRect->childItems()) { QPointF globalPos = item->mapToScene(0, 0); item->setPos(globalPos); item->setParentItem(nullptr); item->setSelected(false); // Check if the item is a pixmap item or a rect item ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ rectItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); } } } } QGraphicsScene::mousePressEvent(event); }
mouse release: (adding to group)
void CustomGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if(DragSelectionInProgress == true){ qDebug() << "SelectionInProgress" << GroupSelectionRect; //Erstelle eine Liste aller selecteten items die nicht das grouprect sind und füge alle items dieser Liste dem grouprect hinzu if(GroupSelectionRect){ QList<QGraphicsItem *> selectedItems = this->selectedItems(); QList<QGraphicsItem *> itemsToAdd; qDebug() << "SELItems:" << selectedItems; foreach(QGraphicsItem *item, selectedItems) { if(item != GroupSelectionRect && item) { ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ // Wenn das Element kein resizablehandlerect ist, füge es der Liste hinzu itemsToAdd.append(item); qDebug() << "ItemToAppend:" << itemsToAdd.count(); } } } QRectF CombinedBoundingRect; if(itemsToAdd.count() > 1){ foreach(QGraphicsItem *item, itemsToAdd) { // Check if the item is a pixmap item or a rect item ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ rectItem->setFlags(rectItem->flags() & ~(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable)); rectItem->setSelected(false); rectItem->setParentItem(GroupSelectionRect); qDebug() << "Parent:" << rectItem->parentItem(); CombinedBoundingRect = CombinedBoundingRect.united(rectItem->sceneBoundingRect()); } } GroupSelectionRect->setRect(CombinedBoundingRect); } } DragSelectionInProgress = false; }else{ } QGraphicsScene::mouseReleaseEvent(event); }
-
@StudentScripter said in Qgraphicsscene setParentItem update childrens position?:
item->setPos(globalPos);
item->setParentItem(nullptr);I don't understand your code - why do you unset the parent?
Also, if you unset the parent you should set the position afterwards, not before. -
@jsulm Cause its like an temporary group. As long as more than 2 items are selected this groupitem is created to move all the items at once. Than when clicked somwhere else not on the group the parent is unset in order to make the items move independently again.
-
@StudentScripter said in Qgraphicsscene setParentItem update childrens position?:
QPointF globalPos = item->mapToScene(0, 0); item->setPos(globalPos);
This is wrong.
You get a global position, and then set it as an item position. Then you change the parent (and thereby the coordinate system).What you probably want to do is something like:
auto scenePos = item->scenePos(); item->setParentItem(nullptr); item->setScenePos(scenePos);
I really recommend reading
https://doc.qt.io/qt-6/graphicsview.html#the-graphics-view-coordinate-system -
@Asperamanca said in Qgraphicsscene setParentItem update childrens position?:
item->setScenePos(scenePos);
Thanks but there is no "setScenePos" with QGraphicsItem.
-
Asperamancareplied to StudentScripter on 9 Feb 2024, 10:30 last edited by Asperamanca 2 Sept 2024, 10:31
@StudentScripter
Ah yes, don't get me started on the API design of that thing...That should do instead
pItem->setPos(scenePos);
Given that you just the the parent to nullptr, it's a top-level item, and thereby pos and scenePos are equivalent.
-
StudentScripterreplied to Asperamanca on 9 Feb 2024, 10:54 last edited by StudentScripter 2 Sept 2024, 11:01
@Asperamanca Thanks i tried it but sadly it doesn't fix the issue. Here is a gif of the issue. Hope this helps, i really can't find the way to make it work properly.
Wanted to share a gif of the issues but even with a 6mb small one it says file to big to upload. Here some pictures instead:
Just two items added to scene:
I select both they keep their position all good and the grouprect is drawn:
I select the grouprect and move it, works fine:
I click somwhere else so the items are unparented works fine too i move the group rect away: NOW i try selecting the two items again, but now everything is moved totaly random: -
@StudentScripter said in Qgraphicsscene setParentItem update childrens position?:
if(rectItem){ rectItem->setFlags(rectItem->flags() & ~(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable)); rectItem->setSelected(false); rectItem->setParentItem(GroupSelectionRect); qDebug() << "Parent:" << rectItem->parentItem(); CombinedBoundingRect = CombinedBoundingRect.united(rectItem->sceneBoundingRect()); }
Well, same story. After setParentItem in the above code, you have to correct the itemPos, since the coordinate system has changed.
You can use the same approach getting scenePos before, but now you need to map the scenePos to the correct coordinate system usingauto newItemPos = GroupSelectionRect->mapFromScene(scenePos);
-
StudentScripterreplied to Asperamanca on 9 Feb 2024, 11:35 last edited by StudentScripter 2 Sept 2024, 13:01
@Asperamanca said in Qgraphicsscene setParentItem update childrens position?:
auto newItemPos = GroupSelectionRect->mapFromScene(scenePos);
Really thanks for trying to help me that much, but i don't quite get how to do it.
This is obviously wrong:
QRectF CombinedBoundingRect; if(itemsToAdd.count() > 1){ foreach(QGraphicsItem *item, itemsToAdd) { // Check if the item is a pixmap item or a rect item ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ // Speichere die aktuelle Szene-Position QPointF scenePos = rectItem->scenePos(); rectItem->setFlags(rectItem->flags() & ~(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable)); rectItem->setSelected(false); rectItem->setParentItem(GroupSelectionRect); qDebug() << "Parent:" << rectItem->parentItem(); rectItem->setPos(scenePos); CombinedBoundingRect = CombinedBoundingRect.united(rectItem->sceneBoundingRect()); } } GroupSelectionRect->setRect(CombinedBoundingRect); }
-
@StudentScripter said in Qgraphicsscene setParentItem update childrens position?:
rectItem->setParentItem(GroupSelectionRect); qDebug() << "Parent:" << rectItem->parentItem(); rectItem->setPos(scenePos);
Should be
rectItem->setParentItem(GroupSelectionRect); qDebug() << "Parent:" << rectItem->parentItem(); auto newItemPos = GroupSelectionRect->mapFromScene(scenePos); rectItem->setPos(newItemPos);
Maybe I shouldn't answer questions when I am in a hurry. But I really recommend reading that chapter I posted. If you understand the coordinate system, it's all logical (if not always easy).
-
StudentScripterreplied to Asperamanca on 10 Feb 2024, 13:21 last edited by StudentScripter 2 Oct 2024, 13:44
@Asperamanca Thank you very much, can't express how much this helps <3, so now my children items don't move randomly anymore. (English is not my mothertongue so understanding everything right is indeed not that easy for me.) Gonna have a look again on the manual page you posted. Cause i have one problem that still persists. Even tho the children items don't move wrongly the parent item is offset everytime a little depending on how much it was moved before.
EDIT: Actually SOLVED it by doing:
GroupSelectionRect->setRect(CombinedBoundingRect.x()-GroupSelectionRect->scenePos().x(),CombinedBoundingRect.y()-GroupSelectionRect->scenePos().y(), CombinedBoundingRect.width(), CombinedBoundingRect.height());
But i wanted to know if thats a good way to do it or if there is an better way?
.
.
.
.Here some pictures to show what happens:
- Just added 2 items nothing special again:
- i select both items, grouprect is created, everything works fine:
- Moved the grouprect all fine: 4) NOW when i unselect an select again the children stay which is good but the parent item on reselecting is offsetted:
Code for adding to group in mouserelease:
if(DragSelectionInProgress == true){ qDebug() << "SelectionInProgress" << GroupSelectionRect; //Erstelle eine Liste aller selecteten items die nicht das grouprect sind und füge alle items dieser Liste dem grouprect hinzu if(GroupSelectionRect){ QList<QGraphicsItem *> selectedItems = this->selectedItems(); QList<QGraphicsItem *> itemsToAdd; qDebug() << "SELItems:" << selectedItems; foreach(QGraphicsItem *item, selectedItems) { if(item != GroupSelectionRect && item) { ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ // Wenn das Element kein resizablehandlerect ist, füge es der Liste hinzu itemsToAdd.append(item); qDebug() << "ItemToAppend:" << itemsToAdd.count(); } } } QRectF CombinedBoundingRect; if(itemsToAdd.count() > 1){ foreach(QGraphicsItem *item, itemsToAdd) { // Check if the item is a pixmap item or a rect item ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ // Speichere die aktuelle Szene-Position QPointF scenePos = rectItem->scenePos(); rectItem->setFlags(rectItem->flags() & ~(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable)); rectItem->setSelected(false); rectItem->setParentItem(GroupSelectionRect); auto newItemPos = GroupSelectionRect->mapFromScene(scenePos); rectItem->setPos(newItemPos); qDebug() << rectItem->scenePos() << "NWItemPos:" << newItemPos; CombinedBoundingRect = CombinedBoundingRect.united(rectItem->sceneBoundingRect()); } } GroupSelectionRect->setRect(CombinedBoundingRect); GroupSelectionRect->drawHandlesIfNecessary(true); GroupSelectionRect->setSelected(true); qDebug() << GroupSelectionRect->pos() << GroupSelectionRect->scenePos(); } } DragSelectionInProgress = false; }else{ } QGraphicsScene::mouseReleaseEvent(event); }
Code for removing from group on mousepress:
if(GroupSelectionRect && (GroupSelectionRect->boundingRect().adjusted(-8, -8, 8, 8).contains(GroupSelectionRect->mapFromScene(event->scenePos())))) { // Der Mausklick erfolgte innerhalb des Bereichs von GroupSelectionRect // Führen Sie Ihre Aktionen hier aus qDebug() << "AUF das GruppenRect geklcikt"; clickedOnGroupRect = true; }else if(GroupSelectionRect && !GroupSelectionRect->boundingRect().adjusted(-8, -8, 8, 8).contains(GroupSelectionRect->mapFromScene(event->scenePos()))) { clickedOnGroupRect = false; if(GroupSelectionRect && !GroupSelectionRect->childItems().isEmpty()){ foreach (QGraphicsItem *item, GroupSelectionRect->childItems()) { //QPointF globalPos = item->mapToScene(0, 0); // item->setPos(globalPos); auto scenePos = item->scenePos(); item->setParentItem(nullptr); //item->setSelected(false); item->setPos(scenePos); // Check if the item is a pixmap item or a rect item ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item); if(rectItem){ rectItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); } } GroupSelectionRect->drawHandlesIfNecessary(false); qDebug()<< "PArentRect:" << GroupSelectionRect->scenePos(); GroupSelectionRect->setRect(0,0,0,0); } } QGraphicsScene::mousePressEvent(event);
- Just added 2 items nothing special again:
-
@StudentScripter Hard for me to say whether this is a good way for you to do things. Much of that depends on what else you want to do in your application. Maybe this way to implement it is in the way of another feature in the future, maybe you can reuse part of it for something else. I don't know enough to have an opinion on that.
I can however highlight some detail points:
- Replace 'foreach' with C++ ranged for loop
foreach(QGraphicsItem *item, selectedItems)
becomes
for(QGraphicsItem* item : selectedItems)
Even better, make it
for(QGraphicsItem* const item : selectedItems)
(unless you plan to change the pointer, but that's not a great idea in most cases)
Even shorter, you can also writefor(auto* const item : selectedItems)
The compiler knows what type you need.
- Package your code in some named functions or lambdas
With good names, this makes the code way easier to read. It also shows explicitly what goes into the function, and might give you hints on where your code could be better designed and ordered.
- Take a look at QGraphicsItem::ItemChange
This function allows you to respond to various changes to the item. You could, for example, do the coordinate transformation (when the item's parent changes) in the item itself, instead of in the event. Whether that's a good idea, I'm not sure. But it's good to know it's possible.
- ResizablePixmapItem *rectItem = qgraphicsitem_cast<ResizablePixmapItem *>(item);
I see that in a couple of places, you downcast to your item type. But the following code only uses functionality of QGraphicsItem. Any special reason you do that? The list you loop through shouldn't have any "wrong" items anyway, as far as I see...
-
@Asperamanca Thanks thats a good call, gonna have a look at it. Thanks for the time and effort. <3 Gonna write again here if i encounter other questions regarding this. :D
1/13