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:
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With