QAbstractProxyModel子类中的sourceModel() - > createIndex()

时间:2014-05-27 18:10:43

标签: c++ qt model

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

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形式的父作为参数之一。但是这样做:

return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), proxyIndex.parent());
由于parent()函数依赖于mapToSource() 而反之亦然

会导致无限循环。

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

  • 它需要大量额外的内存来存储索引
  • 每次结构更改时,源模型都需要更新所有持久性索引(与使用动态模型的按需索引查找相反)
  • 当迭代地图的kyes时,对地图的查询可能会很慢(这可以通过以相反的方式制作两个相同的地图来补救,但代价是更多的内存)

因此我的问题是:

还有另一种方法可以动态处理分层代理模型而不依赖于createIndex()而不会遇到无限循环的函数调用吗?

实际上是否有必要在存储中相对于源结构创建和维护代理模型的结构,还是有办法创建动态代理模型?

谢谢!

3 个答案:

答案 0 :(得分:2)

也许这是一个迟到的回复,但我刚遇到同样的问题。我通过将 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

答案 1 :(得分:1)

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

首先,时间史上最丑陋的黑客在大多数编译中都是安全的:

class HackyDummy : public QAbstractItemModel
{
   friend class ProxyModel;
};

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

现在,当然,这是一个可怕的,可怕的黑客攻击。它也不会在某些编译器中编译,因为C ++规范在从子类朋友访问父类私有和受保护方法时不是很清楚(或者至少被某些人误解)。这引导我们制定更好的计划。但首先,一点点细节:QIdentityProxyModel(差不多)。 QIdentityModel自己的mapToSource / mapFromSource是公共的并且可以重新实现,因此它的createIndex也是如此。因此,可以通过访问QIdentityProxyModel本身内部包含的hack,使用方法从行,列和内部指针创建内部/私有QIdentityProxyModel。

编辑:拼写错误

答案 2 :(得分:1)

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

QModelIndex PropertyModel::mapToSource(const QModelIndex & proxyIndex) const
{
    if(hasNoModel())
    {
        return QModelIndex();
    }

    QModelIndex remapped = createIndex( /* transform proxyIndex here */ );

    return QIdentityProxyModel::mapToSource(remapped);
}

这意味着如果您更改了源模型类型并忘记更新演员表,那么您最终不会遇到可能导致代码崩溃的演员表。