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

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

本文关键字:in QAbstractProxyModel sub-class createIndex gt sourceModel      更新时间:2023-10-16

我正在尝试创建一个代理模型,从源模型动态映射项目。

随着QIdentityProxyModel的实现,我发现实际上不可能通过检查4个核心功能来复制它:

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

考虑基于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();
}

指数()

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);
}
父()

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

问题出在mapToSource()

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

QAbstractItemModel::createIndex是保护函数,除非将调用方声明为友方,否则不能使用。如果不直接修改QAbstractItemModel类,这显然是不可能的。唯一的选择是使用常规的QAbstractItemModel::index,但这需要QModelIndex形式的parent作为参数之一。但是这样做:

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

导致无限循环,因为parent()函数依赖于mapToSource() ,反之亦然,现在

此处显示的替代方法依赖于在上述函数中查询的QPersistentModelIndex对象的存储映射。这种方法虽然可行,但有几个缺点:

  • 需要大量的额外内存来存储索引
  • 所有持久索引需要在每次结构更改时由源模型更新(与动态模型的随需应变索引查找相反)
  • 当迭代map的键时,对map的查询可能很慢(这可以通过反向创建两个相同的map来弥补,但代价是更多的内存)

所以我的问题是:

是否有另一种方法来动态处理分层代理模型,而不依赖于createIndex()和不运行到函数调用的无限循环?

实际上是否有必要在存储中创建和维护代理模型的结构-à-vis源结构,或者是否有一种方法可以创建动态代理模型?

谢谢!

也许这是一个迟来的回复,但我刚刚遇到了同样的问题。我通过将sourceModel()投射到我的模型来解决这个问题,同时我将我的ProxyModel声明为朋友。

这是我的mapToSource定义:

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());
}

和友元声明:

class Model : public QAbstractTableModel
{
   Q_OBJECT
   friend class ProxyModel;
   ...
};

我理解你对只使用QAbstractItemModel接口的担忧(相信我,我有相同的),但让我们面对它,ProxyModel只能与我的Model一起使用,而不是其他(至少根据我的实现)。因此,我不认为这样的"友谊"有什么不好。至于UB,这里也不应该有任何问题,因为如果提供了除我以外的其他模型,qobject_cast将返回0

在前面回答的基础上,我想添加一些替代方法

首先,历史上最丑陋的hack本身在大多数编译器上是安全的:

class HackyDummy : public QAbstractItemModel
{
   friend class ProxyModel;
};
QModelIndex ProxyModel::createSourceIndex(int r, int c, void *i) {
    return ((HackyDummy*) sourceModel())->createIndex(r,c,i);
}

当然,这是一个非常非常糟糕的hack。它也不能在一些编译器中编译,因为c++规范不是很清楚(或者至少被一些人误解了)访问父类private和子类友类的protected方法。这使我们有了一个更好的计划。但首先,有一点细节:QIdentityProxyModel(几乎)做到了这一点。QIdentityModel自己的mapToSource/mapFromSource是公共的和可重新实现的,它的createIndex也是。因此,可以使用一个内部/私有的QIdentityProxyModel方法,通过访问QIdentityProxyModel本身内部包含的hack,从行、列和内部指针创建我们的小源索引。

编辑:错误

我刚刚继承了QIdentityProxyModel,并在我的maptosourcememethod中使用了它的mapToSource方法。

QModelIndex PropertyModel::mapToSource(const QModelIndex & proxyIndex) const
{
    if(hasNoModel())
    {
        return QModelIndex();
    }
    QModelIndex remapped = createIndex( /* transform proxyIndex here */ );
    return QIdentityProxyModel::mapToSource(remapped);
}

这意味着如果您更改源模型类型而忘记更新您的强制转换,您不会以可能导致代码崩溃的强制转换告终。