[SOLVED]Custom QGraphicsItemGroup: boundingRect is not drawn properly



  • I have a custom QGraphicsItemGroup, with fixed set of children items. I need to make it resizable(already done), but, while resizing, boundingRect is not updating -- though, all child items are drawn correctly. I've tried adding prepareGeometryChange() and update() in different places of the program, but that hasn't helped.

    Even this implementation returns default boundingRect:

    @QRectF FigureGraphicsItemGroup::boundingRect() const
    {
    return childrenBoundingRect().adjusted(0, 0, 50, 0);
    }@

    And some necessary code(NB no custom painting):

    m_right: right control for resize.

    @QRectF FigureGraphicsItemGroup::boundingRect() const
    {
    return childrenBoundingRect();
    }

    void FigureGraphicsItemGroup::mousePressEvent(QGraphicsSceneMouseEvent* event)
    {
    QPointF pos = event->pos();

    if (m_right->contains(pos) && m_right->isVisible())
    {
        m_isResized = true;
    }
    else
        QGraphicsItemGroup::mousePressEvent(event);
    

    }

    void FigureGraphicsItemGroup::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
    QPointF pos = event->pos();

    if (m_isResized)
    {
        if (m_previous.x() == -1 && m_previous.y() == -1)
            m_previous = pos;
    
        m_delta = pos.x() - m_previous.x();
        m_previous = pos;
    
        QGraphicsItem::prepareGeometryChange();
        recalculatePositions();
    }
    else
    {
        QGraphicsItemGroup::mouseMoveEvent(event);
    }
    

    }

    void FigureGraphicsItemGroup::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
    {
    QPointF pos = event->pos();

    if (m_right->contains(pos) && m_right->isVisible())
    {
        setCursor(Qt::SizeHorCursor);
    }
    else
    {
        setCursor(Qt::ArrowCursor);
        QGraphicsItemGroup::hoverMoveEvent(event);
    }
    

    }

    void FigureGraphicsItemGroup::reset()
    {
    m_isResized = false;
    m_delta = 0;
    m_previous = QPointF(-1 , -1);
    }

    void FigureGraphicsItemGroup::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    {
    Q_UNUSED(event)
    reset();

    QGraphicsItemGroup::mouseReleaseEvent(event);
    

    }

    QVariant FigureGraphicsItemGroup::itemChange(GraphicsItemChange change, const QVariant &value)
    {
    switch (change)
    {
    case ItemSelectedHasChanged:
    m_isSelected = isSelected();
    m_right->setVisible(m_isSelected);
    break;
    }

    return QGraphicsItem::itemChange(change, value);
    

    }

    void FigureGraphicsItemGroup::recalculatePositions()
    {
    int sumX = 1;

    QFontMetrics fm(m_zoneNumberSimpleTextItem->font());
    
    m_routeNumberSimpleTextItem->setPos(sumX, m_middle - fm.height());
    sumX += m_routeNumberSimpleTextItem->boundingRect().width() * SPACING;
    

    //-------------------------------------------------------------------------------------------
    int diameter = fm.height() * SPACING;

    int pixLength = fm.width(m_zoneNumberSimpleTextItem->text());
    
    if (pixLength > diameter)
        diameter = pixLength;
    
    QPen pen(Qt::red);
    pen.setWidth(1);
    m_zoneCircleEllipseItem->setRect(sumX, m_middle - diameter, diameter, diameter);
    m_zoneCircleEllipseItem->setPen(pen);
    
    m_zoneNumberSimpleTextItem->setPos(sumX + fm.width(m_zoneNumberSimpleTextItem->text()) / 2,
                                       m_middle - fm.height());
    sumX += m_zoneCircleEllipseItem->boundingRect().width() * SPACING;
    

    //-------------------------------------------------------------------------------------------
    m_fuelSimpleTextItem->setPos(sumX, m_middle * 1.1);
    //-------------------------------------------------------------------------------------------
    m_leftCornerSvgItem->setPos(sumX, m_middle - m_leftCornerSvgItem->boundingRect().height() / 4);
    sumX += m_leftCornerSvgItem->boundingRect().width() * m_scale;
    //-------------------------------------------------------------------------------------------
    pen.setColor(m_blue);
    pen.setWidth(4);
    m_leftLineItem->setPen(pen);
    int length = m_leftLineItem->line().length() + m_delta / 2;
    length = length < 0 ? 0 : length;
    m_leftLineItem->setLine(sumX, m_middle, sumX + length, m_middle);
    sumX += m_leftLineItem->boundingRect().width();
    //-------------------------------------------------------------------------------------------
    m_exerciseSvgItem->setPos(sumX - 2, m_middle - m_exerciseSvgItem->boundingRect().height() / 4);
    sumX += m_exerciseSvgItem->boundingRect().width() * m_scale;
    //-------------------------------------------------------------------------------------------
    pen.setColor(m_blue);
    pen.setWidth(4);
    m_rightLineItem->setPen(pen);
    length = m_rightLineItem->line().length() + m_delta / 2;
    length = length < 0 ? 0 : length;
    m_rightLineItem->setLine(sumX , m_middle, sumX + length, m_middle);
    sumX += m_rightLineItem->boundingRect().width();
    //-------------------------------------------------------------------------------------------
    m_rightCornerSvgItem->setPos(sumX - 2, m_middle - m_rightCornerSvgItem->boundingRect().height() / 4);
    sumX += m_rightCornerSvgItem->boundingRect().width() * m_scale;
    //-------------------------------------------------------------------------------------------
    m_right->setRect(sumX, m_middle, 4, 4);
    //-------------------------------------------------------------------------------------------
    }@

    UPDATE well, this behaviour seemes like a bug. As temporary solution, you can add an invisible item to your group, and immediately remove it -- this will make boundingRect redraw correctly.



  • One question, cause I can't seem to see that: how do you draw the bounding rect? Do you simply call painter->drawRect(boundingRect()) in your paint event? If so, please remember that half the pen width falls outside the actual bounding rectangle, which is not updated in the view.

    What you need to do is downsize the boundingRect a bit before drawing it, something like this:
    @QRectF r = boundingRect();
    r.setX(r.x() + penWidth / 2);
    r.setY(r.Y() + penWidth / 2);
    r.setWidth(r.width() - penWidth);
    r.setHeight(r.height() - penWidth);
    @

    Please remember about edge cases, negative coordinates and other stuff in the actual implementation. Also, you may need to increase the size of the bounding rect so that the drawn one doesn't overlap your items.



  • [quote author="jaskij" date="1418829925"]One question, cause I can't seem to see that: how do you draw the bounding rect? Do you simply call painter->drawRect(boundingRect()) in your paint event? If so, please remember that half the pen width falls outside the actual bounding rectangle, which is not updated in the view.

    What you need to do is downsize the boundingRect a bit before drawing it, something like this:
    @QRectF r = boundingRect();
    r.setX(r.x() + penWidth / 2);
    r.setY(r.Y() + penWidth / 2);
    r.setWidth(r.width() - penWidth);
    r.setHeight(r.height() - penWidth);
    @

    Please remember about edge cases, negative coordinates and other stuff in the actual implementation. Also, you may need to increase the size of the bounding rect so that the drawn one doesn't overlap your items.[/quote]

    Well, as I noticed in post, "And some necessary code(NB no custom painting):" -- I haven't overloaded paint function. So, I expect standard behaviour, that would draw boundingRect() corresponding to it's current value. I've already tried inserting prepareGeometryChange() in different parts of my code, but that didn't make a sense =(



  • To the best of my knowledge, unless you overload paint the bounding rect is not drawn - you have to paint it yourself. But I have only custom painting, so I may be wrong here.

    If I'm right, then just overload paint with something like
    @painter->drawRect(smallerBoundingRect);
    Base::paint(...)@



  • No, paint doesn't need to be overloaded for this case. I've made a little hack to achieve it: at the end of recalculalatePositions() I wrote

    @removeFromGroup(m_right);
    addToGroup(m_right);
    m_right->setRect(sumX, m_middle, 4, 4);
    @

    That made boundingRect() draw correctly during resize, but it looks...a little bit strange.



  • This is the paint code of QGraphicsItemGroup:

    @ Q_UNUSED(widget);
    if (option->state & QStyle::State_Selected) {
    Q_D(QGraphicsItemGroup);
    painter->setBrush(Qt::NoBrush);
    painter->drawRect(d->itemsBoundingRect);
    }@

    What you see painted is just the selection rectangle. Normally, the ItemGroup paints nothing.



  • [quote author="Asperamanca" date="1418901635"]This is the paint code of QGraphicsItemGroup:

    @ Q_UNUSED(widget);
    if (option->state & QStyle::State_Selected) {
    Q_D(QGraphicsItemGroup);
    painter->setBrush(Qt::NoBrush);
    painter->drawRect(d->itemsBoundingRect);
    }@

    What you see painted is just the selection rectangle. Normally, the ItemGroup paints nothing.[/quote]

    That's good, because it's what I need. OK, I'll explain my problem in steps:

    1. my groupItem displayed on the scene
    2. I select it -- boundingRect and resize handler are drawn
    3. I resize groupItem with dragging handler, but boundingRect is "frozen" in start position

    And, of course, I expect from my item, that selection rectangle = boundingRect.



  • The paint code uses an internal itemsBoundingRect.
    From what I found in the code, this rect is only ever updated when items are added to or removed from the group.

    I suspect this is a bug. In which case you would need to take care of painting yourself.

    (Edit: This is based on my 4.8 sources. Then again, GraphicsView didn't change much since.)



  • OK, let's consider it a bug.

    Thanks for your reply!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.