Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QGraphicScene/QGraphicsWidget: How to do complex movements over several Widgets



  • I have several Rooms which are derrived from QGraphicsWidget.

    These Room widgets are added to a QGraphicsScene.

    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 used QWidget instead of QGraphicsScene).

    Can I just derrive QGraphicsScene and handle the mouseevents there?

    I first had the stupid idea of using QDrag with a Custom QMimeData 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.


  • Lifetime Qt Champion

    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 and itemAt 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 the events to the Room 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" to QGraphicsView ?


  • Lifetime Qt Champion

    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.


Log in to reply