Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QGraphicsView::itemAt() - How to correctly pick QGraphicsItem?
Qt 6.12 LTS is out! See what's new in the release blog

QGraphicsView::itemAt() - How to correctly pick QGraphicsItem?

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 2 Posters 2.8k Views 1 Watching
  • 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.
  • A Asperamanca

    Seems you are mixing coordinate systems.
    boundingRect is in 'item' coordinates. That doesn't tell you the item's position within the scene.
    Map the boundingRect to scene coordinates using QGraphicsItem::mapToScene, then you can compare it the the scene coordinates of your mouse position

    T Offline
    T Offline
    tomas.soltys
    wrote on last edited by
    #3

    @Asperamanca thanks for your reply. When I map to scene it gets only more obvious that something is oddly wrong.

    void DiagramView::mousePressEvent(QMouseEvent *mouseEvent)
    {
        if (QGraphicsItem *item = this->itemAt(mouseEvent->pos()))
        {
            qDebug() << "Hit " << mouseEvent->pos() << mapToScene(mouseEvent->pos()) << item->mapToScene(item->boundingRect());
        }
        else
        {
            qDebug() << "Miss" << mouseEvent->pos() << mapToScene(mouseEvent->pos());
        }
    }
    

    Output:

    Miss QPoint(680,423) QPointF(267,-3)
    Hit  QPoint(679,426) QPointF(266,0)  QPolygonF(QPointF(250,0)QPointF(380,0)QPointF(380,54)QPointF(250,54)QPointF(250,0))
    Hit  QPoint(679,430) QPointF(266,4)  QPolygonF(QPointF(250,0)QPointF(380,0)QPointF(380,54)QPointF(250,54)QPointF(250,0))
    Hit  QPoint(675,450) QPointF(262,24) QPolygonF(QPointF(250,0)QPointF(380,0)QPointF(380,54)QPointF(250,54)QPointF(250,0))
    Miss QPoint(675,451) QPointF(262,25)
    

    I actually happens for any item at any position exactly same. As if the view/scene was able to "see" only part of the item, as if it was down-scaled scaled and keeping its origin.

    I paint the rectangle in following way:

    painter->drawRoundedRect(QRectF(0,0,130,54),5,5);
    setPos(250,0);
    
    1 Reply Last reply
    0
    • A Offline
      A Offline
      Asperamanca
      wrote on last edited by
      #4

      How do you set the boundingRect?
      Does the item have a shape as well?

      itemAt primarily uses shape(), boundingRect() is only a fallback if shape is not set.

      T 1 Reply Last reply
      0
      • A Asperamanca

        How do you set the boundingRect?
        Does the item have a shape as well?

        itemAt primarily uses shape(), boundingRect() is only a fallback if shape is not set.

        T Offline
        T Offline
        tomas.soltys
        wrote on last edited by
        #5

        @Asperamanca

        QRectF GraphicsNodeItem::boundingRect() const
        {
            return QRectF(0,0,130,54).normalized();
        }
        
        QPainterPath GraphicsNodeItem::shape() const
        {
            QPainterPath path;
            path.addRect(this->boundingRect());
            return path;
        }
        

        Even if I set bounding rectangle to some ridiculous size itemAt() will always return item only if clicked within small rectangle originating in the top left corner of the bounding rectangle.

        A 1 Reply Last reply
        0
        • T tomas.soltys

          @Asperamanca

          QRectF GraphicsNodeItem::boundingRect() const
          {
              return QRectF(0,0,130,54).normalized();
          }
          
          QPainterPath GraphicsNodeItem::shape() const
          {
              QPainterPath path;
              path.addRect(this->boundingRect());
              return path;
          }
          

          Even if I set bounding rectangle to some ridiculous size itemAt() will always return item only if clicked within small rectangle originating in the top left corner of the bounding rectangle.

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

          @tomas-soltys
          That looks weird. What version of Qt are you using?
          Can you try the behavior with a stock QGraphicsRectItem, using the setRect function to set your "boundingRect"?

          T 1 Reply Last reply
          0
          • A Asperamanca

            @tomas-soltys
            That looks weird. What version of Qt are you using?
            Can you try the behavior with a stock QGraphicsRectItem, using the setRect function to set your "boundingRect"?

            T Offline
            T Offline
            tomas.soltys
            wrote on last edited by tomas.soltys
            #7

            @Asperamanca

            QGraphicsRectItem works nicely.

            QGraphicsRectItem *rectItem = new QGraphicsRectItem;
            rectItem->setRect(0, 0, 130, 54);
            rectItem->setPos(250,0);
            

            Qt version is 6.3.1 and I am experiencing this behavior on Linux, MacOS and Windows.

            1 Reply Last reply
            0
            • A Offline
              A Offline
              Asperamanca
              wrote on last edited by
              #8

              @tomas-soltys said in QGraphicsView::itemAt() - How to correctly pick QGraphicsItem?:

              GraphicsNodeItem

              Your code for boundingRect and shape is pretty much identical to the code of QGraphicsRectItem (ignoring the extras that are needed when using a pen as border).
              Is your GraphicsNodeItem a base class? Could boundingRect() be overloaded and thus behave differently?

              T 1 Reply Last reply
              0
              • A Asperamanca

                @tomas-soltys said in QGraphicsView::itemAt() - How to correctly pick QGraphicsItem?:

                GraphicsNodeItem

                Your code for boundingRect and shape is pretty much identical to the code of QGraphicsRectItem (ignoring the extras that are needed when using a pen as border).
                Is your GraphicsNodeItem a base class? Could boundingRect() be overloaded and thus behave differently?

                T Offline
                T Offline
                tomas.soltys
                wrote on last edited by
                #9

                @Asperamanca

                Yes it is a base class.

                class GraphicsNodeItem : public QGraphicsItem
                {
                
                    public:
                
                        GraphicsNodeItem(QGraphicsItem *parent = nullptr);
                        QRectF boundingRect() const override;
                        QPainterPath shape() const override;
                };
                
                class GraphicsPersonItem : public GraphicsNodeItem
                {
                    public:
                
                        explicit GraphicsPersonItem(const FPerson &person, GraphicsNodeItem *parent = nullptr);
                        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
                };
                

                I have noticed that GraphicsNodeItem::shape() is called only when successful hit, whereas GraphicsNodeItem::boundingRect() is being called constantly. Is this correct behavior?

                A 1 Reply Last reply
                0
                • T tomas.soltys

                  @Asperamanca

                  Yes it is a base class.

                  class GraphicsNodeItem : public QGraphicsItem
                  {
                  
                      public:
                  
                          GraphicsNodeItem(QGraphicsItem *parent = nullptr);
                          QRectF boundingRect() const override;
                          QPainterPath shape() const override;
                  };
                  
                  class GraphicsPersonItem : public GraphicsNodeItem
                  {
                      public:
                  
                          explicit GraphicsPersonItem(const FPerson &person, GraphicsNodeItem *parent = nullptr);
                          void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
                  };
                  

                  I have noticed that GraphicsNodeItem::shape() is called only when successful hit, whereas GraphicsNodeItem::boundingRect() is being called constantly. Is this correct behavior?

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

                  @tomas-soltys
                  I think boundingRect is called first, if that indicates a hit, the shape is checked.

                  So if you click far outside of a circle, the click is outside of it's boundingRect and shape() is not called.
                  If you click into the center of the circle, boundingRect indicates a hit, and shape confirms it.
                  If you click at the corner of the circle, the click may be inside the boundingRect, but outside of the shape.

                  T 1 Reply Last reply
                  0
                  • A Asperamanca

                    @tomas-soltys
                    I think boundingRect is called first, if that indicates a hit, the shape is checked.

                    So if you click far outside of a circle, the click is outside of it's boundingRect and shape() is not called.
                    If you click into the center of the circle, boundingRect indicates a hit, and shape confirms it.
                    If you click at the corner of the circle, the click may be inside the boundingRect, but outside of the shape.

                    T Offline
                    T Offline
                    tomas.soltys
                    wrote on last edited by tomas.soltys
                    #11

                    @Asperamanca
                    I have found working solution. Since size of my item depends on QFontMetrics used by QPainter I am setting final dimensions inside GraphicsPersonItem::paint() method. Once I added this->prepareGeometryChange(); at the beginning my picking started to work correctly.

                    1 Reply Last reply
                    0
                    • A Offline
                      A Offline
                      Asperamanca
                      wrote on last edited by
                      #12

                      If possible, calculate the geometry before paint, e,g. when setting text and font. This will save you an extra paint cycle.

                      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
                      • Unsolved