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. Custom QGraphicsEllipseItem mousePressedEvent single clicks

Custom QGraphicsEllipseItem mousePressedEvent single clicks

Scheduled Pinned Locked Moved Solved General and Desktop
qt6qgraphicsitemmouse events
9 Posts 4 Posters 488 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.
  • D Offline
    D Offline
    Deneguil
    wrote on last edited by
    #1

    Hello,

    I have a custom QGraphicsEllipseItem to represent a node in a graph with the following code (everything is inlined for testing purposes).

    constexpr int const R = 50;
    
    class StateWidget : public QGraphicsEllipseItem {
        public:
            float x, y;
            inline StateWidget(QPointF p) : StateWidget(p.x(), p.y()) {}
            inline StateWidget(float x, float y, QGraphicsItem* parent = nullptr): QGraphicsEllipseItem(x,y,R,R) {
                setBrush(QBrush(Qt::white));
                setZValue(10);
            }
        protected:
            inline void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
                event->accept();
                std::cout << "State pressed\n";
            }
    };
    

    These states widgets are then added to a custom GraphicsView through its GraphicsScene. The GraphicsView mouse event code is as such

    void GraphicView::mousePressEvent(QMouseEvent* event) {
        if(itemAt(event->pos()) != nullptr) return;
        std::cout << "GV clicked\n";
    }
    

    When I click into an empty space in the Graphics Scene it does print "GV clicked" correctly. However, I need to double click the ellipse for the StateWidget event to trigger. The documentation mentions that for a QGraphicsItem if the mousePressEvent is overriden then the item will accept move, release and double click events. What I want though is to trigger the event with a single click as I want to be able to select the state. I also don't understand why the mousePressEvent would specifically accept double clicks when mouseDoubleClickEvent exists.

    Pl45m4P A 2 Replies Last reply
    0
    • D Deneguil

      Hello,

      I have a custom QGraphicsEllipseItem to represent a node in a graph with the following code (everything is inlined for testing purposes).

      constexpr int const R = 50;
      
      class StateWidget : public QGraphicsEllipseItem {
          public:
              float x, y;
              inline StateWidget(QPointF p) : StateWidget(p.x(), p.y()) {}
              inline StateWidget(float x, float y, QGraphicsItem* parent = nullptr): QGraphicsEllipseItem(x,y,R,R) {
                  setBrush(QBrush(Qt::white));
                  setZValue(10);
              }
          protected:
              inline void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
                  event->accept();
                  std::cout << "State pressed\n";
              }
      };
      

      These states widgets are then added to a custom GraphicsView through its GraphicsScene. The GraphicsView mouse event code is as such

      void GraphicView::mousePressEvent(QMouseEvent* event) {
          if(itemAt(event->pos()) != nullptr) return;
          std::cout << "GV clicked\n";
      }
      

      When I click into an empty space in the Graphics Scene it does print "GV clicked" correctly. However, I need to double click the ellipse for the StateWidget event to trigger. The documentation mentions that for a QGraphicsItem if the mousePressEvent is overriden then the item will accept move, release and double click events. What I want though is to trigger the event with a single click as I want to be able to select the state. I also don't understand why the mousePressEvent would specifically accept double clicks when mouseDoubleClickEvent exists.

      Pl45m4P Offline
      Pl45m4P Offline
      Pl45m4
      wrote on last edited by
      #2

      @Deneguil said in Custom QGraphicsEllipseItem mousePressedEvent single clicks:

      I also don't understand why the mousePressEvent would specifically accept double clicks when mouseDoubleClickEvent exists.

      I don't understand the problem.
      A double click consists of press, release, press, release in a given time period.
      So why would clicking twice very fast not trigger single events?!


      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
      • D Deneguil

        Hello,

        I have a custom QGraphicsEllipseItem to represent a node in a graph with the following code (everything is inlined for testing purposes).

        constexpr int const R = 50;
        
        class StateWidget : public QGraphicsEllipseItem {
            public:
                float x, y;
                inline StateWidget(QPointF p) : StateWidget(p.x(), p.y()) {}
                inline StateWidget(float x, float y, QGraphicsItem* parent = nullptr): QGraphicsEllipseItem(x,y,R,R) {
                    setBrush(QBrush(Qt::white));
                    setZValue(10);
                }
            protected:
                inline void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
                    event->accept();
                    std::cout << "State pressed\n";
                }
        };
        

        These states widgets are then added to a custom GraphicsView through its GraphicsScene. The GraphicsView mouse event code is as such

        void GraphicView::mousePressEvent(QMouseEvent* event) {
            if(itemAt(event->pos()) != nullptr) return;
            std::cout << "GV clicked\n";
        }
        

        When I click into an empty space in the Graphics Scene it does print "GV clicked" correctly. However, I need to double click the ellipse for the StateWidget event to trigger. The documentation mentions that for a QGraphicsItem if the mousePressEvent is overriden then the item will accept move, release and double click events. What I want though is to trigger the event with a single click as I want to be able to select the state. I also don't understand why the mousePressEvent would specifically accept double clicks when mouseDoubleClickEvent exists.

        A Offline
        A Offline
        Asperamanca
        wrote on last edited by
        #3

        @Deneguil
        You need to understand how event propagate.

        • The event is first detected by your OS/Platform. If the area where it happened "belongs" to your application, it will be forwarded to your application's event loop (which is handled by Qt)
        • Qt forwards it to a global event handler function (which you could override, but which I assume you have not changed)
        • When using the default global event handler, any globally installed event filter will be called and may block the event. This is probably also not the case
        • A use input event is then forwarded to the correct QWidget (in a QWidget application such as this one). That's the topmost widget covering the area of the event (or the event grabber widget, which is mostly important in case of drag-drop operations)
        • QGraphicsView is a widget, so it receives the event. You receive the event and then block it from further propagation by not passing it to your base class. Your journey ends here.
        • The QQGraphicsView base class would forward the event to the QGraphicsScene. You can override events there, too
        • Unless the event is blocked, the QGraphicsScene will detect the topmost QGraphicsItem under the event position, and deliver the event there
        • If the event is not blocked and not accepted, it will propagate to all QGraphicsItems below the event position, until either one of them accepts it, or we run out of items to try.

        So, this line of code causes the behavior you observe:

        if(itemAt(event->pos()) != nullptr) return;
        
        Pl45m4P D 2 Replies Last reply
        0
        • A Asperamanca

          @Deneguil
          You need to understand how event propagate.

          • The event is first detected by your OS/Platform. If the area where it happened "belongs" to your application, it will be forwarded to your application's event loop (which is handled by Qt)
          • Qt forwards it to a global event handler function (which you could override, but which I assume you have not changed)
          • When using the default global event handler, any globally installed event filter will be called and may block the event. This is probably also not the case
          • A use input event is then forwarded to the correct QWidget (in a QWidget application such as this one). That's the topmost widget covering the area of the event (or the event grabber widget, which is mostly important in case of drag-drop operations)
          • QGraphicsView is a widget, so it receives the event. You receive the event and then block it from further propagation by not passing it to your base class. Your journey ends here.
          • The QQGraphicsView base class would forward the event to the QGraphicsScene. You can override events there, too
          • Unless the event is blocked, the QGraphicsScene will detect the topmost QGraphicsItem under the event position, and deliver the event there
          • If the event is not blocked and not accepted, it will propagate to all QGraphicsItems below the event position, until either one of them accepts it, or we run out of items to try.

          So, this line of code causes the behavior you observe:

          if(itemAt(event->pos()) != nullptr) return;
          
          Pl45m4P Offline
          Pl45m4P Offline
          Pl45m4
          wrote on last edited by Pl45m4
          #4

          @Asperamanca

          Aren't events propagated bottom up (innermost child = bottom)?!
          You don't call the base(!) implementation in event handlers for nothing ;-)

          In situations like you describe, the event comes from the top most parent and is propagated down.
          If I'm not mistaken exactly the opposite is the case.
          If child don't accept the event, the parent gets it... if parent don't want the event, its grandparent gets it... until the event is accepted or "eaten" so that the propagation stops


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

          ~E. W. Dijkstra

          A 1 Reply Last reply
          0
          • A Asperamanca

            @Deneguil
            You need to understand how event propagate.

            • The event is first detected by your OS/Platform. If the area where it happened "belongs" to your application, it will be forwarded to your application's event loop (which is handled by Qt)
            • Qt forwards it to a global event handler function (which you could override, but which I assume you have not changed)
            • When using the default global event handler, any globally installed event filter will be called and may block the event. This is probably also not the case
            • A use input event is then forwarded to the correct QWidget (in a QWidget application such as this one). That's the topmost widget covering the area of the event (or the event grabber widget, which is mostly important in case of drag-drop operations)
            • QGraphicsView is a widget, so it receives the event. You receive the event and then block it from further propagation by not passing it to your base class. Your journey ends here.
            • The QQGraphicsView base class would forward the event to the QGraphicsScene. You can override events there, too
            • Unless the event is blocked, the QGraphicsScene will detect the topmost QGraphicsItem under the event position, and deliver the event there
            • If the event is not blocked and not accepted, it will propagate to all QGraphicsItems below the event position, until either one of them accepts it, or we run out of items to try.

            So, this line of code causes the behavior you observe:

            if(itemAt(event->pos()) != nullptr) return;
            
            D Offline
            D Offline
            Deneguil
            wrote on last edited by
            #5

            @Asperamanca

            Thank you very much for your in depth breakdown of the event propagation pipeline.

            I originally wrote the following line to prevent both events from firing.

            if(itemAt(event->pos()) != nullptr) return;
            

            Without it it'd print both "GV clicked" and "State clicked".

            If modified it to this one now and it works perfectly.

            if(itemAt(event->pos()) != nullptr) {
                QGraphics::mousePressEvent(event);
                return;
            }
            

            Both strings are still printed without the return which is why it's still here for apart from that it's perfect.

            Thank you!

            A 1 Reply Last reply
            0
            • artwawA Offline
              artwawA Offline
              artwaw
              wrote on last edited by
              #6

              I am not sure if I follow what exactly you want to achieve but according to the docs for QMouseEvent "the right way" of stopping propagation is to call accept() on the event?

              For more information please re-read.

              Kind Regards,
              Artur

              D 1 Reply Last reply
              0
              • D Deneguil has marked this topic as solved on
              • artwawA artwaw

                I am not sure if I follow what exactly you want to achieve but according to the docs for QMouseEvent "the right way" of stopping propagation is to call accept() on the event?

                D Offline
                D Offline
                Deneguil
                wrote on last edited by
                #7

                @artwaw

                I am calling accept() in the state event handler but it still prints both strings.

                The application is a GUI automata editor so the idea is to have a Graphics View displaying the graph and the user would have the ability to click on a node or edge and it'd display its parameters in a toolbox. So I want to isolate the click event of the Graphics Items from the one of the View as much as possible.

                The return might not be the cleanest way to solve this small issue but it works well enough for the time being. And seeing how there'll be several modes in the application to either add a new state, transition or simply to select one, I should be able to use those to check whether the event is valid or not too.

                1 Reply Last reply
                0
                • Pl45m4P Pl45m4

                  @Asperamanca

                  Aren't events propagated bottom up (innermost child = bottom)?!
                  You don't call the base(!) implementation in event handlers for nothing ;-)

                  In situations like you describe, the event comes from the top most parent and is propagated down.
                  If I'm not mistaken exactly the opposite is the case.
                  If child don't accept the event, the parent gets it... if parent don't want the event, its grandparent gets it... until the event is accepted or "eaten" so that the propagation stops

                  A Offline
                  A Offline
                  Asperamanca
                  wrote on last edited by
                  #8

                  @Pl45m4 said in Custom QGraphicsEllipseItem mousePressedEvent single clicks:

                  @Asperamanca

                  Aren't events propagated bottom up (innermost child = bottom)?!
                  You don't call the base(!) implementation in event handlers for nothing ;-)

                  In situations like you describe, the event comes from the top most parent and is propagated down.
                  If I'm not mistaken exactly the opposite is the case.
                  If child don't accept the event, the parent gets it... if parent don't want the event, its grandparent gets it... until the event is accepted or "eaten" so that the propagation stops

                  You are thinking of QWidget.
                  In QGraphicsView, things are more complex:

                  • Parent can be on top of children thanks to QGraphicsItem::ItemStacksBehindParent or a combination of QGraphicsItem::ItemNegativeZStacksBehindParent and negative Z-Order
                  • Children can be outside the shape of a parent, unless the parent has QGraphicsItem::ItemClipsChildrenToShape set

                  For those reasons, the QGraphicsItem cannot possibly know whom to forward the event to (see also the implementation of QGraphicsItem::mousePressEvent in qgraphicsitem.cpp).

                  The reason you should still forward the event to base class is that the item does a few item-related things, like moving items that are moveable.

                  1 Reply Last reply
                  0
                  • D Deneguil

                    @Asperamanca

                    Thank you very much for your in depth breakdown of the event propagation pipeline.

                    I originally wrote the following line to prevent both events from firing.

                    if(itemAt(event->pos()) != nullptr) return;
                    

                    Without it it'd print both "GV clicked" and "State clicked".

                    If modified it to this one now and it works perfectly.

                    if(itemAt(event->pos()) != nullptr) {
                        QGraphics::mousePressEvent(event);
                        return;
                    }
                    

                    Both strings are still printed without the return which is why it's still here for apart from that it's perfect.

                    Thank you!

                    A Offline
                    A Offline
                    Asperamanca
                    wrote on last edited by
                    #9

                    @Deneguil said in Custom QGraphicsEllipseItem mousePressedEvent single clicks:

                    @Asperamanca

                    Thank you very much for your in depth breakdown of the event propagation pipeline.

                    I originally wrote the following line to prevent both events from firing.

                    if(itemAt(event->pos()) != nullptr) return;
                    

                    Without it it'd print both "GV clicked" and "State clicked".

                    If modified it to this one now and it works perfectly.

                    if(itemAt(event->pos()) != nullptr) {
                        QGraphics::mousePressEvent(event);
                        return;
                    }
                    

                    Both strings are still printed without the return which is why it's still here for apart from that it's perfect.

                    Thank you!

                    However, this way you are bypassing a lot of things, which could lead to confusion the next time you'd like to add something.

                    What you probably want (I'm guessing a bit here) is this:

                    void GraphicView::mousePressEvent(QMouseEvent* event) {
                        QGraphicsView::mousePressEvent(event);
                        if(event->isAccepted()) return;
                        std::cout << "GV clicked\n";
                    }
                    

                    Now you can catch the event in the item, handle and accept it.
                    The view will know that someone has taken care of the event, and it doesn't need to consider it.

                    1 Reply Last reply
                    0

                    • Login

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