前言
Qt版本:6.8.0
作用
-
动态数据排序
通过setSortRole()指定排序依据的数据角色,调用sort()方法可实现:- 升序/降序排列
- 多列联合排序
- 自定义排序规则(需重写lessThan())
-
灵活数据过滤
使用setFilterRegExp()设置过滤规则时:- 支持正则表达式匹配
- 可指定过滤列(setFilterKeyColumn())
- 允许动态修改过滤条件(实时更新视图)
-
数据视图映射
创建数据展示层时:- 保持源数据完整性
- 允许多个代理实例对应同一源模型
- 支持数据转换显示(如日期格式处理)
// 示例:创建带过滤的代理模型
QSortFilterProxyModel *proxy = new QSortFilterProxyModel;
proxy->setSourceModel(sourceModel); // 连接数据源
proxy->setFilterRegExp(QRegExp("^A", Qt::CaseInsensitive)); // 过滤首字母A项
proxy->setFilterKeyColumn(0); // 对首列进行过滤
// 视图绑定代理模型
QTableView view;
view.setModel(proxy);
该模型在保持原始数据结构的同时,为视图层提供:
- 独立的数据展示逻辑
- 零拷贝的数据访问
- 实时数据同步机制
与其他代理模型的对比
代理模型 | 核心功能 | 适用场景 |
---|---|---|
QIdentityProxyModel | 保持结构不变,扩展或拦截数据/操作 | 数据格式化、权限控制、监听 |
QSortFilterProxyModel | 动态排序和过滤数据 | 表格排序、搜索过滤 |
QTransposeProxyModel | 转置行列(行变列,列变行) | 行列互换显示 |
API
父类APi
virtual void setSourceModel(QAbstractItemModel *sourceModel) 设置\源模型给次代理模型
QAbstractItemModel * sourceModel() const 返回此代理模型设置的源模型
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
- 作用:将源模型的索引 sourceIndex 转换为代理模型的索引。
- 参数:sourceIndex 是源模型中的索引。
- 返回值:代理模型中对应的索引。若 sourceIndex 已被代理模型过滤掉(不显示),返回无效索引。
- 场景:当需要通过源模型的位置获取代理模型中的对应项(例如,在源模型数据更新后定位到代理视图中的对应位置)。
QModelIndex mapToSource(const QModelIndex &proxyIndex) const
-
作用:将代理模型的索引 proxyIndex 转换为源模型的索引。
参数:proxyIndex 是代理模型中的索引。
返回值:源模型中对应的索引。若代理索引无效,返回无效索引。
场景:当需要根据代理视图中的操作(如用户点击项)获取源模型的原始数据。
QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const
-
作用:将源模型的选择区域 sourceSelection 转换为代理模型的选择区域。
参数:sourceSelection 是源模型中的选择区域。
返回值:代理模型中对应的选择区域。过滤掉的行/列会被排除。
场景:当源模型数据变化后,需要同步更新代理视图中选中的项。
QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const
-
作用:将代理模型的选择区域 proxySelection 转换为源模型的选择区域。
参数:proxySelection 是代理模型中的选择区域。
返回值:源模型中对应的选择区域。
场景:当需要根据代理视图中的选中项操作源模型数据(如批量删除)。
带filter的函数用于过滤,带sort的函数用于排序
构造和析构函数
QSortFilterProxyModel(QObject *parent = nullptr)
- 参数: parent - 父对象指针,默认为空
- 返回值: 无
- 作用: 创建一个 QSortFilterProxyModel对象,可以选择性地指定父对象
~QSortFilterProxyModel()
- 参数: 无
- 返回值: 无
- 作用: 析构函数,销毁 QSortFilterProxyModel 对象
属性访问函数
bool autoAcceptChildRows() const
-
参数: 无
返回值: 布尔值,表示是否自动接受子行
作用: 获取是否自动接受子行的设置状态
QBindable bindableAutoAcceptChildRows()
-
参数: 无
返回值: QBindable 对象,用于绑定 autoAcceptChildRows 属性
作用: 获取可绑定的 autoAcceptChildRows 属性
QBindable bindableDynamicSortFilter()
-
参数: 无
返回值: QBindable 对象,用于绑定 dynamicSortFilter 属性
作用: 获取可绑定的 dynamicSortFilter 属性
QBindableQt::CaseSensitivity bindableFilterCaseSensitivity()
-
参数: 无
返回值: QBindableQt::CaseSensitivity 对象,用于绑定 filterCaseSensitivity 属性
作用: 获取可绑定的 filterCaseSensitivity 属性
QBindable bindableFilterKeyColumn()
-
参数: 无
返回值: QBindable 对象,用于绑定 filterKeyColumn 属性
作用: 获取可绑定的 filterKeyColumn 属性
QBindable bindableFilterRegularExpression()
-
参数: 无
返回值: QBindable 对象,用于绑定 filterRegularExpression 属性
作用: 获取可绑定的 filterRegularExpression 属性
QBindable bindableFilterRole()
-
参数: 无
返回值: QBindable 对象,用于绑定 filterRole 属性
作用: 获取可绑定的 filterRole 属性
QBindable bindableIsSortLocaleAware()
-
参数: 无
返回值: QBindable 对象,用于绑定 isSortLocaleAware 属性
作用: 获取可绑定的 isSortLocaleAware 属性
QBindable bindableRecursiveFilteringEnabled()
-
参数: 无
返回值: QBindable 对象,用于绑定 recursiveFilteringEnabled 属性
作用: 获取可绑定的 recursiveFilteringEnabled 属性
QBindableQt::CaseSensitivity bindableSortCaseSensitivity()
-
参数: 无
返回值: QBindableQt::CaseSensitivity 对象,用于绑定 sortCaseSensitivity 属性
作用: 获取可绑定的 sortCaseSensitivity 属性
QBindable bindableSortRole()
-
参数: 无
返回值: QBindable 对象,用于绑定 sortRole 属性
作用: 获取可绑定的 sortRole 属性
bool dynamicSortFilter() const
-
参数: 无
返回值: 布尔值,表示是否启用动态排序过滤
作用: 获取动态排序过滤的设置状态
Qt::CaseSensitivity filterCaseSensitivity() const
-
参数: 无
返回值: Qt::CaseSensitivity 枚举值,表示过滤时的大小写敏感性
作用: 获取过滤时的大小写敏感性设置
int filterKeyColumn() const
-
参数: 无
返回值: 整数,表示用于过滤的键列索引
作用: 获取用于过滤的键列索引
QRegularExpression filterRegularExpression() const
-
参数: 无
返回值: QRegularExpression 对象,表示用于过滤的正则表达式
作用: 获取用于过滤的正则表达式
int filterRole() const
-
参数: 无
返回值: 整数,表示用于过滤的数据角色
作用: 获取用于过滤的数据角色
bool isRecursiveFilteringEnabled() const
-
参数: 无
返回值: 布尔值,表示是否启用递归过滤
作用: 获取递归过滤的设置状态
bool isSortLocaleAware() const
-
参数: 无
返回值: 布尔值,表示排序时是否考虑本地化
作用: 获取排序是否考虑本地化的设置状态
属性设置函数
void setAutoAcceptChildRows(bool accept)
-
参数: accept - 是否自动接受子行
返回值: 无
作用: 设置是否自动接受子行
void setDynamicSortFilter(bool enable)
-
参数: enable - 是否启用动态排序过滤
返回值: 无
作用: 设置是否启用动态排序过滤,数据变化时自动更新
// 切换动态更新
QObject::connect(&dynamicCheckBox, &QCheckBox::toggled, [&](bool checked) {
proxyModel.setDynamicSortFilter(checked); // 启用/禁用动态过滤
});
void setFilterCaseSensitivity(Qt::CaseSensitivity cs)
-
参数: cs - 过滤时的大小写敏感性
返回值: 无
作用: 设置过滤时的大小写敏感性
void setFilterKeyColumn(int column)
-
参数: column - 用于过滤的键列索引
返回值: 无
作用: 设置用于过滤的键列索引,如果设置只有column列的数据被过滤
void setFilterRole(int role)
-
参数: role - 用于过滤的数据角色
返回值: 无
作用: 指定过滤依据的角色(如 Qt::DisplayRole 或自定义角色)
void setRecursiveFilteringEnabled(bool recursive)
-
参数: recursive - 是否启用递归过滤
返回值: 无
作用: 设置是否启用递归过滤
void setSortCaseSensitivity(Qt::CaseSensitivity cs)
-
参数: cs - 排序时的大小写敏感性
返回值: 无
作用: 设置排序时的大小写敏感性
void setSortLocaleAware(bool on)
-
参数: on - 排序时是否考虑本地化
返回值: 无
作用: 设置排序时是否考虑本地化
void setSortRole(int role)
-
参数: role - 用于排序的数据角色
返回值: 无
作用: 设置用于排序的数据角色
排序状态查询函数
Qt::CaseSensitivity sortCaseSensitivity() const
-
参数: 无
返回值: Qt::CaseSensitivity 枚举值,表示排序时的大小写敏感性
作用: 获取排序时的大小写敏感性
int sortColumn() const
-
参数: 无
返回值: 整数,表示用于排序的列索引
作用: 获取当前用于排序的列索引
Qt::SortOrder sortOrder() const
-
参数: 无
返回值: Qt::SortOrder 枚举值,表示排序顺序
作用: 获取当前排序顺序(升序或降序)
int sortRole() const
-
参数: 无
返回值: 整数,表示用于排序的数据角色
作用: 获取用于排序的数据角色
重写的公共函数
QModelIndex buddy(const QModelIndex &index) const override
参数: index - 模型索引
返回值: QModelIndex,表示给定索引的伙伴索引
作用: 返回给定索引的伙伴索引
bool canFetchMore(const QModelIndex &parent) const override
参数: parent - 父索引
返回值: 布尔值,表示是否可以获取更多数据
作用: 判断给定父索引下是否可以获取更多数据
int columnCount(const QModelIndex &parent = QModelIndex()) const override
参数: parent - 父索引,默认为无效索引
返回值: 整数,表示列数
作用: 获取给定父索引下的列数
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
参数: index - 模型索引,role - 数据角色,默认为显示角色
返回值: QVariant,表示请求的数据
作用: 获取指定索引和角色的数据
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
参数: data - MIME 数据,action - 拖放动作,row - 行,column - 列,parent - 父索引
返回值: 布尔值,表示拖放操作是否成功
作用: 处理拖放时的 MIME 数据
void fetchMore(const QModelIndex &parent) override
参数: parent - 父索引
返回值: 无
作用: 获取给定父索引下的更多数据
Qt::ItemFlags flags(const QModelIndex &index) const override
参数: index - 模型索引
返回值: Qt::ItemFlags,表示项目标志
作用: 获取指定索引的项目标志
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override
参数: parent - 父索引,默认为无效索引
返回值: 布尔值,表示是否有子项
作用: 判断给定父索引是否有子项
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
参数: section - 节索引,orientation - 方向,role - 数据角色,默认为显示角色
返回值: QVariant,表示请求的头数据
作用: 获取指定节、方向和角色的头数据
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
参数: row - 行,column - 列,parent - 父索引,默认为无效索引
返回值: QModelIndex,表示请求的索引
作用: 获取指定行、列和父索引的模型索引
bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override
参数: column - 列起点,count - 列数,parent - 父索引,默认为无效索引
返回值: 布尔值,表示插入操作是否成功
作用: 在模型中插入列
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
参数: row - 行起点,count - 行数,parent - 父索引,默认为无效索引
返回值: 布尔值,表示插入操作是否成功
作用: 在模型中插入行
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
参数: sourceIndex - 源模型索引
返回值: QModelIndex,表示代理模型索引
作用: 将源模型索引映射到代理模型索引
QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const override
参数: sourceSelection - 源选择
返回值: QItemSelection,表示代理选择
作用: 将源模型选择映射到代理模型选择
QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const override
参数: proxySelection - 代理选择
返回值: QItemSelection,表示源选择
作用: 将代理模型选择映射到源模型选择
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
参数: proxyIndex - 代理模型索引
返回值: QModelIndex,表示源模型索引
作用: 将代理模型索引映射到源模型索引
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
参数: start - 起始索引,role - 角色,value - 值,hits - 匹配数量(默认为1),flags - 匹配标志
返回值: QModelIndexList,表示匹配的索引列表
作用: 在模型中查找匹配项
QMimeData* mimeData(const QModelIndexList &indexes) const override
参数: indexes - 索引列表
返回值: QMimeData*,表示 MIME 数据
作用: 为指定索引创建 MIME 数据
QStringList mimeTypes() const override
参数: 无
返回值: QStringList,表示支持的 MIME 类型列表
作用: 获取模型支持的 MIME 类型
QModelIndex parent(const QModelIndex &child) const override
参数: child - 子索引
返回值: QModelIndex,表示父索引
作用: 获取指定子索引的父索引
bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override
参数: column - 列起点,count - 列数,parent - 父索引,默认为无效索引
返回值: 布尔值,表示删除操作是否成功
作用: 从模型中删除列
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
参数: row - 行起点,count - 行数,parent - 父索引,默认为无效索引
返回值: 布尔值,表示删除操作是否成功
作用: 从模型中删除行
int rowCount(const QModelIndex &parent = QModelIndex()) const override
参数: parent - 父索引,默认为无效索引
返回值: 整数,表示行数
作用: 获取给定父索引下的行数
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
参数: index - 模型索引,value - 值,role - 角色,默认为编辑角色
返回值: 布尔值,表示设置操作是否成功
作用: 设置指定索引和角色的数据
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override
参数: section - 节索引,orientation - 方向,value - 值,role - 角色,默认为编辑角色
返回值: 布尔值,表示设置操作是否成功
作用: 设置指定节、方向和角色的头数据
void setSourceModel(QAbstractItemModel *sourceModel) override
参数: sourceModel - 源模型指针
返回值: 无
作用: 设置代理模型的源模型
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
参数: row - 行,column - 列,idx - 模型索引
返回值: QModelIndex,表示兄弟索引
作用: 获取指定索引的兄弟索引
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
参数: column - 列,order - 排序顺序,默认为升序
返回值: 无
作用: 按指定列和顺序对模型进行排序
QSize span(const QModelIndex &index) const override
参数: index - 模型索引
返回值: QSize,表示跨度大小
作用: 获取指定索引的跨度大小
Qt::DropActions supportedDropActions() const override
参数: 无
返回值: Qt::DropActions,表示支持的拖放动作
作用: 获取模型支持的拖放动作
公共槽函数
void invalidate()
-
参数: 无
返回值: 无
作用: 使代理模型的内部状态无效,强制重新评估所有项目
void setFilterFixedString(const QString &pattern)
-
参数: pattern - 固定字符串模式
返回值: 无
作用: 设置固定字符串过滤器模式,不符合过滤条件的被省略
model->setStringList(QStringList({
"C++",
"Java",
"Python",
"C#",
"JavaScript",
"Ruby",
"Swift",
"Kotlin",
"Go",
"Rust",
"PHP",
"TypeScript",
"Dart",
"Perl",
"Haskell"
}));
proxyModel->setSourceModel(model);
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); // 不区分大小写
proxyModel->setFilterFixedString("C"); // 直接设置过滤字符串
ui->listView->setModel(proxyModel);
void setFilterRegularExpression(const QString &pattern)
-
参数: pattern - 正则表达式模式字符串
返回值: 无
作用: 从字符串设置正则表达式过滤器模式
// 6. 信号槽连接:动态调整过滤规则
QObject::connect(&filterEdit, &QLineEdit::textChanged, [&](const QString &text) {
if (useRegexCheckBox.isChecked()) {
proxyModel.setFilterRegularExpression(text);
} else {
proxyModel.setFilterWildcard(text); // 通配符过滤(如 "A*")
}
});
void setFilterRegularExpression(const QRegularExpression ®ularExpression)
-
参数: regularExpression - 正则表达式对象
返回值: 无
作用: 设置正则表达式过滤器模式,和上面的没区别直接用上面的即可
void setFilterWildcard(const QString &pattern)
-
参数: pattern - 通配符模式
返回值: 无
作用: 设置通配符过滤器模式
// 6. 信号槽连接:动态调整过滤规则
QObject::connect(&filterEdit, &QLineEdit::textChanged, [&](const QString &text) {
if (useRegexCheckBox.isChecked()) {
proxyModel.setFilterRegularExpression(text);
} else {
proxyModel.setFilterWildcard(text); // 通配符过滤(如 "A*")
}
});
信号
void autoAcceptChildRowsChanged(bool autoAcceptChildRows)
-
参数: autoAcceptChildRows - 新的自动接受子行状态
作用: 当自动接受子行的设置改变时发出
void filterCaseSensitivityChanged(Qt::CaseSensitivity filterCaseSensitivity)
-
参数: filterCaseSensitivity - 新的过滤大小写敏感性
作用: 当过滤大小写敏感性改变时发出
void filterRoleChanged(int filterRole)
-
参数: filterRole - 新的过滤角色
作用: 当过滤角色改变时发出
void recursiveFilteringEnabledChanged(bool recursiveFilteringEnabled)
-
参数: recursiveFilteringEnabled - 新的递归过滤状态
作用: 当递归过滤设置改变时发出
void sortCaseSensitivityChanged(Qt::CaseSensitivity sortCaseSensitivity)
-
参数: sortCaseSensitivity - 新的排序大小写敏感性
作用: 当排序大小写敏感性改变时发出
void sortLocaleAwareChanged(bool sortLocaleAware)
-
参数: sortLocaleAware - 新的排序本地化感知状态
作用: 当排序本地化感知设置改变时发出
void sortRoleChanged(int sortRole)
-
参数: sortRole - 新的排序角色
作用: 当排序角色改变时发出
保护函数
void beginFilterChange()
-
参数: 无
返回值: 无
作用: 开始过滤器更改,通知模型过滤器即将更改
bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
-
参数: source_column - 源列索引,source_parent - 源父索引
返回值: 布尔值,表示是否接受该列
作用: 判断是否接受源模型中的指定列
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
-
参数: source_row - 源行索引,source_parent - 源父索引
返回值: 布尔值,表示是否接受该行
作用: 判断是否接受源模型中的指定行
void invalidateColumnsFilter()
-
参数: 无
返回值: 无
作用: 使列过滤器无效,强制重新评估所有列
void invalidateFilter()
-
参数: 无
返回值: 无
作用: 使过滤器无效,强制重新评估所有过滤条件
void invalidateRowsFilter()
-
参数: 无
返回值: 无
作用: 使行过滤器无效,强制重新评估所有行
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
-
参数: source_left - 左侧源索引,source_right - 右侧源索引
返回值: 布尔值,表示左侧是否小于右侧
作用: 比较两个源模型索引的大小关系,用于排序
bind函数的用法
QSortFilterProxyModel中的bindable函数是Qt 6引入的新特性,它们提供了属性绑定系统的支持。
Bindable函数基本概念
Qt 6中引入的属性绑定系统允许你创建属性之间的动态关系,当一个属性变化时,所有绑定到它的属性都会自动更新。
主要的bindable函数包括:
QBindable<bool> bindableAutoAcceptChildRows()
QBindable<bool> bindableDynamicSortFilter()
QBindable<Qt::CaseSensitivity> bindableFilterCaseSensitivity()
QBindable<int> bindableFilterKeyColumn()
QBindable<QRegularExpression> bindableFilterRegularExpression()
QBindable<int> bindableFilterRole()
QBindable<bool> bindableIsSortLocaleAware()
QBindable<bool> bindableRecursiveFilteringEnabled()
QBindable<Qt::CaseSensitivity> bindableSortCaseSensitivity()
QBindable<int> bindableSortRole()
基本用法示例
1. 单向绑定(从源到目标)
将一个属性绑定到另一个属性,使目标属性随源属性变化而自动更新:
QSortFilterProxyModel *proxyModel1 = new QSortFilterProxyModel(this);
QSortFilterProxyModel *proxyModel2 = new QSortFilterProxyModel(this);
// 将proxyModel2的过滤列绑定到proxyModel1的过滤列
QBindable<int> bindable1 = proxyModel1->bindableFilterKeyColumn();
QBindable<int> bindable2 = proxyModel2->bindableFilterKeyColumn();
bindable2.setBinding([&bindable1]() { return bindable1.value(); });
// 现在当proxyModel1的过滤列改变时,proxyModel2的过滤列会自动更新
proxyModel1->setFilterKeyColumn(2); // proxyModel2的过滤列也会变为2
2. 双向绑定
使两个属性保持同步,任一属性变化都会影响另一个:
// 创建两个代理模型
QSortFilterProxyModel *proxyModel1 = new QSortFilterProxyModel(this);
QSortFilterProxyModel *proxyModel2 = new QSortFilterProxyModel(this);
// 获取bindable对象
auto bindable1 = proxyModel1->bindableDynamicSortFilter();
auto bindable2 = proxyModel2->bindableDynamicSortFilter();
// 创建双向绑定
QProperty<bool> sharedProperty(true);
bindable1.setBinding([&sharedProperty]() { return sharedProperty.value(); });
bindable2.setBinding([&sharedProperty]() { return sharedProperty.value(); });
// 使用共享属性更新两个模型
sharedProperty = false; // 两个模型的dynamicSortFilter都会变为false
3. 条件绑定
基于条件创建复杂的绑定关系:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
QCheckBox *caseSensitiveCheckBox = new QCheckBox("区分大小写", this);
QCheckBox *localeSensitiveCheckBox = new QCheckBox("区分本地语言", this);
// 将过滤和排序的大小写敏感性绑定到复选框
QProperty<bool> caseSensitiveProperty;
caseSensitiveProperty.setBinding([caseSensitiveCheckBox]() {
return caseSensitiveCheckBox->isChecked();
});
// 过滤大小写敏感性绑定
proxyModel->bindableFilterCaseSensitivity().setBinding([&caseSensitiveProperty]() {
return caseSensitiveProperty.value() ? Qt::CaseSensitive : Qt::CaseInsensitive;
});
// 排序大小写敏感性绑定
proxyModel->bindableSortCaseSensitivity().setBinding([&caseSensitiveProperty]() {
return caseSensitiveProperty.value() ? Qt::CaseSensitive : Qt::CaseInsensitive;
});
// 本地语言感知绑定
proxyModel->bindableIsSortLocaleAware().setBinding([localeSensitiveCheckBox]() {
return localeSensitiveCheckBox->isChecked();
});
4. 组合多个属性
将多个属性组合到单个绑定中:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
QLineEdit *searchBox = new QLineEdit(this);
QComboBox *columnComboBox = new QComboBox(this);
columnComboBox->addItems({"姓名", "年龄", "年级", "所有列"});
// 根据组合框选择的列设置过滤列
proxyModel->bindableFilterKeyColumn().setBinding([columnComboBox]() {
int index = columnComboBox->currentIndex();
return (index == 3) ? -1 : index; // -1表示所有列
});
// 设置过滤表达式绑定到搜索框
QProperty<QString> searchTextProperty;
searchTextProperty.setBinding([searchBox]() { return searchBox->text(); });
proxyModel->bindableFilterRegularExpression().setBinding([&searchTextProperty]() {
return QRegularExpression(searchTextProperty.value(),
QRegularExpression::CaseInsensitiveOption);
});
5. 绑定到自定义UI组件
将过滤条件绑定到自定义UI控件:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
QSlider *minAgeSlider = new QSlider(Qt::Horizontal, this);
QSlider *maxAgeSlider = new QSlider(Qt::Horizontal, this);
minAgeSlider->setRange(0, 100);
maxAgeSlider->setRange(0, 100);
maxAgeSlider->setValue(100);
// 创建自定义过滤模型
class AgeFilterProxyModel : public QSortFilterProxyModel {
public:
QProperty<int> minAge{0};
QProperty<int> maxAge{100};
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override {
if (!QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent))
return false;
QModelIndex ageIndex = sourceModel()->index(source_row, 1, source_parent);
int age = sourceModel()->data(ageIndex).toInt();
return age >= minAge && age <= maxAge;
}
};
// 使用自定义模型
AgeFilterProxyModel *ageProxyModel = new AgeFilterProxyModel(this);
ageProxyModel->setSourceModel(studentModel);
// 绑定滑块到过滤条件
QProperty<int> minAgeProperty;
QProperty<int> maxAgeProperty;
minAgeProperty.setBinding([minAgeSlider]() { return minAgeSlider->value(); });
maxAgeProperty.setBinding([maxAgeSlider]() { return maxAgeSlider->value(); });
// 将自定义属性与过滤器绑定
ageProxyModel->minAge.setBinding([&minAgeProperty]() { return minAgeProperty.value(); });
ageProxyModel->maxAge.setBinding([&maxAgeProperty]() { return maxAgeProperty.value(); });
// 当滑块值变化时,invalidateFilter以重新应用过滤器
connect(minAgeSlider, &QSlider::valueChanged, ageProxyModel, &QSortFilterProxyModel::invalidateFilter);
connect(maxAgeSlider, &QSlider::valueChanged, ageProxyModel, &QSortFilterProxyModel::invalidateFilter);
高级用法
1. 多代理模型链
创建多个代理模型链,每个处理不同的过滤或排序功能:
// 基础学生模型
StudentTableModel *studentModel = new StudentTableModel(this);
// 名字过滤代理
QSortFilterProxyModel *nameFilterProxy = new QSortFilterProxyModel(this);
nameFilterProxy->setSourceModel(studentModel);
nameFilterProxy->setFilterKeyColumn(0); // 姓名列
// 年龄过滤代理(链在名字过滤代理后)
AgeFilterProxyModel *ageFilterProxy = new AgeFilterProxyModel(this);
ageFilterProxy->setSourceModel(nameFilterProxy);
// 排序代理(最终代理,连接到视图)
QSortFilterProxyModel *sortProxy = new QSortFilterProxyModel(this);
sortProxy->setSourceModel(ageFilterProxy);
sortProxy->setSortRole(Qt::DisplayRole);
// 连接到视图
tableView->setModel(sortProxy);
// 绑定UI控件
QLineEdit *nameSearch = new QLineEdit(this);
nameFilterProxy->bindableFilterRegularExpression().setBinding([nameSearch]() {
return QRegularExpression(nameSearch->text(), QRegularExpression::CaseInsensitiveOption);
});
2. 响应式界面与属性绑定
创建响应式UI,其中多个UI元素相互关联:
QCheckBox *enableFilterCheckBox = new QCheckBox("启用过滤", this);
QLineEdit *searchBox = new QLineEdit(this);
searchBox->setEnabled(false);
// 使搜索框状态绑定到复选框
QProperty<bool> filterEnabledProperty;
filterEnabledProperty.setBinding([enableFilterCheckBox]() {
return enableFilterCheckBox->isChecked();
});
// UI响应性
QObject::connect(&filterEnabledProperty, &QProperty<bool>::valueChanged,
searchBox, &QLineEdit::setEnabled);
// 模型过滤响应性
proxyModel->bindableFilterRegularExpression().setBinding([&filterEnabledProperty, searchBox]() {
if (!filterEnabledProperty.value())
return QRegularExpression(); // 空表达式不过滤
else
return QRegularExpression(searchBox->text(), QRegularExpression::CaseInsensitiveOption);
});
注意事项和最佳实践
-
性能考虑:属性绑定很强大,但过度使用可能导致性能问题,特别是在复杂绑定链中。
-
避免循环绑定:确保不会创建循环依赖,例如A绑定到B,B绑定到C,C又绑定回A。
-
绑定优先级:当使用setBinding()时,它会覆盖任何先前的绑定或手动设置的值。确保按正确的顺序设置绑定。
-
显式更新:虽然绑定会自动传播更改,但有时可能需要调用invalidateFilter()来确保视图更新。
-
Qt 6特性:这些bindable函数仅在Qt 6中可用,如果你的项目需要兼容Qt 5,则不能使用此功能。
性能考虑
对于大型数据集,QSortFilterProxyModel的一些重要性能考虑:
动态过滤:
setDynamicSortFilter(true)会导致每次数据变化时重新排序和过滤
对于频繁更新的大型数据集,可以临时禁用:
proxyModel->setDynamicSortFilter(false);
// 进行批量更新
proxyModel->setDynamicSortFilter(true);
延迟过滤:
对于搜索框实时过滤,可以使用定时器延迟应用过滤器:
QTimer *filterTimer = new QTimer(this);
filterTimer->setSingleShot(true);
connect(searchBox, &QLineEdit::textChanged, [=]() {
filterTimer->start(300); // 300ms延迟
});
connect(filterTimer, &QTimer::timeout, [=]() {
proxyModel->setFilterRegularExpression(searchBox->text());
});
过滤
在Qt中,QSortFilterProxyModel
用于对数据模型进行过滤和排序。若要对列表模型(如 QAbstractListModel
或 QStringListModel
)进行过滤,需通过子类化或直接配置代理模型实现。
1. 基本过滤原理
通过重写 filterAcceptsRow()
方法,决定哪些行(对于列表模型即每个项)应被保留。该方法返回 true
表示保留该行,false
表示过滤掉。
2. 实现过滤的步骤
(1) 子类化 QSortFilterProxyModel
class MyFilterProxyModel : public QSortFilterProxyModel {
public:
// 可选:设置过滤条件(如关键字)
void setFilterKeyword(const QString &keyword) {
m_keyword = keyword;
invalidateFilter(); // 触发重新过滤
}
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override {
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
QString data = sourceModel()->data(index, Qt::DisplayRole).toString(); // 获取显示数据
return data.contains(m_keyword, Qt::CaseInsensitive); // 示例:模糊匹配
}
private:
QString m_keyword;
};
(2) 应用代理模型
// 创建源模型(示例为字符串列表模型)
QStringListModel *sourceModel = new QStringListModel;
sourceModel->setStringList({"Apple", "Banana", "Cherry", "Grape"});
// 创建代理模型并设置源模型
MyFilterProxyModel *proxyModel = new MyFilterProxyModel;
proxyModel->setSourceModel(sourceModel);
// 设置过滤关键字
proxyModel->setFilterKeyword("ap"); // 匹配 "Apple" 和 "Grape"
// 将代理模型设置到视图(如 QListView)
QListView *listView = new QListView;
listView->setModel(proxyModel);
3. 动态过滤
若需动态更新过滤条件(如根据用户输入),调用 invalidateFilter()-保护函数
强制代理模型重新计算过滤结果。
// 示例:当用户输入文本时更新过滤
QLineEdit *searchBox = new QLineEdit;
connect(searchBox, &QLineEdit::textChanged, proxyModel, &MyFilterProxyModel::setFilterKeyword);
4. 使用内置过滤方法
无需子类化时,可直接通过 setFilterFixedString()
或正则表达式过滤:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel;
proxyModel->setSourceModel(sourceModel);
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); // 不区分大小写
proxyModel->setFilterFixedString("ap"); // 直接设置过滤字符串
5. 处理复杂过滤逻辑
若需基于自定义角色或复杂条件过滤,可在 filterAcceptsRow
中访问模型数据的不同角色:
bool filterAcceptsRow(int sourceRow, const QModelIndex&) const override {
QModelIndex index = sourceModel()->index(sourceRow, 0);
int role = Qt::UserRole + 1; // 假设自定义角色
QString data = sourceModel()->data(index, role).toString();
return data.startsWith("A");
}
注意事项
- 性能:频繁调用
invalidateFilter()
可能影响性能,建议对大量数据使用异步处理或延迟过滤。 - 正则表达式:通过
setFilterRegularExpression()
支持更复杂的模式匹配。 - 多列模型:若源模型是多列(如表格),需结合
filterAcceptsColumn()
处理。
排序
在 Qt 中,QSortFilterProxyModel
不仅可以过滤数据,还能对数据进行动态排序。
1. 基本排序原理
QSortFilterProxyModel
默认支持通过调用 sort()
方法对数据进行排序。排序依据可以是数据模型的默认角色(如 Qt::DisplayRole
)或自定义角色。排序逻辑可以通过以下两种方式控制:
- 默认排序:直接使用
sort()
方法,基于DisplayRole
或指定的角色按字符串或数值排序。 - 自定义排序:子类化
QSortFilterProxyModel
并重写lessThan()
方法,实现复杂的排序逻辑。
2. 实现排序的步骤
(1) 基本排序示例
以下代码演示如何对 QStringListModel
进行升序/降序排序:
#include <QApplication>
#include <QStringListModel>
#include <QSortFilterProxyModel>
#include <QListView>
#include <QPushButton>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 1. 创建源模型(字符串列表)
QStringList data = {"Banana", "apple", "Cherry", "Grape"};
QStringListModel sourceModel;
sourceModel.setStringList(data);
// 2. 创建代理模型并绑定源模型
QSortFilterProxyModel proxyModel;
proxyModel.setSourceModel(&sourceModel);
// 3. 设置排序参数
proxyModel.setSortCaseSensitivity(Qt::CaseInsensitive); // 不区分大小写
proxyModel.setSortRole(Qt::DisplayRole); // 按显示文本排序
// 4. 创建视图和排序按钮
QListView listView;
listView.setModel(&proxyModel); // 视图绑定代理模型
QPushButton sortAscButton("Sort Ascending");
QPushButton sortDescButton("Sort Descending");
// 5. 点击按钮触发排序
QObject::connect(&sortAscButton, &QPushButton::clicked, [&]() {
proxyModel.sort(0, Qt::AscendingOrder); // 按升序排序列 0
});
QObject::connect(&sortDescButton, &QPushButton::clicked, [&]() {
proxyModel.sort(0, Qt::DescendingOrder); // 按降序排序列 0
});
// 6. 布局
QWidget window;
QVBoxLayout layout(&window);
layout.addWidget(&listView);
layout.addWidget(&sortAscButton);
layout.addWidget(&sortDescButton);
window.show();
return app.exec();
}
运行效果:
- 点击 “Sort Ascending” 按钮,列表按字母升序排列(不区分大小写):
apple
,Banana
,Cherry
,Grape
。 - 点击 “Sort Descending” 按钮,列表按字母降序排列:
Grape
,Cherry
,Banana
,apple
。
3. 动态排序
启用 setDynamicSortFilter(true)
后,当源模型数据变化时,代理模型会自动重新排序。例如,在数据中添加新项时,列表会自动按当前排序规则更新。
proxyModel.setDynamicSortFilter(true); // 启用动态排序
// 添加新项测试动态排序
sourceModel.setStringList(data << "Apricot");
// 视图会自动更新并按当前顺序排列
4. 自定义排序逻辑
如果需要更复杂的排序规则(如按字符串长度排序),可以子类化 QSortFilterProxyModel
并重写 lessThan()
方法。
示例:按字符串长度排序
class LengthSortProxyModel : public QSortFilterProxyModel {
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override {
// 获取左右项的字符串长度
int leftLength = sourceModel()->data(left, Qt::DisplayRole).toString().length();
int rightLength = sourceModel()->data(right, Qt::DisplayRole).toString().length();
return leftLength < rightLength; // 按长度升序
}
};
// 使用自定义代理模型
LengthSortProxyModel proxyModel;
proxyModel.setSourceModel(&sourceModel);
proxyModel.sort(0, Qt::AscendingOrder); // 按长度升序
结果:
- 按字符串长度排序:
Grape (5)
,apple (5)
,Banana (6)
,Cherry (6)
,Apricot (7)
。
5. 结合过滤与排序
QSortFilterProxyModel
可以同时启用过滤和排序功能。代理模型会先过滤数据,再对过滤后的结果进行排序。
// 设置过滤规则
proxyModel.setFilterRegularExpression("a"); // 过滤包含字母 "a" 的项
// 设置排序规则
proxyModel.sort(0, Qt::AscendingOrder);
结果:
- 过滤后数据:
Banana
,apple
,Grape
,Apricot
。 - 排序后结果:
apple
,Apricot
,Banana
,Grape
。
6. 关键函数说明
-
sort(int column, Qt::SortOrder order)
- 对指定列(列表模型为
0
)按升序(Qt::AscendingOrder
)或降序(Qt::DescendingOrder
)排序。 - 直接调用时会立即触发排序。
- 对指定列(列表模型为
-
setSortRole(int role)
- 指定排序依据的角色,例如
Qt::DisplayRole
(默认)或自定义角色(如Qt::UserRole
存储的数值)。
- 指定排序依据的角色,例如
-
setSortCaseSensitivity(Qt::CaseSensitivity cs)
- 控制排序时是否区分大小写(默认区分)。
-
setDynamicSortFilter(bool enable)
- 启用后,源模型数据变化时自动重新排序。
7. 通过视图交互排序
如果视图支持点击列头排序(如 QTableView
),可直接通过视图触发排序。但对于列表模型(QListView
),通常需要手动按钮或代码控制。
注意事项
- 性能:对大型数据集频繁排序可能导致卡顿,建议异步处理或分页。
- 自定义模型:如果源模型的数据角色需要特殊处理(如数值排序),需确保数据角色正确实现。
- 多列排序:
QSortFilterProxyModel
不支持多列联合排序,需自定义逻辑。