You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

当源模型为代理模型时,如何在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;
}

关键说明

  1. 获取底层主模型:通过递归遍历代理链,确保总能访问到原始的DataBaseModel,避免依赖中间代理的结构。
  2. 索引映射:使用mapToSource和mapFromSource在代理模型与主模型之间转换索引,解决列/行索引偏移问题。
  3. 修正lessThan的bug:原代码错误地将列索引作为data函数的第二个参数(实际应为角色),现在改为从主模型获取目标列数据。
  4. 重复检查逻辑:基于主模型的原始数据判断重复,同时验证重复行在当前源代理中是否可见且排在当前行之前,确保过滤逻辑正确。

内容的提问来源于stack exchange,提问作者Андрей Петров

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 01:22:03