• 50 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 25/03/2019

Positionnez vos widgets avec les layouts

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

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 ? C'est ce que nous allons découvrir dans ce chapitre.

On distingue deux techniques différentes pour positionner des widgets :

  • Le positionnement absolu : c'est celui que nous avons vu jusqu'ici, avec l'appel à la méthodesetGeometry(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 souple et c'est celui que je vous recommande d'utiliser autant que possible. Nous allons l'étudier dans ce chapitre.

Le positionnement absolu et ses défauts

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 constitué de trois fichiers :

  • main.cpp: contenait le mainqui se chargeait juste d'ouvrir la fenêtre principale ;

  • MaFenetre.h: contenait l'en-tête de notre classe MaFenetrequi héritait de QWidget;

  • MaFenetre.cpp: contenait l'implémentation des méthodes de MaFenetre, notamment du constructeur.

C'est l'architecture que nous utiliserons 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 tout premiers chapitres Qt : nous allons seulement utiliser un main. Il y aura un seul fichier :main.cpp.

Voici le code de votre projet, sur lequel nous allons commencer :

#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 :

Notre fenêtre simple
Notre fenêtre simple

Les défauts du positionnement absolu

Dans le code précédent, nous avons positionné notre bouton de manière absolue à l'aide de l'instructionbouton.move(70, 60);
Le bouton a été placé très précisément 70 pixels sur la droite et 60 pixels plus bas.

Le problème... c'est que ce n'est pas souple du tout. Imaginez que l'utilisateur s'amuse à redimensionner la fenêtre :

Un bouton coupé en deux
Un bouton coupé en deux

Le bouton ne bouge pas. 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 la réduit trop.

Dans ce cas, pourquoi ne pas empêcher l'utilisateur de redimensionner la fenêtre ? On avait fait cela grâce à setFixedSizedans 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 setFixedSizene 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). Si l'utilisateur travaille avec une résolution plus faible 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é dans Qt par ce qu'on appelle les layouts . Ce sont des conteneurs de widgets.
C'est justement l'objet principal de ce chapitre.

L'architecture des classes de layout

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 le schéma suivante de mon cru.

Layouts avec Qt
Layouts avec Qt

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 QWizardqui permet de créer des assistants.

Euh... Mais pourquoi tu as écrit QLayouten italique, et pourquoi tu as grisé la classe ?

QLayoutest une classe abstraite. Souvenez-vous du chapitre sur le polymorphisme, nous y avions vu qu'il est possible de créer des classes avec des méthodes virtuelles pures. Ces classes sont dites abstraites parce qu'on ne peut pas instancier d'objet de ce type.

L'utilisation de classes abstraites par Qt pour les layouts est un exemple typique. Tous les layouts ont des propriétés communes et des méthodes qui effectuent la même action mais de manière différente. Afin de représenter toutes les actions possibles dans une seule interface, les développeurs ont choisi d'utiliser une classe abstraite. Voilà donc un exemple concret pour illustrer ce point de théorie un peu difficile.

Les layouts horizontaux et verticaux

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 deux classes :

  • QHBoxLayout;

  • QVBoxLayout.

QHBoxLayoutet QVBoxLayouthéritent de QBoxLayout. Ce sont des classes très similaires (la documentation 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 uniquement ses classes filles QHBoxLayoutetQVBoxLayout(cela revient au même).

Le layout horizontal

L'utilisation d'un layout se fait en 3 temps :

  1. On crée les widgets ;

  2. On crée le layout et on place les widgets dedans ;

  3. On dit à la fenêtre d'utiliser le layout qu'on a créé.

1/ Créez les widgets

Pour les besoins de ce chapitre, nous allons créer plusieurs boutons de type QPushButton:

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 :

QPushButton bouton1("Bonjour");
QPushButton bouton2("les");
QPushButton bouton3("Zéros");

... cette méthode a l'air plus simple mais vous verrez par la suite que c'est plus pratique de travailler directement avec des pointeurs.
La différence entre ces deux codes, c'est que bouton1est 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 trois 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 l'aurait fait avant :

QPushButton *bouton1 = new QPushButton("Bonjour", &fenetre);

On n'a pas fait comme cela et c'est justement fait exprès. Nous n'allons pas placer les boutons dans la fenêtre directement, mais dans un conteneur : le layout.

2/ Créez le layout et placer les widgets dedans

Créons justement ce layout, un layout horizontal :

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 :

layout->addWidget(bouton1);
layout->addWidget(bouton2);
layout->addWidget(bouton3);

La méthodeaddWidgetdu 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 écrirelayout->addWidget(&bouton1);à chaque fois).

3/ Indiquez à 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 ».

fenetre.setLayout(layout);

La méthode setLayoutde la fenêtre attend un pointeur vers le layout à utiliser.
Et voilà, notre fenêtre contient maintenant notre layout, qui contient les widgets. Le layout se chargera d'organiser tout seul les widgets horizontalement.

Résumé du code

Voici le code complet de notre fichiermain.cpp:

#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'habitude 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 :

Layout horizontal
Layout avec Qt

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 :

Layout horizontal agrandi
Layout horizontal agrandi

Les boutons continuent de prendre l'espace en largeur. On peut aussi l'agrandir en hauteur :

Layout horizontal agrandi
Layout horizontal agrandi

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, cela se passe donc comme à la figure suivante :

Schéma des layouts
Schéma des layouts

On vient de voir le layout QHBoxLayoutqui 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 QHBoxLayoutparQVBoxLayoutdans le code précédent. Oui oui, c'est aussi simple que cela !

#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 :

Layout vertical
Layout vertical

Amusez-vous à redimensionner la fenêtre. Vous voyez là encore que le 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 nuit à l'affichage des boutons.

La suppression automatique des widgets

Eh ! Je viens de me rendre compte que tu fais desnewdans tes codes, mais il n'y a pas dedelete! Si tu alloues des objets sans les supprimer, ils ne 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 lesdeletepour 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.

Le layout de grille

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 jeuQGridLayout, qui est en fait un peu un assemblage deQHBoxLayoutetQVBoxLayout. 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 à la figure suivante.

La grille

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) et ainsi de suite.

Utilisation basique de la grille

Essayons d'utiliser unQGridLayoutsimplement 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éthodeaddWidget. Celle-ci accepte deux paramètres supplémentaires : les coordonnées où placer le widget sur la grille.

#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();
}
Boutons disposés selon une grille
Boutons disposés selon une grille

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 ne comprends pas, c'est tous ces points de suspension « ... » là. Cela 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) :

#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();
}
Bouton en bas à droite
Bouton en bas à droite

Si on veut, on peut aussi décaler le bouton encore plus en bas à droite dans une nouvelle ligne et une nouvelle colonne :

layout->addWidget(bouton4, 2, 2);
Bouton en bas à droite
Bouton en bas à droite

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 attributsrowspanetcolspansur les tableaux).

Pour ce faire, il faut appeler une version surchargée deaddWidgetqui accepte deux paramètres supplémentaires : lerowSpanet lecolumnSpan.

  • 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 unrowSpande 2, il occupera alors l'espace indiqué à la figure suivante :

rowSpan

Si on lui donne uncolumnSpande 3, il occupera l'espace indiqué sur la figure suivante :

columnSpan

Essayons de faire en sorte que le bouton « Zéros » prenne deux colonnes de largeur :

layout->addWidget(bouton3, 1, 0, 1, 2);

Les deux derniers paramètres correspondent respectivement au rowSpanet au columnSpan. Le rowSpanest ici de 1, c'est la valeur par défaut on ne change donc rien, mais le columnSpanest de 2. Le bouton va donc « occuper » 2 colonnes :

Spanning du bouton
Spanning du bouton

Faites des tests avec le spanning pour vous assurer que vous avez bien compris comment cela fonctionne.

Le layout de formulaire

Le layout de formulaire QFormLayoutest un layout spécialement conçu pour les fenêtres hébergeant des formulaires.

Un formulaire est en général une suite de libellés (« Votre prénom : ») associés à des champs de formulaire (une zone de texte par exemple) :

Un formulaire
Un formulaire

Normalement, pour écrire du texte dans la fenêtre, on utilise le widgetQLabel(libellé) dont on parlera plus en détail au prochain chapitre.

L'avantage du layout que nous allons utiliser, c'est qu'il simplifie notre travail en créant automatiquement desQLabelpour nous.

L'utilisation d'un QFormLayoutest très simple. La différence, c'est qu'au lieu d'utiliser une méthodeaddWidget, nous allons utiliser une méthode addRowqui prend deux paramètres :

  • le texte du libellé ;

  • un pointeur vers le champ du formulaire.

Pour faire simple, nous allons créer trois champs de formulaire de type « Zone de texte à une ligne » (QLineEdit), puis nous allons les placer dans un QFormLayoutau moyen de la méthode addRow:

#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 en figure suivante :

Layout de formulaire
Layout de formulaire

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) :

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 » vers le champ nom et
« g » vers le champ âge.

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 :

Raccourcis dans un form layout
Raccourcis dans un form layout

Faites Alt + N pour accéder directement au champ du nom !

Combinez les layouts

Avant de terminer ce chapitre, il me semble important que nous jetions un coup d'œil 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 n'est-ce pas un peu limité ? Si je veux faire une fenêtre un peu complexe, ce n'est pas à grands coups deQVBoxLayoutou même deQGridLayoutque 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 faut 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 de la figure suivante :

Schéma des layouts combinés
Schéma des layouts combinés

On voit que notre QVBoxLayoutcontient deux choses, dans l'ordre :

  1. Un QFormLayout(qui contient lui-même d'autres widgets) ;

  2. UnQPushButton.

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 addLayoutau lieu de addWidget(c'est logique me direz-vous).

Voici un bon petit code pour se faire la main :

#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) ; ensuite je crée le layout principal (le QVBoxLayout) et j'y insère le layout enfant que j'ai créé.

Au final, la fenêtre qui apparaît ressemble à la figure suivante :

Layouts combinés

On ne le voit pas, mais la fenêtre contient d'abord unQVBoxLayout, qui contient lui-même un layout de formulaire et un bouton :

Layouts combinés (schéma)
Layouts combinés (schéma)

En résumé

  • Pour placer des widgets sur une fenêtre, deux options s'offrent à nous : les positionner au pixel près (en absolu) ou les positionner de façon souple dans des layouts (en relatif).

  • Le positionnement en layouts est conseillé : les widgets occupent automatiquement l'espace disponible suivant la taille de la fenêtre.

  • Il existe plusieurs types de layouts selon l'organisation que l'on souhaite obtenir des widgets :QVBoxLayout(vertical),QHBoxLayout(horizontal),QGridLayout(en grille),QFormLayout(en formulaire).

  • Il est possible d'imbriquer des layouts entre eux : un layout peut donc en contenir un autre. Cela nous permet de réaliser des positionnements très précis.

Exemple de certificat de réussite
Exemple de certificat de réussite