1.Qt IHM
Comment créer une interface Homme-Machine à l'aide de QT ?
Durée approximative de l'activité pratique (4h) :

Notation en séances : 20 questions pour 20 points
Dans cette activité, on vous demande de bien assimiler les différentes étapes pour aboutir aux différents résultats. Un compte rendu numérique sera demandé à l'issue de cette activité.
Les questions/différents points d'avancement sont à inscrire sur votre compte rendu précédés d'un CRxx, garder bien cette numérotation.
Le compte rendu final sera à déposer sur filezilla en fin de séance.
Qt est normalement installé sur votre poste. Vous devez avoir un raccourci sur votre bureau, si ce n'est pas le cas, crée en un à partir du chemin suivant :
C:\Qt\Qt5.12.12\Tools\QtCreator\bin\qtcreator.exe

la version de Qt peut varier en version ce n'est pas un problème
Qt est une API orientée objet qui offre des composants d'interface graphique, d'accès aux données, de connexions réseaux,, d'analyse XML, etc. Qt est connu pour être la bibliothèque sur laquelle repose l'un des environnements de bureau les plus utilisés dans le monde GNU/Linux.
Chaque OS propose au moins une bibliothèque qui permet de créer des fenêtres. Le défaut de cette méthode est qu'en général, cette bibliothèque ne fonctionne que pour l'OS pour lequel elle a été créée. Il existe cependant des bibliothèques multiplate-forme qui permettent de s'affranchir de ce handicap. Voici quelques-unes des principales bibliothèques multiplate-forme :
- .NET (prononcez « Dot Net ») : développé par Microsoft pour succéder à la vieillissante API Win32. On l'utilise souvent en langage C#. On peut néanmoins utiliser .NET dans une multitude d'autres langages dont le C++.
- GTK+ : une des plus importantes bibliothèques utilisées sous Linux. Elle est portable, c'est-à-dire utilisable sous Linux, Mac OS X et Windows. GTK+ est utilisable en C mais il en existe une version C++ appelée GTKmm (on parle de wrapper ou encore de surcouche).
- wxWidgets : une bibliothèque objet très complète, comparable à Qt. wxWidgets n'est pas beaucoup plus compliquée que Qt. C'est la bibliothèque utilisée pour réaliser la GUI de l'IDE Code::Blocks.
- FLTK : contrairement à toutes les bibliothèques « poids lourds » précédentes, FLTK se veut légère. C'est une petite bibliothèque dédiée uniquement à la création d'interfaces graphiques multiplateforme.
- Qt : très utilisée sous Linux, en particulier dans l'environnement de bureau KDE.
Qt est donc constituée d'un ensemble de bibliothèques, appelées « modules ». On peut y trouver entre autres ces fonctionnalités :
- Module GUI : c'est toute la partie création de fenêtres. Nous nous concentrerons surtout, dans ce cours, sur le module GUI.
- Module OpenGL : Qt peut ouvrir une fenêtre contenant de la 3D gérée par OpenGL.
- Module réseau : Qt fournit une batterie d'outils pour accéder au réseau, que ce soit pour créer un logiciel de Chat, un client FTP, un client Bittorent, un lecteur de flux RSS…
- Module XML : pour ceux qui connaissent le XML, c'est un moyen très pratique d'échanger des données à partir de fichiers structurés à l'aide de balises, comme le XHTML.
- Module SQL : permet d'accéder aux bases de données (MySQL, Oracle, PostgreSQL…).
Qt est déja installé sur les postes de la salle SIN.
Après avoir lancé Qtcreator à l'aide du raccourci présent sur votre bureau
, vous pouvez constater qu'il y a déja des exemples prêts à l'emploi :

Il est possible de faire différentes choses avec cette IDE, le développement de l'application est multi-plateforme.
La procédure pour déployer des applications Qt est différente pour les différentes plateformes :
Qt pour les plateformes X11 ;
Qt pour Windows ;
Qt pour Mac OS X ;
Qt pour les linux embarqués ;
Qt pour Symbian.
Nous allons ici créer un nouveau projet pour la plateforme WINDOWS en allant dans le menu Fichier > Nouveau fichier ou projet.

On vous propose pour Applications (Qt) deux possibilités : application graphique pour ordinateur, ou application en mode console. Nous verrons les autres possibilités plus tard.

Choisissez Application (Qt) puis Qt Widgets Application et cliquez sur "Choose" :
Un assistant s'ouvre alors pour vous demander le nom du projet et l'emplacement où vous souhaitez l'enregistrer :

Donnez le nom "test" puis cliquez sur "suivant".
On vous demande de définir une façon de construire le système, laissez la valeur par défaut :

Cliquez sur "suivant".
On vous demande ensuite le nom des paramètres que vous souhaitez utiliser dans votre programme (laissez les valeurs par défaut) :

Dans la fenêtre suivante, on vous demande la langue par défaut de l'application. Il est trés simple de traduire dans d'autres langues votre application.

Choisissez French de France puis faites "suivants"
On vous demande ensuite quels "Kits" de compilation de Qt vous voulez utiliser. Pour le moment, contentez-vous de laisser les cases cochées comme elles sont :

La fenêtre suivante vous demande quelques informations dont vous avez peut-être moins l'habitude : vous pouvez lier votre projet à un gestionnaire de version.
On peut associer le projet à un gestionnaire de version (comme SVN, Git). C'est un outil très utile, notamment si on travaille à plusieurs sur un code source…
Pour le moment, cliquez simplement sur « Suivant » :

Votre projet (ensemble des fichiers composant votre application) se compose de 6 fichiers comme indiqué ci-dessus. Cliquez sur "Terminer".
Par défaut, vous voila dans l'IDE sur le fichier "main.cpp". C'est dans ce fichier que nous écrirons nos premières lignes de code C++ en utilisant Qt.
Voici le contenu que vous devez avoir :
--------------------------------------------------------------------------------
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
--------------------------------------------------------------------------------
C'est le code minimal d'une application utilisant Qt. Analysons ce code :
#include "mainwindow.h"
Le fichier header permet de définir le prototype des fonctions. C'est aussi là qu'on définit les types personnalisés, les structures et les classes.
Ca sert à quoi? Premièrement à mieux s'y retrouver, on trouve la référence de ce qu'il y a dans un fichier c ou cpp dans le header qu'il inclut.
Mais aussi, ça permet de signaler l'existence d'une fonction pour l'utiliser avant de l'avoir définie.
#include <QApplication>
C'est le seul include dont vous avez besoin au départ. Il permet d'accéder à la classe QApplication, qui est la classe de base de tout programme Qt.
QApplication a(argc, argv);
La première ligne du main() crée un nouvel objet de type QApplication. Cet objet est appelé a (mais vous pouvez l'appeler comme vous voulez). Le constructeur de QApplication exige que vous lui passiez les arguments du programme, c'est-à-dire les paramètres argc et argv que reçoit la fonction main.
return app.exec();
Cette ligne fait 2 choses :
- Elle appelle la méthode exec de notre objet app. Cette méthode démarre le programme et lance donc l'affichage des fenêtres.
- Elle renvoie le résultat de app.exec() pour dire si le programme s'est bien déroulé ou pas. Le return provoque la fin de la fonction main, donc du programme.
On va maintenant lancer ce mini-programme comme ceci : compiler puis Exécuter

ou bien appuyer sur la flèche verte en bas à gauche.

Aprés un petit temps de compilation, le programme se lance :

Dans la plupart des bibliothèques GUI, dont Qt fait partie, tous les éléments d'une fenêtre sont appelés des widgets : les boutons, les cases à cocher, les images…
Pour provoquer l'affichage d'une fenêtre, il suffit de demander à afficher n'importe quel widget. Ici par exemple, nous allons afficher un bouton :
#include "mainwindow.h"
#include <QApplication>
#include <QPushButton> // permet de créer des objets QpushBUtton (des boutons)
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton button("Ca roule en STI2D ?"); // constructeur
button.show(); //permet d'afficher le bouton
return a.exec();
}
Recopier ce texte dans la zone du fichier main.cpp
Astuce : faites CTRL + Espace pour avoir la complétion des fonctions dans l'IDE. Commencer à écrire "QPu" puis faites CTRL + espace et l'IDE va vous proposer 3 possibilités :

Vérifier la marge à gauche si il y a des petites boules rouges c'est qu'il y a des erreurs et qu'il faut les corriger sinon le programme ne compilera pas :

En cliquant sur l'ampoule, vous allez pouvoir avoir une aide parfois précise parfois moins précise sur la solution au problème.
Lancer le programme.
CR 1 : Changer le texte du bouton. Faites une capture d'écran de la fenêtre obtenue. Insérer la dans votre compte-rendu.
L'exécutable qui a été généré ne pourra probablement pas lancer votre programme car il a besoin d'une série de fichiers DLL.
Quand vous exécutez votre programme depuis Qt Creator, la position des DLL est « connue », donc le programme se lance sans erreur.
Pour pouvoir lancer l'exécutable depuis l'explorateur, il faut placer les DLL qui manquent dans le même dossier que l'exécutable. À vous de les chercher sur votre disque.

Prenez les fichiers que vous pensez nécessaire, copiez les sur une clé USB puis essayer sur un autre poste de la salle.
CR 2 : Indiquez sur votre compte rendu si cela fonctionne sur un autre poste ?
CR 3 : Pensez-vous que votre programme fonctionnera sur un macintosh sous tiger OS ? Pourquoi ?
Il existe pour chaque widget, y compris le bouton, un grand nombre de propriétés que l'on peut éditer. Pour chacune d'elle, la documentation de Qt nous indique de quel type est l'attribut, à quoi sert cet attribut et les accesseurs qui permettent de lire et modifier l'attribut.
Essayons donc de modifier le texte du bouton et utiliser quelques propriétés après sa création :
#include "mainwindow.h"
#include <QApplication>
#include <QPushButton> // permet de créer des objets QpushBUtton (des boutons)
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget fenetre; // création d'une fenêtre
fenetre.setFixedSize(300, 150); // taille de la fenêtre
QPushButton bouton("La forme ?", &fenetre); // la fenêtre est le parent du bouton
bouton.setText("Mon bouton !"); // le nouveau texte
bouton.setToolTip("Texte d'aide"); // info bulle
bouton.setFont(QFont("Verdana", 14)); // nouvelle police
bouton.setCursor(Qt::PointingHandCursor); // nouveau curseur
bouton.setIcon(QIcon("chat.jpg")); // ajout smiley devant texte
bouton.setGeometry(60, 50, 180, 70); // position absolue du bouton
fenetre.show(); // affichage
return a.exec();
}
Explication du code :
On créé une fenêtre à l'aide d'un objet de type Qwidget, puis on dimensionne le widget (la fenêtre) avec la méthode setFixedSize. La taille de la fenêtre étant fixée : on ne pourra pas la redimensionner.
On créé un bouton mais en indiquant un pointeur vers le widget parent (en l’occurrence la fenêtre) afin de faire contenir le bouton dans la fenêtre pour pouvoir le redimensionner.
La signature de la méthode setFont est : void setFont(const QFont &)
Le constructeur de QFont attend quatre paramètres. Voici son prototype :
QFont(const QString & family, int pointSize = -1, int weight = -1, bool italic = false)
Seul le premier argument est obligatoire : il s'agit du nom de la police à utiliser. Les autres, comme vous pouvez le voir, possèdent des valeurs par défaut. Nous ne sommes donc pas obligés de les indiquer. Dans l'ordre, les paramètres signifient :
- family : le nom de la police de caractères à utiliser.
- pointSize : la taille des caractères en pixels.
- weight : le niveau d'épaisseur du trait (gras). Cette valeur peut être comprise entre 0 et 99 (du plus fin au plus gras). Vous pouvez aussi utiliser la constante QFont::Bold qui correspond à une épaisseur de 75.
- italic : un booléen, pour indiquer si le texte doit être affiché en italique ou non.
Les attributs qui stockent du texte avec Qt est de type QString. En effet, Qt n'utilise pas la classe string standard du C++ mais sa propre version de la gestion des chaînes de caractères.
La méthode setIcon rajoute une icône au bouton et attend un objet de type QIcon.
Un QIcon se construit en donnant le nom du fichier image à charger :
- sous Windows, pour que cela fonctionne, votre icône smile.png doit se trouver dans le même dossier que l'exécutable (ou dans un sous-dossier si vous écrivez dossier/smile.png).
- sous Linux, il faut que votre icône soit dans votre répertoire HOME. Si vous voulez utiliser le chemin de votre application, écrivez : QIcon(QCoreApplication::applicationDirPath() + "/smile.png").
La méthode setGeometry permet, en plus de déplacer le widget, de lui donner des dimensions bien précise. La méthode setGeometry, qui prend 4 paramètres :
bouton.setGeometry(int abscisse, int ordonnee, int largeur, int hauteur)
À noter aussi, si vous voulez placer le bouton ailleurs dans la fenêtre, la méthode move :
bouton.move(int abscisse, int ordonnee)

CR 4 : Pourquoi n'avez-vous pas l'image du chat à gauche de votre bouton ?
CR 5 : Corriger cela et expliquez comment vous avez fait (copier votre code dans le compte-rendu) Indice : utiliser le chemin absolu pour accèder au fichier et doubler les \ pour être compris en c++
CR 6 : Faites une capture d'écran en ayant utilisé l'application pour chacune des 3 images présentes dans le dossier image.
Le fait de pouvoir créer une classe de base, réutilisée par des sous-classes filles, qui ont elles-mêmes leurs propres sous-classes filles, cela donne à une bibliothèque comme Qt une puissance infinie.
QObject est la classe de base de tous les objets sous Qt.
Un schéma simplifié des héritages de Qt permet de mieux visualiser la hiérarchie des classes :

Dans une fenêtre, tout est considéré comme un widget (même la fenêtre est un widget). C'est pour cela qu'il existe une classe de base QWidget pour tous les widgets. Elle contient énormément de propriétés communes à tous les widgets, comme :
- la largeur
- la hauteur
- la position en abscisse (x)
- la position en ordonnée (y)
- la police de caractères utilisée (eh oui, la méthode setFont est définie dans QWidget et comme QPushButton en hérite, il possède lui aussi cette méthode)
- le curseur de la souris (setCursor est en fait défini dans QWidget et non dans QPushButton car il est aussi susceptible de servir sur tous les autres widgets)
- l'infobulle (toolTip)
- etc.
Grâce à cette technique, il leur a suffit de définir une fois toutes les propriétés de base des widgets (largeur, hauteur…). Tous les widgets héritent de QWidget, donc ils possèdent toutes ces propriétés. Vous savez donc par exemple que vous pouvez retrouver la méthode setCursor dans la classe QProgressBar.
CR 7 : Est-ce que la classe QPushButton partage des propriétés de la classe QProgressBar ?
CR 8 : Est-ce que la classe QLCDNumber partage des propriétés de la classe QWidget ? Quelle(s) autre(s) propriété(s) partage-t-elle ?
Un widget peut en contenir un autre. Par exemple, une fenêtre (un QWidget) peut contenir trois boutons (QPushButton), une case à cocher (QCheckBox), une barre de progression (QProgressBar), etc.

Sur cette capture, la fenêtre contient trois widgets :
- un bouton OK ;
- un bouton Annuler ;
- un conteneur avec des onglets.
Le conteneur avec des onglets est, comme son nom l'indique, un conteneur. Il contient à son tour des widgets :
- deux boutons ;
- une case à cocher (checkbox) ;
- une barre de progression.
Les widgets sont donc imbriqués les uns dans les autres suivant cette hiérarchie :
- QWidget (la fenêtre)
- QPushButton
- QpushButton
- QTabWidget (le conteneur à onglets)
- QPushButton
- QPushButton
- QCheckBox
- QProgressBar
Tous les widgets possèdent un constructeur surchargé qui permet d'indiquer quel est le parent du widget que l'on crée. Il suffit de donner un pointeur pour que Qt sache « qui contient qui ». Quel que soit le widget, son constructeur accepte en dernier paramètre un pointeur vers un autre widget, pointeur qui indique quel est le parent.
Nous allons créer une nouvelle classe qui hérite de QWidget et représente notre fenêtre. Créer une classe pour gérer la fenêtre peut paraître un peu lourd au premier abord, mais c'est ainsi qu'on procède à chaque fois que l'on crée des GUI en POO. Cela nous donnera une plus grande souplesse par la suite :
- MaFenetre.h : contiendra la définition de la classe (Fichier/C++/C++ Header File)
- MaFenetre.cpp : contiendra l'implémentation des méthodes (Fichier/C++/C++ Source File)
Voici le code du fichier MaFenetre.h :
#ifndef MAFENETRE_H
#define MAFENETRE_H
#include <QApplication>
#include <QWidget>
#include <QPushButton>
class MaFenetre : public QWidget // On hérite de QWidget (IMPORTANT)
{
public:
MaFenetre();
private:
QPushButton *m_bouton;
};
#endif // MAFENETRE_H
Voici le code du fichier MaFenetre.cpp :
#include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(200, 150); // on définit la taille de la fenêtre de manière fixée
// Construction du bouton
// NB : le second paramètre du constructeur doit être un pointeur vers le widget parent
m_bouton = new QPushButton("Salut !", this);
m_bouton->setFont(QFont("Comic Sans MS", 14));
m_bouton->setCursor(Qt::PointingHandCursor);
m_bouton->setIcon(QIcon("chat.png"));
m_bouton->move(60, 50);
}
Voici le code du fichier main.cpp :
#include "mainwindow.h"
#include <QApplication>
#include <QPushButton> // permet de créer des objets QpushBUtton (des boutons)
#include "MaFenetre.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MaFenetre fenetre; // on crée un objet de type MaFenetre
fenetre.show();
return app.exec();
}
Remarque : la destruction des widgets enfants est automatique. En effet, quand on supprime un widget parent (ici notre fenêtre), Qt supprime automatiquement tous les widgets qui se trouvent à l'intérieur (tous les widgets enfants).
CR 9 : Que pensez-vous de ce mode de fonctionnement ?
Une application de type GUI réagit à partir d'évènements. Dans Qt, on parle de signaux et de slots :
- Un signal : c'est un message envoyé par un widget lorsqu'un évènement se produit.
- Un slot : c'est la fonction qui est appelée lorsqu'un évènement s'est produit. On dit que le signal appelle le slot. Concrètement, un slot est une méthode d'une classe.
Exemple : le slot quit() de la classe QApplication provoque l'arrêt du programme.
Les signaux et les slots sont considérés par Qt comme des éléments d'une classe à part entière, en plus des attributs et des méthodes.
Exemple : un clic sur un bouton doit provoquer l'arrêt de l'application.

Pour cela, nous devons utiliser une méthode statique de QObject : connect().
La méthode connect prend 4 arguments :
- un pointeur vers l'objet qui émet le signal
- le nom du signal que l'on souhaite « intercepter »
- un pointeur vers l'objet qui contient le slot récepteur
- le nom du slot qui doit s'exécuter lorsque le signal se produit
Voici le nouveau code du fichier MaFenetre.cpp :
#include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(200, 150);
m_bouton = new QPushButton("Quitter", this);
m_bouton->setFont(QFont("Verdana", 14));
m_bouton->move(60, 50);
// Connexion du clic du bouton à la fermeture de l'application
QObject::connect(
m_bouton, // pointeur vers le bouton qui va émettre le signal
SIGNAL(clicked()), // SIGNAL() est une macro du préprocesseur
qApp, // pointeur vers l'objet de type Qapplication créé dans le main
SLOT(quit())); // macro appelée lorsqu'on a cliqué sur le bouton
}
CR 10 : Expliquer le nouveau mode de fonctionnement de la fenêtre issu du code de la page "MaFenetre.cpp" ? Que se passe-t-il concrètement ?
D'autres bibliothèques, comme wxWidgets, utilisent à la place de nombreuses macros et se servent du mécanisme un peu complexe et délicat des pointeurs de fonction (pour indiquer l'adresse de la fonction à appeler en mémoire).
Qt offre la possibilité aux signaux et aux slots de s'échanger des paramètres !
Pour illustrer ce propos, nous allons utiliser deux nouveaux widgets :
- QSlider : un curseur qui permet de définir une valeur
- QLCDNumber : un widget qui affiche un nombre
Voici le nouveau code du fichier MaFenetre.h :
#ifndef DEF_MAFENETRE
#define DEF_MAFENETRE
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QLCDNumber>
#include <QSlider>
class MaFenetre : public QWidget
{
public:
MaFenetre();
private:
QLCDNumber *m_lcd;
QSlider *m_slider;
};
#endif
Et le fichier MaFenetre.cpp :
#include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(200, 100);
m_lcd = new QLCDNumber(this);
m_lcd->setSegmentStyle(QLCDNumber::Flat);
m_lcd->move(50, 20);
m_slider = new QSlider(Qt::Horizontal, this);
m_slider->setGeometry(10, 60, 150, 20);
QObject::connect(m_slider, SIGNAL(valueChanged(int)), m_lcd, SLOT(display(int))) ;
}
On a modifié le type d'afficheur LCD pour qu'il soit plus lisible (avec setSegmentStyle). Quant au slider, on a rajouté un paramètre pour qu'il apparaisse horizontalement (sinon il est vertical).
L'afficheur LCD change de valeur en fonction de la position du curseur du slider avec le code suivant :
QObject::connect(m_slider, SIGNAL(valueChanged(int)), m_lcd, SLOT(display(int))) ;
On dispose du signal et du slot suivants :
- Le signal valueChanged(int) du QSlider : il est émis dès que l'on change la valeur du curseur du slider en le déplaçant. La particularité de ce signal est qu'il envoie un paramètre de type int (la nouvelle valeur du slider).
- Le slot display(int) du QLCDNumber : il affiche la valeur qui lui est passée en paramètre.
Il suffit d'indiquer le type du paramètre envoyé, ici un int, sans donner de nom à ce paramètre. Qt fait automatiquement la connexion entre le signal et le slot et « transmet » le paramètre au slot.
Le type des paramètres doit absolument correspondre : vous ne pouvez pas connecter un signal qui envoie (int, double) à un slot qui reçoit (int, int). C'est un des avantages du mécanisme des signaux et des slots : il respecte le type des paramètres.
En revanche, un signal peut envoyer plus de paramètres à un slot que celui-ci ne peut en recevoir. Dans ce cas, les paramètres supplémentaires seront ignorés.
CR 11 : Expliquer en commentant chaque ligne de "MaFenetre.cpp". Faites une capture d'écran du résultat.
Les boîtes de dialogue « afficher un message » sont contrôlées par la classe QMessageBox. Nous allons créer dans la boîte de dialogue un bouton qui appelle le slot personnalisé ouvrirDialogue(). C'est dans ce slot que nous nous chargerons d'ouvrir une boîte de dialogue.
//----- MaFenetre.h
#ifndef MAFENETRE_H
#define MAFENETRE_H
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMessageBox> // à inclure pour déclarer la classe QMessageBox
class MaFenetre : public QWidget
{
Q_OBJECT
public:
MaFenetre();
public slots:
void ouvrirDialogue();
private:
QPushButton *m_boutonDialogue;
};
#endif
//----- MaFenetre.cpp
#include "MaFenetre.h"
MaFenetre::MaFenetre() : QWidget()
{
setFixedSize(230, 120);
m_boutonDialogue = new QPushButton("Ouvrir la boîte de dialogue", this);
m_boutonDialogue->move(40, 50);
QObject::connect(m_boutonDialogue, SIGNAL(clicked()), this, SLOT(ouvrirDialogue()));
}
void MaFenetre::ouvrirDialogue()
{
// Vous insérerez ici le code d'ouverture des boîtes de dialogue
QMessageBox::information(this, "Titre de la fenêtre", "Bonjour et bienvenue !");
}
La classe QMessageBox permet de créer des objets de type QmessageBox, mais on utilise majoritairement ses méthodes statiques, pour des raisons de simplicité.
La méthode statique information() permet d'ouvrir une boîte de dialogue constituée d'une icône « information ».
StandardButton information (
QWidget * parent,
const QString & title,
const QString & text,
StandardButtons buttons = Ok,
StandardButton defaultButton= NoButton );
Seuls les trois premiers paramètres sont obligatoires, les autres ayant des valeurs par défaut :
- parent : un pointeur vers la fenêtre parente (qui doit être de type QWidget ou hériter de QWidget). Vous pouvez envoyer NULL en paramètre si vous ne voulez pas que la boîte de dialogue ait une fenêtre parente.
- title : le titre de la boîte de dialogue (affiché en haut de la fenêtre).
- text : le texte affiché au sein de la boîte de dialogue.
L'appel de la méthode statique se fait comme celui d'une fonction classique, à la différence près qu'il faut mettre en préfixe le nom de la classe dans laquelle elle est définie (QMessageBox).
NB : lorsque la boîte de dialogue est ouverte, on ne peut plus accéder à sa fenêtre parente en arrière-plan. On dit que la boîte de dialogue est une fenêtre modale : c'est une fenêtre qui « bloque » temporairement son parent en attente d'une réponse de l'utilisateur.
Remarque : il est possible d'enrichir le message à l'aide de balises (X)HTML.
Il existe trois autre méthode qui s'utilise de la même manière que QMessageBox::information dont l'icône change :
// Boîte de dialogue attention
QMessageBox::warning(this, "Titre de la fenêtre", "Fichier introuvable !");
// Boîte de dialogue erreur critique
QMessageBox::critical(this, "Titre de la fenêtre", "Fichier corrompu !");
// Boîte de dialogue question
QMessageBox::question(this, "Titre de la fenêtre", "Effacer ce fichier ?");
Pour personnaliser les boutons de la boîte de dialogue, il faut utiliser le quatrième paramètre de la méthode statique. Ce paramètre accepte une combinaison de valeurs prédéfinies, séparées par un OR ou PIPE (en anglais) touche ALT GR + 6 pour afficher la barre verticale |. On appelle cela des flags.
La liste des flags disponibles est donnée par la documentation :
- boutons « Oui » : QMessageBox::Yes
- boutons « Non » : QMessageBox::No
- boutons « Annuler » : QMessageBox::Cancel
CR 12 : Un fois que l'on clique sur le premier bouton, peut-on à nouveau cliquer sur celui-ci ? Pourquoi ?
CR 13 : Modifier le programme pour afficher 3 boutons :

qui en fonction de l'appui ouvriront :
Information :
Warning :
Question : 
Pour traduire le texte des boutons de la boîte de dialogue, il faut utiliser l'objet QTranslator :
// main.cpp
#include <QApplication>
#include <QTranslator>
#include <QLocale>
#include <QLibraryInfo>
#include "MaFenetre.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QString locale = QLocale::system().name().section('_', 0, 0);
QTranslator translator;
translator.load(
QString("qt_") + locale,
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&translator);
MaFenetre fenetre;
fenetre.show();
return app.exec();
}
Les méthodes statiques que nous venons de voir renvoient un entier (int). On peut tester facilement la signification de ce nombre à l'aide des valeurs prédéfinies par Q :
void MaFenetre::ouvrirDialogue()
{
int reponse = QMessageBox::question(this, "Avertissement", "Voulez-vous effacer ce fichier ?", QMessageBox ::Yes | QMessageBox::No);
if (reponse == QMessageBox::Yes)
{
QMessageBox::information(this, "Confirmation", "Demande enregistrée.");
}
else if (reponse == QMessageBox::No)
{
QMessageBox::critical(this, "Attention", "Le traitement sera interrompu !");
}
}
Pour saisir une information, ou faire un choix parmi une liste, les boîtes de dialogue de saisie sont idéales. Elles sont gérées par la classe QinputDialog. Les boîtes de dialogue « saisir une information » peuvent être de quatre types :
- saisir un texte : QInputDialog::getText() ;
- saisir un entier : QInputDialog::getInteger() ;
- saisir un nombre décimal : QInputDialog::getDouble() ;
- choisir un élément parmi une liste : QInputDialog::getItem().
La méthode statique QInputDialog::getText() a pour prototype :
QString QInputDialog::getText (
QWidget * parent,
const QString & title,
const QString & label,
QLineEdit::EchoMode mode = QLineEdit::Normal,
const QString & text = QString(),
bool * ok = 0,
Qt::WindowFlags f = 0 );
Les paramètres signifient, dans l'ordre :
- parent : pointeur vers la fenêtre parente. Peut être mis à NULL pour ne pas indiquer de fenêtre parente.
- title : titre de la fenêtre, affiché en haut.
- label : texte affiché dans la fenêtre.
- mode : mode d'édition du texte. Permet de dire si on veut que les lettres s'affichent quand on tape, ou si elles doivent être remplacées par des astérisques (pour les mots de passe) ou si aucune lettre ne doit s'afficher. Par défaut, les lettres s'affichent normalement (QLineEdit::Normal).
- text : texte par défaut dans la zone de saisie.
- ok : pointeur vers un booléen pour que Qt puisse vous dire si l'utilisateur a cliqué sur OK ou sur Annuler.
- f : quelques flags (options) permettant d'indiquer si la fenêtre est modale (bloquante) ou pas.
Exemple :
void MaFenetre::ouvrirDialogueQues()
{
// Vous insérerez ici le code d'ouverture des boîtes de dialogue
bool ok;
QString text = QInputDialog::getText(this, "Pseudo", "Entrer votre pseudo :", QLineEdit::Normal, QString(), &ok);
if ( ok && !text.isEmpty() ) {
//
} else {
//
}
}
Si un pseudo a été saisi et que l'utilisateur a cliqué sur OK, alors une boîte de dialogue lui souhaite la bienvenue. Sinon, une erreur est affichée.
CR 14 : En vous aidant de l'exemple ci-dessus, modifier le programme pour saisir une information dans le cas d'une ouverture d'une fenêtre de dialogue de question (indice, n'oubliez pas d'inclure les libliothéques #include <QString> etc. que vous utiliserez dans MaFenetre.cpp)
clique sur le 3eme bouton "Ouvrir la boîte de dialogue question" pour afficher 
si Cancel est appuyé ou si pas de saisie on affiche :

sinon on affiche le nom saisi :

CR 15 : Etablir l'algorithme de cette condition
CR 16 : Dessinez l'algorigramme.
CR 17 : Inscrivez les codes modifiés dans votre rapport
La boîte de dialogue de sélection de police est gérée par la classe QFontDialog. Celle-ci propose en gros une seule méthode statique surchargée :
QFont getFont (
bool * ok,
const QFont & initial,
QWidget * parent,
const QString & caption )
- le pointeur vers un booléen ok, permet de savoir si l'utilisateur a cliqué sur OK ou a annulé.
- on peut spécifier une police par défaut (initial), il faudra envoyer un objet de type QFont.
- la chaîne caption correspond au message qui sera affiché en haut de la fenêtre.
- la méthode renvoie un objet de type QFont correspondant à la police qui a été choisie.
Exemple : la police sélectionnée sera immédiatement appliquée au texte d'un bouton, par l'intermédiaire de la méthode setFont().
void MaFenetre::ouvrirDialogue()
{
bool ok = false;
Qfont police = QFontDialog::getFont(&ok, m_boutonDialogue->font(), this, "Choisissez une police");
if ( ok )
{
m_boutonDialogue->setFont(police);
}
}
La méthode getFont prend comme police par défaut celle qui est utilisée par le bouton m_boutonDialogue.
On teste si l'utilisateur a bien validé la fenêtre et, si c'est le cas, on applique au bouton la police qui vient d'être choisie.
|
Dans la même veine que la sélection de police, nous connaissons probablement tous la boîte de dialogue « Sélection de couleur » : |
|
Utilisez la classe QColorDialoget et sa méthode statique getColor().
QColor QColorDialog::getColor (
const QColor & initial = Qt::white,
QWidget * parent = 0 );
Elle renvoie un objet de type QColor. Vous pouvez préciser une couleur par défaut, en envoyant un objet de type QColor ou en utilisant une des constantes prédéfinies.
Pour appliquer une couleur au bouton, il fat utiliser la méthode setPalette qui sert à indiquer une palette de couleurs. Il faudra vous renseigner dans ce cas sur la classe QPalette.
void MaFenetre::ouvrirDialogue()
{
QColor couleur = QColorDialog::getColor(Qt::white, this);
QPalette palette;
palette.setColor(QPalette::ButtonText, couleur);
m_boutonDialogue->setPalette(palette);
}
Le code ouvre une boîte de dialogue de sélection de couleur, puis crée une palette où la couleur du texte correspond à la couleur qu'on vient de sélectionner, et enfin applique cette palette au bouton.
La sélection de fichiers et de dossiers est gérée par la classe QFileDialog qui propose des méthodes statiques.
QString dossier = QFileDialog::getExistingDirectory(this);
La méthode renvoie un QString contenant le chemin complet vers le dossier demandé.
5.5.2. Ouverture d'un fichier
La boîte de dialogue « Ouverture d'un fichier » est gérée par getOpenFileName().
Sans paramètre particulier, la boîte de dialogue permet d'ouvrir n'importe quel fichier. Vous pouvez néanmoins créer un filtre (en dernier paramètre) pour afficher par exemple uniquement les images.
void MaFenetre::ouvrirDialogue()
{
QString fichier = QfileDialog::getOpenFileName(
this,
"Ouvrir un fichier",
Qstring(), // nom du répertoire par défaut dans lequel l'utilisateur est placé
"Images (*.png *.gif *.jpg *.jpeg)");
QMessageBox::information(this, "Fichier", "Vous avez sélectionné :\n" + fichier);
}
Ce code demande d'ouvrir un fichier image. Le chemin vers le fichier est stocké dans un QString, que l'on affiche ensuite via une QMessageBox :
Le troisième paramètre de getOpenFileName est le nom du répertoire par défaut dans lequel l'utilisateur est placé. La valeur par défaut (QString(), équivaut à écrire ""), donc la boîte de dialogue affiche par défaut le répertoire dans lequel est situé le programme.
Grâce au quatrième paramètre, seules les images de type PNG, GIF, JPG et JPEG s'afficheront.
Le principe de cette boîte de dialogue est de vous donner le chemin complet vers le fichier, mais pas d'ouvrir ce fichier. C'est à vous, ensuite, de faire les opérations nécessaires pour ouvrir le fichier et l'afficher dans votre programme.
5.5.3. Enregistrement d'un fichier
C'est le même principe que la méthode précédente, à la différence près que, pour l'enregistrement, la personne peut cette fois spécifier un nom de fichier qui n'existe pas. Le bouton « Ouvrir » est remplacé par « Enregistrer » :
QString fichier = QfileDialog::getSaveFileName(
this,
"Enregistrer un fichier",
QString(),
"Images (*.png *.gif *.jpg *.jpeg)");
On distingue deux techniques différentes pour positionner des widgets :
- Le positionnement absolu : avec l'appel à la méthode setGeometry (ou move)... Ce positionnement est très précis car on place les widgets au pixel près.
- Le positionnement relatif : c'est le plus souple.
Nous allons créer une fenêtre avec un bouton placé aux coordonnées (70, 60) sur la fenêtre :
#include <QApplication>
#include <QpushButton>
int main(int argc, char *argv[])
{
Qapplication app(argc, argv);
Qwidget fenetre;
QPushButton bouton("Bonjour", &fenetre);
bouton.move(70, 60);
fenetre.show();
return app.exec();
}
Le bouton est positionné de manière absolue à l'aide de l'instruction bouton.move(70, 60);
Si la fenêtre est redimensionnée, le bouton ne bouge pas. Le positionnement absolu sert parfois quand on a vraiment besoin de positionner au pixel près.
Le positionnement relatif, consiste à expliquer comment les widgets sont agencés les uns par rapport aux autres. Le positionnement relatif est géré dans Qt par des layouts : ce sont des conteneurs de widgets.
Il existe par exemple des classes gérant le positionnement horizontal et vertical des widgets ou encore le positionnement sous forme de grille.

Toutes les classes héritent de la classe de base Qlayout (classe abstraite).
On compte donc en gros les classes :
- QBoxLayout
- QHBoxLayout
- QVBoxLayout
- QGridLayout
- QFormLayout
- QStackedLayout
6.2.1. Les layouts horizontaux et verticaux
Nous allons travailler sur deux classes :
- QHBoxLayout ;
- QVBoxLayout.
QHBoxLayout et QVBoxLayout héritent de QBoxLayout.
L'utilisation d'un layout se fait en 3 temps :
- On crée les widgets
- On crée le layout et on place les widgets dedans
- On dit à la fenêtre d'utiliser le layout qu'on a créé
Exemple main.cpp :
#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout> // ne pas oublier !
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
// création des widgets bouton
QPushButton *bouton1 = new QPushButton("Un");
QPushButton *bouton2 = new QPushButton("Deux");
QPushButton *bouton3 = new QPushButton("Trois");
// création d'un layout horizontal
QHBoxLayout *layout = new QHBoxLayout;
// placement des widgets dans le layout horizontal
layout->addWidget(bouton1);
layout->addWidget(bouton2);
layout->addWidget(bouton3);
// placement du layout dans la fenêtre
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
La fenêtre contient le layout qui contient les widgets. Le layout se charge d'organiser les widgets.

Le layout QHBoxLayout organise les widgets horizontalement. Il y en a un autre qui les organise verticalement : QVBoxLayout.
Pour utiliser un layout vertical, il suffit de remplacer QHBoxLayout par QVBoxLayout dans le code précédent.
Remarque : les widgets sont placés dans un layout, qui est lui-même placé dans la fenêtre. Lorsque la fenêtre est supprimée, tous les widgets contenus dans son layout sont supprimés par Qt.
QGridLayout offre une disposition en grille, comme un tableau avec des lignes et des colonnes :
|
Pour placer un widget en haut à gauche, il faudra le placer à la case de coordonnées (0, 0). Pour en placer un autre en-dessous, il faudra utiliser les coordonnées (1, 0) et ainsi de suite. |
|
Utilisation basique de la grille :
#include <QApplication>
#include <QPushButton>
#include <QGridLayout> // ne pas oublier !
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
// création des widgets bouton
QPushButton *bouton1 = new QPushButton("Un");
QPushButton *bouton2 = new QPushButton("Deux");
QPushButton *bouton3 = new QPushButton("Trois");
// création d'un layout grille
QGridLayout *layout = new QGridLayout;
// placement des widgets dans la grille
layout->addWidget(bouton1, 0, 0); // addWidget deux paramètres supplémentaires :
layout->addWidget(bouton2, 0, 1); // les coordonnées du widget sur la grille
layout->addWidget(bouton3, 1, 0);
// placement du layout dans la fenêtre
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
Qt « sait » quel est le widget à mettre en bas à droite en fonction des coordonnées des autres widgets. Le widget qui a les coordonnées les plus élevées sera placé en bas à droite. |
|
|
Pour décaler un bouton encore plus en bas à droite dans une nouvelle ligne et une nouvelle colonne : QPushButton *bouton4 = new QPushButton("autre..."); layout->addWidget(bouton4, 2, 2); |
|
Dans une disposition en grille, un widget peut occuper plusieurs cases à la fois. On parle de spanning.
Pour ce faire, il faut appeler une version surchargée de addWidget qui accepte deux paramètres supplémentaires : le rowSpan et le columnSpan.
- rowSpan : nombre de lignes qu'occupe le widget (par défaut 1) ;
- columnSpan : nombre de colonnes qu'occupe le widget (par défaut 1).
|
Imaginons un widget placé en haut à gauche, aux coordonnées (0, 0). Si on lui donne un rowSpan de 2, il occupera alors l'espace indiqué à la figure ci-contre. Si on lui donne un columnSpan de 3, il occupera l'espace indiqué sur la figure ci-contre. |
|
L'espace pris par le widget au final dépend de la nature du widget (les boutons s'agrandissent en largeur mais pas en hauteur par exemple) et du nombre de widgets sur la grille.
|
layout->addWidget(bouton3, 1, 0, 1, 2); Les deux derniers paramètres correspondent respectivement au rowSpan et au columnSpan. Le bouton va donc « occuper » 2 colonnes (figure ci-contre). |
|
6.2.3. Le layout de formulaire
Le layout de formulaire QFormLayout est un layout spécialement conçu pour les fenêtres hébergeant des formulaires.

La disposition correspond à celle d'un QGridLayout à 2 colonnes et plusieurs lignes. En effet, le QFormLayout n'est en fait rien d'autre qu'une version spéciale du QGridLayout pour les formulaires, avec quelques particularités : il s'adapte aux habitudes des OS pour, dans certains cas, aligner les libellés à gauche, dans d'autres à droite, etc.
Au lieu d'utiliser une méthode addWidget, nous allons utiliser une méthode addRow qui prend deux paramètres :
- le texte du libellé ;
- un pointeur vers le champ du formulaire.
#include <QApplication>
#include <QLineEdit>
#include <QFormLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QLineEdit *nom = new QLineEdit;
QLineEdit *prenom = new QLineEdit;
QLineEdit *age = new QLineEdit;
QFormLayout *layout = new QFormLayout;
// le symbole « & » permet de définir des raccourcis clavier
layout->addRow("Votre &nom", nom);
layout->addRow("Votre &prénom", prenom);
layout->addRow("Votre â&ge", age);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
On peut aussi définir des raccourcis clavier pour accéder rapidement aux champs du formulaire. Pour ce faire, placez un symbole « & » devant la lettre du libellé que vous voulez transformer en raccourci.
L'utilisation du raccourci dépend de votre système d'exploitation. Sous Windows, il faut appuyer sur la touche Alt puis la touche raccourci. Lorsque vous appuyez sur Alt, les lettres raccourcis apparaissent soulignées (figure suivante).
NB : pour afficher un symbole & dans un libellé, tapez-en deux : « && ».
Prenons l'exemple du formulaire et ajoutons un bouton « Quitter ».
Il faut d'abord créer un layout vertical (QVBoxLayout) et placer à l'intérieur notre layout de formulaire puis notre bouton « Quitter ».

QVBoxLayout contient deux choses, dans l'ordre :
- Un QFormLayout (qui contient lui-même d'autres widgets) ;
- Un QPushButton.
Un layout peut donc contenir aussi bien des layouts que des widgets. Pour insérer un layout dans un autre, on utilise addLayout :
#include <QApplication>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QformLayout>
int main(int argc, char *argv[])
{
Qapplication app(argc, argv);
Qwidget fenetre;
// Création du layout de formulaire et de ses widgets
QLineEdit *nom = new QLineEdit;
QLineEdit *prenom = new QLineEdit;
QLineEdit *age = new QlineEdit;
QFormLayout *formLayout = new QformLayout;
formLayout->addRow("Votre &nom", nom);
formLayout->addRow("Votre &prénom", prenom);
formLayout->addRow("Votre â&ge", age);
// Création du layout principal de la fenêtre (vertical)
QVBoxLayout *layoutPrincipal = new QVBoxLayout;
layoutPrincipal->addLayout(formLayout); // Ajout du layout de formulaire
QPushButton *boutonQuitter = new QpushButton("Quitter");
layoutPrincipal->addWidget(boutonQuitter); // Ajout du bouton
// traitement du signal du bouton quitter
QWidget::connect(boutonQuitter, SIGNAL(clicked()), &app, SLOT(quit()));
fenetre.setLayout(layoutPrincipal);
fenetre.show();
return app.exec();
}
On crée d'abord les widgets et layouts « enfants » (le QFormLayout) ; ensuite on crée le layout principal (le QVBoxLayout) et on y insère le layout enfant créé.
|
On ne le voit pas, mais la fenêtre contient d'abord un QVBoxLayout, qui contient lui-même un layout de formulaire et un bouton (figure ci-contre). |
|
L'utilisateur arrive sur une nouvelle fenêtre de votre choix.
CR 18 : Utiliser un layout pour disposer les différents objets composant la fenêtre.
CR 19 : Indiquez les noms des différents objets crées
CR 20 : Créer un formulaire de login qui autorise l'accès aux compte suivant :
- login : admin
- mot de passe : admin
et aussi le compte :
- login : ridley
- mot de passe : scott
Faites valider par le professeur.
Créé avec HelpNDoc Personal Edition: Éditeur de documentation CHM facile






