Adding support for sorting and filtering on a list view will always be useful if the list view is much bigger and/or complex. I have created a simple demo QML based application which shows a basic list view with text item on which user can perform the filtering and sorting of items.
I have created CListModel class which is derived from QAbstractListModel which will hold the model data which is to be shown on the list view. I have also used a custom “NameRole” for the data.
Following is my CListModel class implementation.
#include <QAbstractListModel> enum Roles { NameRole = Qt::UserRole + 1, }; //List Model class CListModel : public QAbstractListModel { public: CListModel(); ~CListModel(); void addData(const QString &unit); int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; protected: QHash<int, QByteArray> roleNames() const; private: QStringList m_names; };
#include "listmodel.h" CListModel::CListModel() { } CListModel::~CListModel() { } void CListModel::addData(const QString &unit) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_names.append(unit); endInsertRows(); } int CListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_names.count(); } QVariant CListModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_names.count()) return QVariant(); const QString &name = m_names[index.row()]; if (role == NameRole) return name; return QVariant(); } QHash<int, QByteArray> CListModel::roleNames() const { QHash<int, QByteArray> roles; roles[NameRole] = "name"; return roles; }
Next we need a Filter model which will perform the sort and filter functionality on our custom list view. I have created FilterProxyModel class by deriving from QSortFilterProxyModel. And additionally I have added two methods for setting the filter string and sort criteria from the QML with macro Q_INVOKABLE.
Following is my FilterProxyModel class implementation.
class FilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: FilterProxyModel(QObject* parent = 0); ~FilterProxyModel(); Q_INVOKABLE void setFilterString(QString string); Q_INVOKABLE void setSortOrder(bool checked); };
FilterProxyModel::FilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { setSortOrder(false); } FilterProxyModel::~FilterProxyModel() { } void FilterProxyModel::setFilterString(QString string) { this->setFilterCaseSensitivity(Qt::CaseInsensitive); this->setFilterFixedString(string); } void FilterProxyModel::setSortOrder(bool checked) { if(checked) { this->sort(0, Qt::DescendingOrder); } else { this->sort(0, Qt::AscendingOrder); } }
Below is my QML implementation.
import QtQuick 2.4 import QtQuick.Controls 1.2 import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.2 ApplicationWindow { id: appWindow visible: true width: 320 height: 480 color: "#6bdce4" property int margin: 12 flags: Qt.FramelessWindowHint ColumnLayout { id: mainLayout anchors.fill: parent anchors.margins: margin Rectangle{ color: "#6bdce4" Layout.fillWidth: true height: 40 z: 2 RowLayout { id: rowLayout anchors.fill: parent anchors.centerIn: parent TextField { placeholderText: "Type here.." Layout.fillWidth: true font.pointSize: 12 style: TextFieldStyle { textColor: "black" background: Rectangle { radius: 5 implicitWidth: 100 implicitHeight: 30 border.color: "#6bdce4" border.width: 1 } } onTextChanged: { filterModel.setFilterString(text); } } CheckBox { text: "Descending" onCheckedChanged:{ filterModel.setSortOrder(checked) } style: CheckBoxStyle { label: Text { color: "white" text: "Descending" font.pointSize: 12 } indicator: Rectangle { implicitWidth: 20 implicitHeight: 20 radius: 3 border.color: control.activeFocus ? "darkblue" : "gray" border.width: 1 Rectangle { visible: control.checked color: "#555" border.color: "#333" radius: 1 anchors.margins: 4 anchors.fill: parent } } } } } } ListView { id: view model: filterModel Layout.minimumHeight: 25 Layout.fillHeight: true Layout.fillWidth: true cacheBuffer: 100 spacing: 10 delegate: Rectangle{ width: parent.width radius: 5 anchors.horizontalCenter: parent.horizontalCenter height: 40 color: Qt.lighter("#6bdce4", 0.8) Text { id: nameTxt text: name font.pointSize: 12 color: "#FFFFFF" anchors.left: parent.left anchors.leftMargin: 20 anchors.verticalCenter: parent.verticalCenter } } } } }
Now first we need to create the instance of CListModel and populate the data which we want to show in the list. Then create an instance of filter model FilterProxyModel class and set the source as our actual model. Also set the role which we want to use for sorting and filtering. Now make the instance of FilterProxyModel available inside the QML file using method,
QQmlContext::setContextProperty("filterModel", &filterModel);
Below is my implementation of main.cpp file.
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); //Create and populate list model instance CListModel listModel; listModel.addData("Isaac Newton"); listModel.addData("Alexander Graham Bell"); listModel.addData("Leonhard Euler"); listModel.addData("Nikola Tesla"); listModel.addData("Charles-Augustin de Coulomb"); listModel.addData("Andre-Marie Ampere"); listModel.addData("Michael Faraday"); listModel.addData("Blaise Pascal"); listModel.addData("Anders Celsius"); listModel.addData("James Watt"); //Create filter model FilterProxyModel filterModel; filterModel.setSourceModel(&listModel); filterModel.setFilterRole(NameRole); filterModel.setSortRole(NameRole); QQmlApplicationEngine engine; QQmlContext* context = engine.rootContext(); context->setContextProperty("filterModel", &filterModel); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
Below are the screenshot from my demo application.
Source code:
Leave a Reply