Qt pour Android : Base de données SQLitetvaira.free.fr/.../qt-android-base-donnees-sqlite.pdfSQLite...

Post on 26-Aug-2020

3 views 0 download

Transcript of Qt pour Android : Base de données SQLitetvaira.free.fr/.../qt-android-base-donnees-sqlite.pdfSQLite...

VersionPDFdudocumenthttp://tvaira.free.fr/dev/qt-android/qt-android-base-donnees-sqlite.htmlThierryVaira<tvaira@free.fr>2019(rev.1.0)

L’applicationàdévelopperdevrautiliserlatechniquediteembeddedSQL:lesinstructionsenlangageSQLserontincorporéesdanslecodesourced’unprogrammeécritdansunautrelangage(icileC++sousQtpourAndroid).

Lire:Activitébasesdedonnées

SQLiteestunmoteurdebasededonnéesrelationnelleaccessibleparlelangageSQLetintégréedanschaqueappareilAndroid.

Remarques:

Sil’applicationcréeunebasededonnées,celle-ciestpardéfautenregistréedanslerépertoire:/data/APP_NAME/databases/DATABASE_NAME .

Ilestpossibleensuitederécupérerlabasededonnéescrééeàpartirdel’émulateur adb :

$adb-dshell"run-ascom.example.tv.APP_NAMEcat/data/com.example.tv.APP_NAME/databases/DATABASE_NAME">bd.sqlite

Onpeutensuitel’ouvriraveclepluginSQLiteManagerdeFirefoxouavecl’application sqlitebrowser ousqliteman sousLinux.

IlexisteaussiunexemplepourunebasededonnéesMySQL.

SQLiteestunebibliothèqueécriteenCquiproposeunmoteurdebasededonnéesrelationnelleaccessibleparlelangageSQL.Contrairementauxserveursdebasesdedonnéestraditionnels,commeMySQLouPostgreSQL,saparticularitéestdenepasreproduireleschémahabituelclient-serveurmaisd’êtredirectementintégréeauxprogrammes.L’intégralitédelabasededonnées(déclarations,tables,indexetdonnées)eststockéedansunfichierindépendantdelaplateforme.SQLiteestlemoteurdebasededonnéesleplusdistribuéaumonde,grâceàsonutilisationdansdenombreuxlogicielsgrandpubliccommeFirefox,Skype,GoogleGears,danscertainsproduitsd’Apple,d’AdobeetdeMcAfeeetdanslesbibliothèquesstandardsdenombreuxlangagescommePHPouPython.Deparsonextrêmelégèreté(moinsde300Kio),ilestégalementtrèspopulairesurlessystèmesembarqués,notammentsurlaplupartdessmartphonesmodernes:l’iPhoneainsiquelessystèmesd’exploitationmobilesSymbianetAndroidl’utilisentcommebasededonnéesembarquée.

QtpourAndroid:BasededonnéesSQLite

SQLite

Qtfournitdenombreusesclassespourlagestiondesbasededonnées.Ilfaudraactiverlemoduledanssonfichierdeprojet .pro pourpouvoiraccéderauxclasses:

...QT+=sql...

Onutiliseraalorslaclasse QSqlDatabase quipermetlaconnexionàunebasededonnées.

Lien:LaclasseQSqlDatabaseQt5(en)

Etensuitelaclasse QSqlQuery pourexécuterdesrequêtesSQL:

Lien:LaclasseQSqlQueryQt5(en)

Onpeutassurerledéploiementdelabasededonnéesliéeàl’applicationdanssonfichierdeprojet .pro :

Android(apk):

...deployment.files+=mabase.sqlitedeployment.path=/assets/dbINSTALLS+=deployment

Desktop(build):

...#copielabasededonnéesdansledossierbuildCONFIG+=file_copiesCOPIES+=bdbd.files=mabase.sqlitebd.path=$$OUT_PWD/bd.base=$$PWD/

Pourl’exemple,onvacréerunebasededonnées mabase.sqlite comprenant2tables:

latable releves quicontiendraladescriptiondesrelevéslatable mesures quicontiendralesmesureshorodatéesdetempératurespourchaquerelevé

APIQt

Déploiement

Exemple

pragmaforeign_keys=on;

CREATETABLEreleves(idReleveINTEGERPRIMARYKEYAUTOINCREMENTNOTNULL,descriptionVARCHAR(255)NULL);

CREATETABLEmesures(idMesureINTEGERPRIMARYKEYAUTOINCREMENTNOTNULL,idReleveINTEGERNOTNULL,horodatageDATETIME,temperatureDOUBLENULL,CONSTRAINTfk_mesures_1FOREIGNKEY(idReleve)REFERENCESreleves(idReleve)ONDELETECASCADE);

Pourlestests,onvainsérerquelquesmesures:

INSERTINTOreleves(idReleve,description)VALUES(1,'Relevédelaserre1');INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0108:00:00',35.23);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0108:30:00',35.1);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0109:00:00',34.45);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0109:30:00',35.02);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0110:00:00',35.53);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0110:30:00',35.24);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0111:00:00',35.25);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0111:30:00',35.7);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0112:00:00',35.61);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0112:30:00',35.65);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0113:00:00',35.75);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0113:30:00',36.03);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0114:00:00',36.1);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0114:30:00',36.05);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0115:00:00',36.33);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(1,'2017-04-0115:30:00',36.5);

INSERTINTOreleves(idReleve,description)VALUES(2,'Relevédelaserre2');INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0108:00:00',35.1);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0108:30:00',35.15);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0109:00:00',35.25);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0109:30:00',35.05);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0110:00:00',35.35);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0110:30:00',35.24);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0111:00:00',35.65);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0111:30:00',35.7);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0112:00:00',35.71);

INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0112:30:00',35.75);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0113:00:00',35.65);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0113:30:00',35.93);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0114:00:00',36.1);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0114:30:00',36.15);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0115:00:00',36.4);INSERTINTOmesures(idReleve,horodatage,temperature)VALUES(2,'2017-04-0115:30:00',36.35);

EtquelquesrequêtesSQLquel’onutiliseraensuitedanslecode:

//lalistedesrelevésSELECTdescriptionFROMrelevesORDERBYreleves.descriptionASC

//Les5dernièresmesuresdurelevé1SELECT*FROM(SELECTmesures.horodatage,mesures.temperatureFROMmesuresINNERJOINrelevesONreleves.idReleve=mesures.idReleveWHEREreleves.description='Relevédelaserre1'ORDERBYmesures.horodatageDESCLIMIT5)tmpORDERBYhorodatageASCLIMIT5

//Lamoyennedes5dernièresmesuresdurelevé1SELECTAVG(temperature)FROM(SELECTmesures.horodatage,mesures.temperatureFROMmesuresINNERJOINrelevesONreleves.idReleve=mesures.idReleveWHEREreleves.description='Relevédelaserre1'ORDERBYmesures.horodatageDESCLIMIT5)tmpORDERBYhorodatageASCLIMIT5

OncréeunprojetQtQMLQuick:

ProjetQt

Aufinal,lefichier .pro seralesuivant:

QT+=quickquickcontrols2sqlCONFIG+=c++11

HEADERS+=\releve.h\mesure.h

SOURCES+=main.cpp\releve.cpp\mesure.cpp

RESOURCES+=qml.qrc\icons/MonApplicationBD/index.theme\$$files(icons/*.png,true)

unix:!macx:{android:{DISTFILES+=\android-sources/AndroidManifest.xmlANDROID_PACKAGE_SOURCE_DIR=$$PWD/android-sources#déploielabasededonnéesavecl'apkdeployment.files+=mabase.sqlitedeployment.path=/assets/dbINSTALLS+=deployment}!android:{#copielabasededonnéesdansledossierbuildCONFIG+=file_copies

COPIES+=bdbd.files=mabase.sqlitebd.path=$$OUT_PWD/bd.base=$$PWD/}}

Onaajoutéuneclasse Releve quiauralacharged’ouvirlabasededonnées mabase.sqlite ,d’effectuerles3requêtesSQLetdefournirlesrésultatsàl’IHM.

Laclasse Releve héritede QObject afindebénificierdesmécanismesQt:

#ifndefRELEVE_H#defineRELEVE_H

#include<QObject>

classReleve:publicQObject{Q_OBJECT

public:explicitReleve(QObject*parent=nullptr);

private:

signals:

publicslots:};

#endif//RELEVE_H

Oninstancieraunobjet Releve quel’onrendraaccessibleàpartirdel’IHMdécritedans qmain.qml .Celaserafaitdanslefichier main.cpp avec setContextProperty() :

#include<QGuiApplication>#include<QIcon>#include<QQmlApplicationEngine>#include<QQmlContext>

#include"releve.h"

intmain(intargc,char*argv[]){QGuiApplication::setApplicationName("MonApplicationBD");QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplicationapp(argc,argv);

QIcon::setThemeName("MonApplicationBD");

QQmlApplicationEngineengine;engine.rootContext()->setContextProperty("Releve",newReleve());engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if(engine.rootObjects().isEmpty())

InteractionsC++/QML

return-1;

returnapp.exec();}

Dans qmain.qml ,onaccèderaànotreobjetavecl’identifiant Releve .

L’interactionentrelaclasseC++ Releve etl’IHM main.qml serabaséesur3mécanismes:

lespropriétésquisedéclarentaveclamacro Q_PROPERTY enC++lesappelsdeméthodesquiserontdéclaréesaveclepréfixe Q_INVOKABLE enC++lessignauxquiserontconnectésavecl’élément Connections enQML

Onajoutera4propriétés:

erreurConnexion :pourgérerl’erreurd’ouverturedelabasededonnéeslisteReleves :pourrécupérerlalistedesrelevésdelatable relevesmesures :lesmesuresd’unrelevésouslaformed’unelistede Mesuremoyenne :lamoyennedel’ensembledes mesures

Les3dernièrespropriétésnesontaccessiblesqu’enlectureauxquellesonassocieraunaccesseurpourREAD .

Q_PROPERTY(boolerreurConnexionMEMBERerreurConnexionNOTIFYerreurChanged)Q_PROPERTY(QStringListlisteRelevesREADgetRelevesNOTIFYlisteRelevesChanged)Q_PROPERTY(QVariantmesuresREADgetMesuresNOTIFYmesuresUpdated)Q_PROPERTY(QStringmoyenneREADgetMoyenneNOTIFYmoyenneUpdated)

Onpourraparexempleaccéderàlapropriété moyenne directementenQMLcommececi:

labelMoyenne.text="Moyenne:"+Releve.moyenne+"°C";

CôtéC++,ilfaudradéfinirl’accesseur getMoyenne() quipermettradelire(READ)lapropriétédumêmenom:

QStringReleve::getMoyenne(){returnmoyenne;}

Oncréera3méthodespubliquesappelablesdepuisQML:

lireReleve() et lireMoyenneReleve() quirecevrontenparamètreslenomdurelevéetlenombredesdernièresmesuresdésirées(0signifieratouteslesmesuresdurelevé)executerRequete() quirecevraenparamètreunerequêteSQLdetype INSERT , UPDATE ouDELETE

Q_INVOKABLEboollireReleve(QStringreleve,intnb=0);Q_INVOKABLEvoidlireMoyenneReleve(QStringreleve,intnb=0);Q_INVOKABLEboolexecuterRequete(QStringrequete);

CôtéQML,onpourraapellerunedecesméthodesdelamanièresuivante:

Button{id:purgerReleveenabled:true;text:"Purgerlerelevé"onClicked:{Releve.executerRequete("DELETEFROMrelevesWHEREreleves.description='"+choixReleve.currentText+"'");//...}}

Onauraaussibesoindessigauxsuivants:

signals:voiderreurChanged();voidlisteRelevesChanged();voidmesuresUpdated();voidmesuresErreur();voidmoyenneUpdated();...

EnQML,ilestpossibledeconnecterunslotàunsignalde target enlepréfixantavec on commececi:

Connections{target:ReleveonMoyenneUpdated:{labelMoyenne.text="Moyenne:"+Releve.moyenne+"°C";labelMoyenne.color="#0000FF"labelMoyenne.visible=true}onMesuresErreur:{labelMoyenne.text="Aucunemesurepourcerelevé!";labelMoyenne.color="#FF0000"labelMoyenne.visible=truelisteMesures.visible=false}}

Onajoutelesmembresprivéssuivants:

private:QSqlDatabasedb;boolerreurConnexion;QStringListreleves;QList<QObject*>mesures;QStringmoyenne;...

Aufinal,ladéclarationdelaclasse Releve estlasuivante:

#ifndefRELEVE_H#defineRELEVE_H

#include<QObject>

#include<QString>#include<QtSql/QtSql>#include<QSqlDatabase>

#defineNOM_BDQString("mabase.sqlite")

classReleve:publicQObject{Q_OBJECTQ_PROPERTY(boolerreurConnexionMEMBERerreurConnexionNOTIFYerreurChanged)Q_PROPERTY(QStringListlisteRelevesREADgetRelevesNOTIFYlisteRelevesChanged)Q_PROPERTY(QVariantmesuresREADgetMesuresNOTIFYmesuresUpdated)Q_PROPERTY(QStringmoyenneREADgetMoyenneNOTIFYmoyenneUpdated)

public:explicitReleve(QObject*parent=nullptr);virtual~Releve();

Q_INVOKABLEboollireReleve(QStringreleve,intnb=0);Q_INVOKABLEvoidlireMoyenneReleve(QStringreleve,intnb=0);Q_INVOKABLEboolexecuterRequete(QStringrequete);QStringListgetReleves();QVariantgetMesures();QStringgetMoyenne();

private:QSqlDatabasedb;boolerreurConnexion;QStringListreleves;QList<QObject*>mesures;QStringmoyenne;

boolrecuperer(QStringrequete,QStringList&donnees);boolrecuperer(QStringrequete,QVector<QStringList>&donnees);boolrecuperer(QStringrequete,QString&donnee);boolcopier(QFile&sfile,QFile&dfile);boolremplacer(QFile&sfile,QFile&dfile);boolestBDPresente(QStringBD);

signals:voiderreurChanged();voidlisteRelevesChanged();voidmesuresUpdated();voidmesuresErreur();voidmoyenneUpdated();

publicslots:};

#endif//RELEVE_H

Dansleconstructeurdelaclasse Releve ,onassureral’ouverturedelabasededonnéesSQLite.

Onpréciseratoutd’abordaveclaméthodestatique addDatabase() letype QSQLITE .

SousAndroid,ilfaudrapréalablementcopierlabasededonnéesdepuisl’apk(“assets:/db”)àlaracinede

BasededonnéesSQLite

l’applicationpuisluidonnerlesdroits.

Remarque:sil’applicationAndroidestdéjàinstallée,labasededonnéesneserapas“re-copier”.Sivoussouhaitezréinstallerlabasededonnées,vousdevezsoitdésinstallerl’applicationAndroidsoitappelerlaméthode remplacer() quisupprimerad’abordlefichier mabase.sqlite avantdelecopier.

Ensuite,onfixeralenomdelabasededonnéesavec setDatabaseName() etonl’ouvriraavec open() .

Releve::Releve(QObject*parent):QObject(parent),erreurConnexion(false){db=QSqlDatabase::addDatabase("QSQLITE");

QFilesfile(QString("assets:/db")+QString("/"+NOM_BD));QFiledfile(QString("./"+NOM_BD));

copier(sfile,dfile);//ou://remplacer(sfile,dfile);

if(estBDPresente(QString("./")+NOM_BD)){db.setDatabaseName(QString("./")+NOM_BD);db.open();erreurConnexion=false;QSqlQueryr;r.exec("pragmaforeign_keys=on;");}else{erreurConnexion=true;}

emiterreurChanged();}

boolReleve::copier(QFile&sfile,QFile&dfile){if(sfile.exists()){returnsfile.copy(dfile.fileName());}returnfalse;}

boolReleve::remplacer(QFile&sfile,QFile&dfile){boolretour;

//supprimelefichierdestinationif(sfile.exists()){if(dfile.exists()){retour=dfile.remove();if(!retour){returnfalse;}

}}

returncopier(sfile,dfile);}

boolReleve::estBDPresente(QStringBD){QFilefichier(BD);

returnfichier.exists();}

Ici,lesignal erreurChanged() estutilepourprévenirl’utilisateursiunproblèmed’ouvertureaeulieu.Danscecas,onafficherauneboîtededialogeavecunmessaged’erreur:

...onErreurChanged:{if(Releve.erreurConnexion){messageErreur.text=qsTr("Problèmed'ouverturedelabasededonnées!")erreurDialog.open()}}

...

Dialog{id:erreurDialogx:(parent.width-width)/2y:(parent.height-height)/2standardButtons:Dialog.Closetitle:"Erreur"Label{id:messageErreurtext:""}}

PourexécuterdesrequêtesSQL,onaurabesoind’unobjet QSqlQuery .IlfaudradistinguerlesrequêtesSQL SELECT ,quiretournentdesrésultats,desrequêtes INSERT , UPDATE ou DELETE quineproduisentpasderésultatsenretour.

Onpasserapardesméthodesprivées:

executerRequete() quirecevraenparamètreunerequêteSQLdetype INSERT , UPDATE ouDELETErecuperer() quirecevraenparamètreunerequêteSQLdetype SELECT etuntypepourstockerlesdonnéessélectionnéesparlarequête.Onsurchargeralaméthodepourlestypessuivants: QString ,QStringList et QVector<QStringList> .

boolReleve::executerRequete(QStringrequete){QSqlQueryr;

if(db.isOpen()){boolretour=r.exec(requete);returnretour;}returnfalse;}

boolReleve::recuperer(QStringrequete,QStringList&donnees)

{QSqlQueryr;boolretour;

if(db.isOpen()){retour=r.exec(requete);if(retour){//pourchaqueenregistrementwhile(r.next()){//onstockel'enregistrementdansleQStringListdonnees<<r.value(0).toString();}returntrue;}else{returnfalse;}}elsereturnfalse;}

boolReleve::recuperer(QStringrequete,QVector<QStringList>&donnees){QSqlQueryr;boolretour;QStringListdata;

if(db.isOpen()){retour=r.exec(requete);if(retour){//pourchaqueenregistrementwhile(r.next()){//onrécupèresousformedeQStringlavaleurdetousleschampssélectionnés//etonlesstockedansunelistedeQStringfor(inti=0;i<r.record().count();i++)data<<r.value(i).toString();

//onstockel'enregistrementdansleQVectordonnees.push_back(data);

//oneffacelalistedeQStringpourleprochainenregistrementdata.clear();}returntrue;}else{returnfalse;}}elsereturnfalse;

}

boolReleve::recuperer(QStringrequete,QString&donnee){QSqlQueryr;boolretour;

if(db.isOpen()){retour=r.exec(requete);if(retour){//onsepositionnesurl'enregistrementr.first();

//onvérifiel'étatdel'enregistrementretournéif(!r.isValid()){returnfalse;}

//onrécupèresousformedeQStringlavaleurduchampif(r.isNull(0)){returnfalse;}donnee=r.value(0).toString();returntrue;}else{returnfalse;}}elsereturnfalse;}

L’utilisationdesméthodes recuperer() seferaàpartirdesméthodespubliquessuivantes:

getReleves() quirécuperalalistedesrelevés

lireReleve() quirecevraenparamètreslenomdurelevéetlenombredesdernièresmesuresdésirées(0signifieratouteslesmesuresdurelevé)etquifabriqueralalistedes Mesure

lireMoyenneReleve() quirecevrontenparamètreslenomdurelevéetlenombredesdernièresmesuresàprendreencomptedanslecalculdelamoyenne(0signifieratouteslesmesuresdurelevé)etquiaffecteral’attribut moyenne

QStringListReleve::getReleves(){if(!erreurConnexion){releves.clear();recuperer("SELECTdescriptionFROMrelevesORDERBYreleves.descriptionASC",releves);}

returnreleves;

}

boolReleve::lireReleve(QStringreleve,intnb){if(erreurConnexion)returnfalse;

QVector<QStringList>relevesMesures;QStringrequete;

if(nb>0){//lesnbdernièresmesuresrequete="SELECT*FROM(SELECTmesures.horodatage,mesures.temperatureFROMmesuresINNERJOINrelevesONreleves.idReleve=mesures.idReleveWHEREreleves.description='"+releve+"'ORDERBYmesures.horodatageDESCLIMIT"+QString::number(nb)+")tmpORDERBYhorodatageASCLIMIT"+QString::number(nb);}else{//touteslesmesuresdurelevérequete="SELECTmesures.horodatage,mesures.temperatureFROMmesuresINNERJOINrelevesONreleves.idReleve=mesures.idReleveWHEREreleves.description='"+releve+"'ORDERBYmesures.horodatageASC";}

qDeleteAll(mesures);mesures.clear();

if(recuperer(requete,relevesMesures)){for(inti=0;i<relevesMesures.count();i++){Mesure*m=newMesure(QDateTime::fromString(relevesMesures.at(i).at(0),"yyyy-MM-ddHH:mm:ss"),relevesMesures.at(i).at(1).toDouble(),this);mesures.append(m);}

if(mesures.count()>0){emitmesuresUpdated();returntrue;}else{emitmesuresErreur();}}else{qDebug()<<Q_FUNC_INFO;emitmesuresErreur();}returnfalse;}

voidReleve::lireMoyenneReleve(QStringreleve,intnb){if(erreurConnexion)return;

QStringrequete;

if(nb>0){//lamoyennedesnbdernièresmesuresrequete="SELECTAVG(temperature)FROM(SELECTmesures.horodatage,mesures.temperatureFROMmesuresINNERJOINrelevesONreleves.idReleve=mesures.idReleveWHEREreleves.description='"+releve+"'ORDERBYmesures.horodatageDESCLIMIT"+QString::number(nb)+")tmpORDERBYhorodatageASCLIMIT"+QString::number(nb);}else{//lamoyennedetouteslesmesuresdurelevérequete="SELECTAVG(mesures.temperature)FROMmesuresINNERJOINrelevesONreleves.idReleve=mesures.idReleveWHEREreleves.description='"+releve+"'ORDERBYmesures.horodatageASC";}

if(recuperer(requete,moyenne)){emitmoyenneUpdated();}}

L’accèsaurelevéde Mesure seferaparl’accesseurdelapropriété listeReleves :

QVariantReleve::getMesures(){returnQVariant::fromValue(mesures);}

Oncréeraunenouvelleclasse Mesure pourstockerunemesurequiestcaractériséepar:

unetempérature(un double )etsonhorodatage(un QDateTime quel’onretournerasouslaformed’un QString )

#ifndefMESURE_H#defineMESURE_H

#include<QObject>#include<QDateTime>

classMesure:publicQObject{Q_OBJECTQ_PROPERTY(doubletemperatureREADgetTemperatureNOTIFYmesure)Q_PROPERTY(QStringhorodatageREADgetHorodatageNOTIFYmesure)

public:explicitMesure(QDateTimehorodatage,doubletemperature,QObject*parent=nullptr);

doublegetTemperature()const;QStringgetHorodatage()const;

private:QDateTimehorodatage;

doubletemperature;

signals:voidmesure();

publicslots:};

#endif//MESURE_H

L’interfaceutilisateurseradécriteenQMLavecQtQuickControls2.

Onutiliseraun AppWindow dans main.qml :

importQtQuick2.11importQtQuick.Window2.3importQtQuick.Controls2.2importQtQuick.Layouts1.3

ApplicationWindow{id:windowtitle:qsTr("MonApplicationBD")width:Screen.desktopAvailableWidthheight:Screen.desktopAvailableHeightpropertyboolerreur:Releve.erreurConnexionvisible:true

onErreurChanged:{if(Releve.erreurConnexion){messageErreur.text=qsTr("Problèmed'ouverturedelabasededonnées!")erreurDialog.open()}}header:ToolBar{RowLayout{spacing:20anchors.fill:parentLabel{id:titretext:qsTr("MonApplicationBD")font.pixelSize:20elide:Label.ElideRighthorizontalAlignment:Qt.AlignHCenterverticalAlignment:Qt.AlignVCenterLayout.fillWidth:true}ToolButton{id:toolButton2icon.name:"menu"onClicked:menu.open()}}}Menu{id:menu

L’IHM

x:parent.width-widthtransformOrigin:Menu.TopRightMenuItem{id:abouttext:"Àpropos"onTriggered:{aPropos.open()}}}Dialog{id:aProposmodal:truefocus:truetitle:"Àpropos"x:(window.width-width)/2y:window.height/6width:Math.min(window.width,window.height)/3*2contentHeight:message.heightLabel{id:messagewidth:aPropos.availableWidthtext:"Exempled'utilisationd'uneBasededonnéesSQLiteenQtQuickControls2."wrapMode:Label.Wrapfont.pixelSize:12}}Dialog{id:erreurDialogx:(parent.width-width)/2y:(parent.height-height)/2standardButtons:Dialog.Closetitle:"Erreur"Label{id:messageErreurtext:""}}footer:Label{width:parent.widthhorizontalAlignment:Qt.AlignRightpadding:10text:qsTr("©http://tvaira.free.fr")font.pixelSize:14font.italic:true}Releves{id:pageReleve}}

L’affichagedurelevéseferadans Releve.qml .Onlisteralesnomsderelevésdansun ComboBox etonajouteralapossibilitédechoisirlenombrededernièresmesuresquel’onsouhaite.

OncomplèteralaGUIavectroisboutons:

“Annuler”:pourmasquerl’affichagedesrésultats

“Voirlerelevé”:pourafficherlesmesures“Purgerlerelevé”:poursupprimerunrelevéetsesmesures

Onpositionneralesélémentsavec Column et Row .

importQtQuick2.9importQtQuick.Window2.2importQtQuick.Controls2.2importQtQuick.Layouts1.3

Page{width:parent.widthpadding:10

Connections{target:ReleveonMoyenneUpdated:{labelMoyenne.text="Moyenne:"+Releve.moyenne+"°C";labelMoyenne.color="#0000FF"labelMoyenne.visible=true}onMesuresErreur:{labelMoyenne.text="Aucunemesurepourcerelevé!";labelMoyenne.color="#FF0000"labelMoyenne.visible=truelisteMesures.visible=false}}Column{id:colonnePagewidth:parent.widthspacing:15Row{Label{id:introductionwrapMode:Label.Wraptext:"Relevésdemesuresdetempératuresdanslesserres"}}Row{ComboBox{id:choixRelevewidth:introduction.widthmodel:Releve.listeRelevesenabled:true;}}Row{spacing:20Label{wrapMode:Label.Wraptext:"Nbdernièresmesures:"anchors.verticalCenter:parent.verticalCenter}SpinBox{id:limiteNbMesuresenabled:choixReleve.count>0?true:false;value:5editable:true

}}Row{spacing:20Button{id:annuleReleveenabled:choixReleve.count>0?true:false;text:"Annuler"onClicked:{listeMesures.visible=falselabelMoyenne.visible=false}}Button{id:voirReleveenabled:choixReleve.count>0?true:false;text:"Voirlerelevé"onClicked:{if(Releve.lireReleve(choixReleve.currentText,limiteNbMesures.value)){listeMesures.visible=trueReleve.lireMoyenneReleve(choixReleve.currentText,limiteNbMesures.value);}}}Button{id:purgerReleveenabled:choixReleve.count>0?true:false;text:"Purgerlerelevé"onClicked:{Releve.executerRequete("DELETEFROMrelevesWHEREreleves.description='"+choixReleve.currentText+"'");listeMesures.visible=falselabelMoyenne.visible=false}}}Row{Mesures{id:listeMesuresvisible:falsewidth:colonnePage.width//onglets.widthheight:Screen.desktopAvailableHeight*0.45color:"#cfcfcf"}}Row{Label{id:labelMoyennevisible:falseanchors.margins:5wrapMode:Label.Wraptext:""}}}}

L’affichagedesmesuresseferadans Mesures.qml .Onutiliseraun ListView .

ListView permetunevueenlistedesélémentsfournisparun model ,icinotrerelevéaccessibleparlapropriété mesures .L’affichagedesélémentsdelalisteseraprisenchargeparun delegate .Ledéléguéfournitunmodèledéfinissantchaqueélémentinstanciéparla ListView .Letype ItemDelegate utiliséiciestl’élémentdevuestandard.

La ListView étantdéfilabledanslavueenfonctiondunombredemesures,onajouteraunScrollIndicator .

importQtQuick2.9importQtQuick.Window2.2importQtQuick.Controls2.2importQtQuick.Layouts1.3importQtQuick.Controls.Material2.1

Rectangle{ListView{id:listeMesuresanchors.fill:parentspacing:10anchors.margins:5model:Releve.mesuresdelegate:ItemDelegate{width:parent.widthheight:colonne.implicitHeightColumn{id:colonnepadding:5Text{text:model.modelData.horodatage;}Text{text:'<b>Température:'+model.modelData.temperature+'°C</b>';font.italic:true}}}focus:trueclip:trueScrollIndicator.vertical:ScrollIndicator{}}}

Onobtient:

Capturesd’écran

Lien:MonApplicationBD.zip

QtfournitlemoduleQtChartspourdessinerdesgraphiques.

Voir:QtpourAndroid:dessinerdesgraphiques

Codesource

Voiraussi

Ilestaussipossibled’utiliserunebasededonnéesMySQL.

Voir:QtpourAndroid:BasededonnéesMySQL

http://tvaira.free.fr/