Qt小例子学习51 - 网格分页过滤器
Filtro.h
#ifndef FILTRO_H
#define FILTRO_H
#include <QSortFilterProxyModel>
class Filtro : public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(bool showOnlyUserRic READ showOnlyUserRic WRITE setshowOnlyUserRic
NOTIFY showOnlyUserRicChanged)
Q_PROPERTY(QString string READ string WRITE setstring NOTIFY stringChanged)
public:
Filtro(QObject *parent = 0);
~Filtro();
Q_INVOKABLE void setStringaFiltro(QString string);
public slots:
void setshowOnlyUserRic(bool showOnlyUserRic);
bool showOnlyUserRic() const;
QString string() const;
void setstring(QString string);
signals:
void showOnlyUserRicChanged();
void oggettiFiltChanged();
void stringChanged();
private:
bool m_showOnlyUserRic;
int m_oggettiFilt;
QString m_string;
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
// QSortFilterProxyModel interface
bool lessThan(const QModelIndex &source_left,
const QModelIndex &source_right) const;
};
#endif // FILTRO_H
Filtro.cpp
#include "filtro.h"
#include "modello.h"
#include <QDebug>
Filtro::Filtro(QObject *parent)
: QSortFilterProxyModel(parent), m_showOnlyUserRic(false), m_oggettiFilt(0),
m_string("")
{
setFilterRole(modello::RicettaUtente);
setSortRole(modello::NomeRicetta);
setDynamicSortFilter(true);
sort(0);
}
Filtro::~Filtro() {}
void Filtro::setStringaFiltro(QString string)
{
this->setFilterCaseSensitivity(
Qt::CaseInsensitive); // lo rendo case insensitive
this->setFilterFixedString(string);
}
QString Filtro::string() const { return m_string; }
void Filtro::setstring(QString string)
{
if (m_string == string)
return;
m_string = string;
emit stringChanged();
invalidateFilter(); // fa rivalutare il filtro e quindi entra di nuovo in
// filterAcceptsRows()
}
bool Filtro::showOnlyUserRic() const { return m_showOnlyUserRic; }
void Filtro::setshowOnlyUserRic(bool showOnlyUserRic)
{
if (m_showOnlyUserRic == showOnlyUserRic)
return;
m_showOnlyUserRic = showOnlyUserRic;
emit showOnlyUserRicChanged();
invalidateFilter(); // fa rivalutare il filtro e quindi entra di nuovo in
// filterAcceptsRows()
}
bool Filtro::filterAcceptsRow(int source_row,
const QModelIndex &source_parent) const
{
QRegExp regEx("*" + string() + "*");
regEx.setPatternSyntax(QRegExp::Wildcard);
regEx.setCaseSensitivity(Qt::CaseInsensitive);
if (showOnlyUserRic() == true)
{
// se devo visualizzare solo
QModelIndex ricUtente = sourceModel()->index(
source_row, 0,
source_parent); // vado a leggere singolarmente ogni riga del modello
QString stringaConfronto =
sourceModel()->data(ricUtente, modello::NomeRicetta).toString();
if (sourceModel()->data(ricUtente, modello::RicettaUtente) == true &&
stringaConfronto.contains(regEx) == true)
{
// se è ricetta utente
return true;
}
else
{
return false;
}
}
else
{
QModelIndex ricUtente = sourceModel()->index(
source_row, 0,
source_parent); // vado a leggere singolarmente ogni riga del modello
QString stringaConfronto =
sourceModel()->data(ricUtente, modello::NomeRicetta).toString();
if (stringaConfronto.contains(regEx))
return true;
// if(sourceModel()->data(ricUtente,modello::NomeRicetta)== string()
// ) //confronto il roles ricetta utene x filtrare il
// {
// return true;
// }
return false;
}
}
bool Filtro::lessThan(const QModelIndex &source_left,
const QModelIndex &source_right) const
{
bool leftData = sourceModel()
->data(source_left, modello::RicettaUtente)
.toBool(); // vado a leggere il valore RicettaUtente e lo
// trasformo in bool
bool rightData = sourceModel()
->data(source_right, modello::RicettaUtente)
.toBool(); // vado a leggere il valore RicettaUtente e lo
// trasformo in bool
if (leftData != rightData)
return leftData;
else
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
color: "#465775"
TextField {
id:textField
anchors.top: parent.top ;anchors.topMargin: 10;
anchors.horizontalCenter: parent.horizontalCenter
placeholderText: "Cerca Ricetta"
width:parent.width*0.95
font.pointSize: 12
background: Rectangle {
radius: 5
width: parent.width
implicitHeight: 30
}
onTextChanged: {
//myModelFiltrato.setStringaFiltro(text)
myModelFiltrato.string=text
}
}
GridView {
id:grid
property int indiceCorretto: 0
function updateModel(){
// alternativa a mettere il datachanged nel metodo add ricetta
model=0
model=myModel
}
function increseCurrIndex(index){
//calcolo il giusto current index per dare effetto di gridView con paginazione
if(Math.floor(index/6)==0)
grid.indiceCorretto= 0 // altrimenti ho valore negativo
else
grid.indiceCorretto=(Math.floor(index/6))*6
}
onCountChanged: {
// faccio queste operazioni per aggiornare il numero dei p.indicator quando filtro le tile
myModel.numIndicatori=grid.count
// per rendere attivo il click sull'ultima mattonella quando
myModel.totTile=grid.count
}
width:parent.width
height: parent.height* 0.70
highlightMoveDuration: 500
highlightFollowsCurrentItem: true
anchors.horizontalCenter: parent.horizotalCenter
anchors.verticalCenter: parent.verticalCenter
cellWidth: parent.width/3
cellHeight: parent.height/3
model: myModelFiltrato
delegate: tile
onMovementEnded: {
increseCurrIndex(currentIndex)
currentIndex=indiceCorretto
}
flow: GridView.FlowTopToBottom
flickDeceleration: 4500
clip: true
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick
snapMode: GridView.SnapToRow
highlightRangeMode: GridView.StrictlyEnforceRange
preferredHighlightBegin: 0
preferredHighlightEnd: 0
}
Component {
id: tile
Item{
width: grid.cellWidth
height: grid.cellHeight
Rectangle{
color: colore//"#EF6F6C"//Qt.rgba(Math.random(),Math.random(),Math.random(),0.90)
width:parent.height*0.90
height: parent.height*0.90
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
border.width: 2
border.color:"#FFFFFF"
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left:parent.left
anchors.right: parent.right
horizontalAlignment: Text.AlignHCenter
text:nomeRicetta + " "+index.toString()
font.pointSize: 17
color: "#ffffff"
wrapMode: Text.WordWrap
}
MouseArea {
anchors.fill: parent
onClicked: {
if(index==myModel.totTile-1)
myModel.addRicetta("pane","green",2,true)
}
onPressAndHold: {
if (index != myModel.totTile-1){
myModel.removeRows(index,1)
}
}
}
}
}
}
// page indicator circolare
PageIndicator {
id: control
visible: false
interactive: false
count: myModel.numIndicatori
currentIndex: Math.floor(grid.currentIndex/6)
anchors.bottom: parent.bottom ; anchors.bottomMargin:25
anchors.horizontalCenter: parent.horizontalCenter
delegate: Rectangle {
implicitWidth: 8
implicitHeight: 8
radius: width / 2
color: "#ffffff"
opacity: index === control.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
MouseArea {
anchors.fill: parent
onClicked: grid.currentIndex = index*6
}
}
}
// page indicator quadrato
PageIndicator {
id: control1
visible: true
interactive: false
count: myModel.numIndicatori
currentIndex: Math.floor(grid.currentIndex/6)
anchors.bottom: parent.bottom ; anchors.bottomMargin:25
anchors.horizontalCenter: parent.horizontalCenter
delegate: Rectangle {
implicitWidth: 25
implicitHeight: 25
//radius: width / 2
color: "#ffffff"
opacity: index === control1.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Text{
anchors.centerIn: parent
text:(index+1).toString()
color: "#465775"
}
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
MouseArea {
anchors.fill: parent
onClicked: grid.currentIndex = index*6
}
}
}
CheckBox {
text: qsTr("Mostra solo ricetteUtente")
checked: myModelFiltrato.showOnlyUserRic
onClicked: {
myModelFiltrato.showOnlyUserRic = !myModelFiltrato.showOnlyUserRic
}
anchors.left: parent.left; anchors.leftMargin: 15
anchors.bottom: parent.bottom ; anchors.bottomMargin: 15
}
}
modello.h
#ifndef MODELLO_H
#define MODELLO_H
#include <QAbstractItemModel>
#include <QObject>
#include <QSortFilterProxyModel>
class Tile
{
public:
Tile(const QString &nomeRicetta, const QString &colore, const int &durata,
const bool &ricettaUtente);
QString nomeRicetta() const;
QString colore() const;
int durata() const;
bool ricettaUtente() const;
private:
QString m_nomeRicetta;
QString m_colore;
int m_durata;
bool m_ricettaUtente;
};
class modello : public QAbstractListModel
{
Q_OBJECT
public:
explicit modello(QObject *parent = 0);
~modello();
Q_PROPERTY(qint16 numIndicatori READ numIndicatori WRITE setnumIndicatori
NOTIFY numIndicatoriChanged)
Q_PROPERTY(qint16 totTile READ totTile WRITE setTotTile NOTIFY
totTileChanged) // property x fare in modo che solo l'ultima
// tile sia cliccabile
enum TileRoles
{
NomeRicetta = Qt::UserRole + 1,
Colore,
Durata,
RicettaUtente
};
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const;
Q_INVOKABLE bool
removeRows(int row, int count,
const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE;
Q_INVOKABLE void addRicetta(QString nomeRic, QString color, int durata,
bool ricettaUtente);
qint16 numIndicatori() const;
qint16 totTile() const;
public slots:
void setnumIndicatori(qint16 numIndicatori);
void setTotTile(qint16 totTile);
signals:
void numIndicatoriChanged();
void totTileChanged();
private:
QList<Tile> m_ricette;
qint16 m_numIndicatori;
qint16 m_totTile;
};
#endif // MODELLO_H
modello.cpp
#include "modello.h"
Tile::Tile(const QString &nomeRicetta, const QString &colore, const int &durata,
const bool &ricettaUtente)
: m_nomeRicetta(nomeRicetta), m_colore(colore), m_durata(durata),
m_ricettaUtente(ricettaUtente) {}
QString Tile::nomeRicetta() const { return m_nomeRicetta; }
QString Tile::colore() const { return m_colore; }
int Tile::durata() const { return m_durata; }
bool Tile::ricettaUtente() const { return m_ricettaUtente; }
modello::modello(QObject *parent)
: QAbstractListModel(parent), m_numIndicatori(1), m_totTile(1)
{
addRicetta("Aggiungi Ricetta", "grey", 1, true);
}
modello::~modello() {}
int modello::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_ricette.count();
}
QVariant modello::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_ricette.count())
return QVariant();
const Tile &tiletemp = m_ricette[index.row()];
switch (role)
{
case NomeRicetta:
return tiletemp.nomeRicetta();
break;
case Colore:
return tiletemp.colore();
break;
case Durata:
return tiletemp.durata();
break;
case RicettaUtente:
return tiletemp.ricettaUtente();
break;
default:
return QVariant();
break;
}
}
QHash<int, QByteArray> modello::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NomeRicetta] = "nomeRicetta";
roles[Colore] = "colore";
roles[Durata] = "durata";
roles[RicettaUtente] = "ricettaUtente";
return roles;
}
bool modello::removeRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);
beginRemoveRows(parent, row, row + count - 1); // row row perchè ne elimino 1
m_ricette.removeAt(row);
endRemoveRows();
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(rowCount() - 1, 0);
emit dataChanged(top, bottom);
setnumIndicatori(rowCount()); // passo il totale degli elementi
setTotTile(rowCount()); // aggiorno la property con il totale mi serve il suo
// emit x il qml
return true;
}
void modello::addRicetta(QString nomeRic, QString color, int durata,
bool ricettaUtente)
{
Tile tileTmp(nomeRic, color, durata, ricettaUtente);
beginInsertRows(QModelIndex(), rowCount(), rowCount());
if (rowCount() == 0)
m_ricette << tileTmp;
else
m_ricette.insert(rowCount() - 1, tileTmp);
endInsertRows();
// mi serve per aggiornare in automatico il qml
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(rowCount() - 1, 0);
emit dataChanged(top, bottom);
setnumIndicatori(rowCount()); // passo il totale degli elementi
setTotTile(rowCount()); // aggiorno la property con il totale mi serve il suo
// emit x il qml
}
qint16 modello::numIndicatori() const { return m_numIndicatori; }
qint16 modello::totTile() const { return m_totTile; }
void modello::setnumIndicatori(qint16 numIndicatori)
{
// divido x 6 perchè ho scelto di visualizzare 6 elemti alla volta
if (numIndicatori % 6 == 0)
m_numIndicatori = (numIndicatori / 6);
else
m_numIndicatori = (numIndicatori / 6) + 1;
emit numIndicatoriChanged();
}
void modello::setTotTile(qint16 totTile)
{
if (m_totTile == totTile)
return;
m_totTile = totTile;
emit totTileChanged();
}
main.cpp
#include "filtro.h"
#include "modello.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
modello mioModello;
// riempio il modello
for (int i = 0; i < 10; i++)
{
mioModello.addRicetta("Pane", "red", 5, false);
mioModello.addRicetta("Branzino", "orange", 3, true);
}
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&mioModello]()
{
mioModello.addRicetta("AAAA", "orange", 5, true);
});
timer.start(1000);
Filtro filtroSuModello;
filtroSuModello.setSourceModel(&mioModello);
filtroSuModello.setFilterRole(mioModello.NomeRicetta);
engine.rootContext()->setContextProperty("myModelFiltrato", &filtroSuModello);
engine.rootContext()->setContextProperty("myModel", &mioModello);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}