Set up the elements collection's items on the GUI thread

ElementsCollectionModel::loadCollections() runs setUpData() for every
item on worker threads (QtConcurrent::map), and setUpData() calls
setText(), setFlags(), setData() and setToolTip() on items that are
already in the model. Each of these changes the model and emits its
dataChanged() signal from a worker thread, which QAbstractItemModel
does not allow. Loading the shipped collection (8838 elements) emitted
dataChanged() 38958 times, all from worker threads.

The expensive part (reading every element file) stays on the worker
threads. Only the result is moved: ElementCollectionItem::setData()
keeps a value set from a worker thread on the item, data() returns it
to the same worker so setUpData() still reads back what it has set,
and the model applies the kept values on the GUI thread when the map
is finished, before emitting loadingFinished(). setUpData() called on
the GUI thread (macros collection, a single added or changed element)
is unchanged.

With this change the same load emits dataChanged() 38958 times, all on
the GUI thread.

Revives the still-needed part of #516, closed only to clear a review
backlog. Its other two changes are left out: the wait in
loadMacrosCollection() guarded a model shared with a running map,
which no longer happens (the macros always get a model of their own),
and qetinformation.h's static QString constants are a size clean-up,
not a bug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-30 10:35:19 +13:00
parent bae336ef5b
commit 79e87cb3b8
3 changed files with 90 additions and 2 deletions
@@ -312,8 +312,15 @@ void ElementsCollectionModel::loadCollections(bool common_collection,
this, &ElementsCollectionModel::loadingProgressValueChanged);
connect(watcher, &QFutureWatcher<void>::progressRangeChanged,
this, &ElementsCollectionModel::loadingProgressRangeChanged);
connect(watcher, &QFutureWatcher<void>::finished,
this, &ElementsCollectionModel::loadingFinished);
//setUpData() ran on worker threads, which only kept the values on
//the items (ElementCollectionItem::setData()): apply them here, on
//the GUI thread, before anyone is told the loading is finished.
connect(watcher, &QFutureWatcher<void>::finished, this, [this]()
{
for (ElementCollectionItem *eci : std::as_const(m_items_list_to_setUp))
eci->applyDeferredData();
emit loadingFinished();
});
connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater);