qt pour android : base de données sqlitetvaira.free.fr/.../qt-android-base-donnees-sqlite.pdfsqlite...
Post on 26-Aug-2020
3 Views
Preview:
TRANSCRIPT
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/
top related