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-r...ata-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::ScrollBa rAlwaysOff) 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: https://gfycat.com/LivelyAptCassowary. 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[])
{
MyItemModel *model = new MyItemModel;
listview->setModel(model);
listview->setMouseTracking(true);
listview
->setEditTriggers
(QListView::NoEditTriggers);
listview->setWordWrap(true);
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();
}
//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();
}
To copy to clipboard, switch view to plain text mode
//myitemmodel.h
#ifndef MYITEMMODEL_H_
#define MYITEMMODEL_H_
#include <QtWidgets>
{
Q_OBJECT;
bool d_cancel;
public:
inline MyItemModel
(QWidget *parent
= nullptr
);
public slots:
inline void loadData();
signals:
private:
inline void loadDataWorker();
};
inline MyItemModel
::MyItemModel(QWidget *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;
item
->setData
("This is item " + QString::number(i
), Qt
::DisplayRole);
emit itemAvailable(item);
}
}
{
appendRow(item);
}
#endif
//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
To copy to clipboard, switch view to plain text mode
Should compile with qmake -project QT+=widgets && qmake && make.
Thanks!
Bookmarks