Progress indicator for slow widget creation
-
This is a general "architecture"-question, not a "how to code it"-question.
Imagine an application like an image (pre)viewer where you want to display a huge number of thumbnails of large image files. The thumbnails shall have some intelligence, so they will be custom widgets (derive from QWidget) and use QPixmap and other cool GUI stuff inside. Reading the files and creating a properly scaled down QPixmap may be a slow operation (e.g. network file access, huge files etc.) and may happen 100ths of times in a row.
I am well aware of QThread based solutions, where you out-source a time consuming task to a new thread and the thread signals back the result. Like in the QThread tutorial :-)
BUT:
QObject is "thread affine" and QWidget and everything derived from it is non-reentrant. My current point of view is:- Thread affinity makes it impossible to create the thumbnail widgets in the worker thread and "return" them as a result - they would belong to the wrong thread. (Actually on linux / GNOME you get a new top level window with all the widgets created in the worker thread stuffed into it and a slightly confused window manager.) QObject::moveToThread? Nope, Qt runtime says "QObject::moveToThread: Widgets cannot be moved to a new thread", a clear statement.
- Creating the widgets in the worker thread but with a GUI-thread object as parent: Nice try, but won"t work, Qt refuses to do that with "QObject::setParent: Cannot set parent, new parent is in a different thread". So basically you end up with GUI objects in the wrong thread again.
The preferred behavior would be that the widgets "pop up" on screen as they get ready. (Like the thumbnails in Windows Explorer - it hurts to say that :-)) Acceptable but not so nice would be an animated progress indicator with an option to cancel the operation and get the thumbnails prepared so far.
Any hint highly welcome! -
Hi,
Shouldn't you rather use a QListView in icon mode and a custom model that will signal batches of thumbnails ready rather that creating hundred of widgets.
-
Good point, thank you for your feedback.
Is it safe to "move" QIcon and QPixmap objects between threads? Neither derives from QObject, so the problems mentioned above do not apply. But I have a vague uneasy feeling about allocating QIcon / QPixmap in one thread and deleting them somewhere else. (Although it seems to work in a small test app and from a pure C++ perspective it should be fine.)Btw, are there any tricky issues if I move QSqlQuery's execution to worker threads? (The second most annoying source for an unresponsive GUI...)
-
QPixmap is not safe, QImage is.
Note that usually, application generate smaller thumbnails and store them either on drive or in a database.
As for QtSql each thread shall have its own connection.
-
@SGaist said in Progress indicator for slow widget creation:
QPixmap is not safe, QImage is.
Important point. Is this insider info or did I just miss it in the doc? So I'll prepare the thumbs as QImage in the worker thread, hand them over to GUI as the get available and maybe convert them there to QPixmap.
Local storage of thumbs is on the todo-list but the first time browsing experience should be as good as possible anyway.
Thank you for your helpful feedback!
-
@stryga42 said in Progress indicator for slow widget creation:
or did I just miss it in the doc?
Yes: https://doc.qt.io/qt-5/qimage.html#details
"When using QPainter on a QImage, the painting can be performed in another thread than the current GUI thread."
And https://doc.qt.io/qt-5/thread-basics.html#gui-thread-and-worker-thread
"The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads."