[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 > Positionner ses widgets avec les layouts
> Lecture du tutoriel
Positionner ses widgets avec les layouts
Comme vous le savez, une fenêtre peut contenir toutes sortes de widgets : des boutons, des champs de texte, des cases à cocher...
Placer ces widgets sur la fenêtre est une science à part entière. Je veux dire par là qu'il faut vraiment y aller avec méthode, si on ne veut pas que la fenêtre ressemble rapidement à un champ de bataille
Comment bien placer les widgets sur la fenêtre ?
Comment gérer les redimensionnements de la fenêtre ?
Comment s'adapter automatiquement à toutes les résolutions d'écran ?
On distingue 2 techniques différentes pour positionner des widgets :
- Le positionnement absolu : c'est celui que nous avons vu jusqu'ici, 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, mais cela comporte un certain nombre de défauts comme nous allons le voir.
- Le positionnement relatif : c'est le plus flexible et c'est celui que je vous recommande d'utiliser autant que possible. Nous allons l'étudier dans ce chapitre.
Nous allons commencer par voir le code Qt de base que nous allons utiliser dans ce chapitre, puis nous ferons quelques rappels sur le positionnement absolu que vous avez déjà utilisé sans savoir exactement ce que c'était
Le code Qt de base
Dans les chapitres précédents, nous avions créé un projet Qt consituté de 3 fichiers :
- main.cpp : contenait le main qui se chargeait juste d'ouvrir la fenêtre principale.
- MaFenetre.h : contenait l'en-tête de notre classe MaFenetre qui héritait de QWidget.
- MaFenetre.cpp : contenait l'implémentation des méthodes de MaFenetre, notamment du constructeur.
C'est l'architecture que l'on utilisera dans la plupart de nos projets Qt.
Toutefois, pour ce chapitre nous n'avons pas besoin d'une architecture aussi complexe, et nous allons faire comme dans les tous premiers chapitre Qt : nous allons juste utiliser un main (1 seul fichier : main.cpp).
Voici le code de votre projet, sur lequel nous allons commencer :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | #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();
}
|
C'est très simple : nous créons une fenêtre, et nous affichons un bouton que nous plaçons aux coordonnées (70, 60) sur la fenêtre.
Le résultat est le suivant :
Les défauts du positionnement absolu
Dans le code précédent, nous avons positionné notre bouton de manière absolue en faisant
bouton.move(70, 60);
Le bouton a été très précisément placé 70 pixels sur la droite et 60 pixels plus bas.
Le problème... c'est que ce n'est pas flexible du tout. Imaginez que l'utilisateur s'amuse à redimensionner la fenêtre :
C'est moche, non ?
Le bouton ne bouge pas de place. Du coup, si on réduit la taille de la fenêtre, il sera coupé en deux, et pourra même disparaître si on réduit trop la taille.
Dans ce cas, pourquoi ne pas empêcher l'utilisateur de redimensionner la fenêtre ? On avait fait ça grâce à setFixedSize dans les chapitres précédents...
Oui, vous pouvez faire cela. C'est d'ailleurs ce que font le plus souvent les développeurs de logiciels qui positionnent leurs widgets en absolu. Cependant, l'utilisateur apprécie aussi de pouvoir redimensionner sa fenêtre. Ce n'est qu'une demi-solution.
D'ailleurs, il y a un autre problème que
setFixedSize ne peut pas régler : le cas des résolutions d'écran plus petites que la vôtre. Imaginez que vous placiez un bouton 1200 pixels sur la droite parce que vous avez une grande résolution (1600 x 1200), et que l'utilisateur soit dans une résolution plus petite que vous (1024 x 768). Il ne pourra jamais voir le bouton, parce qu'il ne pourra jamais agrandir autant sa fenêtre !
Alors quoi ? Le positionnement absolu c'est mal ? Où veux-tu en venir ?
Et surtout, comment peut-on faire autrement ?
Non, le positionnement absolu ce n'est pas "mal". Il sert parfois quand on a vraiment besoin de positionner au pixel près. Vous pouvez l'utiliser dans certains de vos projets, mais autant que possible, préférez l'autre méthode : le positionnement relatif.
Le positionnement relatif, cela consiste à expliquer comment les widgets sont agencés les uns par rapport aux autres, plutôt que d'utiliser une position en pixels. Par exemple, on peut dire "Le bouton 1 est en-dessous du bouton 2, qui est à gauche du bouton 3".
Le positionnement relatif est géré par ce qu'on appelle les
layouts avec Qt.
Ce sont des conteneurs de widgets.
C'est justement l'objet principal de ce chapitre
Pour positionner intelligemment nos widgets, nous allons utiliser des classes de Qt gérant les layouts.
Il existe par exemple des classes gérant le positionnement horizontal et vertical des widgets (ce que nous allons étudier en premier), ou encore le positionnement sous forme de grille.
Pour que vous y voyiez plus clair, je vous propose de regarder ce schéma de mon cru :
Ce sont les classes gérant les layouts de Qt.
Toutes les classes héritent de la classe de base QLayout.
On compte donc en gros les classes :
- QBoxLayout
- QHBoxLayout
- QVBoxLayout
- QGridLayout
- QFormLayout
- QStackedLayout
Nous allons étudier chacune de ces classes dans ce chapitre, à l'exception de QStackedLayout (gestion des widgets sur plusieurs pages) qui est un peu trop complexe pour qu'on puisse travailler dessus ici. On utilisera plutôt des widgets qui le réutilisent, comme QWizard qui permet de créer des assistants.
Euh... Mais pourquoi tu as écrit
QLayout en italique, et pourquoi tu as grisé la classe ?
QLayout est ce qu'on appelle une classe abstraite. Je ne vous en ai pas trop parlé jusqu'ici.
En fait, une classe abstraite est une classe "de base" qu'on ne peut pas instancier. C'est-à-dire qu'
on ne peut pas créer d'objets de type QLayout, il faut forcément créer un objet à partir d'une des classes filles (QBoxLayout, QGridLayout...).
A quoi ça sert de créer une classe qui ne nous permette pas de créer d'objet ?

Ca sert pour le programmeur, pour avoir juste une classe "de base".
Cependant, comme ça n'a pas de sens de créer d'objet de type QLayout, la classe a été définie comme étant abstraite.
Je ne vais pas rentrer dans les détails de "
comment faire pour créer une classe abstraite en C++". Ce serait un peu trop compliqué et hors-sujet.
Tout ce que vous avez besoin de retenir, c'est que
vous pouvez créer des objets de type QBoxLayout, QGridLayout, etc, mais pas d'objets de type QLayout. En quelque sorte, QLayout sert de "modèle" de base pour les autres classes mais on ne peut rien faire avec elle seule
Attaquons sans plus tarder l'étude de nos premiers layouts (les plus simples), vous allez mieux comprendre à quoi tout cela sert
Nous allons travailler sur 2 classes :
QHBoxLayout et QVBoxLayout héritent de
QBoxLayout. Ce sont des classes très similaires (la doc Qt parle de "
convenience classes", des classes qui sont là pour vous aider à aller plus vite mais qui sont en fait quasiment identiques à QBoxLayout).
Nous n'allons pas utiliser QBoxLayout, mais juste ses classes filles QHBoxLayout et QVBoxLayout (ça revient au même).
Le layout horizontal
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éé
1/ Créer les widgets
Pour les besoins de ce tutoriel, nous allons créer plusieurs boutons de type QPushButton :
Code : C++1
2
3 | QPushButton *bouton1 = new QPushButton("Bonjour");
QPushButton *bouton2 = new QPushButton("les");
QPushButton *bouton3 = new QPushButton("Zéros");
|
Vous remarquerez que j'utilise des pointeurs. En effet, j'aurais très bien pu faire sans pointeurs comme ceci :
Code : C++1
2
3 | QPushButton bouton1("Bonjour");
QPushButton bouton2("les");
QPushButton bouton3("Zéros");
|
... cette méthode a l'air plus simple, mais vous verrez que c'est plus pratique de travailler directement avec des pointeurs par la suite

La différence entre ces 2 codes, c'est que bouton1 est un pointeur dans le premier code, tandis que c'est un objet dans le second code.
On va donc utiliser la première méthode avec les pointeurs.
Bon, on a 3 boutons, c'est bien. Mais les plus perspicaces d'entre vous auront remarqué qu'on n'a pas indiqué quelle était la fenêtre parente, comme on aurait fait avant :
Code : C++1 | QPushButton *bouton1 = new QPushButton("Bonjour", &fenetre);
|
On n'a pas fait comme ça, et c'est fait exprès justement. Nous n'allons pas placer les boutons dans la fenêtre directement, mais dans un conteneur : le layout.
2/ Créer le layout et placer les widgets dedans
Créons justement ce layout, un layout horizontal :
Code : C++1 | QHBoxLayout *layout = new QHBoxLayout;
|
Le constructeur de cette classe est simple, on n'a pas besoin d'indiquer de paramètre.
Maintenant que notre layout est créé, rajoutons nos widgets à l'intérieur :
Code : C++1
2
3 | layout->addWidget(bouton1);
layout->addWidget(bouton2);
layout->addWidget(bouton3);
|
La méthode addWidget du layout attend que vous lui donniez en paramètre un pointeur vers le widget à ajouter au conteneur. Voilà pourquoi je vous ai fait utiliser des pointeurs (sinon il aurait fallu écrire
layout->addWidget(&bouton1);
à chaque fois).
3/ Indiquer à la fenêtre d'utiliser le layout
Maintenant, dernière chose : il faut placer le layout dans la fenêtre. Il faut dire à la fenêtre : "
tu vas utiliser ce layout, qui contient mes widgets".
Code : C++1 | fenetre.setLayout(layout);
|
La méthode setLayout de la fenêtre attend un pointeur vers le layout à utiliser.
Et voilà, notre fenêtre contient maintenant notre layout, qui contient les widgets. La layout se chargera d'organiser les widgets horizontalement tout seul.
Résumé du code
Voici le code complet de notre fichier main.cpp :
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 | #include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton *bouton1 = new QPushButton("Bonjour");
QPushButton *bouton2 = new QPushButton("les");
QPushButton *bouton3 = new QPushButton("Zéros");
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(bouton1);
layout->addWidget(bouton2);
layout->addWidget(bouton3);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
J'ai surligné les principales nouveautés.
En particulier, comme d'hab' lorsque vous utilisez une nouvelle classe Qt, pensez à l'inclure au début de votre code :
#include <QHBoxLayout>
Résultat
Voilà à quoi ressemble la fenêtre maintenant que l'on utilise un layout horizontal :
Les boutons sont automatiquement disposés de manière horizontale !
L'intérêt principal du layout, c'est son comportement face aux redimensionnements de la fenêtre.
Essayons de l'élargir :
Les boutons continuent de prendre l'espace en largeur.
On peut aussi l'agrandir en hauteur :
On remarque que les widgets restent centrés verticalement.
Vous pouvez aussi essayer de réduire la taille de la fenêtre. On vous interdira de la réduire si les boutons ne peuvent plus être affichés, ce qui vous garantit que les boutons ne risquent plus de disparaître comme avant !
Schéma des conteneurs
En résumé, la fenêtre contient le layout qui contient les widgets. Le layout se charge d'organiser les widgets.
Schématiquement, ça se passe donc comme ça :
Le layout est invisible à l'affichage
On vient de voir le layout QHBoxLayout qui organise les widgets horizontalement.
Il y en a un autre qui les organise verticalement (c'est quasiment la même chose) : QVBoxLayout.
Le layout vertical
Pour utiliser un layout vertical, il suffit de remplacer QHBoxLayout par QVBoxLayout dans le code précédent. Oui oui, c'est aussi simple que ça
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 | #include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton *bouton1 = new QPushButton("Bonjour");
QPushButton *bouton2 = new QPushButton("les");
QPushButton *bouton3 = new QPushButton("Zéros");
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(bouton1);
layout->addWidget(bouton2);
layout->addWidget(bouton3);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
N'oubliez pas d'inclure QVBoxLayout.
Compilez et exécutez ce code, et admirez le résultat :
Amusez-vous à redimensionner la fenêtre. Vous voyez là encore que la layout adapte les widgets qu'il contient à toutes les dimensions. Il empêche en particulier la fenêtre de devenir trop petite, ce qui aurait empêché l'affichage des boutons.
La suppression automatique des widgets
Eh ! Je viens de me rendre compte que tu fais des new dans tes codes, mais il n'y a pas de delete ! Si tu alloues des objets sans les supprimer, ils vont pas rester en mémoire ?
Si, mais comme je vous l'avais dit plus tôt, Qt est intelligent

En fait, 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 (ici à la fin du programme), tous les widgets contenus dans son layout sont supprimés par Qt.
C'est donc Qt qui se charge de faire les delete pour nous.
Bien, vous devriez commencer à comprendre comment fonctionnent les layouts
Comme on l'a vu au début du chapitre, il y a de nombreux layouts, qui ont chacun leurs spécificités ! Intéressons-nous maintenant au puissant (mais complexe) QGridLayout.
Les layouts horizontaux et verticaux sont gentils, mais il ne permettent pas de créer des dispositions très complexes sur votre fenêtre.
C'est là qu'entre en jeu
QGridLayout, qui est en fait un peu un assemblage de QHBoxLayout et QVBoxLayout. Il s'agit d'une disposition en grille, comme un tableau avec des lignes et des colonnes.
Schéma de la grille
Il faut imaginer que votre fenêtre peut être découpée sous la forme d'une grille avec une infinité de cases, comme ceci :
Si on veut placer un widget en haut à gauche, il faudra le placer à la case de coordonnées (0, 0).
Si on veut en placer un autre en-dessous, il faudra utiliser les coordonnées (1, 0).
Ainsi de suite
Utilisation basique de la grille
Essayons d'utiliser un QGridLayout simplement pour commencer (oui parce qu'on peut aussi l'utiliser de manière compliquée

).
Nous allons placer un bouton en haut à gauche, un à sa droite et un en-dessous.
La seule différence réside en fait dans l'appel à la méthode addWidget. Celle-ci accepte 2 paramètres supplémentaires : les coordonnées où placer le widget sur la grille.
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 | #include <QApplication>
#include <QPushButton>
#include <QGridLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton *bouton1 = new QPushButton("Bonjour");
QPushButton *bouton2 = new QPushButton("les");
QPushButton *bouton3 = new QPushButton("Zéros");
QGridLayout *layout = new QGridLayout;
layout->addWidget(bouton1, 0, 0);
layout->addWidget(bouton2, 0, 1);
layout->addWidget(bouton3, 1, 0);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
Résultat :
Si vous comparez avec le schéma de la grille que j'ai fait plus haut, vous voyez que les boutons ont bien été disposés selon les bonnes coordonnées.
D'ailleurs en parlant du schéma plus haut, il y a un truc que je comprends pas, c'est tous ces points de suspension "..." là. Ca veut dire que la taille de la grille est infinie ? Dans ce cas, comment je fais pour placer un bouton en bas à droite ?
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.
Petit test, rajoutons un bouton aux coordonnées (1, 1) :
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 | #include <QApplication>
#include <QPushButton>
#include <QGridLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton *bouton1 = new QPushButton("Bonjour");
QPushButton *bouton2 = new QPushButton("les");
QPushButton *bouton3 = new QPushButton("Zéros");
QPushButton *bouton4 = new QPushButton("!!!");
QGridLayout *layout = new QGridLayout;
layout->addWidget(bouton1, 0, 0);
layout->addWidget(bouton2, 0, 1);
layout->addWidget(bouton3, 1, 0);
layout->addWidget(bouton4, 1, 1);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
Résultat :
Si on veut, on peut aussi décaler le bouton encore plus en bas à droite dans une nouvelle ligne et une nouvelle colonne :
Code : C++1 | layout->addWidget(bouton4, 2, 2);
|
C'est compris ?
Un widget qui occupe plusieurs cases
L'avantage de la disposition en grille, c'est qu'on peut faire en sorte qu'un widget occupe plusieurs cases à la fois. On parle de
spanning (ceux qui font du HTML doivent avoir entendu parler des attributs
rowspan et
colspan sur les tableaux).
Pour faire cela, il faut appeler une version surchargée de addWidget qui accepte 2 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 suivant :
Si on lui donne un columnSpan de 3, il occupera cet espace :
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 dépend du nombre de widgets sur la grille. En pratiquant vous allez rapidement comprendre comment ça fonctionne.
Essayons de faire en sorte que le bouton "Zéros" prenne 2 colonnes de largeur :
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 | #include <QApplication>
#include <QPushButton>
#include <QGridLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget fenetre;
QPushButton *bouton1 = new QPushButton("Bonjour");
QPushButton *bouton2 = new QPushButton("les");
QPushButton *bouton3 = new QPushButton("Zéros");
QGridLayout *layout = new QGridLayout;
layout->addWidget(bouton1, 0, 0);
layout->addWidget(bouton2, 0, 1);
layout->addWidget(bouton3, 1, 0, 1, 2);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
Notez la ligne :
layout->addWidget(bouton3, 1, 0, 1, 2);
Les 2 derniers paramètres correspondent respectivement au rowSpan et au columnSpan. Le rowSpan est ici de 1, c'est la valeur par défaut on ne change donc rien, mais le columnSpan est de 2.
Le bouton va donc "occuper" 2 colonnes :
Essayez en revanche de monter le columnSpan à 3 : vous ne verrez aucun changement.
En effet, il aurait fallu qu'il y ait un troisième widget sur la première ligne pour que le columnSpan puisse fonctionner.
Faites des tests avec le spanning pour vous assurer que vous avez bien compris comment ça marche
Le layout de formulaire
QFormLayout est un layout assez récent spécialement fait pour les fenêtres qui contiennent des formulaires.
Un formulaire est en général une suite de libellés ("Votre prénom :") associés à des champs de formulaire (zone de texte par exemple) :
Normalement, pour écrire du texte dans la fenêtre, on utilise le widget QLabel (libellé), dont on parlera plus en détail dans le prochain chapitre.
L'avantage du layout que nous allons utiliser, c'est qu'il simplifie notre travail en créant automatiquement des QLabel pour nous.
Vous noterez d'ailleurs que 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 en fonction des habitudes des OS, pour certains les libellés sont alignés à gauche, pour d'autres ils sont alignés à droite, etc.
L'utilisation d'un QFormLayout est très simple. La différence, c'est qu'au lieu d'utiliser une méthode addWidget, nous allons utiliser une méthode addRow qui prend 2 paramètres :
- Le texte du libellé
- Un pointeur vers le champ du formulaire
Pour faire simple, nous allons créer 3 champs de formulaire de type "Zone de texte à une ligne" (QLineEdit), puis nous allons les placer dans un QFormLayout au moyen de la méthode addRow :
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 | #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;
layout->addRow("Votre nom", nom);
layout->addRow("Votre prénom", prenom);
layout->addRow("Votre âge", age);
fenetre.setLayout(layout);
fenetre.show();
return app.exec();
}
|
Résultat :
Sympa, non ?
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.
Explication en image (euh, en code) :
Code : C++1
2
3 | layout->addRow("Votre &nom", nom);
layout->addRow("Votre &prénom", prenom);
layout->addRow("Votre â&ge", age);
|
La lettre "p" est désormais un raccourci vers le champ du prénom.
"n" pour le champ nom.
"g" pour le champ âge.
L'utilisation du raccourci dépend de votre système d'exploitation. Sous Windows, il faut faire Alt puis la touche raccourci.
Lorsque vous appuyez sur Alt, les lettres raccourcis apparaissent soulignées :
Faites Alt + N pour accéder directement au champ du nom !
Souvenez-vous de ce symbole &, il est très souvent utilisé en GUI Design (design de fenêtre) pour indiquer quelle lettre sert de raccourci. On le réutilisera notamment pour avoir des raccourcis dans les menus de la fenêtre.
Ah, et si vous voulez par contre vraiment afficher un symbole & dans un libellé, tapez-en deux : "&&".
Exemple : "Bonnie && Clyde".
Avant de terminer ce chapitre, il me semble important que nous jetions un oeil aux
layouts combinés, une fonctionnalité qui va vous faire comprendre toute la puissance des layouts.
Commençons comme il se doit par une question que vous devriez vous poser :
Les layouts c'est bien joli, mais c'est pas un peu limité ? Si je veux faire une fenêtre un peu complexe, ce n'est pas à grands coups de QVBoxLayout ou même de QGridLayout que je vais m'en sortir !
C'est vrai que mettre ses widgets les uns en-dessous des autres peut sembler limité. Même la grille fait un peu "rigide", je reconnais.
Mais rassurez-vous, tout a été pensé. La magie apparaît lorsque nous commençons à combiner les layouts, c'est-à-dire à
placer un layout dans un autre layout.
Un cas concret
Prenons par exemple notre joli formulaire. Supposons que l'on veuille ajouter un bouton "Quitter". Si vous voulez placer ce bouton en bas du formulaire, comment faire ?
Il va falloir d'abord créer un layout vertical (QVBoxLayout), et placer à l'intérieur notre layout de formulaire
puis notre bouton "Quitter".
Cela donne le schéma suivant :
On voit que notre QVBoxLayout contient 2 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.
Utilisation de addLayout
Pour insérer un layout dans un autre, on utilise addLayout au lieu de addWidget (c'est logique me direz-vous

).
Voici un bon petit code pour se faire la main :
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 | #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");
QWidget::connect(boutonQuitter, SIGNAL(clicked()), &app, SLOT(quit()));
layoutPrincipal->addWidget(boutonQuitter); // Ajout du bouton
fenetre.setLayout(layoutPrincipal);
fenetre.show();
return app.exec();
}
|
J'ai surligné les ajouts au layout vertical principal :
- L'ajout du sous-layout de formulaire (addLayout)
- L'ajout du bouton (addWidget)
Vous remarquerez que je fais les choses un peu dans l'ordre inverse : d'abord je crée les widgets et layouts "enfants" (le QFormLayout), et ensuite je crée le layout principal (le QVBoxLayout) et j'y ajoute le layout enfant que j'ai créé.
Au final, la fenêtre qui apparaît est la suivante :
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 :
Exercice
Essayez d'obtenir le rendu suivant :
Si vous voulez mettre plusieurs boutons en bas sur la même ligne, vous pouvez créer un QHBoxLayout et ajouter ce QHBoxLayout au QVBoxLayout !
Vous pouvez aussi utiliser plus simplement un QGridLayout en utilisant un columnSpan. En effet, un QGridLayout n'est rien d'autre qu'un assemblage de QVBoxLayout et de QHBoxLayout.
Plusieurs méthodes sont donc possibles, libre à vous d'utiliser un QGridLayout ou des QVBoxLayout et QHBoxLayout.
Ce ne devrait pas être un exercice difficile si vous avez bien suivi ce chapitre. Ce sera en tout cas l'occasion de vous assurer que vous avez bien compris
Les layouts sont la base du positionnement de widgets en GUI Design. Ils nous donnent un maximum de flexibilité pour que nos fenêtres s'adaptent à toutes les conditions.
Bien entendu, je vous mentirais si je vous disais qu'absolument tout le monde les utilise. Pour certains logiciels simples, il n'est parfois pas nécessaire de recourir aux layouts. Il est néanmoins recommandé de s'en servir autant que possible.
Nous n'avons pas pu absolument
tout voir à propos des layouts. La différence, c'est que maintenant je vous ai appris à vous servir de la doc et vous pouvez aller compléter ce que vous savez si besoin est.
Dans le prochain chapitre, nous passerons en revue la plupart des widgets courants et simples. En effet, cela fait un moment que je vous fais utiliser pour le besoin du cours quelques widgets comme les boutons et les champs de texte, mais il est maintenant temps de faire un tour d'horizon plus général pour que vous sachiez quels sont les principaux widgets qui peuvent peupler une fenêtre.