[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Officiels
> Programmation
> Apprenez à programmer en C++ ! > [Pratique] Créez vos propres fenêtres avec Qt > Les principaux widgets
> Lecture du tutoriel
Les principaux widgets
Voilà un moment que nous avons commencé à nous intéresser à Qt, je vous parle en long en large et en travers de widgets, mais jusqu'ici nous n'avions toujours pas pris le temps de faire un tour d'horizon rapide de ce qui existait.
C'était voulu. Je voulais dans un premier temps vous faire manipuler un ou deux widgets simples pour vous faire comprendre les concepts de base comme :
- La création de la fenêtre
- Les signaux et les slots
- Les layouts
Il est maintenant temps de faire une "pause" et de regarder ce qui existe comme widgets. Nous étudierons cependant seulement les principaux widgets ici.
Pourquoi ne les verrons-nous pas tous ? Parce qu'il existe un grand nombre de widgets et que certains sont rarement utilisés. D'autres sont parfois tellement complexes qu'ils nécessiteront un chapitre entier pour les étudier.
Néanmoins, avec ce que vous allez voir, vous aurez largement de quoi faire pour être capables de créer la quasi-totalité des fenêtres que vous voulez !
Avec Qt, tout élément de la fenêtre est appelé un
widget.
La fenêtre elle-même est considérée comme un widget.
Dans le code, les widgets sont des classes qui héritent toujours de
QWidget (directement ou indirectement). C'est donc une classe de base très importante, et vous aurez probablement très souvent besoin de lire la doc de cette classe.
Quelques rappels sur l'ouverture d'une fenêtre
Cela fait plusieurs chapitres que l'on crée une fenêtre dans nos programmes à l'aide d'un objet de type QWidget. Cela signifie-t-il que QWidget = Fenêtre ?
Non. En fait,
un widget qui n'est contenu dans aucun autre widget est considéré comme une fenêtre.
Donc quand on fait juste ce code très simple :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13 | #include <QApplication>
#include <QWidget>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
fenetre.show();
return app.exec();
}
|
... cela affiche une fenêtre (vide) :
C'est comme cela que Qt fonctionne. C'est un peu déroutant au début, mais après on apprécie au contraire que ça ait été pensé comme ça.
Donc si je comprends bien, il n'y a pas de classe QFenetre ou quelque chose du genre ?
Tout à fait, il n'y a pas de classe du genre "QFenetre" car n'importe quel widget peut servir de fenêtre. Si vous vous souvenez bien, on avait créé un bouton dans les premiers exemples du cours sur Qt. On avait demandé à afficher ce bouton.
Comme le bouton n'avait pas de parent, une fenêtre avait été ouverte :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12 | #include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton bouton("Salut les Zéros, la forme ?");
bouton.show();
return app.exec();
}
|
Dans la pratique, on ne crée pas de fenêtre-bouton comme là. On crée d'abord une fenêtre, et on place ensuite des widgets à l'intérieur (ces widgets étant parfois organisés grâce aux layouts comme on l'a vu).
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <QApplication>
#include <QWidget>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton bouton("Salut les Zéros, la forme ?", &fenetre);
fenetre.show();
return app.exec();
}
|
Note : je n'ai pas utilisé de layouts dans ce code pour le rendre court et simple. Le bouton est donc positionné de manière absolue au coin en haut à gauche, de coordonnées (0, 0).
Le QPushButton a donc pour parent un QWidget, car on a indiqué en second paramètre de son constructeur un pointeur vers le QWidget :
&fenetre
.
Le QWidget n'a pas de parent car on n'a pas envoyé de pointeur vers un autre widget dans son constructeur, donc c'est une fenêtre.
Ne confondez pas :
- En termes C++ : une classe parente est une classe mère (quand on fait un héritage).
- En termes Qt : un widget parent est un widget qui en contient d'autres. Un widget fils est un widget qui n'en contient aucun autre.
Ici, je suis en train de parler de widgets parents en termes Qt.
Quelques classes particulières pour les fenêtres
Résumons ce que je viens de dire : tout widget peut servir de fenêtre.
C'est le widget qui n'a pas de parent qui sera considéré comme étant la fenêtre.
A ce titre, un QPushButton ou un QLineEdit peuvent être considérés comme des fenêtres s'ils n'ont pas de widget parent.
Toutefois, il y a 2 classes de widgets que j'aimerais mettre en valeur :
- QMainWindow : c'est un widget spécial qui permet de créer la fenêtre principale de l'application. Une fenêtre principale peut contenir des menus, une barre d'outils, une barre d'état, etc.
- QDialog : c'est une classe de base utilisée par toutes les classes de boîtes de dialogue qu'on a vues il y a quelques chapitres. On peut aussi s'en servir directement pour ouvrir des boîtes de dialogue personnalisées.
La fenêtre principale QMainWindow mérite un chapitre entier à elle toute seule. Et elle en aura un. Nous pourrons alors tranquillement passer en revue la gestion des menus, de la barre d'outils et de la barre d'état.
La fenêtre QDialog peut être utilisée pour ouvrir une boîte de dialogue personnalisée générique. Une boîte de dialogue est une fenêtre généralement de petite taille dans laquelle il y a peu d'informations.
La classe QDialog hérite de QWidget comme tout widget qui se respecte, et elle y est même très similaire. Elle y ajoute peu de choses, parmi lesquelles la gestion des
fenêtres modales (une fenêtre par-dessus toutes les autres qui doit être remplie avant de pouvoir accéder aux autres fenêtres de l'application).
Nous allons ici étudier ce que l'on peut faire d'intéressant avec la classe de base QWidget qui permet déjà de réaliser la plupart des fenêtres que l'on veut.
Nous verrons ensuite ce qu'on peut faire avec les fenêtres de type QDialog. Quant à QMainWindow, ce sera pour un autre chapitre comme je vous l'ai dit.
Une fenêtre avec QWidget
Pour commencer, je vous invite à ouvrir la
doc de QWidget en même temps que vous lisez ce chapitre.
Vous remarquerez que QWidget est la classe mère d'un grrrrand nombre d'autres classes.

Les QWidget disposent de beaucoup de propriétés et de méthodes. Donc tous les widgets disposent de ces propriétés et méthodes.
On peut découper les propriétés en 2 catégories :
- Celles qui valent pour tous les types de widgets et pour les fenêtres
- Celles qui n'ont de sens que pour les fenêtres
Jetons un oeil à celles qui me semblent les plus intéressantes. Pour avoir la liste complète, il faudra recourir à la doc, je ne compte pas tout répéter ici !
Les propriétés utilisables pour tous les types de widgets, y compris les fenêtres
Je vous fais une liste rapide pour extraire quelques propriétés qui pourraient vous intéresser. Pour savoir comment vous servir de toutes ces propriétés, lisez le prototype que vous donne la doc.
N'oubliez pas qu'on peut modifier une propriété en appelant une méthode du même nom commençant par "set". Par exemple, si la propriété est cursor, la méthode sera setCursor().
- cursor : curseur de la souris à afficher lors du survol du widget. La méthode setCursor attend que vous lui envoyiez un objet de type QCursor. Certains curseurs classiques (comme le sablier) sont prédéfinis dans une énumération. La doc vous fait un lien vers cette énumération.
- enabled : indique si le widget est activé, si on peut le modifier. Un widget désactivé est généralement grisé. Si vous appliquez setEnabled(false) à toute la fenêtre, c'est toute la fenêtre qui deviendra inutilisable.
- height : hauteur du widget.
- size : dimensions du widget. Vous devrez indiquer la largeur et la hauteur.
- visible : contrôle la visibilité du widget.
- width : largeur.
N'oubliez pas : pour modifier une de ces propriétés, préfixez la méthode par un "set". Exemple :
Code : C++
Ces propriétés sont donc valables pour tous les widgets, y compris les fenêtres. Si vous appliquez un setWidth sur un bouton, ça modifiera la largeur du bouton. Si vous appliquez cela sur une fenêtre, c'est la largeur de la fenêtre qui sera modifiée.
Les propriétés utilisables uniquement sur les fenêtres
Ces propriétés sont faciles à reconnaître, elles commencent toutes par "window".
Elles n'ont de sens que si elles sont appliquées aux fenêtres.
- windowFlags : une série d'options contrôlant le comportement de la fenêtre. Il faut consulter l'énumération Qt::WindowType pour savoir les différents types disponibles. Vous pouvez aussi consulter l'exemple Window Flags du programme "Qt Examples and Demos".
Par exemple pour afficher une fenêtre de type "Outil" avec une petite croix et pas de possibilité d'agrandissement ou de réduction :
Code : C++1 | fenetre.setWindowFlags(Qt::Tool);
|
Une fenêtre de type "Tool"
C'est par là aussi qu'on passe pour que la fenêtre reste par-dessus toutes les autres fenêtres du système (avec le flag Qt::WindowStaysOnTopHint).
- windowIcon : l'icône de la fenêtre. Il faut envoyer un objet de type QIcon, qui lui-même accepte un nom de fichier à charger. Cela donne le code suivant pour charger le fichier icone.png situé dans le même dossier que l'application :
Code : C++1 | fenetre.setWindowIcon(QIcon("icone.png"));
|
Une icône pour la fenêtre
- windowOpacity : contrôle la transparence de la fenêtre (ne fonctionne pas sur tous les OS). La valeur à envoyer est un nombre décimal compris entre 0 (transparent) et 1 (complètement opaque).
Ici, avec la valeur 0.8 :
Code : C++1 | fenetre.setWindowOpacity(0.8);
|
Une fenêtre transparente
- windowTitle : le titre de la fenêtre, affiché en haut.
Code : C++1 | fenetre.setWindowTitle("Le Programme du Zéro v0.0");
|
Une fenêtre avec un titre
Une fenêtre avec QDialog
QDialog est un widget spécialement créé pour générer des fenêtres de type "boîte de dialogue".
Quelle est la différence avec une fenêtre créée à partir d'un QWidget ?
En général les QDialog sont des petites fenêtres secondaires : des boîtes de dialogue.
Elles proposent le plus souvent un choix simple entre :
Les QDialog sont rarement utilisées pour gérer la fenêtre principale. Pour la fenêtre principale on préfère utiliser QWidget, ou carrément QMainWindow si on a besoin de l'artillerie lourde.
Les QDialog peuvent être de 2 types :
- Modales : on ne peut pas accéder aux autres fenêtres de l'application lorsqu'elles sont ouvertes.
- Non modales : on peut toujours accéder aux autres fenêtres.
Par défaut, les QDialog sont modales.
Elles disposent en effet d'un méthode exec() qui ouvre la boîte de dialogue de manière modale. Il s'avère d'ailleurs qu'exec() est un slot (très pratique pour effectuer une connexion ça !).
Je vous propose d'essayer de pratiquer de la manière suivante : nous allons ouvrir une fenêtre principale QWidget qui contiendra un bouton. Lorsqu'on cliquera sur ce bouton, il ouvrira une fenêtre secondaire de type QDialog.
Notre objectif est d'ouvrir une fenêtre secondaire après un clic sur un bouton de la fenêtre principale.
La fenêtre secondaire, de type QDialog, affichera juste une image pour cet exemple.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton *bouton = new QPushButton("Ouvrir la fenêtre", &fenetre);
QDialog secondeFenetre (&fenetre);
QVBoxLayout *layout = new QVBoxLayout;
QLabel *image = new QLabel(&secondeFenetre);
image->setPixmap(QPixmap("icone.png"));
layout->addWidget(image);
secondeFenetre.setLayout(layout);
QWidget::connect(bouton, SIGNAL(clicked()), &secondeFenetre, SLOT(exec()));
fenetre.show();
return app.exec();
}
|
Mon code est indenté de manière bizarroïde je sais. Je trouve que c'est plus lisible : vous pouvez mieux voir comme cela à quelles fenêtres se rapportent les opérations que je fais.
Vous voyez ainsi immédiatement que dans la première fenêtre je n'ai fait que placer un bouton, tandis que dans la seconde j'ai mis un QLabel affichant une image que j'ai placée dans un QVBoxLayout.
D'autre part, j'ai tout fait dans le main pour cet exemple, mais dans la pratique, comme nous le verrons dans les TP, on a en général un fichier .cpp par fenêtre, c'est plus facile à gérer.
Au départ, la fenêtre principale s'affiche, comme ceci :
Si vous cliquez sur le bouton, la boîte de dialogue s'ouvre :
Comme elle est modale, vous remarquerez que vous ne pouvez pas accéder à la fenêtre principale tant qu'elle est ouverte.
Bon intéressons-nous au code. J'ai surligné les 2 lignes qui me paraissaient les plus pertinentes :
- Ligne 12 : la création de la QDialog. Rien de bien extraordinaire à première vue, mais si vous regardez bien vous devriez voir que j'ai mentionné dans le constructeur l'adresse de la fenêtre parente. Cela permet à la QDialog de savoir quelle est la fenêtre qui l'a appelée. Entre autres choses, la QDialog se placera automatiquement de manière centrée par rapport à sa fenêtre mère.
- Ligne 20 : je connecte le clic sur le bouton à la méthode exec() de la QDialog pour ouvrir la boîte de dialogue (de manière modale).
Cela devrait vous donner des bases suffisantes pour désormais savoir comment ouvrir des fenêtres secondaires de type QDialog. Nous n'avons cependant pas tout vu sur cette classe : on peut rendre les QDialog non modales ou encore utiliser les slots accept() et reject() pour les connecter respectivement à des boutons "OK" et "Annuler" et ainsi informer la fenêtre parente afin qu'elle sache si l'opération a été validée ou refusée par l'utilisateur.
Pour savoir faire tout cela, vous savez ce qu'il vous reste à faire. Tout est dans la doc.
Nous allons maintenant étudier la catégorie des widgets "boutons". Nous allons passer en revue :
- QPushButton : un bouton classique, que vous avez déjà largement eu l'occasion de manipuler.
- QRadioButton : un bouton "radio", pour un choix à faire parmi une liste.
- QCheckBox : une case à cocher (on considère que c'est un bouton en GUI Design).
Tous ces widgets héritent de
QAbstractButton qui lui-même hérite de QWidget, qui finalement hérite de QObject :
Comme l'indique son nom, QAbstractButton est une classe abstraite. Si vous vous souvenez des épisodes précédents de notre passionnant feuilleton, une classe abstraite est une classe... qu'on ne peut pas instancier, bravo !

On ne peut donc pas créer d'objets de type QAbstractButton, il faut forcément utiliser une des classes filles. QAbstractButton sert donc juste de modèle de base pour ses classes filles.
QPushButton : un bouton
Le
QPushButton est l'élément le plus classique et le plus commun des fenêtres :
Je ne vous fais pas l'offense de vous expliquer à quoi sert un bouton
Commençons par un rappel important, indiqué par la doc : QPushButton hérite de
QAbstractButton. Et c'est vraiment une info importante, car vous serez peut-être surpris de voir que QPushButton contient peu de méthodes qui lui sont propres. C'est normal, une grande partie d'entre elles se trouvent dans sa classe parente QAbstractButton.
Il faudra donc absolument consulter aussi QAbstractButton, et même sa classe mère
QWidget (et éventuellement aussi QObject mais c'est plus rare), si vous voulez connaître toutes les possibilités offertes au final par un QPushButton.
Par exemple,
setEnabled permet d'activer / désactiver le bouton, et cette propriété se trouve dans QWidget.
Les signaux du bouton
Un bouton émet un signal clicked() quand on l'active. C'est le signal le plus communément utilisé.
On note aussi les signaux pressed() (bouton enfoncé) et released() (bouton relâché), mais ils sont plus rares.
Les boutons à 2 états
Un bouton peut parfois avoir 2 états : enfoncé et relâché (normal).
Pour activer un bouton à 2 états, utilisez setCheckable(true) :
Code : C++1
2 | QPushButton *bouton = new QPushButton("Bouton", &fenetre);
bouton->setCheckable(true);
|
Désormais, un clic sur le bouton le laissera enfoncé, et un nouveau clic le rétablira dans son état normal. Utilisez les signaux pressed() et released() pour récupérer les changements d'état du bouton.
A gauche un bouton normal, à droite un bouton pressé
QCheckBox : une case à cocher
Une case à cocher
QCheckBox est généralement associé à un texte de libellé comme ceci :
On définit le libellé de la case lors de l'appel du constructeur :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <QApplication>
#include <QWidget>
#include <QCheckBox>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QCheckBox *checkbox = new QCheckBox("J'aime les frites", &fenetre);
fenetre.show();
return app.exec();
}
|
La case à cocher émet le signal stateChanged(bool) lorsqu'on modifie son état. Le booléen en paramètre nous permet de savoir si la case est maintenant cochée ou décochée.
Si vous voulez vérifier à un autre moment si la case est cochée, appelez isChecked() qui renvoie un booléen.
On peut aussi faire des cases à cocher à 3 états (le troisième état étant l'état grisé). Renseignez-vous sur la propriété tristate pour savoir faire cela. Notez que ce type de case à cocher est relativement rare.
Enfin, sachez que si vous avez plusieurs cases à cocher, vous pouvez les regrouper au sein d'une
QGroupBox.
QRadioButton : les boutons radio
C'est une case à cocher particulière : une seule case peut être cochée à la fois parmi une liste.
Les radio buttons qui ont le même widget parent sont mutuellement exclusifs. Si vous en cochez un, les autres seront automatiquement décochés.
En général, on place les radio buttons dans une QGroupBox. Utiliser des QGroupBox différentes vous permet de séparer les groupes de radio buttons.
Voici un exemple d'utilisation d'une QGroupBox (qui contient un layout, qui contient les QRadioButton) :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QGroupBox *groupbox = new QGroupBox("Votre plat préféré", &fenetre);
QRadioButton *steacks = new QRadioButton("Les steacks");
QRadioButton *hamburgers = new QRadioButton("Les hamburgers");
QRadioButton *nuggets = new QRadioButton("Les nuggets");
steacks->setChecked(true);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(steacks);
vbox->addWidget(hamburgers);
vbox->addWidget(nuggets);
groupbox->setLayout(vbox);
groupbox->move(5, 5);
fenetre.show();
return app.exec();
}
|
Les radio buttons sont placés dans un layout qui est lui-même placé dans la groupbox, qui est elle-même placée dans la fenêtre. Pfiou ! Le concept des widgets conteneurs est ici utilisé à fond !
Et encore, je n'ai pas fait de layout pour la fenêtre (la flême, et je ne voulais pas trop encombrer le code), ce qui fait que la taille initiale de la fenêtre est un peu petite, mais ce n'est pas grave c'est pour l'exemple.
Voilà le résultat :
Nota : j'ai une nourriture plus équilibrée que ne le laisse suggérer cette dernière capture d'écran quand même, je vous rassure.
Parmi les widgets afficheurs, on compte principalement :
- QLabel : le plus important, un widget permettant d'afficher du texte ou une image.
- QProgressBar : une barre de progression.
Etudions-les en choeur, sans heurts, dans la joie et la bonne humeur.
QLabel : afficher du texte ou une image
C'est vraiment LE widget de base pour afficher du texte à l'intérieur de la fenêtre.

Nous l'avons déjà utilisé indirectement auparavant, via les cases à cocher ou encore les layouts de formulaire.
Voici un libellé :
... du moins, UN des types de libellés possibles comme nous allons le voir.
Un QLabel peut afficher plusieurs types d'éléments :
- Du texte simple,
- Du texte enrichi (gras, italique, souligné, coloré, avec des liens...),
- Une image,
- Et même une image animée !
Nous allons étudier chacun de ces types de contenu, à l'exception de l'image animée qui sort un peu du cadre du chapitre. Et puis de toute façon, ça ne sert qu'à afficher en pratique des GIF animés, ça nous sera donc peu utile.
Afficher un texte simple
Rien de plus simple, on utilise setText() :
Code : C++1 | label->setText("Bonjour les Zéros !");
|
Mais on peut aussi afficher un texte simple dès l'appel au constructeur comme ceci :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QLabel *label = new QLabel("Bonjour les Zéros !", &fenetre);
label->move(30, 20);
fenetre.show();
return app.exec();
}
|
Le résultat est le même que la capture d'écran que je vous ai montrée plus haut :
Vous pouvez jeter aussi un oeil à la propriété alignment qui permet de définir l'alignement du texte dans le libellé.
Afficher un texte enrichi
Vous pouvez envoyer du texte enrichi (formaté) au QLabel, avec du HTML :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QLabel *label = new QLabel("Bonjour les <strong>Zéros</strong> !<br />Etes-vous allés sur le <a href=\"http://www.siteduzero.com\">Site du Zéro</a> aujourd'hui ?", &fenetre);
label->move(30, 20);
fenetre.show();
return app.exec();
}
|
Magie, magie, le texte est correctement mis en forme !
C'est beau la technologie quand même.

Et encore, vous n'avez pas tout vu !
Afficher une image
Vous pouvez demander à ce que le QLabel affiche une image.
Comme il n'y a pas de constructeur qui accepte une image en paramètre, on va appeler le constructeur qui prend juste un pointeur vers la fenêtre parente.
Nous demanderons ensuite à ce que le libellé affiche une image à l'aide de setPixmap().
Cette méthode attend un objet de type
QPixmap. Après lecture de la doc sur QPixmap, il s'avère que cette classe a un constructeur qui accepte le nom du fichier à charger sous forme de chaîne de caractères.
Nous allons donc afficher notre belle icône de tout à l'heure, mais cette fois en grand et dans la fenêtre :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QLabel *label = new QLabel(&fenetre);
label->setPixmap(QPixmap("icone.png"));
label->move(30, 20);
fenetre.show();
return app.exec();
}
|
L'icône doit se trouver dans le même dossier que l'exécutable pour que cela fonctionne.
Et voilà le résultat !
QProgressBar : une barre de progression
Les barres de progression sont gérées par
QProgressBar. Cela permet d'indiquer à l'utilisateur l'avancement des opérations.
Voici quelques propriétés utiles de la barre de progression :
- maximum : la valeur maximale que peut prendre la barre de progression.
- minimum : la valeur minimale que peut prendre la barre de progression.
- value : la valeur actuelle de la barre de progression.
On utilisera donc setValue pour changer la valeur de la barre de progression. Par défaut les valeurs sont comprises entre 0 et 100%.
Qt ne peut pas deviner où en sont vos opérations. C'est à vous de calculer le pourcentage d'avancement de vos opérations. La QProgressBar se contente juste d'afficher le résultat.
Une QProgressBar envoie un signal valueChanged() lorsque sa valeur a été modifiée.
A part ça, rien de bien spécial à signaler. Je vous avais déjà fait manipuler les barres de progression dans le chapitre sur les signaux et les slots pour tester la connexion entre les widgets.
Nous allons maintenant faire le tour des widgets qui permettent d'entrer des données. C'est la catégorie de widgets la plus importante.
Encore une fois, on ne verra pas tout, mais les principaux d'entre eux :
- QLineEdit : champ de texte à une seule ligne.
- QTextEdit : champ de texte à plusieurs lignes pouvant afficher du texte mis en forme.
- QSpinBox : champ de texte adapté à la saisie de nombre entiers.
- QDoubleSpinBox : champ de texte adapté à la saisie de nombre décimaux.
- QSlider : un curseur qui permet de sélectionner une valeur.
- QComboBox : une liste déroulante.
QLineEdit : champ de texte à une seule ligne
Nous avons utilisé ce widget comme classe d'exemple lors du chapitre sur la lecture de la doc de Qt, vous vous souvenez ?
Un
QLineEdit est un champ de texte sur une seule ligne :
Son utilisation est dans la plupart des cas assez simple. Voici quelques propriétés à connaître :
- text : permet de récupérer / modifier le texte contenu dans le champ.
- alignment : l'alignement du texte à l'intérieur.
- echoMode : type d'affichage du texte. Il faudra utiliser l'énumération EchoMode pour indiquer le type d'affichage. Par défaut les lettres entrées s'affichent, mais on peut aussi faire en sorte que les lettres soient masquées pour les mots de passe.
Code : C++1 | lineEdit->setEchoMode(QLineEdit::Password);
|
- inputMask : permet de définir un masque de saisie, pour obliger l'utilisateur à rentrer une chaîne précise (par exemple un numéro de téléphone ne doit pas contenir de lettres). Vous pouvez aussi jeter un oeil aux validators qui sont un autre moyen de valider la saisie de l'utilisateur.
- maxLength : le nombre de caractères maximum qui peuvent être entrés.
- readOnly : le contenu du champ de texte ne peut être modifié. Cette propriété ressemble à enabled (définie dans QWidget), mais avec readOnly on peut quand même copier-coller le contenu du QLineEdit, tandis qu'avec enabled le champ est complètement grisé et on ne peut pas récupérer son contenu.
On note aussi plusieurs slots qui permettent de couper / copier / coller / vider / annuler le champ de texte.
Enfin, certains signaux comme returnPressed() (l'utilisateur a appuyé sur Entrée) ou textChanged() (l'utilisateur a modifié le texte) peuvent être utiles dans certains cas.
QTextEdit : champ de texte à plusieurs lignes
Ce type de champ est similaire à celui qu'on vient de voir, à l'exception du fait qu'il gère l'édition sur plusieurs lignes et, en particulier, qu'il autorise l'affichage de texte enrichi (HTML).
Voici un
QTextEdit :
Il y a un certains nombre de choses que l'on pourrait voir sur les QTextEdit mais ce serait un peu trop long pour ce chapitre qui est plutôt là pour faire une revue rapide des widgets.
Notez les propriétés plainText et html qui permettent respectivement de récupérer & modifier le contenu sous forme de texte simple et sous forme de texte enrichi en HTML. Tout dépend de l'utilisation que vous en faites, normalement dans la plupart des cas vous utiliserez plutôt plainText.
QSpinBox : champ de texte de saisie d'entiers
Une
QSpinBox est un champ de texte (type QLineEdit) qui permet d'entrer uniquement un nombre entier et qui dispose de petits boutons pour augmenter ou diminuer la valeur :
QSpinBox hérite de
QAbstractSpinBox (tiens, encore une classe abstraite !). Vérifiez donc aussi la doc de QAbstractSpinBox pour connaître toutes les propriétés de la spinbox.
Voici quelques propriétés intéressantes :
- accelerated : permet d'autoriser la spinbox a accélérer la vitesse d'augmentation du nombre si on appuie longtemps sur le bouton.
- minimum : valeur minimale que peut prendre la spinbox.
- maximum : valeur maximale que peut prendre la spinbox.
- singleStep : pas d'incrémentation (par défaut de 1). Si vous voulez que les boutons fassent augmenter la spinbox de 100 en 100, c'est cette propriété qu'il faut modifier !
- value : valeur contenue dans la spinbox.
- prefix : texte à afficher avant le nombre.
- suffix : texte à afficher après le nombre.
QDoubleSpinBox : champ de texte de saisie de nombres décimaux
Le
QDoubleSpinBox est très similaire au QSpinBox, à la différence près qu'il travaille sur des nombres décimaux (des double) :
On retrouve la plupart des propriétés de QSpinBox. On peut rajouter la propriété
decimals qui gère le nombre de chiffres après la virgule affichés par le QDoubleSpinBox.
QSlider : un curseur pour sélectionner une valeur
Un
QSlider se présente sous la forme d'un curseur permettant de sélectionner une valeur numérique :
QSlider hérite de
QAbstractSlider (damned, encore une classe abstraite !) qui propose déjà un grand nombre de fonctionnalités de base.
Beaucoup de propriétés sont les mêmes que QSpinBox, je ne les relisterai donc pas ici.
Notons la propriété
orientation qui permet de définir l'orientation du slider (verticale ou horizontale).
Jetez un oeil en particulier à ses signaux, car on connecte en général le signal valueChanged(int) au slot d'autre widget pour répercuter la saisie de l'utilisateur.
Nous avions d'ailleurs manipulé ce widget lors du chapitre sur les signaux et les slots.
QComboBox : une liste déroulante
Une
QComboBox est une liste déroulante :
On ajoute des valeurs à la liste déroulante avec la méthode addItem :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QComboBox *liste = new QComboBox(&fenetre);
liste->addItem("Paris");
liste->addItem("Londres");
liste->addItem("Singapour");
liste->addItem("Tokyo");
liste->move(30, 20);
fenetre.show();
return app.exec();
}
|
On dispose de propriétés permettant de contrôler le fonctionnement de la QComboBox :
- count : nombre d'éléments dans la liste déroulante.
- currentItem : numéro d'indice de l'élément actuellement sélectionné. Les indices commencent à 0. Ainsi, si currentItem renvoie 2, c'est que "Singapour" a été sélectionné dans l'exemple précédent.
- currentText : texte correspondant à l'élément sélectionné. Si on a sélectionné "Singapour", cette propriété contient donc "Singapour".
- editable : indique si le widget autorise l'ajout de valeurs personnalisées ou non. Par défaut, l'ajout de nouvelles valeurs est interdit.
Si le widget est éditable, l'utilisateur pourra rentrer de nouvelles valeurs dans la liste déroulante. Elle se comportera donc aussi comme un champ de texte. L'ajout d'une nouvelle valeur se fait par appui sur la touche "Entrée". Les nouvelles valeurs sont placées par défaut à la fin de la liste.
La QComboBox émet des signaux comme currentIndexChanged() qui indique qu'un nouvel élément a été sélectionné et highlighted() qui indique l'élément survolé par la souris (ces signaux peuvent envoyer un int pour donner l'indice de l'élément ou un QString pour le texte).
Normalement, n'importe quel widget peut en contenir d'autres.
Cependant, certains widgets ont été vraiment créés spécialement pour pouvoir en contenir d'autres :
- QFrame : un widget pouvant avoir une bordure.
- QGroupBox : un widget (que nous avons déjà utilisé) adapté à la gestion des groupes de cases à cocher et de boutons radio.
- QTabWidget : un widget gérant plusieurs pages d'onglets.
Nous allons apprendre à les manipuler, en nous intéressant en particulier à celui qui propose des onglets qui est un petit peu délicat.
QFrame : une bordure
QFrame est très proche de QWidget. En fait, la seule nouveauté c'est qu'il peut générer une bordure. C'est donc un QWidget basique avec une bordure.
QFrame est une classe de base pour de nombreux widgets qui peuvent avoir une bordure, comme les QLabel. Tout ce que nous allons faire avec les QFrame ici, tous les widgets qui en héritent peuvent le faire aussi.
Un QFrame possède quelques propriétés pour gérer la forme de la bordure :
- frameShape : le type de bordure. Il faut utiliser une énumération définie par QFrame pour sélectionner la bordure. Consultez la doc pour avoir la liste des types de bordure.
De manière générale je recommande d'utiliser QFrame::StyledPanel (comme sur ma capture d'écran) car cela crée une bordure dans un style adapté à votre OS. Notez aussi QFrame::HLine et QFrame::VLine, un peu particuliers, qui ne créent pas un rectangle mais juste une ligne horizontale ou verticale. Très utile pour séparer les éléments dans sa fenêtre.
- frameShadow : l'ombre de la bordure. Par défaut il n'y en a pas, mais vous pouvez définir une ombre qui donne l'impression que le widget est surélevé ou enfoncé.
Regardez les énumérations définies par QFrame pour avoir la liste des possibilités.
- lineWidth : l'épaisseur de la ligne de la bordure.
- midLineWidth : l'épaisseur de la ligne intermédiaire (utilisé uniquement pour certaines bordure complexes avec une ombre).
Testons la propriété frameShape :
Code : C++1
2
3 | QFrame *frame = new QFrame(&fenetre);
frame->setFrameShape(QFrame::StyledPanel);
frame->setGeometry(30, 20, 120, 90);
|
Résultat :
Dans la pratique, le QFrame sert à contenir d'autres widgets (à moins que vous aimiez dessiner des rectangles partout pour le plaisir). Il est donc probable que vous définissiez un layout pour le QFrame et que vous placiez des widgets à l'intérieur.
Allez je me fends d'un petit exemple, ça vous rappellera le chapitre sur les layouts :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QFrame *frame = new QFrame(&fenetre);
frame->setFrameShape(QFrame::StyledPanel);
frame->setGeometry(30, 20, 120, 90);
QLineEdit *lineEdit = new QLineEdit("Entrez votre nom");
QPushButton *bouton1 = new QPushButton("Cliquez ici");
QPushButton *bouton2 = new QPushButton("Ou là...");
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(lineEdit);
vbox->addWidget(bouton1);
vbox->addWidget(bouton2);
frame->setLayout(vbox);
fenetre.show();
return app.exec();
}
|
On n'a rien fait de bien nouveau. Le QFrame contient un layout vertical qui organise ses widgets enfants : un QLineEdit et deux QPushButton.
Le QFrame lui-même est placé de manière absolue sur la fenêtre (ses coordonnées sont définies dans setGeometry). Je fais ça pour simplifier l'exemple, mais dans la pratique le QFrame devrait être lui-même placé dans un autre layout (le layout principal de la fenêtre).
QGroupBox : un groupe de widgets
Nous avons déjà utilisé
QGroupBox en pratique plus haut, lorsque nous avons utilisé les boutons radio. Je ne vais donc pas trop m'éterniser dessus.
Un QGroupBox propose de créer une bordure comme QFrame, mais il a en plus l'avantage de permettre de définir un titre pour le conteneur.
QGroupBox n'hérite pas de QFrame. On n'a donc pas le choix dans le type de bordure, mais on a une propriété flat qui permet d'aplatir la bordure.
Le titre du conteneur est défini via sa propriété
title, ou directement lors de l'appel au constructeur :
Code : C++1 | QGroupBox *groupBox = new QGroupBox("Titre du QGroupBox", &fenetre);
|
Exercice : essayez d'adapter l'exemple précédent sur QFrame pour utiliser cette fois un QGroupBox. C'est facile, mais attention aux propriétés spécifiques au QFrame qui ne sont ici plus valables.
Le résultat devrait être le suivant :
Il est aussi possible d'ajouter une case à cocher devant le QGroupBox. Pour cela, mettez sa propriété checkable à true :
Code : C++1 | groupBox->setCheckable(true);
|
Lorsque la case est cochée, les widgets à l'intérieur sont activés. Lorsqu'elle est décochée, les widgets sont désactivés. Essayez.
QTabWidget : des pages d'onglets
Le
QTabWidget propose une gestion de plusieurs pages de widgets, organisées sous forme d'onglets :
Ce widget-conteneur est sensiblement plus difficile à utiliser que les autres. En effet, il ne peut contenir qu'un widget par page.
Quoi ? On ne peut pas afficher plus d'un widget par page ???
Mais c'est tout nul !
Sauf... qu'un widget peut en contenir d'autres !

Et si on utilise un layout pour organiser le contenu de ce widget, on peut arriver rapidement à une super présentation. Le tout est de savoir combiner tout ce qu'on a appris jusqu'ici.
D'après le texte d'introduction de la doc de QTabWidget, ce conteneur doit être utilisé de la façon suivante :
- Créer un QTabWidget.
- Créer un QWidget pour chacune des pages (chacun des onglets) du QTabWidget, sans leur indiquer de widget parent.
- Placer des widgets enfants dans chacun de ces QWidget pour peupler le contenu de chaque page. Utiliser un layout pour positionner les widgets de préférence.
- Appeler plusieurs fois addTab() pour créer les pages d'onglets en indiquant l'adresse du QWidget qui contient la page à chaque fois.
Bon, c'est un peu plus délicat comme vous pouvez le voir, mais il faut bien un peu de difficulté, ce chapitre était trop facile.
Si on fait tout dans l'ordre, vous allez voir que l'on n'aura pas de problème.
Je vous propose de lire ce code que j'ai créé qui montre un exemple d'utilisation du QTabWidget. Il est un peu long mais il est commenté et vous devriez arriver à le digérer.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 | #include <QApplication>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
// 1 : Créer le QTabWidget
QTabWidget *onglets = new QTabWidget(&fenetre);
onglets->setGeometry(30, 20, 240, 160);
// 2 : Créer les pages, en utilisant un widget parent pour contenir chacune des pages
QWidget *page1 = new QWidget;
QWidget *page2 = new QWidget;
QLabel *page3 = new QLabel; // Comme un QLabel est aussi un QWidget (il en hérite), on peut aussi s'en servir de page
// 3 : Créer le contenu des pages de widgets
// Page 1
QLineEdit *lineEdit = new QLineEdit("Entrez votre nom");
QPushButton *bouton1 = new QPushButton("Cliquez ici");
QPushButton *bouton2 = new QPushButton("Ou là...");
QVBoxLayout *vbox1 = new QVBoxLayout;
vbox1->addWidget(lineEdit);
vbox1->addWidget(bouton1);
vbox1->addWidget(bouton2);
page1->setLayout(vbox1);
// Page 2
QProgressBar *progress = new QProgressBar;
progress->setValue(50);
QSlider *slider = new QSlider(Qt::Horizontal);
QPushButton *bouton3 = new QPushButton("Valider");
QVBoxLayout *vbox2 = new QVBoxLayout;
vbox2->addWidget(progress);
vbox2->addWidget(slider);
vbox2->addWidget(bouton3);
page2->setLayout(vbox2);
// Page 3 (je ne vais afficher qu'une image ici, pas besoin de layout)
page3->setPixmap(QPixmap("icone.png"));
page3->setAlignment(Qt::AlignCenter);
// 4 : ajouter les onglets au QTabWidget, en indiquant la page qu'ils contiennent
onglets->addTab(page1, "Coordonnées");
onglets->addTab(page2, "Progression");
onglets->addTab(page3, "Image");
fenetre.show();
return app.exec();
}
|
Vous devriez retrouver chacune des étapes que j'ai mentionnées plus haut :
- Je crée d'abord le QTabWidget que je positionne ici de manière absolue sur la fenêtre (mais je pourrais aussi utiliser un layout).
- Ensuite, je crée les pages pour chacun de mes onglets. Ces pages sont matérialisées par des QWidget.
Vous noterez que pour la dernière page je n'utilise pas un QWidget mais un QLabel. Ca revient au même et c'est compatible car QLabel hérite de QWidget. Sur la dernière page, je me contenterai d'afficher une image.
- Je crée ensuite le contenu de chacune de ces pages que je dispose à l'aide de layouts verticaux (sauf pour la page 3 qui n'est constituée que d'un widget). Là il n'y a rien de nouveau.
- Enfin, j'ajoute les onglets avec la méthode addTab(). Je dois indiquer le libellé de l'onglet ainsi qu'un pointeur vers le "widget-page" de cette page.
Résultat, on a un super système d'onglets à 3 pages avec tout plein de widgets dedans !
Je vous conseille de vous entraîner à créer vous aussi un QTabWidget. C'est un bon exercice, et c'est l'occasion de réutiliser la plupart des widgets que l'on a vus dans ce chapitre.
Il faut pratiquer et pratiquer. Ce qu'on fait là ne devrait pas être bien compliqué si vous avez correctement suivi le cours jusqu'ici. Mais il faut quand même pratiquer pour faire des erreurs et se rendre compte qu'on n'avait pas parfaitement tout compris.
C'est à partir de ce moment-là seulement que vous commencerez à maîtriser les widgets.
Pfiou !
Il s'agit peut-être du chapitre le plus long que j'aie jamais écrit... mais je tenais à le faire. En fait, c'est un peu paradoxal car tout cela se trouve dans la doc et je vous ai déjà appris à lire la doc, mais j'estimais qu'il devait quand même forcément y avoir un tour d'horizon des principaux widgets dans mon tutoriel, sinon j'aurais eu l'impression d'avoir été incomplet.
Servez-vous de ce chapitre pour vous faire une idée de ce qui existe, mais ensuite je vous conseille très fortement de passer plus de temps sur la doc que sur ce chapitre. En effet, nous sommes loin d'avoir vu toutes les fonctionnalités de ces widgets, de même que nous n'avons pas vu tous les widgets qui existent !
J'ai réservé les widgets les plus complexes pour de futurs chapitres, qui introduiront pour l'occasion un concept de programmation important lorsqu'on programme des GUI : le modèle MVC.
Bon, vous pensez pas qu'il serait temps de pratiquer tout ce qu'on a appris avec un petit TP là ?