Best Approach for minimal footprint QListView of many thumbnail images
I've got a QListView (using QStandardItemModel) monitoring a large directory tree and showing a large number of thumbnails recursively stored within it. The problem I have is that there are so many thumbnails that it is beginning to impact performance of our application. I would very much like to have the thumbnails only load the actual images when they absolutely must be shown - that is to say, only when the user scrolls to bring the items into the view.
Right now the code is just scanning and create a QStandardItem with icon for each image right at the start. Terribly wasteful.
I'm looking for a general approach on how to this. I want to make a "virtual" list-View, so to speak.
So far, the only thing I can come up with is a custom QStandardItem-derivative which stores a thumbnail path. It defers loading the image until its override of QStandardItem::data() is called with Qt::DecorationRole. The idea is that Qt won't call this until it needs to actually display the icon.
Does this sound like a good approach? Is there a better one I should be using? Perhaps a different view/model/item class? And what about the UNloading of thumbnails when items are scrolled out of view?
I'm not looking for someone to write my code for me, just to be pointed in the right direction with a bit of explanation. Or in the very least perhaps the recommendation of what approaches NOT to try.
As you can probably tell my Qt Model/View experience is thin and I would hate to waste cycles going down the wrong road.
I think you can get this using a custom model
Where you override ( as the keys ones)
bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE;
void fetchMore(const QModelIndex &parent) Q_DECL_OVERRIDE;
Note: The sample is based on QAbstractListModel. You might want QAbstractItemModel.
One thing you can do is use a "rolling window" in your model that loads only the thumbnails from the visible items. You could also delay the loading of the thumbnails until e.g. the view stopped scrolling for a certain amount of time showing a place holder in between.
Hi! Another possibility would be using a custom delegate to render the items. The model would only know the names of the files and the delegates would create the thumbnails automatically once they are shown. Also, once the delegates get automatically destroyed, the thumbnails would get destroyed, too. I think this would result in minimal memory footprint, although the GUI might feel laggy. If the latter should prove to be unacceptable you could use one or more seperate threads for the computation of the thumbnails.
@mrjj Thanks, I'm looking at that example now. Seems to be a bit more involved when QAbstractItemModel is involved vs QAbstractListModel but I should be able to figure something out.
@Wieland That sounds like exactly what I need. I guess I have a lot of reading to do...
Not sure would be the easiest to get doing.
Also I kinda like your logic using QStandardItem::data() and Qt::DecorationRole but im not sure
when it would be called.
If you need to "unload" over time, you should aim for solution that does this easy.
@mrjj I'm not sure what you or @SGaist are talking about by a "rolling window". Obviously that describes the effect I am trying to accomplish: A rolling window... class that loads/unloads thumbnails as needed. But I'm sure if you literally mean some specific Qt class or not? Whatever I use to achieve this must somehow be tied into the existing QListView signals, yes? Like checking scrolling notifications or the like?
Sorry for being vague.
There is not class for it as such. It would be subclassing the view to be able to catch the events
related to scrolling. For each such event, look if the items actually visible and handle when new ones comes
into view and when some leaves the view ( for unloading)
I think I would start with the Delegate and see if laggy as it would give u "unloading" automatically.
@Wieland The custom delegate proved to be the best approach for me, thanks.
- I just created a class derived from QStyledItemDelegate and installed it to the QStandardItemModel I was already using.
- I overrode it initStyleOption() function. which called the base class version there (which found no icon)
- Then in my derived version, detected this lack of icon and issued an asynchronous request to load it.
- In the meantime, I filled out the QStyleOptionViewItem with a default empty icon and returned this to the caller. So for a brief moment, the user sees an blank square
- When the asynchronous request to load the icon is done, it signals my widget which updates the items icon.
*To make things simpler, I made sure that when I create all of my QStandardModelItems, I give them a custom piece of data (a custom role) that holds the path of the thumbnail to be loaded.
*I have a timer running that periodically checks and unloads non-visible icons. This timer gets reset every time the user scrolls or loads a new icon.
Works quite well. Thanks
Thank you for reporting back. Its very helpful for others forum users. :)