The best and most efficient approach is to use QGraphicsItem::itemChange(). That way you can avoid moving the item forward and backward. Also by reimplementing this function, you can snap up any other attempt to move the item, not just mouseMove() but also setPos(), and you can even intercept transformation changes (which can also affect the item's position). Here's one way to stop an item from being moved if it collides with another (untested code):
{
if (change == ItemPositionChange) {
QPainterPath sceneShape
= mapToParent
(shape
()).
translate(val.
toPointF());
sceneShape = parent->mapToScene(sceneShape);
if (scene()->items(sceneShape, Qt::IntersectsItemShape).size() > 1) {
// Collision! Don't move.
return pos();
}
}
}
QVariant MyItem::itemChange(GraphicsItemChange change, const QVariant &val)
{
if (change == ItemPositionChange) {
QPainterPath sceneShape = mapToParent(shape()).translate(val.toPointF());
if (QGraphicsItem *parent = parentItem())
sceneShape = parent->mapToScene(sceneShape);
if (scene()->items(sceneShape, Qt::IntersectsItemShape).size() > 1) {
// Collision! Don't move.
return pos();
}
}
return QGraphicsItem::itemChange(change, val);
}
To copy to clipboard, switch view to plain text mode
This is a bit crude, but I hope you get the picture. Map your shape to your parent coordinates, translate it by your future position (provided through the val-argument), and map that to the scene if necessary. Now ask the scene if any item other than your own collides with that shape. If so, return the old pos instead of the new one to effectively abort the repositioning.
It's crude, because 1) you really want to check if the list of items is empty, or contains only your item (not just that it contains at most 1 item). And 2) because instead of just not moving the item, it's common to neatly position the item at the point of intersection / right next to the other. That's hard to do in general, so I'll leave that up to the app writer to figure out ;-).
Bookmarks