Aller au menu - Aller au contenu

[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

Avatar
Auteur : M@teo21
Note : 18 / 20 (7 votes)
Visualisations : 29 040

Plus d'informations Plus d'informations
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 :p

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 :

Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

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 consituté de 3 fichiers :



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 :

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

Un bouton coupé en deux
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 :)

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 ce schéma de mon cru :

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 :



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 ? :euh:


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 ? o_O


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

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

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

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 :

Layout horizontal agrandi


Les boutons continuent de prendre l'espace en largeur.

On peut aussi l'agrandir en hauteur :

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 ! :D


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 :

Schéma des layouts
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 :p

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 :

Layout vertical


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.

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

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

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

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 :

Code : C++
1
layout->addWidget(bouton4, 2, 2);


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 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.



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 :

rowSpan


Si on lui donne un columnSpan de 3, il occupera cet espace :

columnSpan


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 :

Spanning du bouton


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

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

Formulaire


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 :



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 :

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

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 :

Raccourcis dans un form layout


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".

Combiner les layouts

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 :

Schéma des layouts combinés


On voit que notre QVBoxLayout contient 2 choses, dans l'ordre :

  1. Un QFormLayout (qui contient lui-même d'autres widgets)
  2. 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 :



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 :

Layouts combinés


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 :

Layouts combinés (schéma)



Exercice



Essayez d'obtenir le rendu suivant :

Exercice des layouts



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

Dans mon exemple, les boutons "Aide" et "Envoyer" ne font rien (je n'ai pas géré de signaux et de slots pour eux). Le résultat que vous devez obtenir est juste visuel, n'essayez pas de tenter d'envoyer le formulaire sur internet et de le stocker dans une base de données, il est un peu trop tôt encore :p

Q.C.M.

Les layouts permettent-ils d'obtenir un positionnement absolu ou relatif des widgets ?
Quelle est la particularité d'une classe abstraite ?
Laquelle de ces classes permet de disposer plusieurs widgets sur une même ligne ?
Quelle méthode de la fenêtre doit-on appeler pour lui indiquer le layout principal qu'elle doit utiliser ?
Un layout peut-il en contenir un autre ?
Quelles sont les coordonnées du coin en haut à gauche dans un QGridLayout ?
Quel symbole permet de définir un raccourci clavier, notamment pour les formulaires construits avec un QFormLayout ?

Statistiques de réponses au QCM


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.

Je vous recommande de lire leur page d'explication générale sur les layouts puis de regarder les différentes classes de layouts. N'oubliez pas de consulter les classes parentes à chaque fois, ce sont souvent elles qui contiennent les méthodes et attributs qui semblent manquer.
Jetez un oeil aux "stretch factors", qui permettent de définir des tailles proportionnelles pour les widgets, ainsi qu'à l'alignement des widgets.


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.
Chapitre précédent Sommaire Chapitre suivant
Retour en haut Retour en haut


Créé : le 18/09/2007 à 17:13:58
Modifié : le 23/10/2008 à 14:17:50
Avancement : 100%
Licence : Copie non autorisée

12 commentaires

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 52 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0346s (0.0198s)