QSortFilterProxyModel
类可以提供model和view之间排序和过滤数据的支持.QSortFilterProxyModel
可以用来对item进行排序、筛选。可以把一个model的 Model
结构通过model的索引(index)结构映射到他新提供的索引(index)里面,从而转换 Model
模型的结构。就视图(view)而言,这种方法允许重新构造给定的源模型(Model),而不需要对基础数据进行任何转换,也不需要在内存中复制数据。
让我们假设以下场景:
我们想要对定制模型提供的项进行排序和筛选。
在没有排序和过滤的情况下,建立模型和视图的代码如下:
QTreeView *treeView = new QTreeView;
MyItemModel *model = new MyItemModel(this);
treeView->setModel(model);
这样做,我们可能没有排序功能,或者排序达不到我们想要结果,因为 QTreeView
支持的排序数据结构比较少,比如: int
、 QString
等,这远远是不够的,我已我们需要设置自己的排序或者过滤规则。为了给 MyItemModel
添加排序和过滤支持,我们需要创建一个 QSortFilterProxyModel
,用MyItemModel作为参数调用setSourceModel(),然后在视图上安装QSortFilterProxyModel:
代码如下:
QTreeView *treeView = new QTreeView;
MyItemModel *sourceModel = new MyItemModel(this);
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(sourceModel);
treeView->setModel(proxyModel);
此时,排序和过滤都没有启用,仅仅是把原始数据显示在视图中。通过 QSortFilterProxyModel
所做的任何更改都应用于原始模型。QSortFilterProxyModel
充当原始模型的包装器。如果需要将源 QModelIndexes
转换为已排序/过滤的模型索引,或者相反,请使用 mapToSource()
、 mapFromSource()
、 mapSelectionToSource(
)和 mapSelectionFromSource()
。
注意:
- 默认情况下,只要原始模型发生变化,模型就会动态地重新排序和筛选数据。可以通过设置dynamicSortFilter属性来更改此行为。
具体看例子 Basic Sort/Filter Model 和 Custom Sort/Filter Model。简单做了修改。放在gitee上。
QTableView和QTreeView有一个sortingEnabled属性,它控制用户是否可以通过单击视图的水平标题对视图进行排序。
treeView->setSortingEnabled(true);
默认是不可排序的。当这个性质打开时候,点击header就会对本列的item排序,通过重复点击header,用户可以交替的看到升序和排序的排序。
在幕后,视图调用模型上的 sort()
虚拟函数来重新排列模型中的数据。为了使你的数据可以排序,你可以在你的模型中实现 sort()
或者使用 QSortFilterProxyModel
来包装你的模型。 QSortFilterProxyModel
提供了一个通用的 sort()
实现,它操作于项中的 sortRole()
(默认为 Qt::DisplayRole
),并且它理解几种数据类型,包括 int
、 QString
和 QDateTime
。对于分层模型,排序会递归地应用到所有子项。字符串比较默认是区分大小写的;这可以通过设置 sortCaseSensitivity
属性来改变。
在项目中进行比较时,自定义排序行为是通过子类化QSortFilterProxyModel
和重新实现lessThan()
来实现的。例如:
bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);
if (leftData.userType() == QMetaType::QDateTime) {
return leftData.toDateTime() < rightData.toDateTime();
} else {
static const QRegularExpression emailPattern("[\\w\\.]*@[\\w\\.]*");
QString leftString = leftData.toString();
if (left.column() == 1) {
const QRegularExpressionMatch match = emailPattern.match(leftString);
if (match.hasMatch())
leftString = match.captured(0);
}
QString rightString = rightData.toString();
if (right.column() == 1) {
const QRegularExpressionMatch match = emailPattern.match(rightString);
if (match.hasMatch())
rightString = match.captured(0);
}
return QString::localeAwareCompare(leftString, rightString) < 0;
}
}
此代码片段来自 《自定义排序/筛选模型》示例,可以点击链接,去看看源码的具体实现。
排序的另一种方法是在视图上禁用排序,并向用户施加一定的顺序。这是通过在 QSortFilterProxyModel
(或者在原始模型上,如果它实现了 sort()
)上显式调用 sort()
,并将所需的列和顺序作为参数来实现的。例如,在 QSortFilterProxyModel
上显式地调用 sort()
,并将所需的列和顺序作为参数。
proxyModel->sort(2, Qt::AscendingOrder);
QSortFilterProxyModel
可以通过列 -1
进行排序,在这种情况下,它返回到底层源模型的排序顺序。
过滤
除了排序之外, QSortFilterProxyModel
还可以用来隐藏不符合某个过滤器的项目。过滤器使用 QRegExp
对象指定,并应用于每个项目的 filterRole()
(默认为 Qt::DisplayRole
),对于给定的列。 QRegExp
对象可以用来匹配一个正则表达式、一个通配符模式或一个固定的字符串。例如: QRegExp
对象可以用来匹配正则表达式( regular expression
)、通配符模式( wildcard pattern
)或固定的字符串( fixed string
)。
例如:
proxyModel->setFilterRegExp(QRegExp(".png", Qt::CaseInsensitive,
QRegExp::FixedString));
proxyModel->setFilterKeyColumn(1);
对于分层模型,过滤器递归地应用于所有子元素。如果一个父项与筛选器不匹配,那么它的子项将不会显示。
一个常见的用例是让用户在QLineEdit中指定过滤器正则表达式、通配符模式或固定字符串,并将 textChanged()
信号连接到 setFilterRegularExpression()
、 setFilterWildcard()
或 setFilterFixedString()
以重新应用过滤器。
可以通过重新实现 filterAcceptsRow()
和 filterAcceptsColumn()
函数来实现定制过滤行为。例如(来自自定义排序/筛选模型示例),下面的实现忽略 filterKeyColumn
属性并对列 0
、 1
和 2
执行筛选:
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const
{
QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);
return (sourceModel()->data(index0).toString().contains(filterRegExp())
|| sourceModel()->data(index1).toString().contains(filterRegExp()))
&& dateInRange(sourceModel()->data(index2).toDate());
}
此代码片段来自自定义《排序/筛选模型》示例。
如果您正在处理大量的过滤,并且必须重复调用 invalidateFilter()
,那么使用 beginResetModel() / endResetModel()
可能会更有效,这取决于您的模型的实现。然而, beginResetModel() / endResetModel()
返回代理模型到它的原始状态,失去选择信息,并将导致代理模型被重新填充。
子类化
因为 QAbstractProxyModel
和它的子类是从 QAbstractItemModel
派生出来的,所以很多关于将普通模型子类化的建议也适用于代理模型。此外,值得注意的是,这个类中的许多函数的默认实现都是为了调用相关源模型中的等效函数而编写的。对于行为更复杂的源模型,可能需要重写这种简单的代理机制;例如,如果源模型提供了一个定制的 hasChildren()
实现,那么您也应该在代理模型中提供一个。
想知道更多,可以访问QtCreater文档, QSortFilterProxyModel .
说明
文中的《自定义排序/筛选模型》和《排序/筛选模型》,都在附件中,这些代码从QtCreater中可以搜索到,这是很好的。不过我习惯与使用Vs进行代码编辑,所以把代码拷贝出来了。进行了编辑。后续有时间会进行解读。
basicsortfiltermodel.zipcustomsortfiltermodel.zip