Solved QGraphicScene/QGraphicsWidget: How to do complex movements over several Widgets
-
I have several
Rooms
which are derrived fromQGraphicsWidget
.These
Room
widgets are added to aQGraphicsScene
.Now I want to do the following.
Hold down the right mouse button on a
Room
.Hold it down until I reach the destination
Room
.While hold down I can hover over more rooms and trigger certain actions (like call a function which changes theire appearance) when hover over them until I reach my destination rooms.
When I reach the final room and release the right button some action should be toggled which affects all the rooms I hit on the way. So I need to keep track of which room I passed.
If I don't reach the final room (release out of room widget). All Actions get reversed.
Whats the easiest way to achieve what I described?
Do I have to write a custom event filter? Like it was done with this similar issue:
https://forum.qt.io/topic/107430/detect-if-mouse-buttons-are-pressed-when-sliding-into-widget/2
(But there I usedQWidget
instead ofQGraphicsScene
).Can I just derrive QGraphicsScene and handle the mouseevents there?
I first had the stupid idea of using
QDrag
with a CustomQMimeData
which keeps track of the passed widgets but that does not seem to work. I think drag and drop actions work only between two widgets directly right?Im looking forward to hear youre opinion.
Let me know if something is unclear.
-
Hi,
That's something you should rather do at the QGraphicsView level since it's what is showing your Rooms. You will likely be interested in the mouseMoveEvent and friends.
-
Thanks for the hint to the View. The approach works well. I figured out with
qgraphicsitem_cast
anditemAt
I can always identify which widget is clicked.So far my code looks like this (could be still buggy)
void DungeonView::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { if (auto room = qgraphicsitem_cast<Room *>(itemAt(event->pos())) ; room->hasPlayer()) { mShootArrowSelectOn = true; room->setTarget(true); mlastRoom = room; mMarkedRooms.push_back(room); setCursor(Qt::CrossCursor); return; } } // forward to handle drag and drop when left Button is pressed QGraphicsView::mousePressEvent(event); } void DungeonView::mouseReleaseEvent(QMouseEvent *event) { if (mShootArrowSelectOn && event->button() == Qt::RightButton) { for (auto &room : mMarkedRooms) { room->setTarget(false); // do action here } mMarkedRooms.clear(); mlastRoom = nullptr; mShootArrowSelectOn = false; setCursor(Qt::ArrowCursor); } else { // forward to handle drag and drop when left Button is pressed QGraphicsView::mouseReleaseEvent(event); } } void DungeonView::mouseMoveEvent(QMouseEvent *event) { auto room = qgraphicsitem_cast<Room *>(itemAt(event->pos())); if(room == nullptr) { if(!mShootArrowSelectOn && mlastRoom != nullptr) { setCursor(Qt::ArrowCursor); } // forward to handle drag and drop when left Button is pressed QGraphicsView::mouseMoveEvent(event); return; } if(room == mlastRoom) { // forward to handle drag and drop when left Button is pressed QGraphicsView::mouseMoveEvent(event); return; } if (mShootArrowSelectOn && !maxArrowRangeReached() && isNeigbourOfLastMarkedRoom(room)) { room->setTarget(true); mMarkedRooms.push_back(room); mlastRoom = room; } else { // forward to handle drag and drop when left Button is pressed QGraphicsView::mouseMoveEvent(event); } }
Now this gives annother question. As you can see in the comments currently I forward my actions to the
QGraphicsView
functions. Reasons for this is they forward theevents
to theRoom
widgets to perform a drag and drop Operation with them.Is it a good idea to make the drag and drop in the
Room
then? Or should I also move it "up" toQGraphicsView
? -
Hi
Nothing bad in forwarding to base class.
and in many cases required as not to break functionality.
handling DnD in room seems perfectly valid as it hold the needed info
but only you can say if it fits better in a level above.