为什么QSortFilterProxyModel搭配QStandardItemModel时TreeView出现额外空白行
问题原因
- 非UI线程操作数据模型
Qt所有模型视图相关类都不是线程安全的,你使用QtConcurrent::run在后台线程执行buildFileTree方法,直接修改QStandardItemModel的内部数据,会导致模型内部索引、行计数混乱,代理模型读取源模型数据时会读到大量无效条目,最终显示为空白行,这是出现空白行的核心原因。 - 代理模型构造未正确初始化父类
FileFilterModel的构造函数没有显式调用父类QSortFilterProxyModel的构造函数,会导致父类内部状态未正确初始化,过滤逻辑异常。 - 过滤逻辑重复且混用过时API
filterAcceptsRow实现中,既使用了新版的filterRegularExpression做匹配,最后又重复用已过时的filterRegExp做二次判断,逻辑冗余还可能出现匹配冲突;同时递归判断子项匹配后已经返回true,后面的匹配逻辑完全多余。
修复方案
1. 调整模型修改逻辑到UI线程
如果扫描的文件夹体积不大,可以直接去掉异步逻辑,在UI线程执行模型修改操作;如果文件夹层级深、文件多,可以把后台扫描文件结构和修改模型的逻辑拆分,扫描完成后通过信号槽把结果投递到UI线程再创建节点修改模型,避免跨线程操作模型的问题:
// 修改MainWindow的on_actionManage_Folders_triggered方法 void MainWindow::on_actionManage_Folders_triggered() { ui->progressLabel->setText("Adding folder"); QString folderName = QFileDialog::getExistingDirectory(this, "Select Folder", QDir::homePath()); if(folderName.isEmpty()) return; // 小文件夹直接在UI线程执行即可 filemodel->buildFileTree(folderName); ui->progressLabel->setText("Folder added"); }
2. 修复FileFilterModel的构造函数
FileFilterModel::FileFilterModel(QObject *parent) : QSortFilterProxyModel(parent) // 显式调用父类构造完成初始化 { }
3. 简化过滤逻辑,统一使用新版API
bool FileFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index0 = sourceModel()->index(sourceRow, filterKeyColumn(), sourceParent); if (!index0.isValid()) return false; // 先判断当前节点是否匹配过滤规则 QString currentText = sourceModel()->data(index0).toString(); if (currentText.contains(filterRegularExpression())) return true; // 递归判断子节点是否有匹配,有匹配就保留当前父节点 int childRowCount = sourceModel()->rowCount(index0); for (int i = 0; i < childRowCount; ++i) { if (filterAcceptsRow(i, index0)) return true; } // 自身和子节点都不匹配则过滤当前行 return false; }
额外优化建议
初始化代理模型时显式设置过滤列和过滤角色,避免默认值异常:
// 在MainWindow构造函数中添加如下代码 fileFilterModel->setFilterKeyColumn(0); fileFilterModel->setFilterRole(Qt::DisplayRole);
内容的提问来源于stack exchange,提问作者Janet Wu
相关产品推荐
相关产品推荐

