上一篇介绍介绍了C++model的基本用法,只实现了增删改,实际项目开发中,还有很常见的功能就是排序和查询。Qt提供了QSortFilterProxyModel实现排序和查询的功能。
排序查询QSortFilterProxyModel,顾名思义,排序过滤代理model,搜索查询本质上就是过滤。至于代理,就是QSortFilterProxyModel只是原model的代理,原始数据还是在原model上,代理model根据相应的规则存储进行处理后的数据,源数据在源model是不变的。比如芒果的代理商,实际上果农果园里有很多品种的芒果,但市场上主流的两三种,代理商售卖就只卖这两三种,果农就是原model,代理商就是QSortFilterProxyModel。
QSortFilterProxyModel提供了以下接口绑定model
void setSourceModel(QAbstractItemModel *sourceModel) override排序
sort接口实现排序,第二个参数是选择升序还是降序
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
对于我们自定义的model来说,需要选择要排序的role类型,一般搭配setSortRole使用
void setSortRole(int role);
对于自定义的类型,需要自定义自己的排序规则,可重载
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);
//! [4]
//! [6]
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也很容易实现
重写函数
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool DataListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
auto typeData = sourceModel()->data(index0, DataListModel::TypeRole).toString();
auto sizeData = sourceModel()->data(index0, DataListModel::SizeRole1).toString();
return typeData.contains(filterRegExp()) || sizeData.contains(filterRegExp());
}
里面实现想要过滤的内容,上面的例子就返回满足TypeRole和SizeRole1其中一项的数据
执行过滤是调用,参数就是过滤规则
void setFilterRegExp(const QRegExp ®Exp);
QRegExp除了关键的key值pattern外,还有Qt::CaseSensitivity支持是否区分大小写,第三个参数是其他一些匹配规则,我也很少用到
explicit QRegExp(const QString &pattern, Qt::CaseSensitivity cs = Qt::CaseSensitive,
PatternSyntax syntax = RegExp);
demo展示
以上就是基于QSortFilterProxyModel实现的排序和搜索功能,实现还是很简单的
源model和上一篇博客介绍的model是一样的,在此基础上增加一个QSortFilterProxyModel,不同的是QML显示的是QSortFilterProxyModel,就是QML显示的是处理的数据,这个应该好理解。
#includeclass DataListProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit DataListProxyModel(QObject *parent = nullptr); Q_INVOKABLE void setFilter(const QString &text); Q_INVOKABLE void startSort(); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; // bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; };
#include "datalistproxymodel.h"
#include "datalistmodel.h"
DataListProxyModel::DataListProxyModel(QObject *parent):
QSortFilterProxyModel(parent)
{
}
void DataListProxyModel::setFilter(const QString &text)
{
QRegExp regExp(text);
this->setFilterRegExp(regExp);
}
void DataListProxyModel::startSort()
{
setSortRole(DataListModel::TypeRole);
sort(0, Qt::DescendingOrder);
}
bool DataListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
auto typeData = sourceModel()->data(index0, DataListModel::TypeRole).toString();
auto sizeData = sourceModel()->data(index0, DataListModel::SizeRole1).toString();
return typeData.contains(filterRegExp()) || sizeData.contains(filterRegExp());
}
主函数main
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
DataListModel model;
model.append( ModelData("wolf.jpg", "Medium") );
model.append( ModelData("polarbear.jpg", "Large") );
model.append( ModelData("quoll.jpg", "Small") );
DataListProxyModel proxyModel;
proxyModel.setSourceModel(&model);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myModel", &proxyModel);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
设置setSourceModel,该例子采用C++与QML交互的另一种方式,model是在c++创建,把对象注册到QML中使用,该例子是注册对象QSortFilterProxyModel,使用代理model进行显示
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column {
anchors.fill: parent
Row {
TextField {
id: searchInput
width: 100
height: 30
onTextChanged: {
myModel.setFilter(text)
}
}
Button {
text: "sort"
onClicked: {
myModel.startSort()
}
}
}
ListView {
id: listview
clip: true
width: parent.width
height: parent.height
model: myModel
delegate: Item {
id: delegate
width: listview.width
height: 30
Row {
spacing: 5
Text {
text: type
}
Text {
text: size
color: "red"
}
Button {
height: parent.height
onClicked: {
myModel.remove(index);
}
}
}
}
}
}
结语
以上就是一个排序搜索的功能,界面布局有点挫,将就着看,重点是功能。更多例子了解可参考Qt例子
前两个例子是Widget的实现的, 但QSortFilterProxyModel的用法是一样的,第三个是QML,但使用的是QML,没有基于QSortFilterProxyModel,有需要可以看看。由于边幅的原因,基于model的撤销恢复功能留到下一篇来介绍了。



