GraphicsScene: Grouping multiple Items Transforms (resize)
-
A temporary
QGraphicsItemGroup
might work.
You can even draw a rect around the group to see what area you are moving or resizing. Then you can draw the handle.The boundingRect() function of QGraphicsItemGroup returns the bounding rectangle of all items in the item group. QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on its children (i.e., with respect to the geometry of the group item, the children are treated as if they were transformable).
-
@Pl45m4 Yes had a look at that and subclassed QGraphicsItemGroup and also inherited from resizablehandlerect. I wanted to override the paint event to trigger painting the handles but the paint event receives no signal at all.
#ifndef RESIZABLEGRAPHICSGROUPITEM_H #define RESIZABLEGRAPHICSGROUPITEM_H #include <QGraphicsItemGroup> #include "resizablehandlerect.h" class ResizableGraphicsGroupItem : public QGraphicsItemGroup, public ResizableHandleRect { public: explicit ResizableGraphicsGroupItem(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; signals: }; #endif // RESIZABLEGRAPHICSGROUPITEM_H
Also im trying this in my scene but somehow it crashes:
void CustomGraphicsScene::onSelectionChanged() { if (!isUpdatingSelection) { QList<QGraphicsItem*> selectedItemsList = this->selectedItems(); QList<QStandardItem*> standardItemsList; // Durchlaufe alle ausgewählten Elemente foreach (QGraphicsItem* graphicsItem, selectedItemsList) { QStandardItem* standardItem = FindListItemFromGraphicsItem(graphicsItem); if (standardItem) { // Füge das gefundene QStandardItem zur Liste hinzu standardItemsList.append(standardItem); } } if (selectedItemsList.count() > 1) { // Erstellen Sie eine neue Gruppe if (GroupSelectionRect) { this->destroyItemGroup(GroupSelectionRect); this->removeItem(GroupSelectionRect); delete GroupSelectionRect; } GroupSelectionRect = new ResizableGraphicsGroupItem(); // Fügen Sie die ausgewählten Elemente zur Gruppe hinzu foreach (QGraphicsItem* item, selectedItemsList) { GroupSelectionRect->addToGroup(item); } // Fügen Sie die Gruppe zur Szene hinzu this->addItem(GroupSelectionRect); } emit SendSelectionChangedFromSceneToLayerList(standardItemsList); } // Deine Logik zur Verarbeitung der ausgewählten Elemente this->update(); }
-
@StudentScripter said in GraphicsScene: Grouping multiple Items Transforms (resize):
Yes had a look at that and subclassed QGraphicsItemGroup and also inherited from resizablehandlerect. I wanted to override the paint event to trigger painting the handles but the paint event receives no signal at all.
What kind of class is
ResizableHandleRect
?
NeitherQGraphicsItem
norQGraphicsItemGroup
is aQObject
, so you can't use Signals and Slots there.
But ifResizableHandleRect
is aQObject
, you need to switch the order of inheritance. Always inherit from theQObject
based class first when using multiple inheritance. -
@Pl45m4 So i checked it, but now i have a issue with actually adding items to the rect:
On the first time selecting the selection rect is created, but on the second time it only adds 1 item to the selection rect while the other items stay selected:CustomGraphicsScene::CustomGraphicsScene(QObject *parent) : QGraphicsScene(parent) { //Erstellt die Hauptinstanz des UndoStack undoStack = new QUndoStack(this); //Setzt die generelle Hintergrundfarbe für die gesamte Scene setBackgroundBrush(Qt::gray); //Höhe & Breite, setzt das eigentliche SceneRect SceneRectWidth = 900; SceneRectHeight = 600; setSceneRect(QRectF(0, 0, SceneRectWidth, SceneRectHeight)); if (!connect(this, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()))) { qDebug() << "Failed to connect signal and slot"; } GroupSelectionRect = new ResizableGraphicsGroupItem(); addItem(GroupSelectionRect); } CustomGraphicsScene::~CustomGraphicsScene() { disconnect(this, &QGraphicsScene::selectionChanged, this, &CustomGraphicsScene::onSelectionChanged); qDebug() << "isDestructed"; } void CustomGraphicsScene::onSelectionChanged() { qDebug() << "selectedItems_" << selectedItems().count() << selectedItems(); if (selectedItems().count() > 1) { // Clear the previous items from the group if (GroupSelectionRect) { if (GroupSelectionRect) { // Durchlaufe jedes Element in der Gruppe foreach (QGraphicsItem* item, GroupSelectionRect->childItems()) { // Entferne das Element aus der Gruppe GroupSelectionRect->removeFromGroup(item); } } qDebug() << "GroupSelChilds:" << GroupSelectionRect->childItems(); // Durchlaufe alle ausgewählten Elemente erneut, füge sie zur Gruppe hinzu foreach (QGraphicsItem* graphicsItem, selectedItems()) { ResizablePixmapItem* rectItem = dynamic_cast<ResizablePixmapItem*>(graphicsItem); if (rectItem) { //rectItem->drawHandlesIfNecessary(false); // Füge das GraphicsItem zur Gruppe hinzu GroupSelectionRect->addToGroup(rectItem); } } } } qDebug() << "GroupSelChilds:" << GroupSelectionRect->childItems();
Here is a explanation with pictures:
1) im adding 2 items to the scene:
2) im selecting both the selection rect gets created:
3) im adding a 3rd item and trying to select the 3rd and the 2nd item: (but here suddenly all 3 items get selected and only the 3rd item gets the groupitemrect
-
@StudentScripter said in GraphicsScene: Grouping multiple Items Transforms (resize):
- im adding a 3rd item and trying to select the 3rd and the 2nd item: (but here suddenly all 3 items get selected and only the 3rd item gets the groupitemrect
Because your logic behind this seems tempting to "simply" do as you did, but is wrong (doesn't work as you might expect).
See
QGraphicsScene::selectionChanged()
The selection changes whenever an item is selected or unselected, a selection area is set, cleared or otherwise changed, if a preselected item is added to the scene, or if a selected item is removed from the scene.
QGraphicsScene emits this signal only once for group selection operations. For example, if you set a selection area, select or unselect a QGraphicsItemGroup, or if you add or remove from the scene a parent item that contains several selected items, selectionChanged() is emitted only once after the operation has completed (instead of once for each item).
The standard selection bahavior is like this (nothing special):
If you add your
QGraphicsItemGroup
the way you do currently, you get this:
It's not 100% the same, because I'm not selecting all at once by using some rubberband rect, but the root of the misbehavior should be the same.
See how the
selectedItems()
counter returned fromQGraphicsScene
changes?
You would expect it to behave like it does without the group: One at a time... Counter goes from 1 to 10 and adding one after another to the group.But, since
QGraphicsItemsGroup
is aQGraphicsItems
itself, it shadows all selected items which are part of it.Therefore the
QGraphicsScene
, i.e.
your line hereif (selectedItems().count() > 1)
would count
0, 1, 0, 1, 0, 1
because all previously (more than 2)
selectedItems
are added to the group, which itself, is unselected.
So everything the scene sees, is the group containing X selected items, but counting 0 plus a maximum of one additional selected item, which was not added due toif (selectedItems().count() > 1)
and the fact that every change (see signal doc.) triggers the signal.When you select a second one, it's added again and the scene is left with 0 selected items (= the group).
So add some own logic to count group and non-group members or move the counting away from a function directly connected to
selectionChanged()
.Edit:
Btw: If you are planning to make your
QGraphicsItemGroup
selectable and movable, you could even run into some recursion, because then, at some point you would try to add the whole group item to ifself, which Qt obviously prevents.
(Got the warning in my little example as I made the group selectable) -
@Pl45m4 said in GraphicsScene: Grouping multiple Items Transforms (resize):
Btw: If you are planning to make your QGraphicsItemGroup selectable and movable, you could even run into some recursion, because then, at some point you would try to add the whole group item to ifself, which Qt obviously prevents.
(Got the warning in my little example as I made the group selectable)Yes, it already is selectable and movable. So what whould you suggest instead? Also big thanks for the big effort you put into explaining, this helps a lot. <3 Have a nice evening.
-
@StudentScripter said in GraphicsScene: Grouping multiple Items Transforms (resize):
So what whould you suggest instead?
Find another way to keep track of the items which are members of the group.
Also I've noticed that selected items which are part of the group and get removed from the group desync and are painted in (wrong) selected state, even though they are not. So you have to restore they state somehow, if this occures.
-
@Pl45m4 Got i solved by just handling the selection in the mousepress and release event. However now im stuck with painting handles to resize the groupitem. I don't really know how to do that as qgraphicsgrouptitem has no setRect method.
Here is my graphicsgroupitem;
#include "resizablegraphicsgroupitem.h" #include "qpainter.h" #include <QPen> ResizableGraphicsGroupItem::ResizableGraphicsGroupItem() { // Initialisierungen hier, falls benötigt setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsScenePositionChanges | QGraphicsItem::ItemSendsGeometryChanges); setOwnerItem(this); } QRectF ResizableGraphicsGroupItem::boundingRect() const { return childrenBoundingRect(); } void ResizableGraphicsGroupItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { qDebug() << "Test"; Q_UNUSED(option); Q_UNUSED(widget); painter->save(); //Malt eine Linie um das Rechteck painter->setPen(QPen(Qt::green)); painter->drawRect(boundingRect()); painter->restore(); }
I coded such a handle class before, but i don't know how to get it to work with a qgraphicsgroupitem:
#include "resizablehandlerect.h" #include <QGraphicsScene> #include <QDebug> #include <QCursor> ResizableHandleRect::ResizableHandleRect() { } ResizableHandleRect::~ResizableHandleRect() { } void ResizableHandleRect::drawHandles() { qDebug() << "ResizableHandleRect DrawHandles triggered!"; // Erstellen und Hinzufügen der Handles zur Szene //Populate handles in list if(handleList.count() == 0){ handleList.append(new HandleItem(HandleItem::TopLeft)); handleList.append(new HandleItem(HandleItem::TopCenter)); handleList.append(new HandleItem(HandleItem::TopRight)); handleList.append(new HandleItem(HandleItem::RightCenter)); handleList.append(new HandleItem(HandleItem::BottomRight)); handleList.append(new HandleItem(HandleItem::BottomCenter)); handleList.append(new HandleItem(HandleItem::BottomLeft)); handleList.append(new HandleItem(HandleItem::LeftCenter)); } //Set up pen QPen mPen; mPen.setWidth(2); mPen.setColor(Qt::black); //Top left handle QPointF topLeftCorner = selectorFrameBounds().topLeft() + QPointF(-8.0,-8.0); topLeftHandleRect = QRectF(topLeftCorner,QSize(8,8)); handleList[0]->setRect(topLeftHandleRect); if(!handleList.isEmpty() && (!handlesAddedToScene)){ handleList[0]->setPen(mPen); //handleList[0]->setBrush(QBrush(Qt::black)); ownerItem->scene()->addItem(handleList[0]); handleList[0]->setParentItem(ownerItem); handleList[0]->setCursor(Qt::SizeFDiagCursor); } //Top Center QPointF topCenterCorner = QPointF(selectorFrameBounds().left() + selectorFrameBounds().width() / 2.0 - 4.0, selectorFrameBounds().top()-4.0); topCenterHandleRect = QRectF(topCenterCorner,QSize(8,8)); handleList[1]->setRect(topCenterHandleRect); if(!handleList.isEmpty() && (!handlesAddedToScene)){ handleList[1]->setPen(mPen); //handleList[1]->setBrush(QBrush(Qt::gray)); ownerItem->scene()->addItem(handleList[1]); handleList[1]->setParentItem(ownerItem); handleList[1]->setCursor(Qt::SizeVerCursor); } //Top right QPointF topRightCorner = selectorFrameBounds().topRight() + QPointF(0,-8.0); topRightHandleRect = QRectF(topRightCorner,QSize(8,8)); handleList[2]->setRect(topRightHandleRect); if(!handleList.isEmpty() && (!handlesAddedToScene)){ handleList[2]->setPen(mPen); //handleList[1]->setBrush(QBrush(Qt::gray)); ownerItem->scene()->addItem(handleList[2]); handleList[2]->setParentItem(ownerItem); handleList[2]->setCursor(Qt::SizeBDiagCursor); } //Right Center QPointF rightCenterCorner = QPointF(selectorFrameBounds().right() - 4.0, selectorFrameBounds().top() + selectorFrameBounds().height() / 2.0 - 4.0); rightCenterHandleRect = QRectF(rightCenterCorner, QSize(8, 8)); handleList[3]->setRect(rightCenterHandleRect); if (!handleList.isEmpty() && (!handlesAddedToScene)) { handleList[3]->setPen(mPen); ownerItem->scene()->addItem(handleList[3]); handleList[3]->setParentItem(ownerItem); handleList[3]->setCursor(Qt::SizeHorCursor); } //Bottom right QPointF bottomRightCorner = selectorFrameBounds().bottomRight() + QPointF(0,0); bottomRightHandleRect = QRectF(bottomRightCorner,QSize(8,8)); handleList[4]->setRect(bottomRightHandleRect); if(!handleList.isEmpty() && (!handlesAddedToScene)){ handleList[4]->setPen(mPen); //handleList[2]->setBrush(QBrush(Qt::gray)); ownerItem->scene()->addItem(handleList[4]); handleList[4]->setParentItem(ownerItem); handleList[4]->setCursor(Qt::SizeFDiagCursor); } // Bottom Center QPointF bottomCenterCorner = QPointF(selectorFrameBounds().left() + selectorFrameBounds().width() / 2.0 - 4.0, selectorFrameBounds().bottom()-4.0); bottomCenterHandleRect = QRectF(bottomCenterCorner, QSize(8, 8)); handleList[5]->setRect(bottomCenterHandleRect); if (!handleList.isEmpty() && (!handlesAddedToScene)) { handleList[5]->setPen(mPen); ownerItem->scene()->addItem(handleList[5]); handleList[5]->setParentItem(ownerItem); handleList[5]->setCursor(Qt::SizeVerCursor); } //Bottom left QPointF bottomLeftCorner = selectorFrameBounds().bottomLeft() + QPointF(-8,0); bottomLeftHandleRect = QRectF(bottomLeftCorner,QSize(8,8)); handleList[6]->setRect(bottomLeftHandleRect); if(!handleList.isEmpty() && (!handlesAddedToScene)){ handleList[6]->setPen(mPen); //handleList[3]->setBrush(QBrush(Qt::gray)); ownerItem->scene()->addItem(handleList[6]); handleList[6]->setParentItem(ownerItem); handleList[6]->setCursor(Qt::SizeBDiagCursor); } // Left Center QPointF leftCenterCorner = QPointF(selectorFrameBounds().left() - 4.0, selectorFrameBounds().top() + selectorFrameBounds().height() / 2.0 - 4.0); leftCenterHandleRect = QRectF(leftCenterCorner, QSize(8, 8)); handleList[7]->setRect(leftCenterHandleRect); if (!handleList.isEmpty() && (!handlesAddedToScene)) { handleList[7]->setPen(mPen); ownerItem->scene()->addItem(handleList[7]); handleList[7]->setParentItem(ownerItem); handleList[7]->setCursor(Qt::SizeHorCursor); } handlesAddedToScene = true; } void ResizableHandleRect::setOwnerItem(QGraphicsItem *value) { ownerItem = value; } void ResizableHandleRect::drawHandlesIfNecessary(bool ShouldDrawHandles) { qDebug() << "DrawHandles" << ShouldDrawHandles << ownerItem->isSelected(); if(!ownerItem){ qWarning() << "ResizableHandleRect : No ownerItem set. Not drawing any\ handle. Please call setOwnerItem on your ResizableHandleRect subclass"; return; } if(ownerItem->isSelected() && ShouldDrawHandles == true){ drawHandles(); handlesAddedToScene = true; }else if(!ownerItem->isSelected() | (ShouldDrawHandles == false)){ //Remove the handles foreach (HandleItem * handleItem, handleList) { ownerItem->scene()->removeItem(handleItem); } qDeleteAll(handleList); handleList.clear(); handlesAddedToScene = false; } }