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++ | 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++ | 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++ | fenetre.setWindowOpacity(0.8);
|
Une fenêtre transparente
- windowTitle : le titre de la fenêtre, affiché en haut.
Code : C++ | 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'une 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.
