QTableView performance: slow repaint of scroll area viewport
-
Hi everyone!
I would like to ask you if the behavior that I see is normal and expected in debug builds.Context:
I'm working on a CAN bus tool which has to display received and decoded frames or messages. I use QTableView + custom model with a vector of messages inside. Table view gets data by simple memory access via index with no additional calculation or formatting.
I noticed that no matter what the model does, any update of the model triggers QEvent::Paint of qt_scrollarea_viewport of the QTableView which usually takes 30-120 ms. even in very simple cases and small number of rows.Simplified example:
Toolchain: MCVS 2019 64 bit
Qt: 5.15.2
OS: Windows 10
Build: debug
Running from Qt Creator ("Run" button)#include "mainwindow.h" #include <QApplication> #include <QElapsedTimer> #include <QEvent> #include <QDebug> #include <QAbstractItemModel> #include <QTimer> class Application : public QApplication { QElapsedTimer timer; public: Application(int &argc, char **argv) : QApplication(argc, argv) { } bool notify(QObject *receiver, QEvent *event) override { const auto type = event->type(); const auto name = receiver ? receiver->objectName() : QString{}; timer.start(); const bool ret = QApplication::notify(receiver, event); const auto elapsed = timer.elapsed(); if(elapsed > 25) qWarning() << "Slow event detected:" << type << receiver << name << elapsed; return ret; } }; class TestModel : public QAbstractTableModel { public: enum Roles { DataRole = Qt::UserRole }; enum Columns { Time, MessageInfo, Data, Description, ColumnCount }; explicit TestModel(QObject *parent = nullptr) : QAbstractTableModel(parent) { } void updateRow(int row) { if (row > rowCount()) return; emit dataChanged(index(row, 0), index(row, columnCount() - 1)); } void updateView() { emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } int rowCount(const QModelIndex & = QModelIndex()) const override { return 25; } int columnCount(const QModelIndex & = QModelIndex()) const override { return 4; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { switch (role) { case Qt::DisplayRole: return QStringLiteral("%1:%2").arg(index.row() + 1).arg(index.column() + 1); } return {}; } }; int main(int argc, char *argv[]) { Application a(argc, argv); MainWindow w; TestModel model; int row = 0; QTimer updateTimer; updateTimer.setInterval(100); QObject::connect(&updateTimer, &QTimer::timeout, [&](){ //qDebug() << "Update timer timeout"; model.updateRow(row); // update one row per timeout if (++row >= model.rowCount()) row = 0; }); w.setTableModel(&model); updateTimer.start(); w.show(); return a.exec(); }
Note:
MainWindow
is auto-generated form withQTableView
inside.MainWindow::setTableModel
just callsQTableView::setModel
It seems doesn't matter how frequently we update the model, every 50 ms. or 1 second. Repaint event takes the same amount of time. The time increases with increasing view port size.
Even so in release builds it works well and snappy, I still see accidental, slow (50-100 ms.) repaints in release mainly when I resize, mode or change focus of the main window, probably it's normal, but please let me know if it's ok that it still takes up to 100 ms.
Here is piece of a log from debug build:
... Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 76 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 77 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 79 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 80 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 81 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 81 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 82 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 83 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 81 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 82 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 73 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 74 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 73 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 74 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 75 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 76 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 79 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 80 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 75 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 75 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 75 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 76 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 75 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 75 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 75 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 76 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 76 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 77 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 80 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 81 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 79 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 80 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 92 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 93 Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 86 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 78 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 77 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 78 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 77 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 79 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 84 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 85 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 82 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 83 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 83 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 84 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 76 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 77 Update timer timeout Slow event detected: QEvent::Paint QWidget(0x2f1a784aea0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 76 Slow event detected: QEvent::UpdateRequest MainWindow(0xf414cff9a0, name = "MainWindow") "MainWindow" 77 ...
and screenshot:
-
Hi and welcome to devnet,
If you sample code is a good substitute with what happens with your real application, you trigger a full repaint because you are telling the view that all cells have changed for all roles which is not the right thing to do.
There are some optimization that you can apply such as telling the view that you are using uniform height and that kind of things that helps. -
Hi and welcome to devnet,
If you sample code is a good substitute with what happens with your real application, you trigger a full repaint because you are telling the view that all cells have changed for all roles which is not the right thing to do.
There are some optimization that you can apply such as telling the view that you are using uniform height and that kind of things that helps.@SGaist Hi and thank you for your feedback.
I've updated my
emit dataChanged
:void updateRow(int row) { if (row > rowCount()) return; emit dataChanged(index(row, 0), index(row, columnCount() - 1), {Qt::DisplayRole}); }
Repaint time remains the same unfortunately. In any case, it's very good hint and will improve performance of my app. Thank you.
Regarding the uniform height: from what I saw
QTableView
doesn't havesetUniformRowHeights
. During the test I used the following configuration of the vertical header:
However, I'm not sure if it has the same effect. Or should I addQt::SizeHintRole
to my model and return fixed height? -
As it was advised here, I've set fixed section resize policy for the vertical header:
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); if (auto vheader = ui->tableView->verticalHeader()) { vheader->setSectionResizeMode(QHeaderView::ResizeMode::Fixed); vheader->setDefaultSectionSize(30); } } MainWindow::~MainWindow() { delete ui; } void MainWindow::setTableModel(QAbstractTableModel *model) { ui->tableView->setModel(model); }
However, no changes in debug build. Thus, I can only assume that repaint is an actual bottleneck here, or I'm missing something else.
-
Updating a model with 20Hz is not really useful - Noone will see this. Also update to a recent Qt version.
-
Updating a model with 20Hz is not really useful - Noone will see this. Also update to a recent Qt version.
@Christian-Ehrlicher said in QTableView performance: slow repaint of scroll area viewport:
Noone will see this
Fluent video picture motion for humans starts at around 24/25 Hz... so you can see changing values :) Still might not make too much sense to update the whole view.
-
Updating a model with 20Hz is not really useful - Noone will see this. Also update to a recent Qt version.
@Christian-Ehrlicher fair point regarding 20Hz, but as I mentioned even if we update model every second, it's noticeable that UI "delays" for more than 50-100 ms. when you interact with it.
Regarding recent Qt version, I also have Qt 6.6.1 installed and it seems behave better when window/viewport is static. There are no expensive repaint events even if model emits
dataChanged
.However, it still lags in debug build when I resize or change visibility of a main window:
Slow event detected: QEvent::Paint QWidget(0x235ac867ec0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 109 Slow event detected: QEvent::Paint QWidget(0x235ac867ec0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 109 Slow event detected: QEvent::UpdateRequest MainWindow(0xb3ce2ff620, name = "MainWindow") "MainWindow" 110 Slow event detected: QEvent::Paint QWidget(0x235ac867ec0, name = "qt_scrollarea_viewport") "qt_scrollarea_viewport" 173
Anyways, lagging during window resize in debug build is tolleratale. It's usable at least.
-
@Christian-Ehrlicher said in QTableView performance: slow repaint of scroll area viewport:
Noone will see this
Fluent video picture motion for humans starts at around 24/25 Hz... so you can see changing values :) Still might not make too much sense to update the whole view.
@Pl45m4 said in QTableView performance: slow repaint of scroll area viewport:
Still might not make too much sense to update the whole view
I'm emitting
dataChanged
only for updated rows and (now) only display role, so the update scope is limited)
The idea was to have smooth value changing (frame counts, cycle times...) when frames arrive under 100 ms. period for example. Otherwise, you could see value change in jumps/steps. In any case, it works well in release build, even with 20Hz model update frequencyMy main concern was to know if it's expected behavior in debug configuration or there is just some problem in the way how I manage model and data updates.
-
Please provide a minimal compileable example in a bug report with a recent Qt version. Also the OS is important. I never seen such slow times on machine here. MacOS has some issues but I don't care since I don't use it.
-
Please provide a minimal compileable example in a bug report with a recent Qt version. Also the OS is important. I never seen such slow times on machine here. MacOS has some issues but I don't care since I don't use it.
@Christian-Ehrlicher I can try to submit a bug report if you say that it's not normal.
Setup info:
Qt: 5.15.2 (MCVS 2019 32 bit), 6.6.1 (MCVS 2019 64 bit)
OS: Windows 10
Build: debug
Running from Qt Creator ("Run" button) -
@Christian-Ehrlicher I can try to submit a bug report if you say that it's not normal.
Setup info:
Qt: 5.15.2 (MCVS 2019 32 bit), 6.6.1 (MCVS 2019 64 bit)
OS: Windows 10
Build: debug
Running from Qt Creator ("Run" button)@svadym said in QTableView performance: slow repaint of scroll area viewport:
I can try to submit a bug report if you say that it's not normal.
This would be nice. With a small reproducer (main.cpp only), a recent Qt version (6.9 and up) and some benchmark results from you. Also reduce the update rate to e.g. 10hz. It's fast enough - Noone can read that fast.