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. Custom widget is flickering and clipping while moving
Qt 6.11 is out! See what's new in the release blog

Custom widget is flickering and clipping while moving

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 3 Posters 3.0k Views 3 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.
  • x86meX Offline
    x86meX Offline
    x86me
    wrote on last edited by
    #1

    I already posted this question on stackoverflow, but after thinking about it, I am probably more likely to get a useful answer from here.

    I'll give you a minimal reproduceable example that was a part of the more complex widget. Here we just have a custom widget(called MovableItem) with a simple paintEvent. Widget is created, placed onto the central widget and moved to mouse position on MainWindow::mousePressEvent-s.

    When moving, it seems like the widget is getting clipped at the side it's moving towards.

    mainwindow.h

    #include <QMainWindow>
    #include <QMouseEvent>
    #include <QPropertyAnimation>
    #include "movableitem.h"
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
        void mousePressEvent(QMouseEvent *event) override;
    
        QWidget* mItem;
    };
    

    mainwindow.cpp

    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
    {
        resize(640, 480);
        QWidget* central_widget = new QWidget(this);
        mItem = new MovableItem(central_widget);
        mItem->move(20, 20);
        setCentralWidget(central_widget);
    }
    
    void MainWindow::mousePressEvent(QMouseEvent *event) {
        QPoint pos = event->pos();
        QPropertyAnimation* anim = new QPropertyAnimation(mItem, "geometry");
        anim->setDuration(750);
        anim->setStartValue(QRect(mItem->x(), mItem->y(), mItem->width(), mItem->height()));
        anim->setEndValue(QRect(pos.x(), pos.y(), mItem->width(), mItem->height()));
        anim->start();
    }
    
    
    MainWindow::~MainWindow() {}
    

    movableitem.h

    #include <QWidget>
    #include <QPainter>
    #include <QPainterPath>
    
    
    class MovableItem : public QWidget
    {
        Q_OBJECT
    public:
        MovableItem(QWidget *parent = nullptr);
        QSize sizeHint() const override;
        void paintEvent(QPaintEvent *event) override;
    };
    

    movableitem.cpp

    #include "movableitem.h"
    
    MovableItem::MovableItem(QWidget *parent) : QWidget(parent)
    {
        setParent(parent);
    }
    
    
    QSize MovableItem::sizeHint() const {
        return QSize(150, 40);
    }
    
    void MovableItem::paintEvent(QPaintEvent *event) {
        QRect r = rect();
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
    
        QPainterPath path;
        path.addRoundedRect(r, 5, 5);
    
        QBrush brush(QColor(217, 217, 217));
        painter.fillPath(path, brush);
    
        painter.drawPath(path);
    }
    

    Example
    stackoverflow_gif.gif

    As you can see, movement is not fluid, but choppy. I have no idea what is happening. Am I doing something completely wrong, do I need to implement some additional functions, is double buffering needed, is this because of Qt's automatic clipping ? Should I look into QGraphicsView to solve this problem ?

    CP71C 1 Reply Last reply
    0
    • x86meX x86me

      I already posted this question on stackoverflow, but after thinking about it, I am probably more likely to get a useful answer from here.

      I'll give you a minimal reproduceable example that was a part of the more complex widget. Here we just have a custom widget(called MovableItem) with a simple paintEvent. Widget is created, placed onto the central widget and moved to mouse position on MainWindow::mousePressEvent-s.

      When moving, it seems like the widget is getting clipped at the side it's moving towards.

      mainwindow.h

      #include <QMainWindow>
      #include <QMouseEvent>
      #include <QPropertyAnimation>
      #include "movableitem.h"
      
      class MainWindow : public QMainWindow
      {
          Q_OBJECT
      
      public:
          MainWindow(QWidget *parent = nullptr);
          ~MainWindow();
          void mousePressEvent(QMouseEvent *event) override;
      
          QWidget* mItem;
      };
      

      mainwindow.cpp

      #include "mainwindow.h"
      
      MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
      {
          resize(640, 480);
          QWidget* central_widget = new QWidget(this);
          mItem = new MovableItem(central_widget);
          mItem->move(20, 20);
          setCentralWidget(central_widget);
      }
      
      void MainWindow::mousePressEvent(QMouseEvent *event) {
          QPoint pos = event->pos();
          QPropertyAnimation* anim = new QPropertyAnimation(mItem, "geometry");
          anim->setDuration(750);
          anim->setStartValue(QRect(mItem->x(), mItem->y(), mItem->width(), mItem->height()));
          anim->setEndValue(QRect(pos.x(), pos.y(), mItem->width(), mItem->height()));
          anim->start();
      }
      
      
      MainWindow::~MainWindow() {}
      

      movableitem.h

      #include <QWidget>
      #include <QPainter>
      #include <QPainterPath>
      
      
      class MovableItem : public QWidget
      {
          Q_OBJECT
      public:
          MovableItem(QWidget *parent = nullptr);
          QSize sizeHint() const override;
          void paintEvent(QPaintEvent *event) override;
      };
      

      movableitem.cpp

      #include "movableitem.h"
      
      MovableItem::MovableItem(QWidget *parent) : QWidget(parent)
      {
          setParent(parent);
      }
      
      
      QSize MovableItem::sizeHint() const {
          return QSize(150, 40);
      }
      
      void MovableItem::paintEvent(QPaintEvent *event) {
          QRect r = rect();
          QPainter painter(this);
          painter.setRenderHint(QPainter::Antialiasing);
      
          QPainterPath path;
          path.addRoundedRect(r, 5, 5);
      
          QBrush brush(QColor(217, 217, 217));
          painter.fillPath(path, brush);
      
          painter.drawPath(path);
      }
      

      Example
      stackoverflow_gif.gif

      As you can see, movement is not fluid, but choppy. I have no idea what is happening. Am I doing something completely wrong, do I need to implement some additional functions, is double buffering needed, is this because of Qt's automatic clipping ? Should I look into QGraphicsView to solve this problem ?

      CP71C Offline
      CP71C Offline
      CP71
      wrote on last edited by
      #2

      @x86me
      Hi
      Ok,
      I am not sure you can remove flickering into paintEvent of your widget, but maybe I'm wrong.
      Because I think your flickering depends on of the parent's painter, so you could move draw to parent.

      A possible workaround could be:
      Remove paintEvent from your widget
      Add paintEvent into you MainWindow
      So here you can find a workaround for you, but I am not sure is the best way to do this ;)

      void MainWindow::paintEvent(QPaintEvent *event)
      {
      setUpdatesEnabled(false);

      QRect r = mItem->geometry();
      QPainter painter(this);
      
      painter.setRenderHint(QPainter::Antialiasing);
      
      QPainterPath path;
      path.addRoundedRect(r, 5, 5);
      
      QBrush brush(QColor(217, 217, 217));
      painter.fillPath(path, brush);
      
      painter.drawPath(path);
      
      setUpdatesEnabled(true);
      

      }

      x86meX 1 Reply Last reply
      1
      • Chris KawaC Offline
        Chris KawaC Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on last edited by
        #3

        When animating anything painting should be as fast as possible, so simplify if you can, e.g. use painter.drawRoundedRect(r, 5, 5); instead of path rendering.

        You're using a wrong rectangle. Antialiasing is masking it a little, but if you disable it you'll see that you're cutting off the bottom and right part of the rectangle i.e. you should be using rect().adjusted(0,0,-1,-1). If you want antialiasing enabled you need to also leave some space for it , so rect().adjusted(1,1,-1,-1).

        In general antialiasing and animations don't go well together, unless you use some sophisticated temporal solution, which Qt doesn't. It can result in ghosting, shimmering and all kinds of artifacts, like what you see. Consider disabling antialiasing when animation starts and turn it back on when animation finishes.

        On every mouse press you're creating new animation object and never delete it. Consider deleting it automatically when animation finishes i.e. anim->start(QAbstractAnimation::DeleteWhenStopped).

        1 Reply Last reply
        2
        • Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on last edited by
          #4

          One more note - animations like this should not have a constant time, but rather constant speed.

          If you give it a constant time like here you will have fast moving object when you click far away and slow movement when you click close. That's because object needs to cover different distances in the same amount of time.

          You should rather set the animation duration based on the distance the object is to travel i.e. divide your constant by the distance between start and end point. This will result in smoother feel.

          Also consider using different easing curve for the animation. A linear move does not look good for object movement. It should have some easing on the ends so it looks more physical i.e. it accelerates and decelerates.

          1 Reply Last reply
          2
          • CP71C CP71

            @x86me
            Hi
            Ok,
            I am not sure you can remove flickering into paintEvent of your widget, but maybe I'm wrong.
            Because I think your flickering depends on of the parent's painter, so you could move draw to parent.

            A possible workaround could be:
            Remove paintEvent from your widget
            Add paintEvent into you MainWindow
            So here you can find a workaround for you, but I am not sure is the best way to do this ;)

            void MainWindow::paintEvent(QPaintEvent *event)
            {
            setUpdatesEnabled(false);

            QRect r = mItem->geometry();
            QPainter painter(this);
            
            painter.setRenderHint(QPainter::Antialiasing);
            
            QPainterPath path;
            path.addRoundedRect(r, 5, 5);
            
            QBrush brush(QColor(217, 217, 217));
            painter.fillPath(path, brush);
            
            painter.drawPath(path);
            
            setUpdatesEnabled(true);
            

            }

            x86meX Offline
            x86meX Offline
            x86me
            wrote on last edited by x86me
            #5

            Thank you for your answers they helped a lot.
            I ended up leaving the MovableItem's paintEvent untouched because it turns out that just calling setUpdatesEnabled inside the MainWindow::paintEvent solves the problem.

            void MainWindow::paintEvent(QPaintEvent *event) {
                setUpdatesEnabled(false);
                QMainWindow::paintEvent(event);
                setUpdatesEnabled(true);
            }
            

            I know there are few imperfections in the example like handling animations and wrong rect dimensions which I noticed after posting, but even after correcting them, flickering persisted. Turning off antialiasing didn't help either(assuming painter.setRenderHint(QPainter::Antialiasing, false) turns it off).

            This was originally a navigation item that you could drag and drop inside the custom layout. So after testing it, repeatedly calling code like this(not the prettiest I have to admit) also works without any flickering:

            item->widget()->parentWidget()->setUpdatesEnabled(false);
            item->widget()->move(new_x, new_y);
            item->widget()->parentWidget()->setUpdatesEnabled(true);
            
            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