Qt Quick Tutorial: 3 Things To Do List Part 3 - Filtering

At the end of Part 2 we had a todo list that output our dummy data regardless of the value set in today. To fix that we need to write a filter model. Create filtermodel.h and filtermodel.cpp and add them to CMakeLists as before. In order to implement the filter we’re going to derive from QSortFilterProxyModel.
Header
For the moment this is only going to be a very small implementation as we are just using it to filter out the three tasks we have set for today from all the others and we can rely on the underlying functionality to do the bulk of the work for us. Any functionality we want to be accessible from QML needs to be declared Q_INVOKABLE.
#ifndef FILTERMODEL_H
#define FILTERMODEL_H
#include <QSortFilterProxyModel>
class FilterModel : public QSortFilterProxyModel {
Q_OBJECT
public:
FilterModel(QObject *parent = nullptr);
bool showTodayOnly();
Q_INVOKABLE void setShowTodayOnly(const bool &today);
private:
bool _showTodayOnly;
};
#endif
CPP
In setShowTodayOnly we’re using setFilterRole to isolate the data we want to filter on and using setFilterFixedString to ensure the whole string is matched, setting the string to "" effectively clears it
#include "datamodel.h"
#include "filtermodel.h"
FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent) {
// empty
}
bool FilterModel::showTodayOnly() {
return _showTodayOnly;
}
void FilterModel::setShowTodayOnly(const bool &today) {
_showTodayOnly = today;
if (today) {
this->setFilterRole(DataModel::TodayRole);
this->setFilterFixedString("true");
} else {
this->setFilterFixedString("");
}
}
Main
CPP
Back in main.cpp we need to instanciate our FilterModel, set the source model and tell it to showTodayOnly as the default state:
FilterModel filterModel;
filterModel.setSourceModel(dataList);
filterModel.setShowTodayOnly(true);
Then we need to remove
engine.setInitialProperties({ { "dataModel", QVariant::fromValue(dataModel) } });
and instead load our filterModel into the root context so that all our views can access it:
QQmlContext *context = engine.rootContext();
context->setContextProperty("dataModel", &filterModel);
QML
As we now have our model loaded into the root context we no longer need to alias dataModel to toDoView.model so we can remove this line:
property alias dataModel: toDoView.model
Then change require model in toDoView to model: dataModel.
Now when you run the app you will only get the items marked true for today showing:

That’s great but we need a way to see all of our todo’s so lets make that See all button at the bottom work. To do that we need to add a state to our toDoContainer, both so we know what state we’re currently in and so we can change other things within the view. Here we’re changing the titleText and the changeView text. The default state is referred to by an empty string "" and isn’t required to be explicitly included here.
states: [
State {
name: ""
},
State {
name: "viewAll"
PropertyChanges {
target: titleText
text: "All the To Do Items"
}
PropertyChanges {
target: txt
text: "See Today"
}
}
]
Now we need a way to invoke our viewAll state, to do that we’re going to add a MouseArea to our CustomButtonand have it trigger the state change and filter when we click on it by setting up a buttonClicked signal that we can use to pass the function to the onClicked signal.
Rectangle {
property alias txt: txt
signal buttonClicked()
...
MouseArea {
anchors.fill: parent
onClicked: buttonClicked()
}
}
Back in main.qml we need to link that buttonClicked signal to the action we want to take:
CustomButton {
id: changeView
anchors.centerIn: parent
color: "#aa5c2b80"
txt.text: "See all"
txt.color: "#fafafa"
txt.padding: 16
onButtonClicked: {
if (root.state === "viewAll") {
root.state = ""
dataModel.setShowTodayOnly(true);
} else {
root.state = "viewAll"
dataModel.setShowTodayOnly(false);
}
}
}
Now if you run the app you can switch between the two views using the purple button.

Comments powered by Talkyard.