上一篇介绍介绍了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);
以上就是基于QSortFilterProxyModel实现的排序和搜索功能,实现还是很简单的
源model和上一篇博客介绍的model是一样的,在此基础上增加一个QSortFilterProxyModel,不同的是QML显示的是QSortFilterProxyModel,就是QML显示的是处理的数据,这个应该好理解。
- #include <QSortFilterProxyModel>
-
- class 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的撤销恢复功能留到下一篇来介绍了。