Search code examples
listviewmodelsqml

QML ListView does not update information after endInsertRows()


I'm experimenting with C++, QML and OCaml. Now I'm fixing a strange issue: if I add new row to my model QML does not update itself. Yes, I know about beginInsetRows()...endInsertRows().

My model has 1 column and 1 row. I want to add 1 more row and I call beginInsertRows(QModelIndex(-1,-1), 1, 1), do some computations and call endInsertRows(). I have added some debug output to qt5/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp

void QAbstractItemModel::endInsertRows()
{
Q_D(QAbstractItemModel);
    qDebug() << "Insert QAbstractItemModel::endInsertRows()";
    QAbstractItemModelPrivate::Change change = d->changes.pop();
    d->rowsInserted(change.parent, change.first, change.last);
    qDebug() << "emitting rowsInserted("<<change.parent<<","<<change.first<<","<<change.last<<")";
    emit rowsInserted(change.parent, change.first, change.last, QPrivateSignal());
}

According to my application log signal rowsInserted should be emitted.

Going to call AbstractModel::beginInsertRows 
Calling AbstractModel::rowCount 
rowCount = 1
Going to call AbstractModel::endInsertRows 
Insert QAbstractItemModel::endInsertRows() 
emitting rowsInserted( QModelIndex(-1,-1,0x0,AbstractModel(0x11d3680) )  , 1 , 1 ) 
End inserting rows. cpp_data.length = 2
Sending event to change model
Call update (0,0)...(1,0)
Going to call AbstractModel::dataChanged 
Calling AbstractModel::parent

Than I started thinking about how is implemented ListView and how it handles signals about adding new rows. Output of grep command really suprised me:

......./qt5/qtdeclarative/src/quick/items$ grep rowsInserted qquicklistview* 
......./qt5/qtdeclarative/src/quick/items$

So, Can u please explain me how ListView handles changes in the model and what is correct way to signal that rows has been inserted?

P.S. I don't know if any source files will be useful, but this is whole source tree and this is the most interesting place in the sources

P.P.S. After debuggin I realised what is wrong

void QQuickVisualDataModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end)
{
    Q_D(QQuickVisualDataModel);
    if (parent == d->m_adaptorModel.rootIndex)
        _q_itemsInserted(begin, end - begin + 1);
}

There parent is QModelIndex(-1,-1,0x0,AbstractModel(0xfa9690) ) but rootIndex is QModelIndex(-1,-1,0x0,QObject(0x0) ) and they are not equal and that's why event does not raised. Need more time to realise why this happens....

P.P.P.S. After some experimentations with pure-C++ version of the same application I discovered the reason of this bug. In member _q_rowsInserted above parent is the value which we have submitted in beginInserRows() an rootIndex is object of class QPersistentModelIndex. rootIndex is usually equal QModelIndex(-1,-1,NULL,NULL). Also you should know that operator== of class QModelIndex compares all 4 fields of this class.

I.e. if we call beginInsertRows(QModelIndex(),...) than parent will be equal rootIndex because these indexes both equal to QMoldeIndex(-1,-1,NULL,NULL). But if we will call beginInsertRows(createIndex(-1,-1),....) than our parent will be a QModelIndex(-1,-1,NULL,<non-null pointer to our model>) and it will obviously not be equal rootIndex because NULL != . So, it seems that it is a bad idea to call beginInsertRows(createIndex(....),....).

Can somebody explain me what happens in Qt internals?

(Maybe now is better to close this question and reopen another, more specific and concrete question about Qt internals. I'll think about it.)


Solution

  • Right way to create QModelIndex is to return empty QModelIndex if row==-1 or column==-1. It is important not to put pointer to model inside result QModelIndex. Something can don't work properly if u will do that.