调用QSortFilterProxyModel::invalidateFilter后QTreeView的rootIndex重置问题
Qt多标签树视图过滤时rootIndex失效的稳定解决方案
问题根因
该问题本质是两个使用误区导致的:
- 给绑定了
QSortFilterProxyModel的QTreeView设置的rootIndex是源模型的索引,而非代理模型自身的索引,代理刷新后源模型与代理模型的索引映射失效,视图无法定位原有根节点 - 单个代理同时承担「固定展示分支」和「内容过滤」两个职责,过滤规则刷新会全量重置代理的节点映射,导致原有分支索引失效
最优方案:分层代理拆分职责
将「固定展示分支」和「内容过滤」两个逻辑拆分到两层独立的代理实现,从根源避免索引冲突:
- 第一层为固定分支代理,继承
QSortFilterProxyModel,专门过滤出指定源分支的所有内容,初始化后不会主动刷新过滤规则,输出稳定的分支数据集 - 第二层为普通过滤代理,使用默认
QSortFilterProxyModel即可,输入为第一层代理的输出,仅处理用户输入的过滤规则,刷新时不会影响第一层的分支固定逻辑
固定分支代理示例代码
class FixedBranchProxy : public QSortFilterProxyModel { public: explicit FixedBranchProxy(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {} // 传入要固定展示的源模型分支根索引 void setSourceBranchRoot(const QModelIndex &sourceRoot) { m_sourceRoot = sourceRoot; invalidateFilter(); } protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { if (!m_sourceRoot.isValid()) return true; // 仅允许源分支本身及分支下的子孙节点通过 QModelIndex current = sourceModel()->index(source_row, 0, source_parent); while (current.isValid()) { if (current == m_sourceRoot) return true; current = current.parent(); } return false; } private: QModelIndex m_sourceRoot; };
使用流程
- 每个标签页的树视图对应独立的两层代理链:
全局源模型 -> FixedBranchProxy -> 过滤用QSortFilterProxyModel -> QTreeView - 初始化时给
FixedBranchProxy设置要展示的源模型分支索引,树视图无需额外调用setRootIndex,默认展示最上层过滤代理的根节点即可 - 用户输入过滤内容时,仅修改第二层过滤代理的规则并调用
invalidateFilter,不会影响第一层的分支固定逻辑,视图永远不会跳转到全量模型数据
轻量适配方案:修正索引转换逻辑
如果不想调整现有代理结构,也可以修正索引设置逻辑,无需手动保存恢复索引:
每次调用invalidateFilter刷新过滤规则后,将你要展示的源模型分支索引通过QSortFilterProxyModel::mapFromSource()转换为代理模型的索引,再调用QTreeView::setRootIndex()传入转换后的索引即可。
内容的提问来源于stack exchange,提问作者ColinKennedy
相关产品推荐
相关产品推荐

