Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sourceModel()->createIndex() in QAbstractProxyModel sub-class

Tags:

c++

model

qt

I am attempting to create a proxy model that dynamically maps items from source model.

Following the implementation of QIdentityProxyModel with the intention to go from there I have discovered that it is in fact impossible to replicate it by examining the 4 core functions:

mapFromSource()
mapToSource()
index()
parent()

Consider this based on QIdentityProxyModel:

mapFromSource()

QModelIndex ProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
    if(sourceIndex.isValid())
        return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());
    else
        return QModelIndex();
}

mapToSource()

QModelIndex ProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
    if(proxyIndex.isValid())
        return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
    else
        return QModelIndex();
}

index()

QModelIndex ProxyModel::index(int row, int column, const QModelIndex &parent) const
{
    const QModelIndex sourceParent = mapToSource(parent);
    const QModelIndex sourceIndex = sourceModel()->index(row, column, sourceParent);
    return mapFromSource(sourceIndex);
}

parent()

QModelIndex ProxyModel::parent(const QModelIndex &index) const
{
    const QModelIndex sourceIndex = mapToSource(index);
    const QModelIndex sourceParent = sourceIndex.parent();
    return mapFromSource(sourceParent);
}

THE ISSUE

Problem lies in mapToSource() line

return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());

The QAbstractItemModel::createIndex is protected function and cannot be used unless the caller is declared friend. That is obviously not an option without modifying the QAbstractItemModel class directly. The only alternative is to use regular QAbstractItemModel::index but that requires parent in form of QModelIndex as one of the arguments. However doing this:

return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), proxyIndex.parent());

causes an infinite loop due to the fact that parent() function relies on mapToSource() and vice versa now.

The alternative approach shown for example here relies on the stored map of QPersistentModelIndex objects that are queried in the above mentioned functions. This approach while viable has several disadvantages:

  • It requires a lots of extra memory to store the indexes
  • All persistent indexes need to be updated by the source model upon every structure change (as opposed to on demand index lookup with dynamic models)
  • Queries to the map may be slow when iterating over kyes of the map (this can be remedied by making two identical maps in reverse at the expense of yet more memory)

Hence my questions:

Is there another way to handle the hierarchical proxy model dynamically without relying on createIndex() and without running into infinite loop of function calls?

Is it in fact necessary to create and maintain the structure of the proxy model in a storage vis-à-vis the source structure or is there a way to create a dynamic proxy model?

Thanks!

like image 251
Resurrection Avatar asked Oct 13 '25 06:10

Resurrection


1 Answers

Perhaps this is a late reply, but I've just faced the same problem. I solved it by casting sourceModel() to my model, meanwhile I declared my ProxyModel as a friend.

This is my mapToSource definition:

QModelIndex ProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
   Model* pModel = qobject_cast<Model*>(sourceModel());
   if (!pModel || !proxyIndex.isValid()) return QModelIndex();

   ...
   return pModel->createIndex(row, col, proxyIndex.internalPointer());
}

and the friend declaration:

class Model : public QAbstractTableModel
{
   Q_OBJECT

   friend class ProxyModel;
   ...
};

I understand your concern about using only QAbstractItemModel interface (believe me, I had the same), but let's face it the ProxyModel can only be used with my Model and nothing else (at least according to my implementation). Therefore I don't see anything bad in "friendship" like this. As for UB there shouldn't be any issues here as well, because qobject_cast would return 0 if other model other than mine was provided.

like image 111
UVV Avatar answered Oct 14 '25 20:10

UVV