Ecrire sa première application avec Qt 4
III. Afficher plusieurs widgets
III-1. Premier essai
III-2. Les gestionnaires de dispositions
III-3. Comment ordonner ses widgets ?
III-4. Dispositions plus complexe
III-5. Pour la suite
III. Afficher plusieurs widgets
Nous allons, comme vu précédemment, passer la vitesse supérieure. Vous êtes
désormais prêt à apprendre à afficher plusieurs composants, si vous avez bien compris l'exemple
précédent. Voyons donc comment procéder pour concevoir une interface comportant plusieurs widgets.
III-1. Premier essai
On peut penser qu'il suffit de créer plusieurs composants de la même
façon que le QPushButton dans la section précédente. Cela donnerait donc
le code suivant, si l'on veut rajouter un bouton à l'exemple précédent.
# include <QApplication>
# include <QPushButton>
int main (int argc, char * * argv)
{
QApplication app (argc, argv);
QPushButton pb (" Hello, Qt! " );
QPushButton pb2 (" Bye, Qt! " );
QObject:: connect (& pb2, SIGNAL (clicked ()), & app, SLOT (quit ()));
pb.resize (400 ,100 );
pb2.resize (400 ,100 );
pb.show ();
pb2.show ();
return app.exec ();
}
|
La compilation se déroule normalement. Cependant, à l'exécution, vous aurez
normalement droit à deux fenêtres séparées, contenant chacune un bouton. Or, nous voulions
avoir deux boutons dans la même fenêtre. Cependant, Qt nous offre une solution, qui
va être étudiée dans le paragraphe suivant : les gestionnaires de dispositions.
III-2. Les gestionnaires de dispositions
 |
Les gestionnaires de dispositions servent à organiser les widgets
sur notre fenêtre. Il en existe de différents types, dont vertical, horizontal,
en grille. Le gestionnaire de disposition vertical disposera les widgets de haut en bas,
dans l'ordre où vous les insérerez dans le code (celui qui sera affiché en haut sera
celui qui aura été inséré dans le gestionnaire de dispositions en premier). Le gestionnaire
horizontal les disposera de la même manière à l'horizontale, et le gestionnaire de dispositions en grille
permettra d'organiser nos widgets comme dans un tableau, avec des numéros de colonne et de ligne, sachant
qu'on peut faire occuper N colonnes et P lignes à un widget précis, par exemple.
|
L'utilisation de ces gestionnaires de disposition facilite énormément l'organisation d'une fenêtre. En plus du fait
de pouvoir organiser facilement notre fenêtre, ces derniers permettent, lors d'un redimensionnement, de préserver
une certaine cohérence dans l'affichage --- chose qui n'est pas faite si l'on donne des positions absolues aux widgets (tel widget sera
à la position (X,Y) sur la fenêtre, par exemple).
Dans Qt, il existe 3 gestionnaires de dispositions importants, dont il faut abuser sans modération :
- QVBoxLayout : gestionnaire de dispositions vertical
- QHBoxLayout : gestionnaire de dispositions horizontal
- QGridLayout : gestionnaire de dispositions en grille

Principe d'utilisation d'un QHBoxLayout
De simples fonctions membres de ces classes vous permettront d'organiser simplement vos widgets
dans vos fenêtres. Maintenant qu'ils vous ont été présentés, il est temps de se concentrer sur leur utilisation.
III-3. Comment ordonner ses widgets ?
Nous avons donc vu les outils et leur utilité, passons donc à leur
mise en pratique.
Considérons le code suivant, qui va être commenté ensuite.
|
# include <QApplication>
# include <QHBoxLayout>
# include <QPushButton>
# include <QLabel>
int main (int argc, char * * argv)
{
QApplication app (argc, argv);
QWidget w;
QHBoxLayout hbl;
QPushButton pb (" Un bouton " );
QLabel label (" Un texte " );
hbl.addWidget (& pb);
hbl.addWidget (& label);
w.setLayout (& hbl);
w.show ();
return app.exec ();
}
|
Pour commencer, une chose importante : l'opérateur & est
beaucoup utilisé dans ce code car le code de Qt utilise énormément les
pointeurs, pour contrôler le plus précisément possible la durée de vie des objets, entre autres.
Il serait revenu au même donc d'utiliser l'opérateur new pour instancier des objets dynamiques et
ainsi il n'aurait plus fallu utiliser l'opérateur &, à l'opposé de ce qui est fait dans le code précédent.
|
# include <QApplication>
# include <QHBoxLayout>
# include <QPushButton>
# include <QLabel>
|
Comme dit dans la section Premiers Pas, lorsque l'on utilise une classe de Qt,
il faut inclure l'en-tête correspondant. Notez que l'on inclue pas <QWidget> car il est inclus dans
les en-têtes de QPushButton et QLabel, du fait que ce sont deux classes dérivant de
QWidget.
|
int main (int argc, char * * argv)
{
QApplication app (argc, argv);
QWidget w;
QHBoxLayout hbl;
|
On constate donc la présence de la fonction main habituelle, ainsi
que de la création d'une application Qt. Concentrons nous sur les deux lignes suivantes.
La première définit un QWidget, qui est la classe de base pour tout composant graphique.
Soit dit en passant : on peut afficher un QWidget, cependant il ne représente rien. Il est vide. Par contre,
on peut faire comprendre à Qt très simplement que l'on veut qu'un gestionnaire de dispositions
s'insère dans un QWidget "brut" et gère ce qui va être affiché à l'intérieur du QWidget. Il faut savoir
que n'importe quelle instance de QWidget ou d'une classe dérivée de QWidget est affichable et peut,
grâce aux gestionnaires de dispositions notamment, contenir d'autres éléments graphiques. Ici, nous créons simplement
un QWidget et un gestionnaire de dispositions horizontal (QHBoxLayout). Remarquez que ces derniers ne sont
aucunement liés pour le moment.
|
QPushButton pb (" Un bouton " );
QLabel label (" Un texte " );
|
Ici nous instancions deux widgets (un bouton pb et un "conteneur de texte", autrement appelé Label en anglais, nommé label)
dans le but de pouvoir les afficher côte à côte horizontalement, en fin de parcours.
|
hbl.addWidget (& pb);
hbl.addWidget (& label);
|
Ce code a pour effet de rajouter nos deux widgets dans la "file" du gestionnaire de dispositions.
En somme, on lui demande de mettre nos deux widgets côte à côte horizontalement.
Cependant, nous n'avons pour le moment indiqué nullepart que nous voulions afficher le tout. Nous n'avons pas non
plus établi de lien entre le gestionnaire de dispositions horizontal et le QWidget.
 |
Un gestionnaire de disposition n'est pas un élément graphique. Il est en réalité
"abstrait" et ne sert qu'à ordonner. On peut imaginer les limites qu'il pose dans la fenêtre
mais ces limites demeurent imaginaires (certes utiles pour nous, mais imaginaires tout de même).
On ne peut donc pas "afficher" un gestionnaire de dispositions. Il faut le lier à un élément
affichable.
|
Voici la fameuse ligne qui relie le widget et le gestionnaire de dispositions. C'est cette ligne
qui fait en sorte que l'intérieur du widget est ordonné selon le gestionnaire de dispositions.
Et enfin, on affiche le widget.
Compilez et exécutez. Vous aurez bien un bouton et un texte côte à côte,
alignés horizontalement.
III-4. Dispositions plus complexe
On peut désormais s'intéresser à des dispositions de widgets
plus complexes. Tout d'abord, étudions l'utilisation d'un gestionnaire de dispositions
en grille (QGridLayout).
|
# include <QApplication>
# include <QPushButton>
int main (int argc, char * * argv)
{
QWidget * window = new QWidget;
QPushButton* pb1 = new QPushButton (" Utilisation " );
QPushButton* pb2 = new QPushButton (" de " );
QPushButton* pb3 = new QPushButton (" QGridLayout " );
QPushButton* pb4 = new QPushButton (" grâce " );
QPushButton* pb5 = new QPushButton (" à Qt " );
QGridLayout * layout = new QGridLayout;
layout- > addWidget (pb1, 0 , 0 );
layout- > addWidget (pb2, 0 , 1 );
layout- > addWidget (pb3, 1 , 0 , 1 , 2 );
layout- > addWidget (pb4, 2 , 0 );
layout- > addWidget (pb5, 2 , 1 );
window- > setLayout (layout);
window- > show ();
return app.exec ();
}
|
Tout d'abord, notez l'utilisation des pointeurs. Comme mentionné plus haut,
Qt utilise en majorité des pointeurs; il faut donc s'y accommoder dès maintenant.
Rien d'inhabituel jusqu'à la création de notre gestionnaire de dispositions en grille.
Comme vous pouvez le voir
ici,
on ajoute simplement un widget dans le gestionnaire en donnant un pointeur vers le widget et en spécifiant le numéro de ligne et de colonne où l'on veut le placer. Cependant,
on agit différemment pour le troisième bouton. En effet, on utilise ici
cette "version" de addWidget,
qui permet en plus de la précédente de spécifier sur combien de lignes (ici une) et sur combien de colonnes (ici deux) doit s'étaler
le widget. Pour terminer, on relie le widget et le layout et on affiche notre fenêtre.
Il est maintenant temps de découvrir une autre fonctionnalité : l'ajout d'un gestionnaire
de dispositions dans un autre. Cela se fait via la fonction membre
addLayout
de
QBoxLayout (donc disponible seulement dans les classes dérivant de
QBoxLayout, comme
QHBoxLayout et
QVBoxLayout). Voici un petit exemple pour illustrer
son utilisation.
|
QWidget* w = new QWidget;
QHBoxLayout* hbl = new QHBoxLayout;
QPushButton* pb1 = new QPushButton (" Bouton 1 " );
QPushButton* pb2 = new QPushButton (" Bouton 2 " );
QPushButton* pb3 = new QPushButton (" Bouton 3 " );
QPushButton* pb4 = new QPushButton (" Bouton 4 " );
QPushButton* pb5 = new QPushButton (" Bouton 5 " );
hbl- > addWidget (pb1);
hbl- > addWidget (pb2);
hbl- > addWidget (pb3);
QVBoxLayout* vbl = new QVBoxLayout;
vbl- > addLayout (hbl);
vbl- > addWidget (pb4);
vbl- > addWidget (pb5);
w- > setLayout (vbl);
w- > show ();
|
Compilez et exécutez. Vous voyez donc comment s'organisent les widgets
lorsque l'on insère des gestionnaires de dispositions dans d'autres.
Pour terminer, voyons l'utilité d'une autre fonction membre :
addStretch de
QBoxLayout.
Cette dernière permet d'organiser plus facilement l'espacement entre des widgets. Rien de mieux qu'un simple
exemple pour comprendre.
|
QWidget* w = new QWidget;
w- > resize (300 ,100 );
QHBoxLayout* hbl = new QHBoxLayout;
QPushButton* pb1 = new QPushButton (" Fait avec Qt " );
QPushButton* pb2 = new QPushButton (" Pour Developpez " );
hbl- > addWidget (pb1);
hbl- > addStretch ();
hbl- > addWidget (pb2);
w- > setLayout (hbl);
w- > show ();
|
En exécutant ce code, vous verrez alors que le premier bouton
est aligné totalement à gauche et le deuxième totalement à droite. Vous voyez
donc cette fonction permet d'ajouter un espacement qui est géré intelligemment, selon
le nombre de widgets que l'on a avant et après. Pour mieux comprendre, essayez donc ce code.
|
QWidget* w = new QWidget;
w- > resize (300 ,100 );
QHBoxLayout* hbl = new QHBoxLayout;
QPushButton* pb1 = new QPushButton (" Fait avec Qt " );
QPushButton* pb2 = new QPushButton (" Pour Developpez " );
hbl- > addStretch ();
hbl- > addWidget (pb1);
hbl- > addWidget (pb2);
w- > setLayout (hbl);
w- > show ();
|
On voit alors que les deux boutons s'alignent sagement à droite.
Notez que c'est souvent sous cette forme là que se trouvent les boutons "Valider"
et "Annuler" d'un dialogue, des fois que cela vous donne des idées ;-).
III-5. Pour la suite
Je ne sais pas si vous avez ressenti une gêne due au fait
d'entasser le code dans main, mais cela ne devrait pas tarder si
vous essayez d'ajouter encore plus de widgets. Regardez donc le code suivant.
|
# include <QApplication>
# include <QHBoxLayout>
# include <QVBoxLayout>
# include <QGridLayout>
# include <QTextEdit>
# include <QPushButton>
int main (int argc, char * * argv)
{
QApplication app (argc, argv);
QWidget* w = new QWidget;
w- > resize (600 ,600 );
QHBoxLayout* hbl = new QHBoxLayout;
QVBoxLayout* vbl = new QVBoxLayout;
QGridLayout* gl = new QGridLayout;
QVBoxLayout* big_vbl = new QVBoxLayout;
QTextEdit* textedit = new QTextEdit (" Salut, entre ton texte! " );
QPushButton* pb1 = new QPushButton (" Bouton 1 " );
QPushButton* pb2 = new QPushButton (" Bouton 2 " );
QPushButton* pb3 = new QPushButton (" Bouton 3 " );
QPushButton* pb4 = new QPushButton (" Bouton 4 " );
QPushButton* pb5 = new QPushButton (" Bouton 5 " );
QPushButton* pb6 = new QPushButton (" Bouton 6 " );
QPushButton* pb7 = new QPushButton (" Bouton 7 " );
QPushButton* pb8 = new QPushButton (" Bouton 8 " );
QPushButton* pb9 = new QPushButton (" Bouton 9 " );
hbl- > addWidget (pb1);
hbl- > addWidget (pb2);
vbl- > addLayout (hbl);
vbl- > addWidget (pb3);
gl- > addWidget (pb4,0 ,0 ,1 ,2 );
gl- > addWidget (pb5,1 ,0 );
gl- > addWidget (pb6,1 ,1 );
gl- > addWidget (pb7,2 ,0 );
gl- > addWidget (pb8,2 ,1 );
big_vbl- > addLayout (vbl);
big_vbl- > addStretch ();
big_vbl- > addLayout (gl);
big_vbl- > addStretch ();
big_vbl- > addWidget (textedit);
big_vbl- > addWidget (pb9);
w- > setLayout (big_vbl);
w- > show ();
return app.exec ();
}
|
Difficile de s'y retrouver dans main, n'est-ce pas ? C'est pourquoi, lorsqu'il s'agit
de créer une fenêtre "non triviale", il y a une meilleure méthode, qui facilite la lecture du code,
mais qui permet également de rendre plus facile à maintenir le code de notre interface graphique.
Cette méthode, qui s'appuie sur l'héritage, consiste à faire hériter une nouvelle classe d'une classe
de Qt existante et adaptée à nos besoins pour créer une fenêtre personnalisée avec Qt. Rendez-vous dans la prochaine section.


Copyright © 2007 Alp Mestan.
Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.