Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Receive Drag/Drop events for items in QGraphicsScene
QtWS25 Last Chance

Receive Drag/Drop events for items in QGraphicsScene

Scheduled Pinned Locked Moved Solved General and Desktop
qgraphicsviewdrag and dropevent handlingqgraphicsscene
8 Posts 4 Posters 1.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    sticky thermos
    wrote on 14 Jul 2021, 03:52 last edited by sticky thermos
    #1

    Hi,
    I'm new to working with QT (though experienced in C++). I'm trying to make a basic chess game, and I've come across the QGraphicsView framework. I think this may be a better fit than using a QGridLayout - if I should turn back now please let me know :)

    I am using QT 6

    I've created my own classes that derive from QGraphicsView, QGraphicsScene and QGraphicsPixmapItem in hopes of being able to handle the events that are sent to each. My CustomView (derives from QGraphicsView) creates the CustomScene (derives from QGraphicsScene) and adds a few CustomItems (derives from QGraphicsPixmapItem). I'm able to see these pop up on the screen and can drag the items around with my mouse - so far so good.

    In the CustomView, I can get and handle mousemove events. I can make do with that, but I see that there are also events about dragging and dropping. However, I have never received these events. When does this event occur?
    I have tried overriding the drag/drop events in my CustomItems, but I don't ever receive those either (and I've called setAcceptDrops(true)).

    So my question is can I receive drag and drop events on the individual pixmap items on a QGraphicsScene, or do I have to figure it out manually using the mouse events on the QGraphicsView?

    Thanks in advance

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 14 Jul 2021, 19:17 last edited by
      #2

      Hi and welcome to devnet,

      Can you share your CustomItems implementation ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      S 1 Reply Last reply 14 Jul 2021, 23:12
      2
      • M Offline
        M Offline
        mchinand
        wrote on 14 Jul 2021, 20:17 last edited by
        #3

        Have you checked out the Robot Drag and Drop Example?

        S 1 Reply Last reply 14 Jul 2021, 23:24
        2
        • S SGaist
          14 Jul 2021, 19:17

          Hi and welcome to devnet,

          Can you share your CustomItems implementation ?

          S Offline
          S Offline
          sticky thermos
          wrote on 14 Jul 2021, 23:12 last edited by
          #4

          @SGaist
          Thanks! I've included the code below. Locally, I have the code split into headers and source files but I rewrote it inline for readability here.
          Also, I lied about the names (CustomView, CustomScene etc) to keep it simple; most of the names below are still self explanatory:

          // main.cpp
          #include "Controller.h"
          
          #include <QApplication>
          #include <iostream>
          
          int main(int argc, char *argv[])
          {
              QApplication a(argc, argv);
              Controller w;
              w.show();
              w.setup();
              return a.exec();
          }
          
          // Controller / main window
          #include "Controller.h"
          #include "ui_chess.h"
          
          #include "ChessBoard.h"
          #include <QLayout>
          
          #include <QBrush>
          #include <QPen>
          #include <QGraphicsPixmapItem>
          #include <QMainWindow>
          #include <QPainter>
          #include <QGridLayout>
          
          #include <iostream>
          #include <memory>
          
          QT_BEGIN_NAMESPACE
          namespace Ui { class Chess; }
          QT_END_NAMESPACE
          
          class Controller : public QMainWindow {
          public:
          Q_OBJECT
          
          Controller(QWidget *parent)
              : QMainWindow(parent)
              , ui(new Ui::Chess)
              , m_Layout(new QGridLayout())
              , m_ChessBoard(new ChessBoard(this)) // ChessBoard is a QGraphicsView*
          {
              ui->setupUi(this);
          
              QWidget *widget = new QWidget();
              widget->setLayout(m_Layout);
              setCentralWidget(widget);
          
              m_Layout->setObjectName("TopLevelGridLayout");
          
              // future plans to add some stuff here, e.g. clock, moves, score etc
              m_Layout->addWidget(new QWidget(), 0,0);
              m_Layout->addWidget(new QWidget(), 0,1);
              m_Layout->addWidget(new QWidget(), 0,5);
              
              m_Layout->addWidget(m_ChessBoard, 1,1,3,3);
          }
          
          // separated part of init because I'm not sure what's allowed to happen inside of constructor
          void setup() { 
              m_ChessBoard->setup();
          }
          
          private:
              std::unique_ptr<Ui::Chess> ui;
              QGridLayout* m_Layout;
              ChessBoard* m_ChessBoard;
          };
          
          // Chessboard -> contains the main ChessBoard class and some other minor classes
          #include <QGraphicsView>
          #include <QGraphicsScene>
          #include <QObject>
          #include <QWidget>
          #include <QPaintEvent>
          
          // For now this class does nothing special
          class ChessScene : public QGraphicsScene {
          public:
              ChessScene(QWidget* parent) : QGraphicsScene(parent) {
              }
          };
          
          class ChessPiece : public QGraphicsPixmapItem {
          public:
              ChessPiece(const QPixmap &pixmap, QGraphicsItem *parent, std::string name) 
                  : QGraphicsPixmapItem(pixmap, parent)
                  , m_name(name) {
                  setAcceptDrops(true); //  I think this line is the crucial part, but it still doesn't seem to work...
              }
          protected:
              void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override {
                  std::cout << "Piece: " << m_name << '\t' << __FUNCTION__ << std::endl;
                  QGraphicsPixmapItem::dragEnterEvent(event);
              }
              void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) override {
                  std::cout << "Piece: " << m_name << '\t' << __FUNCTION__ << std::endl;
                  QGraphicsPixmapItem::dragLeaveEvent(event);
              }
              void dragMoveEvent(QGraphicsSceneDragDropEvent *event) override {
                  std::cout << "Piece: " << m_name << '\t' << __FUNCTION__ << std::endl;
                  QGraphicsPixmapItem::dragMoveEvent(event);
              }
              void dropEvent(QGraphicsSceneDragDropEvent *event) override {
                  std::cout << "Piece: " << m_name << '\t' << __FUNCTION__ << std::endl;
                  QGraphicsPixmapItem::dropEvent(event);
              }
          
              std::string m_name;
          };
          
          class ChessBoard : public QGraphicsView
          {
              Q_OBJECT
          
          public:
              ChessBoard(QWidget* parent) 
                : QGraphicsView(parent)
                , m_scene(new ChessScene(this))
              {
                  setObjectName(QString("ChessBoardView"));
                  setScene(m_scene);
              }
          
              QSize minimumSizeHint() const override { return {kMinWidth, kMinHeight}; }
          
              void setup() {
                  addPiecesToScreen();
                  setSizeIncrement(1,1);
              }
              QGraphicsScene* getScene() { return m_scene; }
          
              /* Events */
          protected:
              void dragEnterEvent(QDragEnterEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
              }
             void dragLeaveEvent(QDragLeaveEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
              }
             void dragMoveEvent(QDragMoveEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
              }
              void dropEvent(QDropEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
              }
          
              void mouseDoubleClickEvent(QMouseEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
                  QGraphicsView::mouseDoubleClickEvent(event);
              }
              void mouseMoveEvent(QMouseEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
                  QGraphicsView::mouseMoveEvent(event);
              }
              void mousePressEvent(QMouseEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
                  QGraphicsView::mousePressEvent(event);
              }
              void mouseReleaseEvent(QMouseEvent *event) {
                  std::cout << __FUNCTION__ << std::endl;
                  QGraphicsView::mouseReleaseEvent(event);
              }
          
          private:
              void addPiecesToScreen() {
                 auto cp = new ChessPiece(QPixmap{":/resources/assets/bK.png"}, nullptr, "BlackKing");
                 cp->setFlags(QGraphicsItem::GraphicsItemFlag::ItemIsFocusable | QGraphicsItem::GraphicsItemFlag::ItemIsMovable);
                 m_scene->addItem(cp);
          
                 cp = new ChessPiece(QPixmap{":/resources/assets/wK.png"}, nullptr, "WhiteKing");
                 cp->setFlags(QGraphicsItem::GraphicsItemFlag::ItemIsFocusable | QGraphicsItem::GraphicsItemFlag::ItemIsMovable);
                 m_scene->addItem(cp);
               }
          
          private:
              QGraphicsScene* m_scene;
              static constexpr const uint8_t kMinWidth { 50 };
              static constexpr const uint8_t kMinHeight { kMinWidth };
          };
          

          When this runs, I'm able to see prints of Mouse[Press/Move/Release]Events coming from the ChessBoard.

          I expect to see those, in addition to drag[Enter/Leave/Move]Events and dropEvents, from both the ChessBoard and the ChessPiece (the QGraphicsPixmapItem).

          I'd appreciate any feedback!

          1 Reply Last reply
          0
          • M mchinand
            14 Jul 2021, 20:17

            Have you checked out the Robot Drag and Drop Example?

            S Offline
            S Offline
            sticky thermos
            wrote on 14 Jul 2021, 23:24 last edited by
            #5

            @mchinand I hadn't seen it until you pointed it out. It looks very relevant, but I can't seem to find the problem in my own code. I'm successfully calling setAcceptDrops(true) as it mentions. Are there other changes between QT 5 and QT 6?

            P 1 Reply Last reply 14 Jul 2021, 23:48
            0
            • S sticky thermos
              14 Jul 2021, 23:24

              @mchinand I hadn't seen it until you pointed it out. It looks very relevant, but I can't seem to find the problem in my own code. I'm successfully calling setAcceptDrops(true) as it mentions. Are there other changes between QT 5 and QT 6?

              P Online
              P Online
              Pl45m4
              wrote on 14 Jul 2021, 23:48 last edited by Pl45m4
              #6

              @sticky-thermos

              Haven't used d'n'd that much but the difference I see between your code and the example is, that you don't create a QDrag object to initalize the dnd process in your mouse handlers.
              I dont know if this is necessary in your case. Like I said, haven't used dropEvent in this way before :)

              • https://doc.qt.io/qt-5/qdrag.html#details

              Edit:

              Finally we execute the drag. QDrag::exec() will reenter the event loop, and only exit if the drag has either been dropped, or canceled. In any case we reset the cursor to Qt::OpenHandCursor.

              So, no QDrag object, no dropEvents on the destination widget.

              Also your logic seems a bit wrong. The to-be-dropped widget has to create the Drag object and the drop destination has to acceptDrops.
              If I have seen correctly, your to-be-dropped object, the chessPiece accept drops and not the ChessBoard.

              (Assuming you want to dnd your chess figures onto the board and move them around by dnd them on one valid field on the chessBoard)

              To stay with the example:
              ChessPiece = ColorCircle
              ChessBoard = RobotParts

              In the example, the colorCircles are dropped on the robotParts, transferring the corresponding color by passing the mimeData.


              If debugging is the process of removing software bugs, then programming must be the process of putting them in.

              ~E. W. Dijkstra

              S 1 Reply Last reply 18 Jul 2021, 23:14
              0
              • P Pl45m4
                14 Jul 2021, 23:48

                @sticky-thermos

                Haven't used d'n'd that much but the difference I see between your code and the example is, that you don't create a QDrag object to initalize the dnd process in your mouse handlers.
                I dont know if this is necessary in your case. Like I said, haven't used dropEvent in this way before :)

                • https://doc.qt.io/qt-5/qdrag.html#details

                Edit:

                Finally we execute the drag. QDrag::exec() will reenter the event loop, and only exit if the drag has either been dropped, or canceled. In any case we reset the cursor to Qt::OpenHandCursor.

                So, no QDrag object, no dropEvents on the destination widget.

                Also your logic seems a bit wrong. The to-be-dropped widget has to create the Drag object and the drop destination has to acceptDrops.
                If I have seen correctly, your to-be-dropped object, the chessPiece accept drops and not the ChessBoard.

                (Assuming you want to dnd your chess figures onto the board and move them around by dnd them on one valid field on the chessBoard)

                To stay with the example:
                ChessPiece = ColorCircle
                ChessBoard = RobotParts

                In the example, the colorCircles are dropped on the robotParts, transferring the corresponding color by passing the mimeData.

                S Offline
                S Offline
                sticky thermos
                wrote on 18 Jul 2021, 23:14 last edited by
                #7

                @Pl45m4 Thanks! You're right, I was missing the QDrag object. I created that and I'm getting the drop events on the objects now. I had the wrong understanding about drag and drop: I thought that a dragEnter was equivalent to a mousePress, and a dragLeave to a mouseRelease. Thanks for that bit of info :) I'm going to tweak the design a bit with this new info, maybe try getting the ChessBoard to acceptDrops instead as you mentioned.

                For anyone in the future, this is what I have for the QGraphicsView::mouseMoveEvent:

                void ChessBoard::mouseMoveEvent(QMouseEvent *event) {
                    std::cout << __FUNCTION__ << std::endl;
                    QGraphicsView::mousePressEvent(event);
                    QDrag *drag = new QDrag(this);
                    QMimeData *mime = new QMimeData; // this is necessary even if nothing is set in the mime data
                    drag->setMimeData(mime);
                    drag->exec();
                }
                

                Thank you everyone for your help!

                P 1 Reply Last reply 19 Jul 2021, 10:48
                2
                • S sticky thermos
                  18 Jul 2021, 23:14

                  @Pl45m4 Thanks! You're right, I was missing the QDrag object. I created that and I'm getting the drop events on the objects now. I had the wrong understanding about drag and drop: I thought that a dragEnter was equivalent to a mousePress, and a dragLeave to a mouseRelease. Thanks for that bit of info :) I'm going to tweak the design a bit with this new info, maybe try getting the ChessBoard to acceptDrops instead as you mentioned.

                  For anyone in the future, this is what I have for the QGraphicsView::mouseMoveEvent:

                  void ChessBoard::mouseMoveEvent(QMouseEvent *event) {
                      std::cout << __FUNCTION__ << std::endl;
                      QGraphicsView::mousePressEvent(event);
                      QDrag *drag = new QDrag(this);
                      QMimeData *mime = new QMimeData; // this is necessary even if nothing is set in the mime data
                      drag->setMimeData(mime);
                      drag->exec();
                  }
                  

                  Thank you everyone for your help!

                  P Online
                  P Online
                  Pl45m4
                  wrote on 19 Jul 2021, 10:48 last edited by
                  #8

                  @sticky-thermos said in Receive Drag/Drop events for items in QGraphicsScene:

                  void ChessBoard::mouseMoveEvent(QMouseEvent *event) {
                  std::cout << FUNCTION << std::endl;
                  QGraphicsView::mousePressEvent(event);
                  QDrag *drag = new QDrag(this);
                  QMimeData *mime = new QMimeData; // this is necessary even if nothing is set in the mime data
                  drag->setMimeData(mime);
                  drag->exec();
                  }

                  Looks good, but I would add a check to differenciate whether your mouseButton is down. Otherwise you are creating a lot of drags from just moving your mouse around on your chessBoard.

                  Also, you are passing the ChessBoard::mouseMoveEvent(QMouseEvent *event) to QGraphicsView::mousePressEvent(event).
                  QGraphicsView::mouseMoveEvent would be more fitting :)


                  If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                  ~E. W. Dijkstra

                  1 Reply Last reply
                  0

                  5/8

                  14 Jul 2021, 23:24

                  • Login

                  • Login or register to search.
                  5 out of 8
                  • First post
                    5/8
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved