Click & drag a QGraphicsItem inside QGraphicsScene?
-
I'm having some confusion with clicking and dragging of a QGraphicsItem inside a QGraphicsScene. I'd like to implement the functionality that:
- a single click in an empty part of the scene adds an item
- a single click on an existing item removes this item.
- a click and mouse move action drags the item around
I've currently tried to implement this with a mouseReleaseEvent for my custom scene implementation:
For my QGraphicsItem called Node I set:Node::Node(QPointF pos) : QGraphicsItem() { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setPos(pos); }
As expected, I can now drag my items around the scene. However, for add and remove functionality I'm trying this approach by overriding scene methods:
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){ if (mouseEvent->button() == Qt::LeftButton){ QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform()); Node *node = qgraphicsitem_cast<Node *>(item); if(node){ removeItem(node); }else{ QPointF pt = mouseEvent->scenePos(); Node* node = new Node(pt); addItem(node); } } QGraphicsScene::mouseReleaseEvent(mouseEvent); }
However, the user clicking and dragging currently also leads to the item being removed, since click and drag triggers a mouseReleaseEvent event too. How can I detect if the item is being dragged around the scene to catch this special case and not trigger a delete? Is there perhaps a better approach to this in general?
I've checked for dragging tutorials, but somehow everything I find is associated with inter-application drag and drop & mimedata which is not what I'm after.
-
I'm having some confusion with clicking and dragging of a QGraphicsItem inside a QGraphicsScene. I'd like to implement the functionality that:
- a single click in an empty part of the scene adds an item
- a single click on an existing item removes this item.
- a click and mouse move action drags the item around
I've currently tried to implement this with a mouseReleaseEvent for my custom scene implementation:
For my QGraphicsItem called Node I set:Node::Node(QPointF pos) : QGraphicsItem() { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setPos(pos); }
As expected, I can now drag my items around the scene. However, for add and remove functionality I'm trying this approach by overriding scene methods:
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){ if (mouseEvent->button() == Qt::LeftButton){ QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform()); Node *node = qgraphicsitem_cast<Node *>(item); if(node){ removeItem(node); }else{ QPointF pt = mouseEvent->scenePos(); Node* node = new Node(pt); addItem(node); } } QGraphicsScene::mouseReleaseEvent(mouseEvent); }
However, the user clicking and dragging currently also leads to the item being removed, since click and drag triggers a mouseReleaseEvent event too. How can I detect if the item is being dragged around the scene to catch this special case and not trigger a delete? Is there perhaps a better approach to this in general?
I've checked for dragging tutorials, but somehow everything I find is associated with inter-application drag and drop & mimedata which is not what I'm after.
@LiberRexPaimon said in Click & drag a QGraphicsItem inside QGraphicsScene?:
- a single click on an existing item removes this item.
- a click and mouse move action drags the item around
I was about to say: they are both mouse downs followed by mouse ups. A notable difference is that for a click the up happens at around the same place as the down but for a drag it happens at another position. So note where the mouse down occurred and compare the position of the corresponding mouse up to see the distance. When I came across just this advice at https://doc.qt.io/qt-5/dnd.html#dragging:
For widgets that need to distinguish between mouse clicks and drags, it is useful to reimplement the widget's mousePressEvent() function to record to start position of the drag:
...
This particular approach uses the QPoint::manhattanLength() function to get a rough estimate of the distance between where the mouse click occurred and the current cursor position. This function trades accuracy for speed, and is usually suitable for this purpose.Have a look at the discussion and code there. It's for widgets but I assume the same principle holds for
QGraphicsItem
/Scene
. -
I'm having some confusion with clicking and dragging of a QGraphicsItem inside a QGraphicsScene. I'd like to implement the functionality that:
- a single click in an empty part of the scene adds an item
- a single click on an existing item removes this item.
- a click and mouse move action drags the item around
I've currently tried to implement this with a mouseReleaseEvent for my custom scene implementation:
For my QGraphicsItem called Node I set:Node::Node(QPointF pos) : QGraphicsItem() { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setPos(pos); }
As expected, I can now drag my items around the scene. However, for add and remove functionality I'm trying this approach by overriding scene methods:
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){ if (mouseEvent->button() == Qt::LeftButton){ QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform()); Node *node = qgraphicsitem_cast<Node *>(item); if(node){ removeItem(node); }else{ QPointF pt = mouseEvent->scenePos(); Node* node = new Node(pt); addItem(node); } } QGraphicsScene::mouseReleaseEvent(mouseEvent); }
However, the user clicking and dragging currently also leads to the item being removed, since click and drag triggers a mouseReleaseEvent event too. How can I detect if the item is being dragged around the scene to catch this special case and not trigger a delete? Is there perhaps a better approach to this in general?
I've checked for dragging tutorials, but somehow everything I find is associated with inter-application drag and drop & mimedata which is not what I'm after.
@LiberRexPaimon said in Click & drag a QGraphicsItem inside QGraphicsScene?:
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){
if (mouseEvent->button() == Qt::LeftButton){
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
Node *node = qgraphicsitem_cast<Node >(item);
if(node){
removeItem(node);
}else{
QPointF pt = mouseEvent->scenePos();
Node node = new Node(pt);
addItem(node);
}
}
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}In addition to what @JonB said:
This implementation would remove nodes, even when you click and drag another node and only release the mouse button over an existing node. So I would add some check whether the node on which the mouse button was released, was pressed (mouse down) before. Therefore you could add abool clicked
to yourNode
class and check if it'strue
before removing the item from your scene to ensure that it is the correct item that was meant to be removed.