使用QSortFilterProxyModel添加树项时触发ASSERT错误的求助
问题解答
关于插入时的ASSERT断言问题
首先不需要急着重写mapToSource,先排查核心操作逻辑:
- 禁止通过代理模型执行增删操作:
QSortFilterProxyModel是源模型的包装层,增删改操作必须直接针对ExampleTreeViewModel执行,代理会自动同步源模型的变更。如果你的代码是调用代理的insertRows,就会出现代理的rowCount(parent)是过滤后的数据量(可能远小于源模型的真实数量),导致插入位置超出代理的有效范围,触发first <= rowCount(parent)断言。 - 若直接操作源模型仍触发断言,大概率是树形结构下代理的索引映射异常,此时需检查:
- 源模型的
parent()方法是否完全符合Qt模型规范(QAbstractItemModelTester验证过的话,这一步基本没问题); - 代理模型的
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
相关产品推荐
相关产品推荐

