启用排序后QTreeWidget遍历顶层项性能骤降求助
QTreeWidget启用排序后过滤性能暴跌的解决方案
问题根源
启用setSortingEnabled(true)后,QTreeWidget内部会维护一个排序后的项索引映射结构。此时调用topLevelItem(i)会触发索引到实际项的查找开销,而调用setHidden()时,视图会因为项状态变化重新校验排序后的列表,100万条数据下这些操作的累积开销会被放大到数分钟级别——这就是性能骤降的核心原因。
可行优化方案
方案1:临时移除项后再执行过滤
通过将所有项从视图中临时移除,绕过QTreeWidget的排序索引维护逻辑,过滤完成后重新添加项并恢复排序状态:
// 保存当前排序状态 const bool wasSortingEnabled = isSortingEnabled(); setSortingEnabled(false); // 批量移除所有顶层项 QList<QTreeWidgetItem*> topItems; while (QTreeWidgetItem* item = invisibleRootItem()->takeChild(0)) { topItems.append(item); } // 执行过滤逻辑 for (QTreeWidgetItem* pItem : topItems) { bool hide = false; for (const auto& filter : mFilters) { if (hide) break; hide = filter.checkFilter(pItem); } pItem->setHidden(hide); } // 重新添加所有项 for (QTreeWidgetItem* item : topItems) { invisibleRootItem()->addChild(item); } // 恢复排序 if (wasSortingEnabled) { setSortingEnabled(true); // 保持之前的排序规则 sortItems(sortColumn(), sortOrder()); }
这种方式下,项不在视图中时调用setHidden()不会触发任何排序相关的内部更新,过滤操作的耗时会回到接近未启用排序时的水平。
方案2:改用QTreeView + QSortFilterProxyModel架构
QTreeWidget是封装好的组件,在大数据量场景下灵活性不足。改用QTreeView+QStandardItemModel+自定义QSortFilterProxyModel的组合,将过滤和排序逻辑交给模型层处理,从根本上优化性能:
- 实现自定义过滤代理模型:
#include <QSortFilterProxyModel> #include <QStandardItemModel> class CustomFilterProxy : public QSortFilterProxyModel { public: explicit CustomFilterProxy(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {} // 注入你的过滤器集合 void setFilters(const QList<YourFilterType>& filters) { m_filters = filters; invalidateFilter(); // 触发过滤更新 } protected: bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override { QStandardItemModel* sourceModel = qobject_cast<QStandardItemModel*>(this->sourceModel()); QStandardItem* item = sourceModel->itemFromIndex(sourceModel->index(sourceRow, 0, sourceParent)); // 应用过滤规则 bool accept = true; for (const auto& filter : m_filters) { if (filter.checkFilter(item)) { // 假设checkFilter返回true表示需要隐藏 accept = false; break; } } return accept; } private: QList<YourFilterType> m_filters; };
- 替换原QTreeWidget的使用方式:
// 初始化模型和视图 QStandardItemModel* sourceModel = new QStandardItemModel(this); // 向sourceModel中添加你的100万条数据... CustomFilterProxy* proxyModel = new CustomFilterProxy(this); proxyModel->setSourceModel(sourceModel); QTreeView* treeView = new QTreeView(this); treeView->setModel(proxyModel); treeView->setSortingEnabled(true); // 当需要更新过滤规则时 proxyModel->setFilters(mFilters);
这种方案的优势在于:过滤和排序由专门的代理模型高效处理,视图只渲染符合条件的项,无需手动遍历设置hidden,大数据量下的性能表现远优于QTreeWidget的手动过滤。
为什么你之前的优化无效
你尝试的遍历原始列表、关闭排序等操作,都没有避开QTreeWidget在启用排序后维护的索引映射——只要项还在视图中,调用setHidden()就会触发视图的内部校验逻辑,累积的开销依然巨大。只有彻底断开项与视图的绑定(方案1),或者改用模型层处理过滤(方案2),才能解决问题。
内容的提问来源于stack exchange,提问作者Fiurilli
相关产品推荐
相关产品推荐

