Qt 5.2 Model-View-Pattern: How to inform model object about changes in underlying data structure

前端 未结 2 1606
执念已碎
执念已碎 2020-12-03 09:14

I have a class used for permanent storage of some item that are organized in a table-like manner. This class is totally unrelated to Qt and comes from a different library. L

2条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-03 09:49

    From the documentation of method bool QAbstractItemModel::insertRows(int row, int count, const QModelIndex & parent = QModelIndex()):

    If you implement your own model, you can reimplement this function if you want to support insertions. Alternatively, you can provide your own API for altering the data. In either case, you will need to call beginInsertRows() and endInsertRows() to notify other components that the model has changed.

    The same goes for removeRows() and moveRows() (they have their own begin*() and end*() methods). For modifying data of existing item there's a dataChanged() signal.

    Here's how it goes (answer for question 1):

    Implement your own methods for inserting/deleting/modifying data, where each of those methods must look like this:

    beginInsertRows(parentIndex, beginRow, endRow);
    // code that modifies underlying data
    endInsertRows();
    

    beginRow and endRow must be provided to inform which where the rows will be inserted and how many of them (endRow-beginRow).

    For beginDeleteRows() and beginMoveRows() it's the same.

    When you have a method which simply modified data in existing item, then this method must emit signal at the end: dataChanged().

    If you do a lot of changes in the data, it sometimes is simpler to just call beginResetModel() and endResetModel() in the method performing this huge modification. It will cause all views to refresh all data in it.

    Answer for question 2:

    This is up to the View class implementation if it will "double-update". When you enter data in the View, data is sent to the model through one of the edition methods in model (insertRows(), setData(), etc). Default implementation of those methods always use begin*() and end*() methods and so the proper notification signals are emitted by the model. All Views listen to those signals, including the one you used for entering the data, therefore the "double-update" will be performed.

    The only way to define this behaviour is to inherit the View and reimplement its protected slots (like dataChanged() and similar) to avoid updating if the value was detected to be provided by this view.

    I'm not sure if Qt views do that already or not. Answer to this requires someone more educated in Qt internals, or looking into Qt source code (which I don't have at the moment). If somebody knows this, please comment and I will update the answer.

    I think it's not that bad to reload the data from model - it guarantees that what you see is indeed the value from the model. You avoid possible problems with the Editor and the View bugs.

    Answer for question 3:

    When you reload whole model, then there is no simple way to keep track of selection. In that case you need to ask view->selectionModel() about current selection and try to restore it after reload.

    However if you do partial refreshing (using methods I described in answer 1), then the View will track the selection for you. Nothing to worry about.

    Final comments:

    If you want to edit data from outside of model class, you can do it. Just expose begin*() and end*() methods as public API, so other code that edits data can notify model and views about changes.

    While it can be done, it's not a good practice. It may lead to bugs, because it's easy to forget about calling notification everywhere you modify the data. If you have to call model API to notify about changes, why not already move all editing code insise the model and expose editing API?

提交回复
热议问题