Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    Custom QGraphicsItem-derived items keep repainting needlessly

    General and Desktop
    2
    5
    2181
    Loading More Posts
    • 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.
    • N
      neuviemep last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • G
        Guigui last edited by

        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.

        1 Reply Last reply Reply Quote 0
        • N
          neuviemep last edited by

          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!

          1 Reply Last reply Reply Quote 0
          • N
            neuviemep last edited by

            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.

            1 Reply Last reply Reply Quote 0
            • N
              neuviemep last edited by

              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.

              1 Reply Last reply Reply Quote 0
              • First post
                Last post