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. Heavy flickering in QListView when appending items if VerticalScrollBarPolicy == Qt::ScrollBarAlwaysOff

Heavy flickering in QListView when appending items if VerticalScrollBarPolicy == Qt::ScrollBarAlwaysOff

Scheduled Pinned Locked Moved Unsolved General and Desktop
flickeringqlistviewscrollbar
9 Posts 4 Posters 2.6k Views
  • 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.
  • B Offline
    B Offline
    bepaald
    wrote on 20 Jan 2019, 10:13 last edited by bepaald
    #1

    Hi!

    I have a QListView whose model is getting items appended from a worker thread (as discussed here: https://forum.qt.io/topic/98481/ui-responsiveness-and-qlistview-updating-during-load-of-large-data-into-model). After implementing this in my actual program I noticed the listview flickering a lot while items were being added. After some testing I found that removing the call to QListView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff) removed all the flickering. I have made a little video showing this. I do not have the privileges to upload it here directly, but I've posted it here. At the start of the video, everything is fine with the scrollbar present, then without the scrollbar the list is barely usable while items are added.

    It looks to me like the QListView is doing a lot of extra (unnecessary) repaints when the scrollbar is not there. I almost think this might be a bug, but maybe I'm just being stupid or missing something obvious. Does anyone know what's going on?

    Also, is there a workaround? Any classes or functions to reimplement?

    The code I used:

    //main.cc
    #include <QtWidgets>
    
    #include "myitemmodel.h"
    
    int main(int argc, char *argv[])
    {
      QApplication app(argc, argv);
      QWidget widget;
    
      QVBoxLayout *layout = new QVBoxLayout(&widget);
      QListView *listview = new QListView(&widget);
      QPushButton *button = new QPushButton("Populate list", &widget);
    
      MyItemModel *model = new MyItemModel;
    
      listview->setModel(model);
      listview->setMouseTracking(true);
      listview->setEditTriggers(QListView::NoEditTriggers);
      listview->setResizeMode(QListView::Adjust);
      listview->setWordWrap(true);
      listview->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
      listview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    
      listview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // <-- PROBLEM!
    
      QObject::connect(button, &QPushButton::clicked, model, &MyItemModel::loadData);
    
      layout->addWidget(listview);
      layout->addWidget(button);
    
      widget.setMinimumSize(QSize(320, 380));
    
      widget.show();
      return app.exec();
    }
    
    //myitemmodel.h
    #ifndef MYITEMMODEL_H_
    #define MYITEMMODEL_H_
    
    #include <QtWidgets>
    
    class MyItemModel : public QStandardItemModel
    {
      Q_OBJECT;
    
      bool d_cancel;
      QThread *d_worker;
    
     public:
      inline MyItemModel(QWidget *parent = nullptr);
     public slots:
      inline void loadData();
      inline void addAvailableItem(QStandardItem *item);
     signals:
      void itemAvailable(QStandardItem *item);
     private:
      inline void loadDataWorker();
    };
    
    inline MyItemModel::MyItemModel(QWidget *parent)
      :
      QStandardItemModel(parent),
      d_cancel(false),
      d_worker(nullptr)
    {
      connect(this, &MyItemModel::itemAvailable, this, &MyItemModel::addAvailableItem, Qt::QueuedConnection);
    }
    
    inline void MyItemModel::loadData()
    {
      // here, I should cancel any running threads and wait for them to stop
      d_cancel = true;
      if (d_worker)
      {
        d_worker->wait();
        delete d_worker;
        d_worker = nullptr;
      }
      d_cancel = false;
    
      // clear the model
      clear();
    
      // start thread to load data
      d_worker = QThread::create([=]{loadDataWorker();});
      d_worker->start();
    }
    
    inline void MyItemModel::loadDataWorker()
    {
      for (uint i = 0; i < 2000; ++i)
      {
        if (d_cancel)
          break;
    
        QStandardItem *item = new QStandardItem;
        item->setData("This is item " + QString::number(i), Qt::DisplayRole);
        QThread::msleep(2);
    
        emit itemAvailable(item);
      }
    }
    
    inline void MyItemModel::addAvailableItem(QStandardItem *item)
    {
      appendRow(item);
    }
    
    #endif
    

    Should compile with qmake -project QT+=widgets && qmake && make.

    Thanks!

    1 Reply Last reply
    0
    • B Offline
      B Offline
      bepaald
      wrote on 29 Jan 2019, 15:35 last edited by bepaald
      #2

      I tried to hide the scrollbar by setting the style width to 0. I hoped it might achieve the desired behavior through a different code path, but alas, using QListView::verticalScrollBar()->setStyleSheet("QScrollBar {width:0px;}"); produces the exact same flickering as calling QListView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff), while setting to 1px works flawlessly (with a weirdly slim scrollbar).

      Any workaround suggestion is still highly appreciated.

      1 Reply Last reply
      0
      • S Offline
        S Offline
        SGaist
        Lifetime Qt Champion
        wrote on 29 Jan 2019, 22:31 last edited by
        #3

        Hi,

        What version of Qt are you using ?
        On what OS ?
        Did you try to profile your application ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        1
        • B Offline
          B Offline
          bepaald
          wrote on 30 Jan 2019, 08:04 last edited by
          #4

          I am using Qt 5.12 on Arch Linux. I have not tried profiling the application, I would not even know how. Honestly, I am not even completely sure what it means. What would I be able to learn about this problem by profiling?

          I have seen the same behavior on my laptop as well, but it runs the same Qt and OS. I would also be interested to hear if anyone could verify the problem on any other OS, or possibly other (recent) Qt versions.

          Thanks!

          JonBJ 1 Reply Last reply 30 Jan 2019, 08:14
          0
          • B bepaald
            30 Jan 2019, 08:04

            I am using Qt 5.12 on Arch Linux. I have not tried profiling the application, I would not even know how. Honestly, I am not even completely sure what it means. What would I be able to learn about this problem by profiling?

            I have seen the same behavior on my laptop as well, but it runs the same Qt and OS. I would also be interested to hear if anyone could verify the problem on any other OS, or possibly other (recent) Qt versions.

            Thanks!

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on 30 Jan 2019, 08:14 last edited by JonB
            #5

            @bepaald
            Just a word about "profiling". You will (probably) compile/link your code with special flags so that when it runs it gathers statistics about just how much time is spent executing what in your code and where everything is being called from. Then there will be an external tool which presents these statistics to you for analysis. This is often used to understand where a program is being inefficient; if you are a programmer then profiling is well worth getting to know. In your case @SGaist is suggesting it may show just where this redrawing originates.

            What is available to you for profiling depends on your platform + compiler. Certainly if Linux + Qt Creator then the latter already supplies the tools; if you are Windows + Visual Studio then VS has the tools. Further details for Qt profiling at https://wiki.qt.io/Profiling_and_Memory_Checking_Tools & http://doc.qt.io/qtcreator/creator-cache-profiler.html.

            1 Reply Last reply
            3
            • B Offline
              B Offline
              bepaald
              wrote on 5 Feb 2019, 15:39 last edited by bepaald 2 May 2019, 15:40
              #6

              Thanks for your responses. I tried profiling the application by running the application through valgrind's callgrind tool. I then looked at the generated fiel with kcachegrind, but while I do see some differences, nothing looks like a clear problem to me. I noticed QListView::paintEvent is called (through QWidget::event) by QFrame::event in both cases, but in the working code (the one with the scrollbars visible) QAbstractSlider::event is also one of the callers, while it is missing in the bad example. But that's probably expected, and not necessarily a problem.

              If someone with more experience feels like looking at the callgrind files, that would be much appreciated. EDIT: apparently I can not attach files, so never mind this.

              Also, should I compile my own Qt with debugging symbols to have more useful output?

              [0_1549381136500_callgrind.out.2561.GOOD](Uploading 100%)
              [0_1549381144300_callgrind.out.2523.BAD](Uploading 100%)

              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on 10 Feb 2019, 21:18 last edited by
                #7

                Qt 5.12.1 just got released, can you check again if you still have the same problem ?

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                B 1 Reply Last reply 22 Feb 2019, 08:05
                1
                • VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on 11 Feb 2019, 09:06 last edited by
                  #8

                  d_cancel is a race condition, change its type to std::atomic_bool
                  What I suspect is happening is that this intersect check is not working correctly when you disable the scrollbars

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  1 Reply Last reply
                  2
                  • SGaistS SGaist
                    10 Feb 2019, 21:18

                    Qt 5.12.1 just got released, can you check again if you still have the same problem ?

                    B Offline
                    B Offline
                    bepaald
                    wrote on 22 Feb 2019, 08:05 last edited by
                    #9

                    Thanks for the replies and apologies for the late response. I'm having a very busy period. I have in the meantime submitted a bug report: https://bugreports.qt.io/browse/QTBUG-73177

                    @SGaist said in Heavy flickering in QListView when appending items if VerticalScrollBarPolicy == Qt::ScrollBarAlwaysOff:

                    Qt 5.12.1 just got released, can you check again if you still have the same problem ?

                    Yes, I did check 5.12.1 and nothing has changed. I've updated my bug report accordingly.

                    @VRonin said in Heavy flickering in QListView when appending items if VerticalScrollBarPolicy == Qt::ScrollBarAlwaysOff:

                    d_cancel is a race condition, change its type to std::atomic_bool
                    What I suspect is happening is that this intersect check is not working correctly when you disable the scrollbars

                    Thanks, I just wrote that little example code quickly and indeed, d_cancel could lead to problems (though not very likely). rest assured, in the actual project where I'm seeing this problem, the equivalent bool is already a std::atomic_bool.

                    Thanks for the hint as to where the problem is, I will try to investigate.

                    I have tried to install the debug packages of qt on my machine, but the easiest way to do so, they overwrite the normal packages and make my desktop unusable (since I'm running KDE which uses qt). So I need to figure out how to install a second, separate version or try to debug on a different (virtual) machine. Either way, just getting it set up will take more time than i have had lately. To be continued.

                    Thanks again!

                    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