Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QGraphicsScene incorrect "artifacts" on scrolling "bug"?
Forum Updated to NodeBB v4.3 + New Features

QGraphicsScene incorrect "artifacts" on scrolling "bug"?

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 4 Posters 525 Views 2 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.
  • JonBJ Offline
    JonBJ Offline
    JonB
    wrote on last edited by JonB
    #1

    I do a lot of work with QGraphicsScene, and claim to know what I am doing :) I have "incorrect artifact" behaviour on scrolling a QGraphicsView. I do not understand what is happening, and want to know whether this might be a "bug" or whether anyone can explain why I see what I do. (I know all about incorrect boundingRect() and artifacts, this is not the issue here.)

    I work with Qt 5.15.3 under Ubuntu 22.04, Xorg (not Wayland). Would someone be kind enough to try it with Qt5 or Qt6 on whatever platform and see if they have the same problem, please?

    Reduced code to single file to reproduce. You only have to copy & paste these two files.

    // main.cpp
    #include <QApplication>
    
    #include "main.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        MyGraphicsScene scene;
        scene.setSceneRect(0, 0, 1000, 1000);
    
        MyGraphicsView view;
        view.setScene(&scene);
        view.setGeometry(200, 200, 500, 500);
        view.addFixedToViewRect();
        view.show();
    
        return a.exec();
    }
    
    // main.h
    #ifndef MAIN_H
    #define MAIN_H
    
    #include <QDebug>
    #include <QGraphicsRectItem>
    #include <QGraphicsView>
    #include <QTimer>
    
    class MyGraphicsScene : public QGraphicsScene
    {
        Q_OBJECT
    
    private:
        QGraphicsRectItem *fixedToScene;
    
    public:
        MyGraphicsScene(QWidget *parent = nullptr) :
            QGraphicsScene(parent)
        {
            fixedToScene = this->addRect(450, 450, 100, 100, QPen(), Qt::red);
        }
    
    protected:
        virtual void drawBackground(QPainter *painter, const QRectF &rect) override
        {
            qDebug() << "drawBackground()" << rect;
            // just fill the whole scene, doesn't matter about `rect`
            painter->fillRect(sceneRect(), Qt::green);
        }
    };
    
    class MyGraphicsView : public QGraphicsView
    {
        Q_OBJECT
    
    private:
        QGraphicsRectItem *fixedToView;
    
    public:
        MyGraphicsView(QWidget *parent = nullptr) :
            QGraphicsView(parent)
        {
            connect(this, &MyGraphicsView::viewMoved, this, &MyGraphicsView::repositionFixedToView);
        }
    
        void addFixedToViewRect()
        {
            fixedToView = scene()->addRect(0, 0, 100, 100, QPen(), Qt::cyan);
        }
    
    protected:
        virtual void scrollContentsBy(int dx, int dy) override
        {
            QGraphicsView::scrollContentsBy(dx, dy);
            qDebug() << "scrollContentsBy()";
            emit viewMoved();
            // In case there is an issue doing the repositioning *during* scrolling,
            // you can comment out the immediate signal emission above and do it on a delayed timer
            // but it makes "little" difference:
            // I do get "redraw artifacts" less often, but they still happen after a few clicks
    //        QTimer::singleShot(500, this, &MyGraphicsView::viewMoved);
        }
    
    signals:
        void viewMoved();
    
    private slots:
        void repositionFixedToView()
        {
            QPointF point(mapToScene(200, 350));
            qDebug() << "repositionFixedToView()" << point;
            fixedToView->setPos(point);
    
            // The "redraw artifacts" can be avoided by uncommenting the line below
            // but I don't believe I should have to do this...?
    //        scene()->invalidate(scene()->sceneRect(), QGraphicsScene::BackgroundLayer);
        }
    };
    
    #endif // MAIN_H
    
    • There is a QGraphicsScene with a fixed size.
    • Attached is a QGraphicsView with fixed, smaller size. So scrollbars show and can be used to scroll around.
    • I add a QGraphicsRectItem (fixedToView) to the scene. I wish this item to move with the scrolled view, so it stays at the same place on the view the whole time.
    • Just for comparison I add another one (fixedToScene) which just sits in a fixed place on the scene.
    • I override QGraphicsView::scrollContentsBy() to know when the view is scrolled. I emit a signal from it.
    • I attach a slot, repositionFixedToView() to the "scrolled by" signal. It moves fixedToView, by mapping its desired, fixed view coordinates to the (new) scene coordinates and calls setPos() to move that rectangle there. So that rectangle retains its position on the view.

    I find the following behaviour:

    • If I drag the scrollbar to move the view everything is fine. The item is moved without "artifact" and retains its position on the view. This may be because the slot is called multiple times during "drag scroll", which is different from the bad state when I...

    • ...If I click the scroll buttons I get really bad "artifacts" left. This happens immediately, on first clicks. For all 4 directions.
      Screenshot from 2024-06-27 14-54-47.png

    • You can see I have commented out code to control whether the signal, and hence the slot action, happens actually inside the scrollContentsBy() or delayed with a timer so that it happens after the scrolling is done. In my "real" program it seems to make no difference, they both misbehave equally. In this test it appears a little better: only after about the 3rd click does the artifact appears. (Note that I am not clicking quickly: I am allowing the full time to pass so that all previous scrolling has completed.)
      Screenshot from 2024-06-27 14-57-07.png

    I also have a commented out line to call scene()->invalidate(scene()->sceneRect(), QGraphicsScene::BackgroundLayer); in the slot after every scroll. This does fix/clear the artifacts. But I don't think I should have to do this, should I?? When a QGraphicsItem is moved on a scene/view Qt knows that has uncovered a background area and should call drawBackground() to redraw correctly.

    Is this a bug or have I misunderstood something, please?

    Pl45m4P 1 Reply Last reply
    1
    • JonBJ JonB

      I do a lot of work with QGraphicsScene, and claim to know what I am doing :) I have "incorrect artifact" behaviour on scrolling a QGraphicsView. I do not understand what is happening, and want to know whether this might be a "bug" or whether anyone can explain why I see what I do. (I know all about incorrect boundingRect() and artifacts, this is not the issue here.)

      I work with Qt 5.15.3 under Ubuntu 22.04, Xorg (not Wayland). Would someone be kind enough to try it with Qt5 or Qt6 on whatever platform and see if they have the same problem, please?

      Reduced code to single file to reproduce. You only have to copy & paste these two files.

      // main.cpp
      #include <QApplication>
      
      #include "main.h"
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
      
          MyGraphicsScene scene;
          scene.setSceneRect(0, 0, 1000, 1000);
      
          MyGraphicsView view;
          view.setScene(&scene);
          view.setGeometry(200, 200, 500, 500);
          view.addFixedToViewRect();
          view.show();
      
          return a.exec();
      }
      
      // main.h
      #ifndef MAIN_H
      #define MAIN_H
      
      #include <QDebug>
      #include <QGraphicsRectItem>
      #include <QGraphicsView>
      #include <QTimer>
      
      class MyGraphicsScene : public QGraphicsScene
      {
          Q_OBJECT
      
      private:
          QGraphicsRectItem *fixedToScene;
      
      public:
          MyGraphicsScene(QWidget *parent = nullptr) :
              QGraphicsScene(parent)
          {
              fixedToScene = this->addRect(450, 450, 100, 100, QPen(), Qt::red);
          }
      
      protected:
          virtual void drawBackground(QPainter *painter, const QRectF &rect) override
          {
              qDebug() << "drawBackground()" << rect;
              // just fill the whole scene, doesn't matter about `rect`
              painter->fillRect(sceneRect(), Qt::green);
          }
      };
      
      class MyGraphicsView : public QGraphicsView
      {
          Q_OBJECT
      
      private:
          QGraphicsRectItem *fixedToView;
      
      public:
          MyGraphicsView(QWidget *parent = nullptr) :
              QGraphicsView(parent)
          {
              connect(this, &MyGraphicsView::viewMoved, this, &MyGraphicsView::repositionFixedToView);
          }
      
          void addFixedToViewRect()
          {
              fixedToView = scene()->addRect(0, 0, 100, 100, QPen(), Qt::cyan);
          }
      
      protected:
          virtual void scrollContentsBy(int dx, int dy) override
          {
              QGraphicsView::scrollContentsBy(dx, dy);
              qDebug() << "scrollContentsBy()";
              emit viewMoved();
              // In case there is an issue doing the repositioning *during* scrolling,
              // you can comment out the immediate signal emission above and do it on a delayed timer
              // but it makes "little" difference:
              // I do get "redraw artifacts" less often, but they still happen after a few clicks
      //        QTimer::singleShot(500, this, &MyGraphicsView::viewMoved);
          }
      
      signals:
          void viewMoved();
      
      private slots:
          void repositionFixedToView()
          {
              QPointF point(mapToScene(200, 350));
              qDebug() << "repositionFixedToView()" << point;
              fixedToView->setPos(point);
      
              // The "redraw artifacts" can be avoided by uncommenting the line below
              // but I don't believe I should have to do this...?
      //        scene()->invalidate(scene()->sceneRect(), QGraphicsScene::BackgroundLayer);
          }
      };
      
      #endif // MAIN_H
      
      • There is a QGraphicsScene with a fixed size.
      • Attached is a QGraphicsView with fixed, smaller size. So scrollbars show and can be used to scroll around.
      • I add a QGraphicsRectItem (fixedToView) to the scene. I wish this item to move with the scrolled view, so it stays at the same place on the view the whole time.
      • Just for comparison I add another one (fixedToScene) which just sits in a fixed place on the scene.
      • I override QGraphicsView::scrollContentsBy() to know when the view is scrolled. I emit a signal from it.
      • I attach a slot, repositionFixedToView() to the "scrolled by" signal. It moves fixedToView, by mapping its desired, fixed view coordinates to the (new) scene coordinates and calls setPos() to move that rectangle there. So that rectangle retains its position on the view.

      I find the following behaviour:

      • If I drag the scrollbar to move the view everything is fine. The item is moved without "artifact" and retains its position on the view. This may be because the slot is called multiple times during "drag scroll", which is different from the bad state when I...

      • ...If I click the scroll buttons I get really bad "artifacts" left. This happens immediately, on first clicks. For all 4 directions.
        Screenshot from 2024-06-27 14-54-47.png

      • You can see I have commented out code to control whether the signal, and hence the slot action, happens actually inside the scrollContentsBy() or delayed with a timer so that it happens after the scrolling is done. In my "real" program it seems to make no difference, they both misbehave equally. In this test it appears a little better: only after about the 3rd click does the artifact appears. (Note that I am not clicking quickly: I am allowing the full time to pass so that all previous scrolling has completed.)
        Screenshot from 2024-06-27 14-57-07.png

      I also have a commented out line to call scene()->invalidate(scene()->sceneRect(), QGraphicsScene::BackgroundLayer); in the slot after every scroll. This does fix/clear the artifacts. But I don't think I should have to do this, should I?? When a QGraphicsItem is moved on a scene/view Qt knows that has uncovered a background area and should call drawBackground() to redraw correctly.

      Is this a bug or have I misunderstood something, please?

      Pl45m4P Online
      Pl45m4P Online
      Pl45m4
      wrote on last edited by
      #2

      Can't test right now, but does it make a difference when you flip these two lines?

      @JonB said in QGraphicsScene incorrect "artifacts" on scrolling "bug"?:

          QGraphicsView::scrollContentsBy(dx, dy);
          
          emit viewMoved();
      

      If debugging is the process of removing software bugs, then programming must be the process of putting them in.

      ~E. W. Dijkstra

      JonBJ 1 Reply Last reply
      0
      • Pl45m4P Pl45m4

        Can't test right now, but does it make a difference when you flip these two lines?

        @JonB said in QGraphicsScene incorrect "artifacts" on scrolling "bug"?:

            QGraphicsView::scrollContentsBy(dx, dy);
            
            emit viewMoved();
        
        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #3

        @Pl45m4
        Worth a try, thank you, but same behaviour/artifacts.

        1 Reply Last reply
        0
        • O Offline
          O Offline
          ollarch
          wrote on last edited by
          #4

          Tested on:
          Windows 10 x64
          Qt 5.12.2
          Qt 6.6.1
          Visual Studio 2022

          Same result. Afer playing with it it gets artifacts depending on where I click on the scrollbar.
          After minimizing the window and showing it again, the artifacts disapear.

          JonBJ 1 Reply Last reply
          1
          • O ollarch

            Tested on:
            Windows 10 x64
            Qt 5.12.2
            Qt 6.6.1
            Visual Studio 2022

            Same result. Afer playing with it it gets artifacts depending on where I click on the scrollbar.
            After minimizing the window and showing it again, the artifacts disapear.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #5

            @ollarch
            Thank you for testing! And confirming still present in Qt6 and on a different platform.

            Yes, it seems to depend on "how much" you scroll. Certainly happens when using the small steps of the scroll buttons. Yes, it will disappear whenever you force the background to get repainted, e.g. after minimize/restore. [Actually on my platform minimize/maximize does not remove the artifacts, I don't think it redraws, but e.g. maximize does.] That's because the artifacts left are "not really there", they just have not been wiped by a background redraw. In my real app I have the view's rubber banding switched on, if you drag the rubberband over the artifact-ed area they also disappear, because that redraws background.

            The really strange one is if you comment out the emit/signal/slot which is executing within the scrollContentBy() and replace with the QTimer::singleShot() call. One might say it goes wrong because I am doing the item moving whilst handling the scroll by. But with the timer we are saying "if you scroll the window and then later on move a QGraphicsItem you get artifacts".

            Now it would be really helpful if a Qt expert commented on whether they think anything is incorrect in my code, this behaviour is consequential on the fact that I am filling the background....?

            1 Reply Last reply
            1
            • JoeCFDJ Offline
              JoeCFDJ Offline
              JoeCFD
              wrote on last edited by JoeCFD
              #6

              I do see two blue rectangles sometimes with Qt 5.15.3 and Qt 6.7 under Ubuntu 22.04, Xorg (not Wayland), but no overlapped blue rectangles(artifacts in your images). There is only one left after resizing. No single change in your code.

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