IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

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!"); 
	// c'est celui  qui va nous faire quitter l'application pour une raison 
	// qui semble assez naturelle ...
	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

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

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

	w.setLayout(&hbl);
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.

	w.show();
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 en 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 en 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 en 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.

/* code à mettre dans main(), comme d'habitude */
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 : en 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 ;-).

Vous pouvez désormais vous amuser à entasser des widgets dans tous les sens pour maitriser l'utilisation des gestionnaires de disposition. Vous avez également en la page sur les gestionnaires de dispositions (layouts) en général de la documentation officielle afin d'en apprendre plus. Cette page vous mènera également vers la documentation des diverses classes tournant autour des gestionnaires de dispositions.


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.

 

Valid XHTML 1.1!Valid CSS!

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.