Goal: To have a scrollable list of custom widgets amounting hunderts of thousands (and possibly more) in a Qt5 C++ application under Windows 7, 10.
Problem: The program stops responding after minimizing the window to the task bar and restoring it again. It doesn't crash though. The CPU usage constants 25%. The GUI doesn't become responsive again even after several minutes of waiting. Furthermore, a large amount of memory is being consumed in general (more than 200M), which I think is too much even for 100k QLabels (aprox 2k per QLabel).
Here are some suggested solutions to a similar problem, which I don't find suitable for my case.
Example: The following example illustrates the problem. For the sake of the demonstration a list of QLabels is used, but it could be any class derived from QWidget.
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QLabel>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "MainWindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
QScrollArea *scrollArea = new QScrollArea(this);
QFrame *frame = new QFrame();
QVBoxLayout *l = new QVBoxLayout(frame);
int N = 121004;
scrollArea->setWidget(frame);
scrollArea->setWidgetResizable(true);
for (int n = 0; n < N; n++) { l->addWidget(new QLabel(QString::number(n), this)); }
resize(640, 480);
setCentralWidget(scrollArea);
}
I have some bad news and some good news:
The bad news: You cannot do this with Qt Widgets directly.
The good news: There's a way to do it, that's independent of the number of items you have in the list (even billions), but you need to give yourself the time to learn how to do this.
So, first thing: QScrollArea is SO not the way to do this. The correct way to do this is using the Model/View/Controller programming paradigm. The data model that has the information to be displayed has to be completely separated from the view, so that Qt could only worry about displaying the items the user is trying to view. Think about it: If you have a billion elements to put in that list, does that mean the user has to see them all at once? Does that mean Qt has to render them all? In your code, that's what you're asking Qt to do. Are you surprised it's slow?
Advice Nr. 1: Read how Qt manages Model/View programming, then choose the correct viewing tool. I suggest QListView for what you described. QTableView will make things easier for you if you can put things in a table.
The control on the list is done through delegates. A delegate is the class responsible for drawing widgets in the view. The default will just do text vs icons.
Advice Nr. 2: Forget about creating Qt widgets for every element. I just finished answering another guy's question on why this won't work, even when using delegates. Take a look at Qt Torrent Example to see how controls are drawn there.
What you can do is draw controls, not widgets. This is because every widget you create has to go to the main event loop in Qt, which will make your program slow (and you experienced that already). If you loop from one to million just to add numbers it'll take a significant amount of time. Do you really want Qt's event loop to loop over all your widgets to process every one of them?
Advice Nr. 3: Start simple! You seem to have a lot to do. Start with model/view, then add a delegate that will paint a custom control, then expand it. Give yourself the time to learn all this.
Good luck!
I have implemented a list widget that can show billions of items with an arbitrary number of widgets per item without any performance issues. Unfortunately, I can not share the code.
It is implemented on top of QAbstractScrollArea
, and does not use Qt's model/view framework. It only deals with keeping track of the range of items that are in view, calling the appropriate draw function on those items, and keeping track of the overall height of all items combined. That's it.
Every item may have one associated widget. This widget may be arbitrarily complex. Item widgets are made visible when the item is in view. In my implementation, items usually lazily create the widgets, which is one of the reasons why this is fast. In case you have a billion items for example, only a very small subset of those will ever be in view, so it would be a waste to spend any effort in constructing widgets for these items.
Since every item is responsible for the way it looks, this gives a lot of flexibility in terms of what is possible to display with such a very generic list widget.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With