Custom QGraphicsItem-derived items keep repainting needlessly



  • In my application, I have a QMdiArea which shows subwindows, which in turn contain QGraphicView-s visualising scenes with various custom items. Some of these are derived from QGraphicsPixmapItem and don't override paint() or boundingRect(), and some are totally custom and do override them. If I add any of these totally custom items to the scene, all items enter an endless paint() cycle on the event loop, making the CPU spike until the custom paint()-overriding item is removed from the scene. My code doesn't force the constant painting, and this happens as the application is idle, even when its main window has lost focus.

    Why does the event loop keep repainting all the items and how can I fix it?

    Qt version is the newest 5.0.2 binary bundle for Windows, and I am compiling the application using VS2012 for x64.



  • Any code you could share about those custom items?
    The loop may be caused by an update() call at the wrong place, like in the paint() method.



  • Sorry, here's the code. I was hoping for generic tips on how to debug the event loop to find the source of the issue, to keep this brief...

    The items reside inside a QGraphicsView-derived GfxInteractiveView class which provides zoom-in/zoom-out functionality with the mouse wheel etc.

    The bottom item is GfxImageItem which is a thin layer on top of QGraphicsPixmapItem.

    On top of that there's a runtime generated pixmap item, also built on top of QGraphicsPixmapItem.

    And last there are the GfxPointItem-s on the very top, which are completely custom:

    @
    /// A draggable item representing an analysis point on the map, drawn on top of the map.
    class GfxPointItem : public QGraphicsObject
    {
    Q_OBJECT

    protected:
    GfxImageItem *imgParent_;
    GfxInteractiveImgView *view_;
    QRectF boundingRect_;
    bool active_, static_;
    QStaticText pointText_, valueText_;

    public:
    GfxPointItem(GfxImageItem *parent, GfxInteractiveImgView *view, const QPointF &pos);

    void setActive(bool arg);
    void setStatic(bool arg);
    void setColor(const QColor &color) { color_ = color; update(); }

    virtual QRectF boundingRect() const { return boundingRect_; }
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    signals:
    void changedPos(int index, QPointF newpos);

    public slots:
    void setPaintScale(qreal value);
    void setPointText(const QString &text);
    void setValueText(const QString &text);

    protected:
    void updateBoundRect();
    void updatePointText();
    QPoint valueTextPos() const;
    QPoint pointTextPos() const;
    };

    GfxPointItem::GfxPointItem(GfxImageItem *parent, GfxInteractiveImgView *view, const QPointF &pos, int index) : QGraphicsObject(parent),
    imgParent_(parent),
    view_(view),
    index_(index), size_(8), fontSize_(8),
    color_(Qt::black),
    paintScale_(view->invscale()),
    drawLabel_(true), active_(false), static_(false), floatPrec_(false)
    {
    QLOGX("Creating new at " << pos.x() << "," << pos.y() << ", index: " << index);
    setPos(pos);
    updatePointText();
    connect(view, SIGNAL(scaleChanged(qreal)), this, SLOT(setPaintScale(qreal)));
    }

    /// An inactive point wil not respond to hover events and will not be movable.
    void GfxPointItem::setActive(bool arg)
    {
    QLOGX("Setting active state: " << arg);
    active_ = arg;
    setAcceptHoverEvents(arg);
    setFlag(QGraphicsItem::ItemIsMovable, arg);
    }

    /// Set or disable static mode on point. In static mode, the point text is not updated when changing position, so it can retain a constant label.
    void GfxPointItem::setStatic(bool arg)
    {
    QLOGX("Setting static mode: " << arg);
    static_ = arg;
    }

    void GfxPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
    QLOGX("Painting point");
    static const int margin = 2;
    setScale(paintScale_);
    QPen pen;
    pen.setWidth(1);
    pen.setColor(color_);
    painter->setPen(pen);

    // paint the centerpoint marker (two crossed lines)
    painter->drawLine(QPointF(-size_, 0), QPointF( size_, 0));
    painter->drawLine(QPointF(0, -size_), QPointF(0, size_));

    // the label box and the two static text lines inside
    pen.setWidth(0);
    painter->setPen(pen);

    QFont font;
    font.setPointSize(fontSize_);
    painter->setFont(font);

    QBrush brush(Qt::SolidPattern);
    brush.setColor(QColor(255, 255, 127)); // sand yellow
    painter->setBrush(brush);

    // point text size, value text size
    QSizeF pts = pointText_.size(),
    vts = valueText_.size();

    // point text position, value text position
    QPoint vtp = valueTextPos(),
    ptp = pointTextPos();

    // point id and position label and value indicator label in a rectangular box
    int shift = (valueText_.text().isEmpty()) ? 0 : vts.height();
    QRectF rect(ptp.x()-margin, ptp.y(), std::max(pts.width(), vts.width())+margin, pts.height() + shift);
    painter->drawRect(rect);
    painter->drawStaticText(ptp, pointText_);
    painter->drawStaticText(vtp, valueText_);
    }

    void GfxPointItem::setPaintScale(qreal value)
    {
    QLOGX("Updating scale: " << value);
    paintScale_ = value;
    update();
    }

    void GfxPointItem::setPointText(const QString &text)
    {
    QLOGX("Setting text: " << text);
    pointText_.setText(text);
    updateBoundRect();
    update();
    }

    void GfxPointItem::setValueText(const QString &text)
    {
    QLOGX("Setting value text: " << text);
    valueText_.setText(text);
    updateBoundRect();
    update();
    }

    void GfxPointItem::updateBoundRect()
    {
    QLOGX("Updating bounding rect");
    boundingRect_.setRect(- size_, pointTextPos().y(),
    (2 * size_) + std::max(pointText_.size().width(), valueText_.size().width()),
    (2 * size_) + pointText_.size().height() + valueText_.size().height());
    }

    void GfxPointItem::updatePointText()
    {
    QLOGX("Updating point text");
    pointText_.setText("P" + QString::number(index_ + 1) + " ("

    • (floatPrec_ ? QString::number(pos().x()) : QString::number(std::floor(pos().x()))) + ","
    • (floatPrec_ ? QString::number(pos().y()) : QString::number(std::floor(pos().y()))) + ")");

    updateBoundRect();
    update();
    }

    void GfxPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
    QLOGX("Mouse move");
    QPointF p = pos();
    QPointF ep = event->pos();

    QGraphicsItem::mouseMoveEvent(event);

    if (!static_) updatePointText();
    emit changedPos(index_, pos());
    }
    @

    When I add a GfxPointItem to the scene, the log shows:

    GfxImageItem::paint(): Painting image
    GfxMapItem::paint(): Painting map
    GfxPointItem::paint(): Painting point
    GfxImageItem::paint(): Painting image
    GfxMapItem::paint(): Painting map
    GfxPointItem::paint(): Painting point
    [...]

    in infinite succession, and the log file grows for as long as the point item is present in the scene. To my knowledge, there's nothing that should cause this.

    Thanks!



  • By placing tracing statements in various overrides of event functions, I've been able to establish that the event that forces the items to repaint comes from the view, not from any of the items. Also, the window that contains the view doesn't seem to be the culprit (its paintEvents were few).

    Still no idea what makes the view believe it needs to constantly repaint all its items after GfxPointItem is added.



  • Turned out the problem was the call to setScale in the item's paint(), which invalidated the area and forced the view to repaint it (with all 3 items) - which caused it to get invalidated again. That in case someone was wondering.


Log in to reply
 

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