Quelques questions fréquemment posées sur Qt …
Ce document est toujours en cours de rédaction.
Dernière modification : 17/05/2021 (rev. 6)
Vous pouvez consulter la FAQ Qt (fr) très complète du site developpez.com.
Qt est une bibliothèque logicielle orientée objet développée en C++. La bibliothèque logicielle fournie par Qt regroupe un ensemble de classes. Qt offre notamment des composants (widgets) permettant de concevoir des interfaces graphiques (GUI).
Remarque : Bien qu’écrite en C++, Qt peut être utilisée dans d’autres environnements comme Python par exemple.
Littéralement un “cadre de développement”. Qt peut être considéré comme un framework car il fournit un ensemble de classes C++ (formant une bibliothèque logicielle) ET une manière (un “cadre”) de les utiliser (notamment le mécanisme signal/slot propre à Qt).
C’est une interface de programmation d’application (API = Application Programming Interface) qui est un ensemble normalisé de classes et/ou de fonctions utilisables par un logiciel. L’API (et donc ses services) est offerte par une bibliothèque logicielle.
Une bibliothèque logicielle est une collection de fonctions prêtes à être utilisées par des programmes. Le plus souvent, les fonctions sont déjà “compilées” et la bibliothèque contient donc du code objet (du code “machine”). Globalement, les bibliothèques se distinguent des programmes exécutables par le simple fait qu’elles ne contiennent pas de point d’entrée main()
. Le mot « librairie » est souvent utilisé à tort. Il ne faut donc pas le faire !
Les bibliothèques sont manipulées par l’éditeur de lien (pour fabriquer un exécutable) et le système d’exploitation (pendant l’exécution du programme).
Une bibliothèque est dite statique si elle est destinée à être copiée dans le programme qui l’utilise pour former un seul et unique exécutable. L’exécutable obtenu est plus volumineux mais “autonome”. Il ne pourra pas être mis à jour (seulement en le refabriquant).
Une bibliothèque est dite partagée si elle est destinée à être associée au programme au moment où il est exécuté. On parle aussi de “bibliothèque à lien dynamique” ou plus simplement de “bibliothèque dynamique”. L’exécutable obtenu est moins volumineux mais il est dépendant de la présence des bibliothèques au moment de son exécution. Il pourra être mis à jour en faisant simplement évoluer les bibliothèques. Les bibliothèques peuvent être partagées entre plusieurs exécutables en même temps.
Pour identifier les bibliothèques dynamiques d’un exécutable, on peut utiliser :
ldd
sous GNU/LinuxRemarque : les fichiers d’extension .h
(ou .hh
ou .hpp
) ne sont pas à proprement parler des bibliothèques. Ce sont des fichiers d’en-têtes (header) contenant exclusivement des déclarations. En C/C++, les déclarations sont donc regroupées dans des fichiers d’en-têtes (header) et ne génèrent aucun code objet (code “machine”). Les déclarations énoncent seulement ce qui va exister en précisant les noms et les types.
Les extensions des fichiers de bibliothèques logicielles dynamiques :
.dll
(dynamic link library).so
(shared object)Remarque : les extensions des fichiers de bibliothèques statiques sont généralement .a
ou .lib
.
Lors de la fabrication de l’exécutable, c’est la phase d’édition de liens (linker) qui a la responsabilité de lier les bibliothèques. Il y aura donc soit une édition de liens dynamique (choix par défaut) soit une édition de liens statique. Avant, on utilisait l’utilitaire ld
. Maintenant, l’édition de liens est intégrée au compilateur avec des outils comme gcc
/g++
.
Un module Qt est un service logiciel (une “fonctionnalité” de la bibliothèque Qt) fourni sous la forme d’un ensemble de classes. Le module doit être “activé” dans la variable QT
du fichier .pro
d’un projet Qt pour pouvoir l’utiliser.
La bibliothèque logicielle Qt est donc modulaire. Généralement, le module Qt est fourni par une seule bibliothèque partagée (.dll
ou .so
) :
libQt5Core.so
pour le module core
, libQt5Gui.so
pour le module gui
et libQt5Widgets.so
pour le module widgets
sous GNU/LinuxQt5Core.dll
pour le module core
, Qt5Gui.dll
pour le module gui
et Qt5Widgets.dll
pour le module widgets
© WindowsDans le cas de conception d’interfaces graphiques (GUI) avec Qt5, les modules core
, gui
et widgets
sont obligatoires.
Par exemple, le module widgets
contient les classes QWidget
, QLabel
, QPushButton
, QComboBox
, … On peut remarquer que toutes les classes Qt sont préfixées d’un Q
. Elles héritent aussi toutes de la classe mère QObject
qui définit la notion d’un “objet” spécifique à Qt.
Les modules Qt sont nombreux (cela fait la richesse de cette API), on peut citer : sql
(accès aux base de données), network
(réseau), charts
(graphiques), etc …
Qt Creator est un environnement de développement intégré (EDI). Il a été créé avec le framework Qt. Il est censé faciliter la conception de programmes Qt.
Un environnement de développement intégré (EDI ou IDE pour Integrated Development Environment) est un ensemble d’outils intégrés dans un même logiciel et qui permettent de développer des programmes.
Il comporte généralement (et au minimum) :
En gagnant en expertise, le développeur recherchera et utilisera d’autres outils intégrés à un EDI pour le rendre plus efficace dans son travail. On peut citer sans aucun doute au moins un outil de travail collaboratif comme subversion (svn
) ou git
.
Qt est une bibliothèque logicielle. Qt Creator est un environnement de développement intégré (EDI).
Et cela n’a strictement rien à voir !
Il est très important de comprendre que Qt Creator n’est pas indispensable pour créer des programmes Qt. Un simple éditeur de texte (comme vim
), un compilateur C++ (comme g++
), un utilitaire (comme make
) et bien évidemment les bibliothèques et outils Qt (qmake
, moc
, uic
, …) suffisent pour fabriquer un programme Qt !
La programmation événementielle est une programmation basée sur les événements. Elle est utilisée principalement dans les environnements graphiques (GUI). C’est notamment le cas dans les systèmes d’exploitation avec un environnement graphique (comme © Windows par exemple).
La programmation événementielle est architecturée autour d’une boucle principale fournie et divisée en deux sections : la première section attend et détecte les événements, la seconde les gère.
Pour chaque événement à gérer (un clic de souris, un appui sur une touche, …), il faut lui associer une action à réaliser (le code d’une fonction ou méthode) : c’est le gestionnaire d’événement (handler).
Ensuite, à chaque fois que l’événement sera détecté par la boucle d’événements, le gestionnaire d’événement (handler) sera alors exécuté.
Remarque : dans Qt, la boucle d’événement est réalisée par l’appel bloquant a.exec()
dans le fichier main.cpp
. Si on le remplace par un return 0
, la fenêtre s’affiche (show()
) mais se ferme immédiatement et le programme se termine !
Un signal. Dans Qt, un signal se déclare comme une méthode (une fonction membre) d’une classe. Évidemment, un signal ne se définit pas. Un signal n’est donc pas réellement une méthode et n’a pas de “code” associé. On rappelle qu’un signal est un événement (un clic sur un bouton par exemple).
Lire : Signal/Slot
Un slot est un gestionnaire d’événement (ou de signal Qt). Concrètement, un slot est une méthode (une fonction membre) d’une classe. Il représente le code qui sera exécuté lorsque le signal (auquel il est connecté) sera émis.
Pour connecter (associer) un signal à un slot, on utilise l’appel connect()
.
Pour émettre “manuellement” un signal, on utilise emit
.
Qt fournit “automatiquement” des signaux et des slots “prêts à l’emploi”. Il est bien évident que l’on peut créer ses propres signaux et ses propres slots.
Lire : Signal/Slot
Un projet Qt est défini par un fichier d’extension .pro
. Ce fichier est très important car il décrit le contenu d’un projet Qt par l’intermédiaire de variables.
Au minimum, il contient :
# la liste des modules Qt utilisés
QT += core gui widgets
# la liste des fichiers sources (de définitions)
SOURCES += main.cpp xxx.cpp
# la liste des fichiers d'en-têtes (de déclarations)
HEADERS += xxx.h
Il existe d’autres variables permettant par exemple de lister des dépendances, des paramètres passés au compilateur, des ressources, etc.
Ce fichier .pro
est utilisé par l’utilitaire qmake
fourni par Qt.
Remarque : Qt Creator génère probablement un fichier .pro.user
qui est spécifique à la configuration personnelle de l’utilisateur. Ce fichier n’a aucune “valeur” dans un développement collaboratif. Il est d’autre part spécifique à la version de Qt Creator utilisée et à la plateforme (© Windows, GNU/Linux, MacOs, …).
qmake
est un utilitaire fourni par Qt.
qmake
prend en entrée un fichier de projet Qt .pro
et génère un fichier de règles (Makefile
par exemple) permettant de fabriquer un exécutable spécifique à la plateforme (© Windows, GNU/Linux, MacOs, iOS, Android, …).
Attention : à chaque fois que le fichier .pro
est modifié, l’utilitaire qmake
doit être exécuté pour que le fichier de règles de fabrication soit mis à jour. Idem lorsque l’on change de plateforme.
Remarque : qmake
est aussi capable de générer (option -project
) initialement un fichier de projet Qt .pro
.
Un fichier Makefile
contient la description des opérations nécessaires pour générer des fichiers (une application, une bibliothèque, …). Pour faire simple, il contient les règles à appliquer lorsque les dépendances ne sont plus respectées (par exemple recompiler un code source qui a été modifié).
Une règle est une suite d’instructions qui seront exécutées pour construire une cible si la cible n’existe pas ou si des dépendances sont plus récentes.
Remarque : De nos jours, les fichiers Makefile
sont de plus en plus rarement générés à la main par le développeur mais construit à partir d’outils automatiques tels qu’autoconf
, cmake
ou qmake
qui facilitent la génération de Makefile
complexes et spécifiquement adaptés à l’environnement dans lequel les actions de production sont censées se réaliser.
Un fichier Makefile
sera lu et exécuté par l’utilitaire make
.
make
est un utilitaire de type “moteur de production” : il sert à appeler des commandes créant des fichiers. Make exécute les commandes seulement si elles sont nécessaires.
Oui.
Non ! Les fichiers Makefile
sont différents et spécifiques à la plateforme utilisée. D’où l’intérêt d’utiliser un utilitaire comme qmake
qui sera capable de générer les Makefile spécifiques à la plateforme.
Par exemple :
\
et GNU/Linux et MacOs le /
..o
sous GNU/Linux et .obj
sous © Windows, .so
sous GNU/Linux et .dll
sous © Windows, etc …Avec Qt, les éléments (ou composants) graphiques prédéfinis sont appelés des widgets (pour windows gadgets).
Les widgets sont les éléments principaux de la création d’interface graphique utilisateur (GUI) avec Qt.
Les widgets peuvent afficher des données et des informations sur un état, recevoir des actions de l’utilisateur et agir comme un conteneur pour d’autres widgets qui doivent être regroupés. Les widgets classiques sont les boutons (QPushButton
) , les listes déroulantes (QComboBox
) etc …
Quelques caractéristiques de base des widgets :
Un layout est un système de disposition pour l’organisation et le positionnement automatique des widgets. Ce gestionnaire de placement permet l’agencement facile et le bon usage de l’espace disponible. Un layout est invisible.
Il permet un positionnement relatif et ainsi d’éviter un positionnement absolu (généralement très déconseillé car cela pose des problèmes de résolution d’écran, de redimensionnement, …). Les layouts peuvent s’imbriquer les uns dans les autres.
Les layouts sont spécialisés. Dans Qt, les plus utilisés sont :
QHBoxLayout
: placement automatique en horizontalQVBoxLayout
: placement automatique en verticalQGridLayout
: placement en grilleQt Designer est un logiciel qui permet de créer des interfaces graphiques Qt dans un environnement convivial. L’utilisateur, par glisser-déposer, place les composants d’interface graphique et y règle leurs propriétés facilement. Les fichiers d’interface graphique sont formatés en XML et portent l’extension .ui
. Lors de la compilation, le fichier d’interface graphique .ui
est converti en classe C++ par l’utilitaire uic
, fourni par Qt.
Remarque : Qt Designer n’est évidemment pas indispensable pour produire des interfaces graphiques Qt.
Un kit de développement logiciel (SDK = Software Development Kit ou devkit) est un ensemble d’outils logiciels destinés aux développeurs, facilitant le développement d’un logiciel sur une plateforme donnée (par exemple, iOS, Android, GNU/Linux, OS X, Microsoft Windows).
Dans Qt, un kit de développement doit être présent et configuré. Il regroupe au minimum qmake
, make
(ou cmake
) et gcc/g++/gdb
.
Par exemple dans Qt Creator, la notion de kit de développement est la suivante :
Remarque : dans Qt Creator, un kit peut être auto-détecté ou configuré manuellement.
C’est la détection et la présence de qmake
qui permet d’obtenir les informations sur la configuration de Qt (chemins, versions, etc …) à partir d’un fichier qt.conf
:
Evidemment, plusieurs outils (et kits) peuvent cohabiter :
Sous Ubuntu, la configuration de Qt Creator est sotckée dans le fichier ~/.config/QtProject/QtCreator.ini
. Il n’est pas inutile d’en faire une sauvegarde !
Et la configuration de Qt (installée par exemple à partir des paquets de la distribution ) est stokée dans le fichier /usr/lib/x86_64-linux-gnu/qt5/qt.conf
:
$ cat /usr/lib/x86_64-linux-gnu/qt5/qt.conf
[Paths]
Prefix=/usr
ArchData=lib/x86_64-linux-gnu/qt5
Binaries=lib/qt5/bin
Data=share/qt5
Documentation=share/qt5/doc
Examples=lib/x86_64-linux-gnu/qt5/examples
Headers=include/x86_64-linux-gnu/qt5
HostBinaries=lib/qt5/bin
HostData=lib/x86_64-linux-gnu/qt5
HostLibraries=lib/x86_64-linux-gnu
Imports=lib/x86_64-linux-gnu/qt5/imports
Libraries=lib/x86_64-linux-gnu
LibraryExecutables=lib/x86_64-linux-gnu/qt5/libexec
Plugins=lib/x86_64-linux-gnu/qt5/plugins
Qml2Imports=lib/x86_64-linux-gnu/qt5/qml
Settings=/etc/xdg
Translations=share/qt5/translations
Remarque : le fichier qt.conf
est lu par qmake
.
On utilise la méthode statique QString::number()
pour convertir des nombres.
QString dix = QString::number(10); // en entier
QString a = QString::number(10, 16); // en hexadécimal
double d = 0.5;
QString unDemi = QString::number(d); // un double
QString unTiers = QString::number(1./3., 'f', 2); // avec une précision
La classe QString
fournit des méthodes pour convertir une chaîne de caractères en nombre : toInt()
, toLong()
, toFloat()
, toDouble()
, …
QString dix = "10";
int i = dix.toInt();
QString unDemi = "0.5";
double d = unDemi.toDouble();
De plus ces méthodes possèdent des paramètres optionnels :
QString str = "FF";
bool ok;
int n = str.toInt(&ok, 16);
qDebug() << n << ok; // 255 true
Remarque : lire Comment afficher des messages de débogage ? pour voir l’utilisation de qDebug()
.
La classe QString
fournit la méthode arg()
pour formater une chaîne de caractères.
int age = 20;
QString str = QString("J'ai %1 ans").arg(age); // "J'ai 20 ans"
int age = 20;
double taille = 1.99;
QString str = QString("J'ai %1 ans et je mesure %2.").arg(age).arg(taille); // "J'ai 20 ans et je mesure 1.99."
int n = 10;
QString str = QString("0x") + QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); // en hexadécimal : "0x0A"
Remarque : La méthode arg()
possède énormément de versions différentes. Documentation : doc.qt.io/qt-4.8/qstring.html
Pour obtenir une chaîne de caractères en hexadécimal :
int dix = 10; // en entier
QString a = QString::number(dix, 16); // a en hexadécimal
À partir d’une chaîne de caractères en hexadécimal :
QString str = "0A"; // une valeur en hexadécimal
bool ok;
int dix = str.toInt(&ok, 16);
qDebug() << n << ok; // 10 true
Remarque : lire Comment afficher des messages de débogage ? pour voir l’utilisation de qDebug()
.
Par défaut, QString
encode les chaînes de caractères en ISO 8859-1 (latin-1) remplaçant le standard ASCII mais il ne couvre pas complètement le français. En France, nous utilisons l’encodage ISO 8859-15 (latin-9) qui pallie ces problèmes. Sous Unix/Linux, l’encodage par défaut est l’UTF-8.
Il est possible d’encoder directement une chaîne de caractères en utilisant les méthodes statiques QString::fromAscii()
, QString::fromLatin1()
, QString::fromUtf8()
et QString::fromLocal8Bit()
.
Si vous rencontrez des problèmes d’encodage (par exemple les accents), utilisez la méthode qui correspond :
QString str = QString::fromUtf8("Une chaîne de caractères");
La classe QTextCodec
fournit des conversions entre les encodages de texte.
Il est possible de choisir son encodage de manière générale avec la méthode statique QTextCodec::setCodecForCStrings()
:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
La classe QString
contient de nombreuses méthodes pour manipuler des chaînes de caractère. Sa documentation : doc.qt.io/qt-4.8/qstring.html.
Quelques méthodes intéressantes pour faire un traitement sur une trame par exemple :
startsWith()
et endsWith()
pour vérifier les délimiteurs de début de trame (par exemple le dollar $
) et de fin de trame (par exemple la séquence “\r\n
”)contains()
ou count()
pour détecter (ou compter) la présence de certains caractères (le plus souvent des délimiteurs comme la virgule ,
, le point-virgule ;
ou l’étoile *
)section()
ou split()
pour le découpage à partir de délimiteurs (comme la virgule ,
, le point-virgule ;
…) ou mid()
pour une extraction sans délimiteurreplace()
pour le remplacement et remove()
pour la suppression de caractèresindexOf()
pour obtenir la position d’un caractèreVérifier le format d’une trame NMEA0183 GGA :
QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000*0E";
// Faire des essais :
//QString phrase = "";
//QString phrase = "GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000*0E";
//QString phrase = "$GPAAM,A,A,0.10,N,WPTNME*32";
//QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000";
QString checksum;
const QString debutTrame = "$";
const QString typeTrame = "GPGGA";
const QString debutChecksum = "*";
// phrase vide ?
if(phrase.length() != 0)
{
// est-ce une phrase NMEA 0183 ?
if(phrase.startsWith(debutTrame))
{
// est-ce la bonne phrase ?
if(phrase.startsWith(debutTrame + typeTrame))
{
// y-a-t-il un checksum ?
if(phrase.contains(debutChecksum))
{
checksum = phrase.section(debutChecksum, 1, 1);
qDebug() << "checksum extrait : 0x" << checksum;
}
else
qDebug() << "Attention : il n'y a pas de checksum dans cette phrase !";
}
else
qDebug() << "Erreur : ce n'est pas une trame GGA !";
}
else
qDebug() << "Erreur : ce n'est pas une trame NMEA 0183 !";
}
else
qDebug() << "Erreur : phrase vide !";
Remarque : lire Comment afficher des messages de débogage ? pour voir l’utilisation de qDebug()
.
Récupérer l’horodatage d’une trame NMEA0183 GGA :
QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000*0E";
QString horodatage;
unsigned int heure, minute;
double seconde;
// découpe la trame avec le délimiteur ',' et récupère le deuxième champ
horodatage = phrase.section(',', 1, 1);
// découpe une chaine à partir d'une position et un nombre de caractères
heure = horodatage.mid(0, 2).toInt();
minute = horodatage.mid(2, 2).toInt();
seconde = horodatage.mid(4, 2).toDouble();
La classe QString
ne fournit pas de méthode directe pour obtenir une chaîne de type char *
. Il faut passer par les méthodes toAscii()
(seulement en Qt4), toLatin1()
, toUtf8()
, toLocal8Bit()
qui retournent un objet de type QByteArray
. La classe QByteArray
fournit les méthodes constData()
pour obtenir un const char *
et data()
pour un char *
.
QString str("Hello wordl!");
printf(str.toUtf8().constData());
Par contre, il existe un constructeur QString(const char * str)
pour convertir une chaîne C (char *) en QString.
On peut aussi utiliser la fonction qPrintable()
qui reçoit en paramètre une référence sur un QString
et qui retourne un const char *
: const char * qPrintable(const QString & str)
. C’est l’équivalent de str.toLocal8Bit().constData()
.
Pour transformer un std::string
en QString
, il faut utiliser la méthode statique QString::fromStdString()
.
Pour l’opération inverse, on utilisera la méthode toStdString()
.
On utilise un objet de la classe QDateTime
(ou QDate
ou QTime
) que l’on initialise avec la méthode statique QDateTime::currentDateTime()
(ou QDate::currentDate()
ou QTime::currentTime()
). On obtient une chaîne de caractères formatée en appelant la méthode toString(format)
:
QDateTime maintenant = QDateTime::currentDateTime();
QString date = maintenant.toString("dd/MM/yyyy");
QString heure = maintenant.toString("hh:mm:ss");
Il existe plusieurs paramètres de format.
Remarque : il existe une fonction inverse fromString()
qui permet d’initialiser un objet QDateTime
(ou QDate
ou QTime
) à partir d’une chaîne de caractères formatée.
On peut utiliser le pattern Model-View de Qt pour associer des données (le modèle) à un affichage (une vue) sous forme de list.
Lire qt-modelview.pdf et tp-qt-5-calepin.pdf.
La classe QListView
fournit une implémentation par défaut d’un modèle/vue sous la forme d’une vue en liste qui affiche les éléments contenus dans un modèle.
Il existe d’autres types de vue : sous forme de table (QTableView
) et sous forme d’arbre (QTreeView
).
QListView *listeViewDonnees;
QStringListModel *modele;
QStringList donnees;
donnees << "donnee 1" << "donnee 2" << "donnee 3";
listeViewDonnees = new QListView(this);
modele = new QStringListModel(this);
modele->setStringList(donnees);
listeViewDonnees->setModel(modele);
On peut utiliser le pattern Model-View de Qt pour associer des données (le modèle) à un affichage (une vue) sous forme de table.
Lire qt-modelview.pdf et tp-qt-5-calepin.pdf.
La classe QTableView
fournit une implémentation par défaut d’un modèle/vue sous la forme d’une vue en table qui affiche les éléments contenus dans un modèle.
La personnalisation d’un QTableView
se fait en utilisant les nombreuses méthodes offertes par cette classe : setColumnWidth()
, setMinimumWidth()
, setMaximumWidth()
, setSelectionMode()
, setSelectionBehavior()
, setSortingEnabled()
, sortByColumn()
, setHorizontalScrollMode()
, setVerticalScrollMode()
, …
La classe QTableView
possède un entête vertical et un entête horizontal qui sont accessibles par les méthodes : verticalHeader()
et horizontalHeader()
. Elles retournent un QHeaderView
qui possède de nombreuses méthodes de personnalisation : hide()
, setFixedWidth()
, setFixedHeight()
, setDefaultSectionSize()
, setResizeMode()
, setStretchLastSection()
, …
Il existe d’autres types de vue : sous forme de liste (QListView
) et sous forme d’arbre (QTreeView
).
Si vous ne voulez pas utiliser votre propre modèle, vous pouvez utiliser la classe QTableWidget
qui fournit une vue de table basée avec un modèle par défaut. Les éléments des cellules d’un QTableWidget
sont des objets QTableWidgetItem
.
// Des données (simplement pour initialiser le modèle)
QStringList listePays;
listePays << "France" << "Angleterre" << "Espagne" << "Italie" << "Allemagne";
// Un modèle initialisé avec des données
QStringListModel *modele = new QStringListModel(listePays);
// Une vue Table (un widget)
vueTable = new QTableView(this);
// Association de la vue Table et du modèle
vueTable->setModel(modele);
On va maintenant utiliser un autre modèle pour la vue Table :
// Des données
QStringList listePays;
listePays << "France" << "Angleterre" << "Espagne" << "Italie" << "Allemagne";
QStringList capitalesPays;
capitalesPays << "Paris" << "Londres" << "Madrid" << "Rome" << "Berlin";
// Un autre modèle initialisé avec des données
autreModele = new QStandardItemModel(listePays.size(), 2);
for (int ligne = 0; ligne < listePays.size(); ligne++)
{
QStandardItem *item = new QStandardItem(listePays.at(ligne));
autreModele->setItem(ligne, 0, item);
}
for (int ligne = 0; ligne < capitalesPays.size(); ligne++)
{
QStandardItem *item = new QStandardItem(capitalesPays.at(ligne));
autreModele->setItem(ligne, 1, item);
}
// Une vue Table (un widget)
vueTable = new QTableView(this);
// Association de la vue Table et du modèle
vueTable->setModel(autreModele);
// Les éléments de la vue Table ne seront pas "éditables"
vueTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
Si on veut récupérer une donnée par un simple clic sur la vue de la liste :
connect(vueListe, SIGNAL(clicked(QModelIndex)), this, SLOT(selectionner(QModelIndex)));
Et dans le slot void selectionner(QModelIndex index) :
qDebug() << index.data().toString();
// Ajout d'une nouvelle ligne
int numeroLigne = modele->rowCount();
modele->insertRow(numeroLigne);
// Ajout de la donnée pour cette nouvelle ligne
QModelIndex index = modele->index(numeroLigne); // l'index de cette ligne
modele->setData(index, QString("Belgique"));
Ou :
// Ajout d'un élément
QStandardItem *item = new QStandardItem(QString("..."));
modele->setItem(numeroLigne, numeroColonne, item);
// Récupération du nombre de lignes
int nbLignes = modele->rowCount();
if(nbLignes > 0)
{
// Suppression d'une ligne : la dernière
modele->removeRows(nbLignes-1, 1);
}
On peut personnaliser l’affichage de la vue Table :
// Masquer les numéros de ligne
vueTable->verticalHeader()->setHidden(true);
// Redimensionner automatiquement la colonne pour occuper l'espace disponible
vueTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // voir aussi QHeaderView::ResizeToContents
Et on peut ajouter un en-tête aux colonnes :
QStringList nomColonnes; // nom des colonnes
nomColonnes << "Pays" << "Capitale";
autreModele->setHorizontalHeaderLabels(nomColonnes);
On récupère un QStandardItem
depuis le modèle :
QStandardItem *item = modele->item(numeroLigne, numeroColonne);
QFont texteLabel;
texteLabel.setPointSize(18);
item->setFont(texteLabel);
item->setBackground(QColor(Qt::green));
item->setForeground(QColor(Qt::red));
item->setTextAlignment(Qt::AlignHCenter);
// un QTableWidget avec un nombre de lignes et de colonnes
tableWidget = new QTableWidget(4, 3, this);
// Ou un QTableWidget
tableWidget = new QTableWidget(this);
// on fixe le nombre de lignes
tableWidget->setRowCount(4);
// on fixe le nombre de colonnes
tableWidget->setColumnCount(3);
// Ou on peut insérer directement une ligne
tableWidget->insertRow(numeroLigne);
Un QString
:
QTableWidgetItem *element = new QTableWidgetItem(QString("Une donnée"));
tableWidget->insertRow(l);
tableWidget->setItem(l, c, element); // l : ligne (row) et c : colonne
Un widget :
comboBox = new QComboBox(this);
comboBox->addItem("Item 1");
comboBox->addItem("Item 2");
// on peut ajouter des propriétés : l (ligne) et c (colonne)
comboBox->setProperty("l", l);
comboBox->setProperty("c", c);
// on peut connecter un slot
connect(comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slot(int)));
tableWidget->setCellWidget(l, c, cb);
QPushButton *bouton = new QPushButton("Valider", this);
bouton->setProperty("l", l);
bouton->setProperty("c", c);
connect(bouton, SIGNAL(clicked()), this, SLOT(slot2()));
tableWidget->setCellWidget(l, c, bouton);
Par exemple, un slot :
void MaClasse::slot2()
{
// on récupère la ligne et la colonne du bouton
int r = sender()->property("l").toInt();
int c = sender()->property("c").toInt();
qDebug() << Q_FUNC_INFO << l << c;
// ...
}
On insère des QCheckBox
dans un QTableWidget
:
QCheckBox *checkBox = new QCheckBox(this);
twItineraires->setCellWidget(l, c, checkBox);
// etc ...
Une alternative à la méthode sender()
(vu ci-dessous) est l’utilisation d’un QSignalMapper
. Cette classe permet d’associer un signal sans paramètre d’un objet à un autre signal pourvu d’un paramètre défini.
// Pour la gestion des checkbox dans les tables
mapperSelectionne = new QSignalMapper(this);
On connecte le signal déclencheur souhaité de chaque objet (ici clicked()
) avec le slot map()
du QSignalMapper
:
connect(checkBox, SIGNAL(clicked()), mapperSelectionne, SLOT(map()));
On indique ensuite au QSignalMapper
avec la méthode setMapping()
le signal qui sera émis :
// le deuxième paramètre est ici de type QWidget*,
// mais on peut aussi le faire avec des int, QObject* ou QString
mapperSelectionne->setMapping(checkBox, checkBox);
À chaque appel au slot map()
, le QSignalMapper
identifie l’objet qui déclenche le slot (avec sender()
) et émet le signal mapped()
défini avec setMapping()
:
connect(mapperSelectionne, SIGNAL(mapped(QWidget *)), this, SLOT(activer(QWidget *)));
Il ne reste plus qu’à définir le slot activer(QWidget *)
. À la place d’un slot, on peut indiquer un signal qui sera alors émis.
Avec la méthode setFlags()
:
QTableWidgetItem *element = new QTableWidgetItem(QString("Une donnée"));
tableWidget->insertRow(l);
tableWidget->setItem(l, c, element); // l : ligne (row) et c : colonne
// Aucune propriété
item->setFlags(Qt::NoItemFlags);
// Sélectionnable
item->setFlags(Qt::ItemIsSelectable);
// Éditable
item->setFlags(Qt::ItemIsEditable);
// Activé
item->setFlags(Qt::ItemIsEnabled);
// Ou plusieurs
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
On peut détecter le changement d’une cellule
connect(tableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(slot(int,int)));
Puis dans le slot, on peut récupérer le contenu de la cellule :
void MaClasse::slot(int l, int c)
{
QTableWidgetItem *item;
item = tableWidget->item(l, c);
qDebug() << item->data(0).toString();
}
QTableWidgetItem *item;
item = tableWidget->item(l, c);
item->setData(0, QString("Coucou !");
Ses couleurs d’arrière et d’avant plan :
QTableWidgetItem *item;
item = tableWidget->item(l, c);
QColor couleur1(Qt::blue);
QColor couleur2(Qt::yellow);
item->setBackgroundColor(couleur1);
item->setForeground(couleur2);
Sa police de caractères :
QTableWidgetItem *item;
item = tableWidget->item(l, c);
QFont font("Liberation Sans", 20, QFont::Normal);
font.setPointSize(22);
font.setBold(true);
font.setItalic(true);
font.setUnderline(true);
item->setFont(font);
Son alignement dans la cellule :
tableWidget->item(l, c)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
// Sur un élément
QTableWidgetItem *item;
item = tableWidget->item(l, c);
tableWidget->scrollToItem(item);
// sur une cellule
tableWidget->setCurrentCell(l, c);
int nb = tableWidget->rowCount();
// on efface les lignes du tableau une par une
for(int i = 0; i < nb; i++)
{
tableWidget->removeRow(0);
}
Ou :
// on efface les QTableWidgetItem contenus dans le tableau
tableWidget->clear();
// on efface toutes les lignes du tableau
tableWidget->setRowCount(0);
Il existe de nombreuses méthode de paramétrage d’un QTableWidget
:
QTableWidget *tableWidget = new QTableWidget(this);
QStringList header; // nom des colonnes
header << "Nom" << "Prénom"; // ...
// On fixe le nombre de colonnes
tableWidget->setColumnCount(header.size());
// On applique les noms des colonnes
tableWidget->setHorizontalHeaderLabels(header);
// on cache les numéros de ligne
tableWidget->verticalHeader()->setHidden(true);
QHeaderView * headerView = tableWidget->horizontalHeader();
// on redimensionne automatiquement la colonne pour occuper l'espace disponible
// Qt 5 :
headerView->setSectionResizeMode(QHeaderView::Stretch);
// Qt 4 :
headerView->setResizeMode(QHeaderView::Stretch);
A adapter :
void IHMItineraire::resizeTW(QTableWidget *tw)
{
int w = 0, h = 0;
// redimensionner les colonnes en fonction du contenu
tw->resizeColumnsToContents();
// redimensionner les lignes en fonction du contenu
//tw->resizeRowsToContents();
// les barres de défilements
tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// calcul largeur (w) et hauteur (h)
w += tw->contentsMargins().left() + tw->contentsMargins().right();
h += tw->contentsMargins().top() + tw->contentsMargins().bottom();
w += tw->verticalHeader()->width();
h += tw->horizontalHeader()->height();
for (int i=0; i<tw->columnCount(); ++i)
w += tw->columnWidth(i);
for (int i=0; i<tw->rowCount(); ++i)
h += tw->rowHeight(i);
// fixe largeur (w) et hauteur (h)
tw->setMinimumWidth(w);
tw->setMaximumWidth(w);
//tw->setMinimumHeight(h);
//tw->setMaximumHeight(h);
//qDebug() << "w =" << w << "h =" << h;
}
qDebug() << qVersion();
Remarque : lire Comment afficher des messages de débogage ? pour voir l’utilisation de qDebug()
.
Il existe plusieurs méthodes :
en appelant la méthode showFullScreen()
en utilisant la méthode desktop()
de la classe QApplication
puis en appelant resize()
const int width = qApp->desktop()->availableGeometry(this).width(); // ou : qApp->desktop()->width()
const int height = qApp->desktop()->availableGeometry(this).height(); // ou : qApp->desktop()->height()
resize(width, height);
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), qApp->desktop()->availableGeometry()));
Sinon, il faudra utiliser la méthode move()
et faire quelques calculs …
On utilisera la classe QSplashScreen
pour afficher un écran de démarrage.
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
// Le splash
QSplashScreen splash;
// En avant-plan et sans décoration (titre ...)
splash.setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
// L'image de l'écran de démarrage
splash.setPixmap(QPixmap("splash.png"));
// On affiche
splash.show();
// On peut afficher un message sur l'image
splash.showMessage(QString::fromUtf8("Lancement de l'application ..."), Qt::AlignHCenter | Qt::AlignTop, Qt::black);
// On ferme le splash après un certain temps (ici 2 s)
QTimer::singleShot(2000, &splash, SLOT(close()));
// On affiche l'application
QTimer::singleShot(2000, &w, SLOT(show()));
return a.exec();
}
Il est aussi possible de synchroniser les 2 affichages :
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
// Le splash
QSplashScreen splash;
QStringList chargement;
chargement << "" << QString::fromUtf8("Lancement de l'application ...") << QString::fromUtf8("Chargement des paramètres ...") << QString::fromUtf8("Chargement des styles ...") << QString::fromUtf8("Chargement des widgets ...");
// En avant-plan et sans décoration (titre ...)
splash.setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
splash.setPixmap(QPixmap("/home/tv/Documents/cours/BTS-Avignon/divers/logos/splash.png"));
splash.show();
for(int i = 0;i < chargement.size();i++)
{
// On peut afficher un message sur l'image
splash.showMessage(chargement.at(i), Qt::AlignHCenter | Qt::AlignTop, Qt::black);
// on fait quelque chose
usleep(500000);
a.processEvents();
}
// On affiche l'application
w.show();
// On ferme le splash lorsque l'e MainWindo'application sera affichée
splash.finish(&w);
return a.exec();
}
Il faut charger le fichier de traduction adapté dans la fonction main()
de votre application :
#include <QApplication>
#include <QTranslator>
#include <QLocale>
#include <QLibraryInfo>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString locale = QLocale::system().name().section(’_’, 0, 0);
QTranslator translator;
translator.load(QString("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
a.installTranslator(&translator);
// ...
return a.exec();
}
QLayoutItem *itemL;
while ((itemL = mainLayout->takeAt(0)) != 0)
{
QWidget * widget = widget = itemL->widget();
mainLayout->removeItem(itemL);
if(widget != 0)
{
delete widget;
}
}
Qt permet la personnalistion des widgets par l’utilisation de feuilles de style. Les feuilles de style Qt et les règles syntaxiques sont presque identiques à celles du CSS de l’HTML. Si vous connaissez déjà le CSS, vous pouvez rapidement les mettre en oeuvre pour Qt.
Lire la documentation Style Sheets Qt5 ou Qt4.
Modification d’un attribut de style pour un widget (ici un QLineEdit
) :
// Pour l'application
qApp->setStyleSheet("QLineEdit { background-color: yellow }");
// Pour le widget
nameEdit->setStyleSheet("background-color: yellow");
On peut regrouper l’ensemble des paramètres dans un fichier externe (.qss
par exemple) :
QLineEdit {
background-color: yellow;
}
QLineEdit:focus {
border-width: 2px;
padding: 0px;
}
Et l’appliquer au démarrage de l’application :
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QFile file("default.qss");
if(file.open(QFile::ReadOnly))
{
QString styleSheet = QLatin1String(file.readAll());
a.setStyleSheet(styleSheet);
}
// ...
return a.exec();
}
Ou dans un fichier ressource intégré à l’application :
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QFile file(":/stylesheet/default.qss");
if(file.open(QFile::ReadOnly))
{
QString styleSheet = QLatin1String(file.readAll());
a.setStyleSheet(styleSheet);
}
// ...
return a.exec();
}
Le fichier ressource .qrc
:
<RCC>
<qresource prefix="/stylesheet">
<file>default.qss</file>
</qresource>
</RCC>
La procédure est décrite dans ce document : qt-internationalisation.pdf.
WebKit est un moteur web open source capable de charger le contenu d’une page web et d’en faire le rendu. Le module QtWebkit
est l’intégration du moteur WebKit pour Qt. Il faut l’activer dans son fichier de projet .pro
:
QT += webkit
Dans ce module, Qt fournit de nombreuses classes dont la classe QWebView
:
QWebView *webView = new QWebView(this);
// sans les barres de défilement (au choix)
webView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
// avec les barres de défilement (au choix)
webView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOn);
webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOn);
// Lire un paramètre
//qDebug() << webView->page()->settings()->testAttribute( QWebSettings::JavascriptEnabled );
// Modifier des paramètres
QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,true );
QWebSettings::globalSettings()->setAttribute(QWebSettings::JavaEnabled,true );
QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled,true );
QWebSettings::globalSettings()->setAttribute(QWebSettings::AutoLoadImages,true);
// un slot :
//connect(webView, SIGNAL(urlChanged(QUrl)), this, SLOT(webview_changed(const QUrl &)));
// L'url de la page
QUrl URL = QString("http://www.google.fr");
// une authentification
//URL.setUserName(username);
//URL.setPassword(password);
// modifier le zoom
//webView->setZoomFactor(1.0);
// on charge la page
webView->load(URL);
Qt comprend des macros globales pour écrire des messages d’avertissement et de débogage :
qDebug()
pour écrire des sorties de débogage personnalisées.qInfo()
pour les messages d’information.qWarning()
pour signaler des avertissements et des erreurs récupérables dans votre application.qCritical()
pour écrire des messages d’erreur critiques et des erreurs de système de rapports.qFatal()
pour écrire des messages d’erreur fatale peu avant la sortie.Si vous incluez le fichier d’en-tête <QtDebug>
, la macro qDebug()
peut également être utilisée comme un flux de sortie (comme cout
par exemple).
Des espaces et un saut de ligne sont automatiquement ajoutés :
qDebug() << "QLabel" << pLabel << "at position" << pLabel->pos(); // QLabel QLabel(0x1a58dd0) at position QPoint(0,0)
qDebug().nospace() << "QLabel" << pLabel << "at position" << pLabel->pos(); // QLabelQLabel(0x1631720) at position QPoint(0,0)
On peut l’utiliser aussi comme la fonction C printf()
:
int a = 10;
double pi = 3.141592;
qDebug("a = %d et pi = %.3f", a, pi); // a = 10 et pi = 3.142
Remarque : Sous Windows, le message est envoyé à la console, s’il s’agit d’une application console sinon, il est envoyé au débogueur. Il faut donc ajouter console
à la variable QT
de votre fichier .pro
si vous voulez l’affichage dans la console.
qDebug()
, qInfo()
, et qWarning()
sont des outils de débogage. Ils ne font rien si QT_NO_DEBUG_OUTPUT
, QT_NO_INFO_OUTPUT
ou QT_NO_WARNING_OUTPUT
ont été respectivement définis lors de la compilation, par exemple pour une version release.
Si vous ne voulez plus les affichage de débogage, mettre dans le fichier .pro
:
DEFINES += QT_NO_DEBUG_OUTPUT
L’opérateur de flux de qDebug()
gère la plupart des types standards et Qt. Par contre, il vous faudra le surcharger pour vos propres types :
Exemple pour type Coordonnees
possédant deux membres x et y :
QDebug operator<<(QDebug dbg, const Coordonnees &c)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "(" << c.x() << ", " << c.y() << ")";
return dbg;
}
Il faut tout d’abord initialiser le générateur de nombres pseudo-aléatoire :
qsrand(QTime::currentTime().msec());
Puis, on utilise qrand()
: on peut générer un nombre entre 0 et 9 par exemple :
// un nombre entre 0 et RAND_MAX
int nombre1 = qrand();
// un nombre entre 0 et 9
int nombre2 = qrand() % 10;
Ou encore mieux utiliser une fonction qui retourne un nombre compris entre une valeur min et une valeur max :
int generateur(int min, int max)
{
return static_cast<int>(min + (static_cast<float>(qrand()) / RAND_MAX * (max - min)));
}
float generateur(float min, float max)
{
return static_cast<float>(min + static_cast<float>(qrand()) / (static_cast<float>(RAND_MAX/(max - min))));
}
À cause de l’approximation des nombres réels, tester l’égalité de deux nombres réels avec ==
n’est pas possible.
Il faudra donc utiliser la fonction qFuzzyCompare()
qui retourne vrai si deux éléments sont égaux. Il existe aussi qFuzzyIsNull()
qui retourne vrai si le nombre réel est nul.
Voir d’autres fonctions intéressantes.
Qt fournit un minuteur ou temporisateur avec la classe QTimer
. Cette classe est relativement simple à utiliser grâce aux méthodes suivantes :
int interval() const
: permet de connaître l’intervalle de temps (en ms) entre chaque déclenchement du minuteurvoid setInterval(int msec)
: permet de fixer l’intervalle en millisecondes entre deux déclenchements successifs du minuteurbool isActive() const
: permet de déterminer si le minuteur est activé ou pasvoid start()
: démarre le minuteurvoid stop()
: arrête le minuteur// on met à zéro un compteur représentant le nombre de secondes
int compteur = 0;
// on crée un minuteur
QTimer *timer = new QTimer(this);
// on connecte le signal d'expiration timeout() de la minuterie à un slot
connect(timer, SIGNAL(timeout()), this, SLOT(compter()));
// on choisit un intervalle en ms
timer->setInterval(1000);
// on démarre le minuteur
timer->start();
// timer->start(1000);
L’action périodique (le slot) :
// Méthode appelée toutes les secondes par le QTimer
void MaClasse::compter()
{
// on incrémente le compteur de secondes
compteur++;
qDebug() << compteur;
}
Qt fournit un minuteur ou temporisateur avec la classe QTimer
. Cette classe est relativement simple à utiliser grâce aux méthodes suivantes :
bool isSingleShot() const
: permet de déterminer si le timer est en mode de déclenchement unique (temporisateur) ou pas (minuteur)void setSingleShot(bool singleShot)
: permet de mettre le minuteur en mode déclenchement unique (temporisateur)On peut aussi utiliser la méthode statique QTimer::singleShot()
:
// on ferme l'application après un certain temps (ici 1s)
QTimer::singleShot(1000, qApp, SLOT(quit()));
Qt 5.0 est sorti le 19 décembre 2012. Bien que marquant des changements majeurs sur bien des points, le passage à Qt5 casse au minimum la compatibilité au niveau des sources. Lire : Transition_from_Qt_4.x_to_Qt5.
En Qt 5, il faut intégrer la module QtWidgets
qui est un module séparé dans le fichier .pro
:
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
Ensuite, il faut corriger les erreurs de compilation comme ‘error: QPushButton: No such file or directory’.
#include <QtWidgets/QPushButton> /* pour Qt5 */
Pour cela, on peut tester QT_VERSION
et assurer une comptabilité Qt4 / Qt5 :
#include <QtGlobal> /* déclare QT_VERSION */
#if QT_VERSION >= 0x050000
#include <QtWidgets> /* les widgets de Qt5 */
#else
#include <QtGui> /* les widgets de Qt4 */
#endif
Oui ! Voir : Qt pour Android
Pour bénéficier des mécanismes de Qt (les signaux et les slots par exemple) dans vos propres classes, il faudra que celles-ci héritent de QObject
(ou d’une classe fille de QObject
) et intègrent la macro Q_OBJECT
dans leur déclaration :
class MaClasse : public QObject
{
Q_OBJECT
public:
MaClasse( QObject *parent=0 ) : QObject(parent) {}
private:
signals:
public slots:
};
Cette classe ne pourra pas être compilée directement par un compilateur C++. Elle doit d’abord être traitée par l’outil moc
de Qt. L’utilitaire qmake
détectera si vos classes ont besoin du moc
et génèrera le fichier Makefile
qui contiendra les règles de fabrication.
Si vous obtenez l’erreur d’édition des liens undefined reference to 'vtable for xxx'
, vous devez :
make clean
)qmake
Dans un slot, la méthode sender()
retourne l’adresse de l’objet qui a déclenché ce slot (en émettant un signal).
Par exemple, deux clics de boutons connectés à un même slot :
QPushButton *bouton1 = new QPushButton("Bouton 1");
QObject::connect(btn, SIGNAL(clicked()), this, SLOT(monSlot()));
QPushButton *bouton2 = new QPushButton("Bouton 2");
QObject::connect(btn, SIGNAL(clicked()), this, SLOT(monSlot()));
Dans le slot, il est possible de récupérer l’adresse de l’objet bouton qui a déclenché le slot :
MaClasse::monSlot()
{
QPushButton *bouton = qobject_cast<QPushButton*>(sender());
}
Pour cela, on utilise la classe QSettings
:
// Le nom du fichier INI : nom-executable.ini
QString fichierINI = qApp->applicationName() + ".ini";
QSettings parametres(fichierINI, QSettings::IniFormat);
// Lecture des paramètres de configuration
QString adresseIP = parametres.value("connexion/adresse","127.0.0.1").toString();
int port = parametres.value("connexion/port", "5000").toInt();
qDebug() << QString("adresse ip : %1").arg(adresseIP);
qDebug() << QString("port : %1").arg(port);
Pour cela, on utilise la classe QSettings
:
// Le nom du fichier INI : nom-executable.ini
QString fichierINI = qApp->applicationName() + ".ini";
QSettings parametres(fichierINI, QSettings::IniFormat);
// Les paramètres à enregistrer
QString adresseIP = "127.0.0.1";
int port = 5000;
// Écriture des paramètres de configuration (version 1)
//parametres.beginGroup("connexion"); // cf. endGroup()
//parametres.setValue("adresse", adresseIP);
//parametres.setValue("port", port);
//parametres.endGroup();
// Écriture des paramètres de configuration (version 1)
parametres.setValue("connexion/adresse", adresseIP);
parametres.setValue("connexion/port", port);
XML signifie eXtensible Markup Language (Langage de balisage extensible). Le langage est standardisé par la spécification W3C XML 1.0 du 10/02/98.
Un document XML bien formé signifie que le texte XML obéit aux règles syntaxiques de XML. Le document considéré comme valide (facultatif) signifie que le texte XML est bien formé et répond à une structure définie par une DTD (Definition Type Document).
Un document XML est composé d’éléments désignés par des balises et structuré sous la forme d’un arbre avec un et un seul élément racine (root).
Exemple de fichier XML :
<?xml version="1.0" encoding="UTF-8"?>
<interfaces>
<interface id="100">
<peripherique>/dev/ttyUSB0</peripherique>
</interface>
</interfaces>
Qt fournit un module QtXml
qu’il faut activer dans son fichier de projet .pro
:
QT += xml
Dans ce module, Qt fournit de nombreuses classes qui permettent de gérer des documents XML :
On utilise la classe QFile
pour la gestion de fichier. On utilise un objet DOM (Document Objet Model) de la classe QDomDocument
pour agir sur le document XML :
// Le fichier XML
QFile fichierXML("test.xml");
if(!(fichierXML.open(QIODevice::ReadWrite | QIODevice::Truncate)))
{
QMessageBox::critical(0,"Erreur","Erreur ouverture fichier XML !");
return false;
}
else
{
QDomDocument documentXML;
documentXML.setContent(&fichierXML, false);
QDomNode xmlNode = documentXML.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
documentXML.insertBefore(xmlNode, documentXML.firstChild());
// crée l'élément racine
QDomElement root = documentXML.createElement("interfaces");
documentXML.appendChild(root);
// crée et ajoute un élément
QDomElement elementInterface = documentXML.createElement("interface");
// ajoute un attribut à cete élément
elementInterface.setAttribute("id", 100);
root.appendChild(elementInterface);
// crée et ajoute un sous-élément
QDomElement elementPeripherique = documentXML.createElement("peripherique");
elementInterface.appendChild(elementPeripherique);
QDomText text = documentXML.createTextNode("/dev/ttyUSB0);
elementPeripherique.appendChild(text);
// écrit le fichier XML
QTextStream out(&fichierXML);
documentXML.save(out, 2);
fichierXML.close();
}
XML signifie eXtensible Markup Language (Langage de balisage extensible). Le langage est standardisé par la spécification W3C XML 1.0 du 10/02/98.
Un document XML bien formé signifie que le texte XML obéit aux règles syntaxiques de XML. Le document considéré comme valide (facultatif) signifie que le texte XML est bien formé et répond à une structure définie par une DTD (Definition Type Document).
Un document XML est composé d’éléments désignés par des balises et structuré sous la forme d’un arbre avec un et un seul élément racine (root).
Exemple de fichier XML :
<?xml version="1.0" encoding="UTF-8"?>
<interfaces>
<interface id="100">
<peripherique>/dev/ttyUSB0</peripherique>
</interface>
</interfaces>
Qt fournit un module QtXml
qu’il faut activer dans son fichier de projet .pro
:
QT += xml
Dans ce module, Qt fournit de nombreuses classes qui permettent de gérer des documents XML :
On utilise la classe QFile
pour la gestion de fichier. On utilise un objet DOM (Document Objet Model) de la classe QDomDocument
pour agir sur le document XML :
// Le fichier XML
QFile fichierXML("test.xml");
if(!(fichierXML.open(QIODevice::ReadOnly)))
{
QMessageBox::critical(0,"Erreur","Le fichier XML n'a pas pu être ouvert !");
return false;
}
else
{
QDomDocument documentXML;
documentXML.setContent(&fichierXML, false);
// lit l'élément racine
QDomElement racine = documentXML.documentElement(); // <interfaces>
if(racine.isNull())
{
qDebug() << "Erreur racine !";
return;
}
// récupère le premier élément
QDomNode noeudInterface = racine.firstChild();
if(noeudInterface.isNull())
{
qDebug() << "Erreur racine vide !";
return;
}
while(!noeudInterface.isNull())
{
QDomElement elementInterface = noeudInterface.toElement(); // <interface>
if(elementInterface.isNull())
{
qDebug() << "Erreur element !";
break;
}
QDomNode noeudPeripherique = elementInterface.firstChild();
if(!noeudPeripherique.isNull())
{
QDomElement elementPeripherique = noeudPeripherique.toElement(); // <peripherique>
qDebug() << elementInterface.attribute("id").toInt(); // l'id
qDebug() << interface.peripherique = elementPeripherique.text(); // le périphérique
}
// au suivant
noeudInterface = noeudInterface.nextSibling();
}
fichierXML.close();
}
Le format CSV (Comma-separated values) est un format informatique ouvert représentant des données tabulaires sous forme de valeurs séparées par un délimiteur (initialement des virgules). Les séparateurs ne sont pas standardisés (virgules, points-virgules, etc…) rend ce format peu pratique pour une utilisation autre que des échanges de données ponctuels. Ce format est toutefois très populaire parce qu’il est très facile à générer.
Les fichiers CSV sont des fichiers texte : ils peuvent donc être manipulés avec un éditeur de texte. Mais les fichiers CSV sont essentiellement utilisés par des logiciels de type tableur (Microsoft Excel, LibreOffice/OpenOffice calc, …).
Chaque ligne du texte correspond à une ligne du tableau et les virgules correspondent aux séparations entre les colonnes. Les portions de texte séparées par une virgule correspondent ainsi aux contenus des cellules du tableau.
Une ligne est une suite ordonnée de caractères terminée par un caractère de fin de ligne (CRLF), la dernière ligne pouvant en être exemptée.
Exemple de fichier CSV :
Sexe;Prénom;Année de naissance
M;Alphonse;1932
On utilise la classe QFile
pour la gestion de fichier. Il n’existe pas de classe Qt native pour gérer le format CSV.
// Le fichier CSV
QFile *fichierCSV = new QFile("datas.csv");
// Ouverture du fichier en mode texte et en écriture seule
if (fichierCSV->open(QFile::WriteOnly | QIODevice::Text))
{
// Ecriture de l'en-tête
QTextStream entete(fichierCSV);
entete << QString::fromUtf8("Sexe;Prénom;Année de naissance") << endl;
// Ecriture des données
QTextStream datas(fichierCSV);
datas << "\"" << "M" << "\""; // on protège la data avec des "
datas << ";";
datas << "\"" << "Alphonse" << "\""; // on protège la data avec des "
datas << ";";
datas << "\"" << "1932" << "\""; // on protège la data avec des "
datas << ";";
datas << endl;
// On ferme le fichier
fichierCSV->close();
delete fichierCSV;
}
else
{
QMessageBox::critical(0,"Erreur !",("Impossible d'ouvrir le fichier datas.csv"));
delete fichierCSV;
}
Remarque : voir aussi la classe qcsv
Le format CSV (Comma-separated values) est un format informatique ouvert représentant des données tabulaires sous forme de valeurs séparées par un délimiteur (initialement des virgules). Les séparateurs ne sont pas standardisés (virgules, points-virgules, etc…) rend ce format peu pratique pour une utilisation autre que des échanges de données ponctuels. Ce format est toutefois très populaire parce qu’il est très facile à générer.
Les fichiers CSV sont des fichiers texte : ils peuvent donc être manipulés avec un éditeur de texte. Mais les fichiers CSV sont essentiellement utilisés par des logiciels de type tableur (Microsoft Excel, LibreOffice/OpenOffice calc, …).
Chaque ligne du texte correspond à une ligne du tableau et les virgules correspondent aux séparations entre les colonnes. Les portions de texte séparées par une virgule correspondent ainsi aux contenus des cellules du tableau.
Une ligne est une suite ordonnée de caractères terminée par un caractère de fin de ligne (CRLF), la dernière ligne pouvant en être exemptée.
Exemple de fichier CSV :
Sexe;Prénom;Année de naissance
M;Alphonse;1932
On utilise la classe QFile
pour la gestion de fichier. Il n’existe pas de classe Qt native pour gérer le format CSV.
// Le fichier CSV
QFile *fichierCSV = new QFile("datas.csv");
// Ouverture du fichier en mode texte et en écriture seule
if (fichierCSV->open(QFile::ReadOnly | QIODevice::Text))
{
// Lecture des données
QTextStream datas(fichierCSV);
QString ligne;
while(!datas.atEnd())
{
ligne = datas.readLine();
qDebug() << ligne;
}
// On ferme le fichier
fichierCSV->close();
delete fichierCSV;
}
else
{
QMessageBox::critical(0, "Erreur !", ("Impossible d'ouvrir le fichier datas.csv"));
delete fichierCSV;
}
Remarque : voir aussi la classe qcsv
QString data = QString::number(2.5);
// A la française ?
data.replace(QString("."), QString(","));
Qt fournit de nombreuses classes pour la gestion des base de données. Il faudra activer le module dans son fichier de projet .pro
pour pouvoir accéder aux classes :
QT += sql
Remarque : Il faudra aussi disposer d’un pilote de base de données comme pour MySQL (QMYSQL) ou SQLite (QSQLITE).
On utilisera la classe QSqlDatabase
qui permet la connexion à une base de données.
Et ensuite la classe QSqlQuery
pour exécuter des requêtes SQL :
Exemple pour base de données MySQL :
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("localhost");
db.setUserName("root");
db.setPassword("password");
db.setDatabaseName("test"); // mettre le nom de la base de données
if(db.open())
{
qDebug() << "Connexion réussie à " << db.hostName().toStdString();
}
else
{
qDebug() << db.lastError().text();
}
Exemple pour base de données SQLite :
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("test.sqlite"); // mettre le nom du fichier sqlite
if(db.open())
{
qDebug() << "Connexion réussie à " << db.hostName().toStdString();
}
else
{
qDebug() << db.lastError().text();
}
Qt fournit de nombreuses classes pour la gestion des base de données. Il faudra activer le module dans son fichier de projet .pro
pour pouvoir accéder aux classes :
QT += sql
Remarque : Il faudra aussi disposer d’un pilote de base de données comme pour MySQL (QMYSQL) ou SQLite (QSQLITE).
On utilisera la classe QSqlDatabase
qui permet la connexion à une base de données.
Et ensuite la classe QSqlQuery
pour exécuter des requêtes SQL :
QSqlQuery query;
QString requete = "SELECT * FROM mesures";
bool retour = query.exec(requete);
if(retour)
{
while (query.next())
{
qDebug() << "enregistrement -> ";
for(int i=0;i<query.record().count();i++)
qDebug() << query.value(i).toString();
}
}
else qDebug() << query.lastError().text();
Qt fournit de nombreuses classes pour la gestion des base de données. Il faudra activer le module dans son fichier de projet .pro
pour pouvoir accéder aux classes :
QT += sql
Remarque : Il faudra aussi disposer d’un pilote de base de données comme pour MySQL (QMYSQL) ou SQLite (QSQLITE).
On utilisera la classe QSqlDatabase
qui permet la connexion à une base de données.
Et ensuite la classe QSqlQuery
pour exécuter des requêtes SQL :
Il existe plusieurs façons :
QSqlQuery query;
QString requete;
requete = "UPDATE Seuils SET niveauTemperature='23'";
if (!query.exec())
{
qDebug() << r.lastError().text();
}
requete = "DELETE FROM mesures";
if (!query.exec())
{
qDebug() << r.lastError().text();
}
// idem pour INSERT
QSqlQuery query;
// Utilisation des marqueurs '?'
// INSERT INTO `mesures` (`id`, `date`, `heure`, `temperature`) VALUES (...)
query.prepare("INSERT INTO mesures (id, date, heure, temperature) VALUES ('', ?, ?, ?)");
// id en auto-incrément
query.addBindValue("2009-09-10");
query.addBindValue("09:01:00");
query.addBindValue(35.12);
if (query.exec())
{
qDebug() << "Insertion réussie";
}
else
{
qDebug() << r.lastError().text();
}
// Utilisation des marqueurs nominatifs
query.prepare("INSERT INTO mesures (id, date, heure, temperature) VALUES (:id, :date, :heure, :temperature)");
query.bindValue(":id", ""); // auto-incrément
query.bindValue(":date", "2009-09-10");
query.bindValue(":heure", "09:01:00");
query.bindValue(":temperature", 34.92);
if (query.exec())
{
qDebug() << "Insertion réussie";
}
else
{
qDebug() << r.lastError().text();
}
En utilisant le module sql
de Qt de la manière suivante :
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
on obtient à l’exécution ce message d’erreur :
QSqlDatabase: QMYSQL driver not loaded
QSqlDatabase: available drivers: QSQLITE QMYSQL QMYSQL3 QODBC QODBC3 QPSQL QPSQL7
Est-ce que l’erreur provient de Qt ?
Non pas vraiment ! Le module sql
et certaines de ses classes ne sont que des wrappers (des adaptateurs d’interface) vers l’API (SQLite, MySQL, etc …) de chaque système.
Ici, le message d’erreur indique que la bibliothèque logicielle (le fichier .dll
sous © Windows ou .so
sous GNU/Linux) MySQL est introuvable par Qt. Elle n’est donc (dans l’ordre) :
Remarque : l’accès aux bibliothèques logicielles par les exécutables est différent sous © Windows et sous GNU/Linux.
Pour faire (très) simple, © Windows “aime” bien trouver les .dll
spécifiques dans le même répertoire que l’exécutable.
GNU/Linux regroupe généralement l’ensemble des bibliothèques partagées dans les répertoires suivants : /usr/lib
mais aussi dans /lib
et /usr/local/lib
. Voir aussi l’utilitaire ldconfig
et la variable LD_LIBRARY_PATH
.
Remarque : MySQL est un système de gestion de bases de données relationnelles (SGBDR) client/serveur. Ici, c’est le module client (celui qui permet d’effectuer des requêtes SQL vers un serveur) qui fait défaut. Depuis mai 2009, son créateur Michael Widenius a créé MariaDB pour continuer son développement en tant que projet Open Source.
Sous Ubuntu, il faut d’abord s’assurer de l’installation du paquet libqt5sql5-mysql
:
$ sudo apt install libqt5sql5-mysql
Ce paquet installe le driver (ici libqsqlmysql.so
) côté Qt pour être utilisé par le module sql
:
$ dpkg -L libqt5sql5-mysql
/.
/usr
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/qt5
/usr/lib/x86_64-linux-gnu/qt5/plugins
/usr/lib/x86_64-linux-gnu/qt5/plugins/sqldrivers
/usr/lib/x86_64-linux-gnu/qt5/plugins/sqldrivers/libqsqlmysql.so
/usr/share
/usr/share/doc
/usr/share/doc/libqt5sql5-mysql
/usr/share/doc/libqt5sql5-mysql/copyright
/usr/share/doc/libqt5sql5-mysql/changelog.Debian.gz
Maintenant, il faut identifier la bibliothèque du client MySQL (ici libmysqlclient.so
) :
$ ldd /usr/lib/x86_64-linux-gnu/qt5/plugins/sqldrivers/libqsqlmysql.so
linux-vdso.so.1 (0x00007fff13ac2000)
libQt5Sql.so.5 => /usr/lib/x86_64-linux-gnu/libQt5Sql.so.5 (0x00007f60abffd000)
libQt5Core.so.5 => /usr/lib/x86_64-linux-gnu/libQt5Core.so.5 (0x00007f60ab8b2000)
libmysqlclient.so.20 => /usr/lib/x86_64-linux-gnu/libmysqlclient.so.20 (0x00007f60ab2fa000)
...
C’est le paquet libmysqlclientXX
(ici libmysqlclient20
) qui l’a fournie :
$ apt-cache search libmysqlclient
default-libmysqlclient-dev - MySQL database development files (metapackage)
libmysqlclient-dev - Fichiers de développement de la base de données MySQL
libmysqlclient20 - Bibliothèque cliente de la base de données MySQL
libglpk40 - linear programming kit with integer (MIP) support
libcrypt-mysql-perl - Perl module to emulate the MySQL PASSWORD() function
libmariadbclient-dev-compat - MariaDB database development files (libmysqlclient compatibility)
$ apt-file search libmysqlclient.so.20
libmysqlclient20: /usr/lib/x86_64-linux-gnu/libmysqlclient.so.20
libmysqlclient20: /usr/lib/x86_64-linux-gnu/libmysqlclient.so.20.3.21
libmysqlclient20: /usr/lib/x86_64-linux-gnu/libmysqlclient.so.20.3.8
$ sudo apt install libmysqlclient20
Vous pouvez utiliser :
La classe QPrinter est utilisée normalement pour imprimer. Ici, elle va nous permettre de générer un fichier PDF en fixant le format QPrinter::PdfFormat
avec la méthode setOutputFormat()
:
QString nomFichier = QFileDialog::getSaveFileName(0, QString::fromUtf8("Générer PDF"), QCoreApplication::applicationDirPath(), "*.pdf");
if (!nomFichier.isEmpty())
{
if (QFileInfo(nomFichier).suffix().isEmpty())
nomFichier.append(".pdf");
QPrinter printer(QPrinter::HighResolution);
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName(nomFichier);
printer.setOrientation(QPrinter::Portrait);
printer.setPaperSize(QPrinter::A4);
printer.setPageSize(QPrinter::A4);
printer.setPageMargins(15,15,15,15,QPrinter::Millimeter);
qDebug() << "Page px :" << printer.pageRect() << printer.paperRect();
qDebug() << "Page mm :" << printer.pageRect(QPrinter::Millimeter) << printer.paperRect(QPrinter::Millimeter);
qreal left, top, right, bottom;
printer.getPageMargins(&left, &top, &right, &bottom, QPrinter::DevicePixel);
qDebug() << "Marges px :" << left << top << right << bottom;
printer.getPageMargins(&left, &top, &right, &bottom, QPrinter::Millimeter);
qDebug() << "Marges mm :" << left << top << right << bottom;
...
}
Pour générer un contenu dans le fichier PDF, il suffit d’utiliser un objet QPainter
, de l’associer à l’objet QPrinter
et de dessiner à l’intérieur :
...
QPainter painter(&printer);
// Pour écrire du texte
painter.drawText(0, printer.pageRect().y()*2, QString::fromUtf8("Ligne 1"));
// Une nouvelle page
printer.newPage();
painter.drawText(0, printer.pageRect().y()*3, QString::fromUtf8("Ligne 2"));
}
Dans Qt, les imprimantes sont représentées par QPrinter, un périphérique de dessin qui fournit les fonctionnalités spécifiques à l’impression, telles que le support de pages multiples et l’impression recto verso. Cela implique que l’impression passe par l’utilisation d’un QPainter pour dessiner sur une série de pages de la même façon que vous dessineriez sur un widget personnalisé ou une image.
Pour créer puis configurer un objet QPrinter
, on peut utiliser la boîte de dialogue QPrintDialog
:
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Imprimer"));
if (dialog->exec() == QDialog::Accepted)
{
// ...
}
La classe QPrinter possède de nombreuses méthodes pour configurer l’impression :
QPrinter printer(QPrinter::HighResolution);
printer.setOrientation(QPrinter::Portrait);
printer.setPaperSize(QPrinter::A4);
printer.setPageSize(QPrinter::A4);
printer.setPageMargins(15, 15, 15, 15, QPrinter::Millimeter);
...
Lire : Impression avec Qt
QTextEdit
:QTextEdit *editor = new QTextEdit(this);
...
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Imprimer"));
if (dialog->exec() == QDialog::Accepted)
{
QFile *file = new QFile("donnees.dat");
if (file->open(QFile::ReadOnly | QFile::Text))
{
editor->setPlainText(file->readAll());
editor->print(&printer);
file->close();
}
delete file;
}
QPainter
:QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Imprimer"));
if (dialog->exec() == QDialog::Accepted)
{
file = new QFile("donnees.dat");
if (file->open(QFile::ReadOnly | QFile::Text))
{
QString ligne;
QPainter painter;
painter.begin(&printer);
QFont f;
f.setPointSize(10);
f.setBold(true);
painter.setFont(f);
ligne = file->readLine();
painter.drawText(40, 120, ligne);
//printer.newPage();
painter.end();
file->close();
}
delete file;
}
Pour l’exemple, le fichier donnees.dat
peut être généré simplement comme ceci :
QString uneDonnee = "Hello Wordl!";
QFile *file = new QFile("donnees.dat");
if (file->open(QFile::WriteOnly | QIODevice::Text))
{
QTextStream out(file);
out << uneDonnee;
out << endl;
file->close();
}
else
{
QMessageBox::critical(this, "Erreur", QString::fromUtf8("Erreur enregistrement !"), QMessageBox::Ok, 0);
}
delete file;
QTextEdit *editor = new QTextEdit(this);
...
// L'impression
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Imprimer"));
if (editor->textCursor().hasSelection())
dialog->addEnabledOption(QAbstractPrintDialog::PrintSelection);
if (dialog->exec() == QDialog::Accepted)
{
// Solution simple et directe
editor->print(&printer);
}
Pour imprimer un widget, il faut utiliser la méthode QWidget::render()
.
Centre le widget sur la page et le redimensionne pour remplir la page :
QPrinter printer;
QPainter painter;
painter.begin(&printer);
double xscale = printer.pageRect().width()/double(myWidget->width());
double yscale = printer.pageRect().height()/double(myWidget->height());
double scale = qMin(xscale, yscale);
painter.translate(printer.paperRect().x() + printer.pageRect().width()/2,
printer.paperRect().y() + printer.pageRect().height()/2);
painter.scale(scale, scale);
painter.translate(-width()/2, -height()/2);
myWidget->render(&painter);
Remarque : la résolution des imprimantes est probablement différente (souvent plus grande) que la résolution de l’écran.
qreal ratioPx = printer->paperRect().topRight().x() / printer->paperRect(QPrinter::Millimeter).topRight().x(); // mm -> px
qreal ratioMm = printer->paperRect(QPrinter::Millimeter).topRight().x() / printer->paperRect().topRight().x(); // px -> mm
qreal PixelToMillimeter(int pixel)
{
return (qreal)(pixel*ratioMm);
}
int MillimeterToPixel(qreal mm)
{
return (int)(mm*ratioPx);
}
/* x, y et marge en mm */
void ecrireLigneTexteEncadre(QPainter *painter, qreal x, qreal y, qreal marge, QString ligne)
{
QRect rLigne;
QFontMetrics fmTF = painter->fontMetrics();
rLigne = fmTF.boundingRect(ligne);
rLigne.adjust(MillimeterToPixel(x-marge), MillimeterToPixel(y-marge), MillimeterToPixel(x+marge), MillimeterToPixel(y+marge));
painter->drawRect(rLigne);
painter->drawText(MillimeterToPixel(x), MillimeterToPixel(y), ligne);
}
/* x, y et marge en mm */
void ecrireLigneTexteSouligne(QPainter *painter, qreal x, qreal y, qreal marge, QString ligne)
{
QRect rLigne;
QFontMetrics fmTF = painter->fontMetrics();
rLigne = fmTF.boundingRect(ligne);
rLigne.adjust(MillimeterToPixel(x-marge), MillimeterToPixel(y-marge), MillimeterToPixel(x+marge), MillimeterToPixel(y+marge));
painter->drawLine(rLigne.bottomRight(), rLigne.bottomLeft());
painter->drawText(MillimeterToPixel(x), MillimeterToPixel(y), ligne);
}
Suivant votre version de Qt :
QextSerialPort
disponible ici.#include "qextserialport.h"
#define PORT_LINUX "/dev/ttyUSB0"
#define PORT_WINDOWS "COM1"
// instanciation du port en mode synchrone -> QextSerialPort::EventDriven
QextSerialPort *port = new QextSerialPort(QLatin1String(PORT_LINUX), QextSerialPort::EventDriven);
// Ou :
// instanciation du port en mode asynchrone -> QextSerialPort::Polling
QextSerialPort *port = new QextSerialPort(QLatin1String(PORT_LINUX), QextSerialPort::Polling);
// configuration
port->setBaudRate(BAUD9600);
port->setDataBits(DATA_8);
port->setParity(PAR_NONE);
port->setStopBits(STOP_1);
port->setFlowControl(FLOW_OFF);
// ouverture du port en lecture/écriture
port->open(QIODevice::ReadWrite);
/** @todo : réceptionner et/ou envoyer des données sur le port */
// fermeture du port
port->close();
QSerialPort
! Il faudra ajouter le module serialport
à la variable QT
dans le fichier .pro
(QT += serialport
)#include <QSerialPort>
#define PORT_LINUX "/dev/ttyUSB0"
#define PORT_WINDOWS "COM1"
// instanciation du port
QSerialPort *port = new QSerialPort(QLatin1String(PORT_LINUX));
// configuration
port->setBaudRate(QSerialPort::Baud9600);
port->setDataBits(QSerialPort::Data8);
port->setParity(QSerialPort::NoParity);
port->setStopBits(QSerialPort::OneStop);
port->setFlowControl(QSerialPort::NoFlowControl);
// ouverture
port->open(QIODevice::ReadWrite);
/** @todo : réceptionner et/ou envoyer des données sur le port */
// fermeture du port
port->close();
Les fonctions d’écriture sont les suivantes :
qint64 write(const char * data, qint64 maxSize)
qint64 write(const char * data)
qint64 write(const QByteArray & byteArray)
Pour aller plus loin, on dispose :
bytesWritten(qint64 bytes)
: ce signal est émis chaque fois bytes octets de données ont été écrits sur le périphérique.waitForBytesWritten(int msecs)
: elle attend jusqu’à ce que le signal bytesWritten()
ait été émis ou que msecs millisecondes soient passées. Il est déconseillé de l’utiliser à partir du thread GUI.setTimeout(long millisec)
: elle définit les délais d’attente d’écriture (et aussi de lecture) pour le port en millisec millisecondes. C’est un délai d’expiration par caractère individuel et non pour l’opération entière.Les fonctions de lecture sont très nombreuses :
qint64 read(char * data, qint64 maxSize)
QByteArray read(qint64 maxSize)
QByteArray readAll()
qint64 readLine(char * data, qint64 maxSize)
QByteArray readLine(qint64 maxSize = 0)
La classe QextSerialPort
(Qt4) ou QSerialPort
(Qt5) fournit le signal readyRead()
. Ce signal est émis une fois que de nouvelles données sont disponibles pour la lecture à partir du périphérique.
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QUdpSocket
:
Création d’une socket et son attachement sur le port 5000 des interfaces locales de la machine :
QUdpSocket udpSocket = new QUdpSocket(this);
// Attachement locale de la socket UDP :
udpSocket->bind(QHostAddress((QString)"0.0.0.0"), 5000);
udpSocket->bind(QHostAddress::LocalHost, 5000);
// ...
// on ferme la socket
udpSocket->close();
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QUdpSocket
:
On réalise la connexion du signal readyRead()
au slot ReceptionnerDatagrammes()
:
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(ReceptionnerDatagrammes()));
La méthode ReceptionnerDatagrammes()
assure donc la réception des datagrammes UDP. Pour cela, elle réalise un boucle d’attente sur la présence de données dans la socket.
void MaClasse::ReceptionnerDatagrammes()
{
int nbOctets = 0;
int etat;
// datagramme en attente d'être lu ?
while (udpSocket->hasPendingDatagrams())
{
QByteArray donneesDatagramme;
QHostAddress emetteurAdresse;
quint16 emetteurPort;
// Fixe la taille du tableau au nombre d'octets reçus en attente
donneesDatagramme.resize(udpSocket->pendingDatagramSize());
// Lit le datagramme en attente
//nbOctets = udpSocket->readDatagram(datagram.data(), datagram.size());
nbOctets = udpSocket->readDatagram(donneesDatagramme.data(), donneesDatagramme.size(), &emetteurAdresse, &emetteurPort);
QString qs_emetteurAdresse = emetteurAdresse.toString();
qDebug() << "<" << qs_emetteurAdresse.toStdString() << ":" << emetteurPort
<< "> datagramme de " << nbOctets << " octet(s) reçu(s)";
}
}
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QUdpSocket
:
int MaClasse::EnvoyerDatagramme()
{
int nbOctets;
QByteArray datagramme = "WIDD";
//nbOctets = udpSocket->writeDatagram(datagramme.data(), datagramme.size(), QHostAddress::Broadcast, 5000);
nbOctets = udpSocket->writeDatagram(datagramme.data(), datagramme.size(), QHostAddress((QString)"10.7.89.95"), 5000);
return nbOctets;
}
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QTcpSocket
:
Création d’une socket TCP et sa connexion vers un serveur TCP :
QTcpSocket *socket = new QTcpSocket(this);
QHostAddress adresseServeur("127.0.0.1");
int portServeur = 5000;
//socket->connectToHost("127.0.0.1", portServeur);
socket->connectToHost(adresseServeur, portServeur);
if(!socket->isOpen())
{
qDebug() << socket->errorString());
}
else
{
// quelques signaux
connect(socket, SIGNAL(connected()), this, SLOT(estConnecte()));
connect(socket, SIGNAL(disconnected()), this, SLOT(estDeconnecte()));
connect(socket, SIGNAL(readyRead()), this, SLOT(recevoir()));
if(!socket->waitForConnected(5000))
{
qDebug() << socket->errorString());
}
}
Quelques slots :
void MaClasse::estConnecte()
{
QTcpSocket *serveur = qobject_cast<QTcpSocket *>(sender());
if (serveur == 0) // aucun ?
return;
qDebug() << QString::fromUtf8("Connexion réussie au serveur (") + serveur->peerAddress().toString() + QString::fromUtf8(":") + QString::number(serveur->peerPort()) + QString::fromUtf8(")");
}
void MaClasse::estDeconnecte()
{
QTcpSocket *serveur = qobject_cast<QTcpSocket *>(sender());
if (serveur == 0) // aucun ?
return;
qDebug() << QString::fromUtf8("Déconnexion du serveur (") + serveur->peerAddress().toString() + QString::fromUtf8(":") + QString::number(serveur->peerPort()) + QString::fromUtf8(")");
}
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QTcpSocket
:
Avec la méthode write()
par exemple :
QString message = "Hello world !\n";
socket->write(qPrintable(message));
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QTcpSocket
:
On réalise la connexion du signal readyRead()
au slot recevoir()
:
connect(socket, SIGNAL(readyRead()), this, SLOT(recevoir()));
Puis avec la méthode readAll()
par exemple :
void MaClasse::recevoir()
{
QTcpSocket *serveur = qobject_cast<QTcpSocket *>(sender());
if (serveur == 0) // aucun ?
return;
QByteArray donneesRecues;
donneesRecues = serveur->readAll();
QString reponse(donneesRecues.constData());
qDebug() << donneesRecues;
}
Qt fournit un module QtNetwork
qu’il faut activer dans son fichier de projet .pro
:
QT += network
Dans ce module, Qt fournit de nombreuses classes dont la classe QTcpServer
:
Création d’une socket TCP et sa connexion vers un serveur TCP :
QTcpServer *serveur = new QTcpServer(this);
// Démarrage du serveur sur toutes les IP locales disponibles et sur le port 5000
if (!serveur->listen(QHostAddress::Any, 5000))
{
qDebug() QString::fromUtf8("Erreur démarrage serveur : ") + serveur->errorString();
}
else
{
// slot pour gérer la connxion des clients
connect(serveur, SIGNAL(newConnection()), this, SLOT(connecterClient()));
}
Le slot pour gérer la connexion des clients :
void MaClasse::connecterClient()
{
QTcpSocket *nouveauClient = serveur->nextPendingConnection();
qDebug() << QString::fromUtf8("<Dialogue> Connexion client (") + nouveauClient->peerAddress().toString() + QString::fromUtf8(":") + QString::number(nouveauClient->peerPort()) + QString::fromUtf8(")");
// envisager de stocker ce nouveau client
// quelques slots
connect(nouveauClient, SIGNAL(readyRead()), this, SLOT(recevoir()));
connect(nouveauClient, SIGNAL(disconnected()), this, SLOT(deconnecterClient()));
}
void MaClasse::deconnecterClient()
{
// On détermine quel client se déconnecte
QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
if (client == 0) // aucun client ?
return;
dQebug() << QString::fromUtf8("<Dialogue> Déconnexion client (") + client->peerAddress().toString() + QString::fromUtf8(":") + QString::number(client->peerPort()) + QString::fromUtf8(")");
// supprimer le client stocké précedemment
client->deleteLater();
}
Les outils de base de Qt sont :
qmake
: un programme qui gère le processus de création des projets de développement sur différentes plates-formes. Il automatise la génération des Makefiles pour n’importe quel projet logiciel, qu’il soit écrit avec Qt ou non.moc
(Meta Object Compiler) : un programme qui gère les spécificités de Qt en générant des fichiers 100% C++.uic
(User Interface Compiler) : un programme qui génére des classes C++ à partir de fichiers .ui
en XML décrivant une interface graphique.Remarque : g++
et make
sont des outils indépendants qe Qt.
Étape n°1 : création du répertoire et du fichier main.cpp
Étape n°2 : création d’une classe Widget
(avec un QLabel
et un QPushButton
)
Étape n°3 : création et fabrication du projet Qt
On obtient :
Étape n°4 (bonus) : voyage au coeur d’un exécutable Qt
Étape n°5 : nettoyage du projet
Remarque : une fois le projet Qt “nettoyé”, on obtient les seuls fichiers à conserver pour ce projet Qt (par exemple pour un projet collaboratif avec svn
ou git
). Les autres fichiers (dont l’exécutable) sont des fichiers générés (ils n’ont AUCUNE VALEUR pour un développeur).