Model subclassing - how to signal data change
I have a QAbstractItemModel implementation which implements a read only model. The model data may change while a view is attached and I have troubles gettings the view to update correctly. If I use QAbstractItemModel::beginReset() / QAbstractItemModel::endReset() the view will be correct but I loose the selection (and expand state, it is a QTreeView). If I emit layoutAboutToBeChanged() and layoutChanged() the tree view updates correctly most of the time and the selection remains valid. But sometimes the view will not be updated after the data change.
Is it wrong to use the layoutChanged() in this case (where entries are added and removed)?
Should I rather be calling beginInsertRow()/endInsertRow() + beginRemoveRow()/endRemoveRow()? I am a little unsure about these functions since the docs seems to relate these functions to insertRow() / removeRow() which I don’t implement since it is a read only model.
Jakob Schou Jensen
Better to avoid reset() if you can. It is a bit of a canon for killing a musquito, most of the time. Nice for prototyping though :)
The beginInsertRow and related functions you mentioned can be used in read only models. If you insert new data from your back end, you actually need to use those. If you change data in your model, that is, you update existing rows, you need to use the dataChanged() signal as noted by ZapB above.
Agreed. The reset() signal causes the view(s) to completely forget about any cached data and to re-query the model for all data needed to paint the view. This is why the selection also gets reset.
Using the more “targeted” signals allows the views to only repaint the parts that have actually changed.
Of course you can layer your own optimisations on top of that. For example if your dataset has the last point and first points changed, then you may want to emit two dataChanged() signals containing the two smaller ranges rather than one signal containing the range of indices for the entire data set. The first approach will trigger two small updates, the second approach would trigger the entire view to be repainted.
It is up to you to decide where the balancing point is based upon your application specific knowledge of how the data is updated.
… Should I rather be calling beginInsertRow()/endInsertRow() + beginRemoveRow()/endRemoveRow()? I am a little unsure about these functions since the docs seems to relate these functions to insertRow() / removeRow() which I don’t implement since it is a read only model.
If you insert data in the underlying data model for a Qt Model, you always have to call beginIndertXXX endInsertXXX, it i s a must. The only other option is to call reset (beginRestet, endReset) which results in completley new view layouts (like just setting the model to the view).
These functions are not related to editing a model, more to changing the model data (from wherever) and informing the views about it. It is , e.g. needed by the views, to update their persistent model indexes.
Same applies to beginRemoveXXX endRemoveXXX. They are a must if you delete items.
If you only change values, you have to call emit dataChanged(first, last). If you do this for each single cell or first / last is a performance issue. The view will read all data inside the range.
Ok, thanks. I was kind of hoping I didn’t have to use the beginInsertRow()/endInsertRow() + beginRemoveRow()/endRemoveRow() functions. When the data changes I get a complete new data set from a blackbox source – typically with only a few changes compared to previous data set. So I guess I will need to calculate a diff and apply the diff incrementally in order to be able to call beginInsertRow()/endInsertRow() + beginRemoveRow()/endRemoveRow().
(There are no “dataChanged” happening in my data set. Only inserts and removes. This was not clear in my initial post, sorry).