Important: Please read the Qt Code of Conduct -

Dynamically update QGraphicsItem's shape

  • Hello,

    What I'm trying to achieve is having a custom qgraphicsitem wich shape's is a filled rectangle but whith other colliding qgraphicsitem's shape substracted.

    I've tried several things but calling collidingItems() inside the shape() function of my custom qgraphicsitem seems to loop infinitely.

    Does anyone already done something like this?

    Thank you

  • What exactly are you trying to achieve? Are you looking for a visual effect only, or something else? And how long is the change in shape supposed to stay in effect?

  • Hello, thanks for repliying.

    I'm trying to develop a CAD software for print circuit board design. This feature will allow having a ground plane.
    That implies that the shape of my "recangle" should be definitive and that the resulted shape is the real shape of the item (the one used to select it)

  • Well, collidingItem uses the shape() method, so an infinite loop is a logical consequence. However, it isn't a good idea to do all the complex calculations everytime someone calls your implementation of the shape() method: Methods like boundingRect() and shape() are very important to your performance, so they should be fast.

    The easiest way to do this is by caching. I use the following pattern for caching a shape

    • 2 class members
    mutable QPainterPath m_CachedShape;
    mutable bool m_bCachedShapeValid; // Initialize to 'false' in constructor
    • 2 Member functions
    void updateCachedShape() const;
    void invalidateCachedShape();
    • invalidateCachedShape() does nothing else but reset m_bCachedShapeValid to 'false' (but is a good place for future breakpoints)
    • updateCachedShape() updates m_CachedShape - but first checks whether any changes are necessary:
    void MyClass::updateCachedShape
       if (m_bCachedShapeValid) 
       // CODE: Update m_CachedShape
       m_bCachedShapeValid = true;

    Checking whether an update is even necessary breaks your infinite loop


    void MyClass::shape() const
       return m_CachedShape;

  • OK, I will try this, thank you!

  • @yonnak
    One tricky thing may be to decide where to call invalidateCachedShape(). Be careful about that. Too often, and you lose all the advantages. Too rare, and you use an outdated shape.
    If all else fails, you may consider invalidating the shape asynchronously (e.g. via QMetaObject::invokeMethod with option Qt::QueuedConnection), but that should not be necessary normally.
    Also, if your shape also influences how your item paints, or how your boundingRect looks, you need to call update() (for paint) and prepareGeometryChange (for boundingRect) within the invalidate method.

  • I got it working by having the path in cache recalculated on the mouseHoverEvent of the item, but it was too much to compute for each event and the refresh was too slow.
    I ended up deactivated the complex shape of the item when moving or adding "classics" items and recalculate the shape only when editing is done.

Log in to reply