如何更新QAbstractListModel时避免QSortFilterProxyModel自动执行过滤?
解决方案:让QSortFilterProxyModel仅手动触发过滤
要实现源模型更新时仅刷新视图显示,不自动执行过滤,仅在点击按钮时才应用过滤规则,核心是阻止QSortFilterProxyModel自动响应源模型的变更信号触发过滤,改为手动控制过滤时机。下面是具体实现步骤:
1. 自定义手动触发的代理模型
继承QSortFilterProxyModel,重写setSourceModel方法断开默认的自动过滤连接,并添加手动触发过滤的槽函数:
头文件(ManualSortFilterProxyModel.h)
#include <QSortFilterProxyModel> class ManualSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ManualSortFilterProxyModel(QObject *parent = nullptr); // 重写过滤规则,按你的需求实现(比如筛选selected == true) bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; public slots: // 手动触发过滤的槽函数 void triggerFilter(); protected: // 重写setSourceModel,断开自动触发过滤的信号连接 void setSourceModel(QAbstractItemModel *sourceModel) override; };
实现文件(ManualSortFilterProxyModel.cpp)
#include "ManualSortFilterProxyModel.h" #include <QAbstractItemModel> ManualSortFilterProxyModel::ManualSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) {} bool ManualSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { // 按你的需求实现过滤逻辑:仅保留selected为true的行 // 这里假设selected是模型的第1列,根据你的实际模型结构调整索引 QModelIndex index = sourceModel()->index(source_row, 1, source_parent); return sourceModel()->data(index).toBool(); } void ManualSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { // 断开旧源模型的自动过滤信号连接(如果存在) if (QAbstractItemModel *oldSource = sourceModel()) { disconnect(oldSource, &QAbstractItemModel::dataChanged, this, &QSortFilterProxyModel::_q_sourceModelChanged); disconnect(oldSource, &QAbstractItemModel::rowsInserted, this, &QSortFilterProxyModel::_q_sourceModelChanged); disconnect(oldSource, &QAbstractItemModel::rowsRemoved, this, &QSortFilterProxyModel::_q_sourceModelChanged); disconnect(oldSource, &QAbstractItemModel::layoutChanged, this, &QSortFilterProxyModel::_q_sourceModelChanged); } // 调用父类方法设置新源模型 QSortFilterProxyModel::setSourceModel(sourceModel); // 断开新源模型的自动过滤信号连接 if (sourceModel) { disconnect(sourceModel, &QAbstractItemModel::dataChanged, this, &QSortFilterProxyModel::_q_sourceModelChanged); disconnect(sourceModel, &QAbstractItemModel::rowsInserted, this, &QSortFilterProxyModel::_q_sourceModelChanged); disconnect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &QSortFilterProxyModel::_q_sourceModelChanged); disconnect(sourceModel, &QAbstractItemModel::layoutChanged, this, &QSortFilterProxyModel::_q_sourceModelChanged); } } void ManualSortFilterProxyModel::triggerFilter() { // 手动触发过滤:重新计算所有行的过滤状态 invalidateFilter(); }
2. 在主逻辑中使用自定义代理
- 创建自定义代理实例,设置源模型到你的
QAbstractListModel子类。 - 将按钮的点击信号连接到代理的
triggerFilter槽。 - 视图(比如
QListView)设置自定义代理为其模型。
示例代码:
#include "YourCustomListModel.h" #include "ManualSortFilterProxyModel.h" #include <QListView> #include <QPushButton> #include <QVBoxLayout> #include <QWidget> int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建源模型并添加测试数据 YourCustomListModel sourceModel; // 这里假设你已经实现了添加数据的逻辑... // 创建自定义代理并关联源模型 ManualSortFilterProxyModel proxyModel; proxyModel.setSourceModel(&sourceModel); // 创建视图和按钮 QListView listView; listView.setModel(&proxyModel); QPushButton filterBtn("应用筛选"); // 连接按钮点击事件到手动过滤槽 QObject::connect(&filterBtn, &QPushButton::clicked, &proxyModel, &ManualSortFilterProxyModel::triggerFilter); // 布局UI QVBoxLayout layout; layout.addWidget(&listView); layout.addWidget(&filterBtn); QWidget window; window.setLayout(&layout); window.show(); return app.exec(); }
3. 关键说明
- 断开自动连接:通过在
setSourceModel中断开源模型的dataChanged、rowsInserted等信号与代理内部_q_sourceModelChanged槽的连接,彻底阻止代理自动触发过滤。 - 手动触发过滤:点击按钮时调用
invalidateFilter(),这会让代理重新遍历所有源模型行,应用filterAcceptsRow的过滤规则,更新视图显示。 - 视图正常更新:源模型的
dataChanged信号仍会被代理转发到视图,所以当你修改元素的selected状态时,视图会立刻显示这个变更(比如取消选中的样式变化),直到点击按钮才会过滤掉不符合条件的行。
如果需要处理源模型的行插入/删除等场景,可以在合适的时机(比如行插入完成后)手动调用triggerFilter(),或者根据需求调整信号连接逻辑。
内容的提问来源于stack exchange,提问作者Adso4
相关产品推荐
相关产品推荐

