我正在尝试创建一个动态映射源模型中的项目的代理模型。
在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
个对象的存储映射。可行的这种方法有几个缺点:
因此我的问题是:
还有另一种方法可以动态处理分层代理模型而不依赖于createIndex()而不会遇到无限循环的函数调用吗?
实际上是否有必要在存储中相对于源结构创建和维护代理模型的结构,还是有办法创建动态代理模型?
谢谢!
答案 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);
}
这意味着如果您更改了源模型类型并忘记更新演员表,那么您最终不会遇到可能导致代码崩溃的演员表。