简体   繁体   中英

Do I need to notify QTableView that the amount of columns changed in QAbstractTableModel subclass?

I have my own QAbstractTableModel subclass. When new data is inserted in it - I issue beginInsertRows/endInsertRows and then dataChanged with correct indexes. rowCount seems to be changed OK as I see scroller appear and grow, but .... columnCount is not.

The sequence is this:

1) I populate the model BEFORE inserting it into tableview. This way view catches proper amount of columns.

2) I insert the model into the view first, then populate it with data. In this case, even after dataChanged is issued the view will not show anything because it seems like it still thinks that there are no columns. (coulmnCount() returns proper amount - I checked, a few times)

From what I've read in Qt docs it does not look like I need to manually call beginInsertColumns for most models... why then the view fails to pick column count when model receives data?

PSIn the second case calling after populating the model:

view->setModel(0);
view->setModel(model);

sets the column count to proper value but this is ugly...

UPD: I've found that emitting

emit headerDataChanged(Qt::Horizontal, 0 , columnCount());

does fix the column problem, but docs say that I should not........ :)

The model-view architecture has semantics that are not, unforunately, very well described in the documentation. Let me try to lay out the ground rules. Definitions:

  1. Data changes: an existing item has been changed.

  2. Structural changes: rows/columns got added/removed.

  3. Structural change interval: the interval between calls to begin[Insert|Delete][Rows|Columns]() and end[Insert|Delete][Rows|Columns]() , and between calls to beginResetModel() and endResetModel() .

With those definitions, the following must hold. It is up to the model and view implementation to respect those rules. If one party breaks the rules, the other party has undefined behavior. Thus if you, as a model, behave according to the rules, you can expect the view to adhere to them as well. If model breaks the rules, the view is free to break the rules too, and vice versa.

  1. The dataChanged signal is emitted on rows|columns that exist, and its meaning is that data that was previously there got changed. It nonsensical to emit it immediately on rows|columns that have been just added|deleted, since the view knows full well that it must read that data if it so wishes. The signal must not be emitted during a structural change interval, since the view has no way to react to it - see #3 below.

  2. A row|column is considered added|deleted only after the model has called end[Insert|Delete][Rows|Columns]() .

  3. A view does not access a model during the structural change interval.

  4. Structural changes can only be done inside the structural change interval.

  5. Data changes can be only be made outside the structural change interval.

  6. All non-persistent indexes are valid only in a single interval between structural change intervals.

  7. All non-persistent indexes are invalid during the structural change interval. This is the simple outcome of the model being inaccessible during the structural change interval: whatever indexes you have, are useless anyway.

  8. All non-persistent indexes become invalidated once the structural change interval begins.

  9. The [row|column]Count(index) called with valid index remains constant, except during a structural change interval that affected items with index .

The meaning of an invalidated index is that the view cannot use that index anymore, it's undefined behavior if it does so.

My hunch is that your model is not following one or more of the rules given above, thus the view is free to misbehave. It's dangerous. Technically, the view could launch a nuclear strike or format your hard drive. Don't take it lightly :)

I think the reason to why the Qt documentation most often mentions beginInsertRows is that it is much more common. The fact is that you need to call beginInsertColumns() and endInsertColumns() when reimplementing insertColumns(). Also you need to re-implement rowCount() and columnCount(). dataChanged(...) only notifies view on changes in existing rows/columns only.

If you populate your model with all data at once, you could simply call reset() inside your model. But it is not a good idea to call reset() every time a new row is appended, especially for large amounts of data...

I think it is necessary to to call beginInsertColumns() if your model is empty when assigning it to the view. Alternatively you could use a member variable holding the column count that is initialized before assingning the model to the view and return it from columnCount().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM