Repeater calling onItemAdded() when updating existing elements in model
I'm working with a Repeater right now and I'm seeing some behavior that I do not understand.
The Repeater is using a QVariantList called position in a .cpp backend as a model. Initially this list has a single QVector2d set to (0,0). After a while the list is updated in cpp and the signal positionChanged() is emitted, changing the values of the QVector2D to some other numbers.
My intent is to have several positions (i.e., QVectord2ds) in the QVariantList, but I currently have only one.
When I update the list and notify QML using positionChanged() the Repeater first emits itemRemoved() and the emits itemAdded(). I.e., it seems the Repeater does not update the existing object, but rather removes the existing one, and then adds a new one.
Is this intended behavior? It seems counter intuituve to me!
When you assign a new
QVariantListas a model,
Repeatersimply rebuilds everything, because it does not know what has changed, or if the new list has any relation to the previous one. The behavior you expect can only happen with a proper model (QML
QAbstractItemModel, ...) that notifies changes like additions and removals.
@jpnurmi Thanks! That explains it!
I still don't understand why though? What does a ListModel have that a basic QVariantList doesn't? (Several things obviously, but regarding this specifically).
My thought process is that the Repeater's model is a QVariantList, which again is a property exposed to QML from a C++ class. In this class the QVariantList is a member variable. This member variable holds a single QVector2D "disguised" as a QVariant. In my setPosition()-function I do something like:
if (posList_ == newPosList) return; posList_ = newPosList;
where posList_ is the member variable. Now, newPosList is a new object with some address, but posList_ is an existing object with an existing address. In my head the first element of posList_ keeps its same address after posList_ = newPosList, because the contents of newPosList is just copied into posList_.
Further, from QML's point of view, I was assuming the Repeater only had some pointer to this specific spot in memory, i.e., the elements in the list. And therefore QML shouldn't care if some element in the list is replace by another one. I was assuming QML sort of "blindly" has its eyes fixed on a specific spot in memory, and when I emit posChanged() it opens its eyes and looks at that spot, not caring if the actual object is the old one or a new one.
I mean, It seems I'm wrong somewhere, since it doesn't seem to work that way, but where am I wrong exactly?
When using a proper model, the value of Repeater::model does not change when you insert and remove items. For example, ListModel is a QAbstractItemModel that emits signals such as rowsInserted(), rowsRemoved() when appropriate. Repeater keeps internally track of these and applies the respective changes.
QVariantList, however, is a value type that cannot be tracked like that. Every time things change, you simply create a new QVariantList and re-assign it to Repeater::model.
In other words, with ListModel you apply changes to the model whilst the model instance stays the same, whereas with QVariantList you create every time a new list i.e. the whole model instance changes so everything is reset.
@jpnurmi Thank you very much once again!
I will need to take a slightly different approach here then, but I guess it's more in tune with the Qt mindset! :)