[Solved] Updating the underlying data without using the QAbstractItemModel::setData() method.

  • Hello everyone!

    I have a simple question regarding QAbstractItemModel. In my application I have some data of some form and an implementation of QAbstractItemModel retrieving this data and feeding them to a QTreeView. I have also implemented the QAbstractItemModel::setData() function and so I can alter the underlying data. At the end of this function I emit dataChanged() and the the QTreeView is updated immediately. So far so good.

    But, what if I change the underlying data without using the model, but from an other part of my code? How do I notify the QTreeView/QAbstractItemModel that the data was changed so the tree will get updated? The only thing I can think of right now is to create and call the following function after changing the data:

    void CustomQAbstractItemModel::notifyStuffUpdated( Node* nodeAltered )
    QModelIndex topLeftIndex = getIndexForNode(nodeAltered);
    QModelIndex bottomRightIndex = getIndexForNode(nodeAltered);
    emit dataChanged(topLeftIndex,bottomRightIndex);

    Which is almost the same with what I do at the end of the overridden QAbstractItemModel::setData() function. Is this the right/only way of notifying the model/views? I was looking for an "updateModel()" or a "refreshConnectedViews()" function in the model that would do such thing. Thanks for your time guys.


  • Yes, this is basically what you need to do. There is a reset() method, but it will reset everything connected to the model, including things like user selections. I don't recommend you use it in production code, or only in cases where your data has really, really radically changed. Other than that, reserve it for quick mockups.

    Your data store may have knowledge and/or private access to the model. I would recommend you give your QAIM a private interface to notify data changes, and make the data store class a friend of the QAIM. That way, the public interface of the QAIM remains focussed at what it is there for: a data provider for QAbstractItemViews.

  • Thanks for your reply Andre!
    I would like to ask something that is not 100% related with the subject of this thread but I will give it a try before creating a new thread. Multiple "leaves" of my QAbstractItemModel items might contain reference to the same data (attention: don't be confused. The items in the model are unique but there is a private data section in each one of em with a pointer to my "actual data". This pointer may have the same value in multiple leaves). The checked state of the items is saved in the "actual data". When I change the check status of an item (through the QAbstractItemModel::setData() method) I do emit the data changed like I described above. The problem is that I emit dataChanged() with the index of the item I unchecked and not with all the indices that reference to the same data. As a result all the other references in the QTreeView do not change their checked state. (which is expected because they do not know about that change). In order to fix this, I had to emit dataChanged() with null QModelIndices like this:
    emit dataChanged(QModelIndex(),QModelIndex());
    passing null QModelIndex() forces the model/view to refresh everything? To be honest I am not sure what these two parameters represent(what is the "top left" and "bottom right"?). Is it safe to emit dataChanged() with null QModelIndex()?

    Thanks in advance!

  • The best would obviously be that you emit dataChanged() for the actual nodes that have changed. I did not know that you could even emit it with invalid indexes and have the data being updated, so I do not know if that is save. The documentation states that the behaviour is undefined if the items do not have the same parent. I don't know if an invalid parent (assuming the parent of an invalid QModelIndex is again an invalid QModelIndex) is equal to another invalid parent in this definition.

    Normally, the signal allows you to notify views that a block of data has changed. For instance, if the data for a complete row has changed, you would only need to emit the signal once, where the topleft argument represents the cell of the changed row at column 0, and the bottomright argument the cell of that row at column N. In the same way, you can update multiple rows in one go. Updating multiple rows is obviously more efficient than updating one cell at a time. Note that you need to emit the event multiple times if the changed cells do not have the same parent.

    My advice: do not rely on undocumented behaviour. It is not documented that passing two invalid QModelIndexes results in what you need, so do not rely on that being (or staying) the case. Instead, get your DataStore -> QAIM model indexes mappings in order and emit the signal for all the right indices.

  • You are right it is not documented indeed. Now I emit the signal for all the nodes I update.
    Thanks dude!


Log in to reply