Việc sắp xếp 1 list theo 1 tiêu chí thì có thể khá dễ dàng để triển khai, vậy
bạn đã biết cách sắp xếp 1 list theo nhiều tiêu chí.
Thư viện Qt đã
hỗ trợ điều đó bằng QSortFilterProxyModel, vậy triển khai nó thế nào?
Đơn giản, chúng ta chỉ cần override lại hàm lessThan của thư viện này để linh
hoạt theo ý muốn.
Thậm chí chúng ta có thể cài đặt để bật tắt tính
năng của chúng ta chỉ với 1 vài bước đơn giản.
Cùng theo dõi dưới đây:
#ifndef MODEL_H
#define MODEL_H
#include <QAbstractListModel>
#include <QStringList>
//![0]
class Animal
{
public:
Animal(const QString &type, const QString &size);
Animal(const QString &type, const QString &size, const int &height, const int &age);
//![0]
QString type() const;
QString size() const;
int height() const;
int age() const;
private:
QString m_type;
QString m_size;
int m_height;
int m_age;
//![1]
};
class AnimalModel : public QAbstractListModel
{
Q_OBJECT
public:
enum AnimalRoles {
TypeRole = Qt::UserRole + 1,
SizeRole,
HeightRole,
AgeRole
};
AnimalModel(QObject *parent = 0);
//![1]
void addAnimal(const Animal &animal);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash roleNames() const;
private:
QList m_animals;
//![2]
};
//![2]
#endif // MODEL_H
model.cpp
#include "model.h"
Animal::Animal(const QString &type, const QString &size)
: m_type(type), m_size(size)
{
m_height = 0;
m_age = 0;
}
Animal::Animal(const QString &type, const QString &size, const int &height, const int &age)
: m_type(type), m_size(size), m_height(height), m_age(age)
{
}
QString Animal::type() const
{
return m_type;
}
QString Animal::size() const
{
return m_size;
}
int Animal::height() const
{
return m_height;
}
int Animal::age() const
{
return m_age;
}
AnimalModel::AnimalModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void AnimalModel::addAnimal(const Animal &animal)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_animals << animal;
endInsertRows();
}
int AnimalModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_animals.count();
}
QVariant AnimalModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_animals.count())
return QVariant();
const Animal &animal = m_animals[index.row()];
if (role == TypeRole)
return animal.type();
else if (role == SizeRole)
return animal.size();
else if (role == HeightRole)
return animal.height();
else if (role == AgeRole)
return animal.age();
return QVariant();
}
//![0]
QHash AnimalModel::roleNames() const {
QHash roles;
roles[TypeRole] = "type";
roles[SizeRole] = "size";
roles[HeightRole] = "heightR";
roles[AgeRole] = "ageR";
return roles;
}
//![0]
MultipleSortFilterProxyModel.h
#ifndef MULTIPLESORTFILTERPROXYMODEL_H
#define MULTIPLESORTFILTERPROXYMODEL_H
#include <QSortFilterProxyModel>
#include <QModelIndex>
#include "model.h"
class MultipleSortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(bool multipleSortEnable READ multipleSortEnable WRITE setMultipleSortEnable NOTIFY multipleSorterEnableChanged)
Q_PROPERTY(AnimalModel::AnimalRoles sorterRow1 READ sorterRow1 WRITE setSorterRow1 NOTIFY sorterRow1Changed)
Q_PROPERTY(AnimalModel::AnimalRoles sorterRow2 READ sorterRow2 WRITE setSorterRow2 NOTIFY sorterRow2Changed)
private:
// private variables
AnimalModel::AnimalRoles m_sorterRow1;
AnimalModel::AnimalRoles m_sorterRow2;
bool m_multipleSortEnable;
public:
explicit MultipleSortFilterProxyModel(QObject *parent = 0);
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
AnimalModel::AnimalRoles sorterRow1() const;
void setSorterRow1(AnimalModel::AnimalRoles newFilterRow1);
AnimalModel::AnimalRoles sorterRow2() const;
void setSorterRow2(AnimalModel::AnimalRoles newFilterRow2);
bool multipleSortEnable() const;
void setMultipleSortEnable(bool newMultipleSortEnable);
signals:
void sorterRow1Changed();
void sorterRow2Changed();
void multipleSorterEnableChanged();
};
#endif // MULTIPLESORTFILTERPROXYMODEL_H
MultipleSortFilterProxyModel.cpp
#include "MultipleSortFilterProxyModel.h"
MultipleSortFilterProxyModel::MultipleSortFilterProxyModel(QObject *parent):
QSortFilterProxyModel(parent),
m_multipleSortEnable(false),
m_sorterRow1(AnimalModel::AnimalRoles::HeightRole),
m_sorterRow2(AnimalModel::AnimalRoles::AgeRole)
{
}
bool MultipleSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if(!m_multipleSortEnable) return sourceModel()->data(left, sortRole()) < sourceModel()->data(right, sortRole());
if(sourceModel()->data(left, (int)m_sorterRow1) < sourceModel()->data(right, (int)m_sorterRow1)) return true;
else if(sourceModel()->data(left, (int)m_sorterRow2) < sourceModel()->data(right, (int)m_sorterRow2)) return true;
return false;
}
AnimalModel::AnimalRoles MultipleSortFilterProxyModel::sorterRow1() const
{
return m_sorterRow1;
}
void MultipleSortFilterProxyModel::setSorterRow1(AnimalModel::AnimalRoles newFilterRow1)
{
if (m_sorterRow1 == newFilterRow1)
return;
m_sorterRow1 = newFilterRow1;
emit sorterRow1Changed();
}
AnimalModel::AnimalRoles MultipleSortFilterProxyModel::sorterRow2() const
{
return m_sorterRow2;
}
void MultipleSortFilterProxyModel::setSorterRow2(AnimalModel::AnimalRoles newFilterRow2)
{
if (m_sorterRow2 == newFilterRow2)
return;
m_sorterRow2 = newFilterRow2;
emit sorterRow2Changed();
}
bool MultipleSortFilterProxyModel::multipleSortEnable() const
{
return m_multipleSortEnable;
}
void MultipleSortFilterProxyModel::setMultipleSortEnable(bool newMultipleSortEnable)
{
if (m_multipleSortEnable == newMultipleSortEnable)
return;
m_multipleSortEnable = newMultipleSortEnable;
emit multipleSorterEnableChanged();
}
main.cpp
#include "model.h"
#include <QGuiApplication>
#include <qqmlengine.h>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "MultipleSortFilterProxyModel.h"
//![0]
int main(int argc, char ** argv)
{
QGuiApplication app(argc, argv);
AnimalModel model;
model.addAnimal(Animal("ho", "Con", 80, 10));
model.addAnimal(Animal("ho", "To", 90, 30));
model.addAnimal(Animal("ho", "Vua", 80, 20));
model.addAnimal(Animal("meo", "Con", 25, 5));
model.addAnimal(Animal("meo", "To", 30, 10));
model.addAnimal(Animal("meo", "Vua", 28, 8));
model.addAnimal(Animal("huou", "Con", 200, 10));
model.addAnimal(Animal("huou", "To", 200, 15));
model.addAnimal(Animal("huou", "Vua", 200, 12));
model.addAnimal(Animal("ho", "Con", 90, 10));
MultipleSortFilterProxyModel *sortController = new MultipleSortFilterProxyModel;
sortController->setDynamicSortFilter(true);
sortController->setSourceModel(&model);
sortController->setSortRole((int)AnimalModel::AnimalRoles::TypeRole);
sortController->setMultipleSortEnable(false);
sortController->setSorterRow1(AnimalModel::AnimalRoles::AgeRole);
sortController->setSorterRow2(AnimalModel::AnimalRoles::HeightRole);
sortController->sort(0);
// sortController.setFilterRegExp(QString(""));
sortController->setDynamicSortFilter(false);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("myModel", &model);
ctxt->setContextProperty("myModelSortFilter", sortController);
//![0]
view.setSource(QUrl("qrc:view.qml"));
view.show();
return app.exec();
}
view.qml
import QtQuick 2.0
//![0]
ListView {
width: 200; height: 250
// model: myModel
model: myModelSortFilter
delegate: Text { text: "Animal: " + ageR + " , " + heightR + " , " + type + ", " + size}
}
List không ở dạng sắp xếp sẽ như sau:
...
sortController->setMultipleSortEnable(false);
...
// sortController->sort(0);
"ho", "Con", 80, 10
"ho", "To", 90, 30
"ho", "Vua", 80, 20
"meo", "Con", 25, 5
"meo", "To", 30, 10
"meo", "Vua", 28, 8
"huou", "Con", 200, 10
"huou", "To", 200, 15
"huou", "Vua", 200, 12
"ho", "Con", 90, 10
List sắp xếp bằng cách lessThan mặc định với sortRole() = AnimalRoles::TypeRole:
Cột được chọn để sắp xếp là String, nên mặc định sẽ sắp xếp theo bảng chữ cái abc ~ z
...
sortController->setSortRole((int)AnimalModel::AnimalRoles::TypeRole);
...
sortController->setMultipleSortEnable(false);
...
sortController->sort(0);
"ho", "Con", 80, 10
"ho", "To", 90, 30
"ho", "Vua", 80, 20
"ho", "Con", 90, 10
"huou", "Con", 200, 10
"huou", "To", 200, 15
"huou", "Vua", 200, 12
"meo", "Con", 25, 5
"meo", "To", 30, 10
"meo", "Vua", 28, 8
List được sắp xếp khi enable hàm lessThan đã được override lại sẽ thu được kết quả thú vị như sau:
(ở đây ta chọn sắp xếp ưu tiên theo cột AnimalRoles::AgeRole, sau đó đến AnimalRoles::HeightRole)
...
sortController->setMultipleSortEnable(true);
...
sortController->sort(0);
"meo", "Con", 25, 5
"meo", "Vua", 28, 8
"meo", "To", 30, 10
"ho", "Con", 80, 10
"ho", "Con", 90, 10
"huou", "Con", 200, 10
"huou", "Vua", 200, 12
"huou", "To", 200, 15
"ho", "Vua", 80, 20
"ho", "To", 90, 30
Tuấn Ti Tiện