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

使用QSortFilterProxyModel添加树项时触发ASSERT错误的求助

问题解答

关于插入时的ASSERT断言问题

首先不需要急着重写mapToSource,先排查核心操作逻辑:

  • 禁止通过代理模型执行增删操作:QSortFilterProxyModel是源模型的包装层,增删改操作必须直接针对ExampleTreeViewModel执行,代理会自动同步源模型的变更。如果你的代码是调用代理的insertRows,就会出现代理的rowCount(parent)是过滤后的数据量(可能远小于源模型的真实数量),导致插入位置超出代理的有效范围,触发first <= rowCount(parent)断言。
  • 若直接操作源模型仍触发断言,大概率是树形结构下代理的索引映射异常,此时需检查:
    1. 源模型的parent()方法是否完全符合Qt模型规范(QAbstractItemModelTester验证过的话,这一步基本没问题);
    2. 代理模型的filterAcceptsRow是否正确处理父子项过滤关系——如果父项被过滤,代理中该父项的rowCount会被视为0,源模型插入子项后代理同步时可能出现索引映射冲突。这种情况下,要确保filterAcceptsRow逻辑中,若子项符合过滤条件,父项也会被保留(参考下方过滤实现)。

文本过滤与列头排序实现

1. 多属性文本过滤

重写filterAcceptsRow方法,支持按ID、名称等属性匹配,同时处理树形结构的父子联动:

bool ExampleTreeFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    const QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
    if (!sourceIndex.isValid()) return false;

    // 获取自定义角色对应的属性值(假设你定义了IdRole和NameRole)
    const QString id = sourceIndex.data(IdRole).toString();
    const QString name = sourceIndex.data(NameRole).toString();
    const QString filterText = filterRegExp().pattern();

    // 匹配任一目标属性
    bool isMatch = id.contains(filterText, Qt::CaseInsensitive) 
                || name.contains(filterText, Qt::CaseInsensitive);

    // 若当前项不匹配,递归检查子项,子项匹配则父项也显示
    if (!isMatch) {
        const int childCount = sourceModel()->rowCount(sourceIndex);
        for (int i = 0; i < childCount; ++i) {
            if (filterAcceptsRow(i, sourceIndex)) {
                isMatch = true;
                break;
            }
        }
    }

    return isMatch;
}

在UI层将文本框输入绑定到代理的过滤规则:

// 假设textFilter是你的文本框控件
connect(textFilter, &QLineEdit::textChanged, this, [this](const QString &text) {
    this->setFilterRegExp(QRegExp(text, Qt::CaseInsensitive));
});

2. 按列类型排序

重写lessThan方法,根据列的类型(数值/字符串)执行对应比较逻辑:

bool ExampleTreeFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
    const QVariant leftData = left.data();
    const QVariant rightData = right.data();

    // 根据列索引判断数据类型,示例中0列是ID(整数),1列是名称(字符串)
    switch (left.column()) {
    case 0:
        return leftData.toInt() < rightData.toInt();
    case 1:
        return leftData.toString().compare(rightData.toString(), Qt::CaseInsensitive) < 0;
    default:
        // 其他列沿用父类默认比较逻辑
        return QSortFilterProxyModel::lessThan(left, right);
    }
}

最后开启树形视图的排序支持:

// 假设treeView是你的树形视图控件
treeView->setSortingEnabled(true);

此时点击列头即可触发对应列的排序,代理会自动按定义的规则处理。

内容的提问来源于stack exchange,提问作者amplifier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:23:18