当源模型为代理模型时,如何在Qt代理模型中获取正确索引?
解决QSortFilterProxyModel嵌套时列索引不匹配问题
当代理模型嵌套使用时,上层代理直接用主模型的列索引访问源代理模型会失效——因为源代理已经过滤了列,索引映射关系发生变化。需要修改LayersProxyModel的逻辑,让它不依赖直接的列索引,而是通过索引映射访问主模型的原始数据,同时兼容源模型是主模型或代理模型的场景。
解决方案代码
首先在LayersProxyModel类中添加一个辅助函数,用于递归获取最底层的主模型:
// 在LayersProxyModel的private区域声明该函数 DataBaseModel* getBaseModel(QAbstractItemModel* model) const { while (auto proxy = qobject_cast<QSortFilterProxyModel*>(model)) { model = proxy->sourceModel(); } return qobject_cast<DataBaseModel*>(model); }
修改filterAcceptsColumn函数
bool LayersProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const { DataBaseModel* baseModel = getBaseModel(sourceModel()); if (!baseModel) { return false; } // 将当前代理的列索引转换为主模型的列索引 QModelIndex proxyColIndex = sourceModel()->index(0, source_column, source_parent); QModelIndex baseColIndex = sourceModel()->mapToSource(proxyColIndex); if (!baseColIndex.isValid()) { return false; } switch (static_cast<DataBaseModel::Columns>(baseColIndex.column())) { case DataBaseModel::Columns::LayerName: return true; default: return false; } }
修改lessThan函数
bool LayersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { DataBaseModel* baseModel = getBaseModel(sourceModel()); if (!baseModel) { return QSortFilterProxyModel::lessThan(left, right); } int layerNameCol = static_cast<int>(DataBaseModel::Columns::LayerName); // 将代理模型的索引映射到主模型 QModelIndex baseLeft = sourceModel()->mapToSource(left); QModelIndex baseRight = sourceModel()->mapToSource(right); QVariant leftData = baseModel->data(baseModel->index(baseLeft.row(), layerNameCol)); QVariant rightData = baseModel->data(baseModel->index(baseRight.row(), layerNameCol)); return leftData.toString() < rightData.toString(); }
修改filterAcceptsRow函数
bool LayersProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { DataBaseModel* baseModel = getBaseModel(sourceModel()); if (!baseModel) { return false; } // 获取当前行对应的主模型索引 QModelIndex sourceIndex = sourceModel()->index(source_row, 0, source_parent); QModelIndex baseIndex = sourceModel()->mapToSource(sourceIndex); if (!baseIndex.isValid()) { return false; } int layerNameCol = static_cast<int>(DataBaseModel::Columns::LayerName); QVariant currentData = baseModel->data(baseModel->index(baseIndex.row(), layerNameCol)); // 遍历主模型所有行,检查重复的LayerName,同时确保重复行在当前代理中可见且排在当前行之前 for (int row = 0; row < baseModel->rowCount(); ++row) { if (row == baseIndex.row()) { continue; } QVariant prevData = baseModel->data(baseModel->index(row, layerNameCol)); if (prevData == currentData) { // 将主模型行映射回当前源代理模型,检查是否可见且行号小于当前行 QModelIndex proxyPrevIndex = sourceModel()->mapFromSource(baseModel->index(row, 0)); if (proxyPrevIndex.isValid() && proxyPrevIndex.row() < source_row) { return false; } } } return true; }
关键说明
- 获取底层主模型:通过递归遍历代理链,确保总能访问到原始的
DataBaseModel,避免依赖中间代理的结构。 - 索引映射:使用
mapToSource和mapFromSource在代理模型与主模型之间转换索引,解决列/行索引偏移问题。 - 修正
lessThan的bug:原代码错误地将列索引作为data函数的第二个参数(实际应为角色),现在改为从主模型获取目标列数据。 - 重复检查逻辑:基于主模型的原始数据判断重复,同时验证重复行在当前源代理中是否可见且排在当前行之前,确保过滤逻辑正确。
内容的提问来源于stack exchange,提问作者Андрей Петров
相关产品推荐
相关产品推荐

