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 > Les principaux widgets > Lecture du tutoriel

Les principaux widgets

Avatar
Auteur : M@teo21
Note : 19 / 20 (10 votes)
Visualisations : 34 232

Plus d'informations Plus d'informations
Voilà un moment que nous avons commencé à nous intéresser à Qt, je vous parle en long en large et en travers de widgets, mais jusqu'ici nous n'avions toujours pas pris le temps de faire un tour d'horizon rapide de ce qui existait.

C'était voulu. Je voulais dans un premier temps vous faire manipuler un ou deux widgets simples pour vous faire comprendre les concepts de base comme :

Il est maintenant temps de faire une "pause" et de regarder ce qui existe comme widgets. Nous étudierons cependant seulement les principaux widgets ici.

Pourquoi ne les verrons-nous pas tous ? Parce qu'il existe un grand nombre de widgets et que certains sont rarement utilisés. D'autres sont parfois tellement complexes qu'ils nécessiteront un chapitre entier pour les étudier.

Néanmoins, avec ce que vous allez voir, vous aurez largement de quoi faire pour être capables de créer la quasi-totalité des fenêtres que vous voulez ! :)
Pour information, je me base sur la page "liste des widgets" (ici avec l'apparence de vista, mais peu importe l'apparence, ça sera adapté à votre OS).
Je ne compte pas remplacer la doc. Je vous inviterai donc à consulter la doc à chaque fois pour en savoir plus. Mon rôle sera surtout de vous introduire à utiliser de manière basique ces widgets. Je vous fais confiance, je sais que vous saurez en faire une utilisation plus avancée si besoin est. ;)
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Les fenêtres

Avec Qt, tout élément de la fenêtre est appelé un widget. La fenêtre elle-même est considérée comme un widget.

Dans le code, les widgets sont des classes qui héritent toujours de QWidget (directement ou indirectement). C'est donc une classe de base très importante, et vous aurez probablement très souvent besoin de lire la doc de cette classe.


Quelques rappels sur l'ouverture d'une fenêtre



Cela fait plusieurs chapitres que l'on crée une fenêtre dans nos programmes à l'aide d'un objet de type QWidget. Cela signifie-t-il que QWidget = Fenêtre ?

Non. En fait, un widget qui n'est contenu dans aucun autre widget est considéré comme une fenêtre.
Donc quand on fait juste ce code très simple :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <QApplication>
#include <QWidget>


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;
    fenetre.show();

    return app.exec();
}


... cela affiche une fenêtre (vide) :

Fenêtre vide


C'est comme cela que Qt fonctionne. C'est un peu déroutant au début, mais après on apprécie au contraire que ça ait été pensé comme ça.


Donc si je comprends bien, il n'y a pas de classe QFenetre ou quelque chose du genre ?


Tout à fait, il n'y a pas de classe du genre "QFenetre" car n'importe quel widget peut servir de fenêtre. Si vous vous souvenez bien, on avait créé un bouton dans les premiers exemples du cours sur Qt. On avait demandé à afficher ce bouton.
Comme le bouton n'avait pas de parent, une fenêtre avait été ouverte :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <QApplication>
#include <QPushButton>
 
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
 
    QPushButton bouton("Salut les Zéros, la forme ?");
    bouton.show();
 
    return app.exec();
}


Fenêtre bouton


Dans la pratique, on ne crée pas de fenêtre-bouton comme là. On crée d'abord une fenêtre, et on place ensuite des widgets à l'intérieur (ces widgets étant parfois organisés grâce aux layouts comme on l'a vu).

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <QApplication>
#include <QWidget>
#include <QPushButton>


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;
    QPushButton bouton("Salut les Zéros, la forme ?", &fenetre);
    fenetre.show();

    return app.exec();
}


Note : je n'ai pas utilisé de layouts dans ce code pour le rendre court et simple. Le bouton est donc positionné de manière absolue au coin en haut à gauche, de coordonnées (0, 0).


Fenêtre bouton


Le QPushButton a donc pour parent un QWidget, car on a indiqué en second paramètre de son constructeur un pointeur vers le QWidget : &fenetre .
Le QWidget n'a pas de parent car on n'a pas envoyé de pointeur vers un autre widget dans son constructeur, donc c'est une fenêtre.

Ne confondez pas :
  • En termes C++ : une classe parente est une classe mère (quand on fait un héritage).
  • En termes Qt : un widget parent est un widget qui en contient d'autres. Un widget fils est un widget qui n'en contient aucun autre.

Ici, je suis en train de parler de widgets parents en termes Qt.



Quelques classes particulières pour les fenêtres



Résumons ce que je viens de dire : tout widget peut servir de fenêtre.
C'est le widget qui n'a pas de parent qui sera considéré comme étant la fenêtre.

A ce titre, un QPushButton ou un QLineEdit peuvent être considérés comme des fenêtres s'ils n'ont pas de widget parent.

Toutefois, il y a 2 classes de widgets que j'aimerais mettre en valeur :



La fenêtre principale QMainWindow mérite un chapitre entier à elle toute seule. Et elle en aura un. Nous pourrons alors tranquillement passer en revue la gestion des menus, de la barre d'outils et de la barre d'état.

La fenêtre QDialog peut être utilisée pour ouvrir une boîte de dialogue personnalisée générique. Une boîte de dialogue est une fenêtre généralement de petite taille dans laquelle il y a peu d'informations.
La classe QDialog hérite de QWidget comme tout widget qui se respecte, et elle y est même très similaire. Elle y ajoute peu de choses, parmi lesquelles la gestion des fenêtres modales (une fenêtre par-dessus toutes les autres qui doit être remplie avant de pouvoir accéder aux autres fenêtres de l'application).


Nous allons ici étudier ce que l'on peut faire d'intéressant avec la classe de base QWidget qui permet déjà de réaliser la plupart des fenêtres que l'on veut.
Nous verrons ensuite ce qu'on peut faire avec les fenêtres de type QDialog. Quant à QMainWindow, ce sera pour un autre chapitre comme je vous l'ai dit. ;)


Une fenêtre avec QWidget



Pour commencer, je vous invite à ouvrir la doc de QWidget en même temps que vous lisez ce chapitre.

Vous remarquerez que QWidget est la classe mère d'un grrrrand nombre d'autres classes. ;)
Les QWidget disposent de beaucoup de propriétés et de méthodes. Donc tous les widgets disposent de ces propriétés et méthodes.

On peut découper les propriétés en 2 catégories :


Jetons un oeil à celles qui me semblent les plus intéressantes. Pour avoir la liste complète, il faudra recourir à la doc, je ne compte pas tout répéter ici ! ^^


Les propriétés utilisables pour tous les types de widgets, y compris les fenêtres



Je vous fais une liste rapide pour extraire quelques propriétés qui pourraient vous intéresser. Pour savoir comment vous servir de toutes ces propriétés, lisez le prototype que vous donne la doc.

N'oubliez pas qu'on peut modifier une propriété en appelant une méthode du même nom commençant par "set". Par exemple, si la propriété est cursor, la méthode sera setCursor().




N'oubliez pas : pour modifier une de ces propriétés, préfixez la méthode par un "set". Exemple :

Code : C++
1
maFenetre.setWidth(200);


Ces propriétés sont donc valables pour tous les widgets, y compris les fenêtres. Si vous appliquez un setWidth sur un bouton, ça modifiera la largeur du bouton. Si vous appliquez cela sur une fenêtre, c'est la largeur de la fenêtre qui sera modifiée.


Les propriétés utilisables uniquement sur les fenêtres



Ces propriétés sont faciles à reconnaître, elles commencent toutes par "window".
Elles n'ont de sens que si elles sont appliquées aux fenêtres.



Une fenêtre avec QDialog



QDialog est un widget spécialement créé pour générer des fenêtres de type "boîte de dialogue".

Quelle est la différence avec une fenêtre créée à partir d'un QWidget ? :euh:


En général les QDialog sont des petites fenêtres secondaires : des boîtes de dialogue.
Elles proposent le plus souvent un choix simple entre :


Les QDialog sont rarement utilisées pour gérer la fenêtre principale. Pour la fenêtre principale on préfère utiliser QWidget, ou carrément QMainWindow si on a besoin de l'artillerie lourde.

Les QDialog peuvent être de 2 types :


Par défaut, les QDialog sont modales.
Elles disposent en effet d'un méthode exec() qui ouvre la boîte de dialogue de manière modale. Il s'avère d'ailleurs qu'exec() est un slot (très pratique pour effectuer une connexion ça !).

Je vous propose d'essayer de pratiquer de la manière suivante : nous allons ouvrir une fenêtre principale QWidget qui contiendra un bouton. Lorsqu'on cliquera sur ce bouton, il ouvrira une fenêtre secondaire de type QDialog.


Notre objectif est d'ouvrir une fenêtre secondaire après un clic sur un bouton de la fenêtre principale.
La fenêtre secondaire, de type QDialog, affichera juste une image pour cet exemple.

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
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;
        QPushButton *bouton = new QPushButton("Ouvrir la fenêtre", &fenetre);


    QDialog secondeFenetre (&fenetre);
        QVBoxLayout *layout = new QVBoxLayout;
        QLabel *image = new QLabel(&secondeFenetre);
        image->setPixmap(QPixmap("icone.png"));
        layout->addWidget(image);
        secondeFenetre.setLayout(layout);


    QWidget::connect(bouton, SIGNAL(clicked()), &secondeFenetre, SLOT(exec()));
    fenetre.show();

    return app.exec();
}

Mon code est indenté de manière bizarroïde je sais. Je trouve que c'est plus lisible : vous pouvez mieux voir comme cela à quelles fenêtres se rapportent les opérations que je fais.
Vous voyez ainsi immédiatement que dans la première fenêtre je n'ai fait que placer un bouton, tandis que dans la seconde j'ai mis un QLabel affichant une image que j'ai placée dans un QVBoxLayout.

D'autre part, j'ai tout fait dans le main pour cet exemple, mais dans la pratique, comme nous le verrons dans les TP, on a en général un fichier .cpp par fenêtre, c'est plus facile à gérer.


Au départ, la fenêtre principale s'affiche, comme ceci :

QDialog


Si vous cliquez sur le bouton, la boîte de dialogue s'ouvre :

QDialog


Comme elle est modale, vous remarquerez que vous ne pouvez pas accéder à la fenêtre principale tant qu'elle est ouverte.

Bon intéressons-nous au code. J'ai surligné les 2 lignes qui me paraissaient les plus pertinentes :


Cela devrait vous donner des bases suffisantes pour désormais savoir comment ouvrir des fenêtres secondaires de type QDialog. Nous n'avons cependant pas tout vu sur cette classe : on peut rendre les QDialog non modales ou encore utiliser les slots accept() et reject() pour les connecter respectivement à des boutons "OK" et "Annuler" et ainsi informer la fenêtre parente afin qu'elle sache si l'opération a été validée ou refusée par l'utilisateur.

Pour savoir faire tout cela, vous savez ce qu'il vous reste à faire. Tout est dans la doc. ;)

Les boutons

Nous allons maintenant étudier la catégorie des widgets "boutons". Nous allons passer en revue :



Tous ces widgets héritent de QAbstractButton qui lui-même hérite de QWidget, qui finalement hérite de QObject :

Schéma héritage des boutons


Comme l'indique son nom, QAbstractButton est une classe abstraite. Si vous vous souvenez des épisodes précédents de notre passionnant feuilleton, une classe abstraite est une classe... qu'on ne peut pas instancier, bravo ! :D
On ne peut donc pas créer d'objets de type QAbstractButton, il faut forcément utiliser une des classes filles. QAbstractButton sert donc juste de modèle de base pour ses classes filles.


QPushButton : un bouton



Le QPushButton est l'élément le plus classique et le plus commun des fenêtres :

QPushButton


Je ne vous fais pas l'offense de vous expliquer à quoi sert un bouton :p

Commençons par un rappel important, indiqué par la doc : QPushButton hérite de QAbstractButton. Et c'est vraiment une info importante, car vous serez peut-être surpris de voir que QPushButton contient peu de méthodes qui lui sont propres. C'est normal, une grande partie d'entre elles se trouvent dans sa classe parente QAbstractButton.

Il faudra donc absolument consulter aussi QAbstractButton, et même sa classe mère QWidget (et éventuellement aussi QObject mais c'est plus rare), si vous voulez connaître toutes les possibilités offertes au final par un QPushButton.
Par exemple, setEnabled permet d'activer / désactiver le bouton, et cette propriété se trouve dans QWidget.



Les signaux du bouton



Un bouton émet un signal clicked() quand on l'active. C'est le signal le plus communément utilisé.

On note aussi les signaux pressed() (bouton enfoncé) et released() (bouton relâché), mais ils sont plus rares.


Les boutons à 2 états



Un bouton peut parfois avoir 2 états : enfoncé et relâché (normal).

Pour activer un bouton à 2 états, utilisez setCheckable(true) :

Code : C++
1
2
QPushButton *bouton = new QPushButton("Bouton", &fenetre);
bouton->setCheckable(true);


Désormais, un clic sur le bouton le laissera enfoncé, et un nouveau clic le rétablira dans son état normal. Utilisez les signaux pressed() et released() pour récupérer les changements d'état du bouton.

Bouton checked
A gauche un bouton normal, à droite un bouton pressé



QCheckBox : une case à cocher



Une case à cocher QCheckBox est généralement associé à un texte de libellé comme ceci :

Case à cocher


On définit le libellé de la case lors de l'appel du constructeur :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <QApplication>
#include <QWidget>
#include <QCheckBox>


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;
    QCheckBox *checkbox = new QCheckBox("J'aime les frites", &fenetre);
    fenetre.show();

    return app.exec();
}


La case à cocher émet le signal stateChanged(bool) lorsqu'on modifie son état. Le booléen en paramètre nous permet de savoir si la case est maintenant cochée ou décochée.

Si vous voulez vérifier à un autre moment si la case est cochée, appelez isChecked() qui renvoie un booléen.

On peut aussi faire des cases à cocher à 3 états (le troisième état étant l'état grisé). Renseignez-vous sur la propriété tristate pour savoir faire cela. Notez que ce type de case à cocher est relativement rare.

Enfin, sachez que si vous avez plusieurs cases à cocher, vous pouvez les regrouper au sein d'une QGroupBox.


QRadioButton : les boutons radio



C'est une case à cocher particulière : une seule case peut être cochée à la fois parmi une liste.

Radio button


Les radio buttons qui ont le même widget parent sont mutuellement exclusifs. Si vous en cochez un, les autres seront automatiquement décochés.

En général, on place les radio buttons dans une QGroupBox. Utiliser des QGroupBox différentes vous permet de séparer les groupes de radio buttons.

Voici un exemple d'utilisation d'une QGroupBox (qui contient un layout, qui contient les QRadioButton) :

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
#include <QApplication>
#include <QtGui>


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;
    QGroupBox *groupbox = new QGroupBox("Votre plat préféré", &fenetre);

    QRadioButton *steacks = new QRadioButton("Les steacks");
    QRadioButton *hamburgers = new QRadioButton("Les hamburgers");
    QRadioButton *nuggets = new QRadioButton("Les nuggets");

    steacks->setChecked(true);

    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(steacks);
    vbox->addWidget(hamburgers);
    vbox->addWidget(nuggets);

    groupbox->setLayout(vbox);
    groupbox->move(5, 5);

    fenetre.show();

    return app.exec();
}


J'en profite pour signaler que vous pouvez inclure QtGui pour automatiquement inclure tous les widgets : #include <QtGui>
C'est un peu bourrin mais ça marche. :p
Cela vous évite d'avoir à rajouter un nouveau widget à la liste des includes à chaque fois. Attention par contre, la compilation sera un peu plus longue.


Les radio buttons sont placés dans un layout qui est lui-même placé dans la groupbox, qui est elle-même placée dans la fenêtre. Pfiou ! Le concept des widgets conteneurs est ici utilisé à fond !
Et encore, je n'ai pas fait de layout pour la fenêtre (la flême, et je ne voulais pas trop encombrer le code), ce qui fait que la taille initiale de la fenêtre est un peu petite, mais ce n'est pas grave c'est pour l'exemple.

Voilà le résultat :

Plusieurs radio buttons


Nota : j'ai une nourriture plus équilibrée que ne le laisse suggérer cette dernière capture d'écran quand même, je vous rassure. :-°

Les afficheurs

Parmi les widgets afficheurs, on compte principalement :



Etudions-les en choeur, sans heurts, dans la joie et la bonne humeur. :-°


QLabel : afficher du texte ou une image



C'est vraiment LE widget de base pour afficher du texte à l'intérieur de la fenêtre. ^^
Nous l'avons déjà utilisé indirectement auparavant, via les cases à cocher ou encore les layouts de formulaire.

Voici un libellé :

QLabel


... du moins, UN des types de libellés possibles comme nous allons le voir.

QLabel hérite de QFrame, qui est un widget de base permettant d'afficher des bordures. Renseignez-vous auprès de QFrame pour savoir gérer les différents types de bordure.
Par défaut, un QLabel n'a pas de bordure.


Un QLabel peut afficher plusieurs types d'éléments :


Nous allons étudier chacun de ces types de contenu, à l'exception de l'image animée qui sort un peu du cadre du chapitre. Et puis de toute façon, ça ne sert qu'à afficher en pratique des GIF animés, ça nous sera donc peu utile.


Afficher un texte simple



Rien de plus simple, on utilise setText() :

Code : C++
1
label->setText("Bonjour les Zéros !");


Mais on peut aussi afficher un texte simple dès l'appel au constructeur comme ceci :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;

    QLabel *label = new QLabel("Bonjour les Zéros !", &fenetre);
    label->move(30, 20);

    fenetre.show();

    return app.exec();
}


Le résultat est le même que la capture d'écran que je vous ai montrée plus haut :

QLabel


Vous pouvez jeter aussi un oeil à la propriété alignment qui permet de définir l'alignement du texte dans le libellé.


Afficher un texte enrichi



Vous pouvez envoyer du texte enrichi (formaté) au QLabel, avec du HTML :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;

    QLabel *label = new QLabel("Bonjour les <strong>Zéros</strong> !<br />Etes-vous allés sur le <a href=\"http://www.siteduzero.com\">Site du Zéro</a> aujourd'hui ?", &fenetre);
    label->move(30, 20);

    fenetre.show();

    return app.exec();
}


Magie, magie, le texte est correctement mis en forme !

Label avec du HTML


C'est beau la technologie quand même. ^^
Et encore, vous n'avez pas tout vu !


Afficher une image



Vous pouvez demander à ce que le QLabel affiche une image.
Comme il n'y a pas de constructeur qui accepte une image en paramètre, on va appeler le constructeur qui prend juste un pointeur vers la fenêtre parente.

Nous demanderons ensuite à ce que le libellé affiche une image à l'aide de setPixmap().
Cette méthode attend un objet de type QPixmap. Après lecture de la doc sur QPixmap, il s'avère que cette classe a un constructeur qui accepte le nom du fichier à charger sous forme de chaîne de caractères.

Nous allons donc afficher notre belle icône de tout à l'heure, mais cette fois en grand et dans la fenêtre :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;

    QLabel *label = new QLabel(&fenetre);
    label->setPixmap(QPixmap("icone.png"));
    label->move(30, 20);

    fenetre.show();

    return app.exec();
}


L'icône doit se trouver dans le même dossier que l'exécutable pour que cela fonctionne.

Et voilà le résultat !

Une image dans un label



QProgressBar : une barre de progression



Les barres de progression sont gérées par QProgressBar. Cela permet d'indiquer à l'utilisateur l'avancement des opérations.

QProgressBar


Voici quelques propriétés utiles de la barre de progression :


On utilisera donc setValue pour changer la valeur de la barre de progression. Par défaut les valeurs sont comprises entre 0 et 100%.

Qt ne peut pas deviner où en sont vos opérations. C'est à vous de calculer le pourcentage d'avancement de vos opérations. La QProgressBar se contente juste d'afficher le résultat.


Une QProgressBar envoie un signal valueChanged() lorsque sa valeur a été modifiée.

A part ça, rien de bien spécial à signaler. Je vous avais déjà fait manipuler les barres de progression dans le chapitre sur les signaux et les slots pour tester la connexion entre les widgets.


Les champs

Nous allons maintenant faire le tour des widgets qui permettent d'entrer des données. C'est la catégorie de widgets la plus importante.

Encore une fois, on ne verra pas tout, mais les principaux d'entre eux :



QLineEdit : champ de texte à une seule ligne



Nous avons utilisé ce widget comme classe d'exemple lors du chapitre sur la lecture de la doc de Qt, vous vous souvenez ?

Un QLineEdit est un champ de texte sur une seule ligne :

QLineEdit


Son utilisation est dans la plupart des cas assez simple. Voici quelques propriétés à connaître :



On note aussi plusieurs slots qui permettent de couper / copier / coller / vider / annuler le champ de texte.

Enfin, certains signaux comme returnPressed() (l'utilisateur a appuyé sur Entrée) ou textChanged() (l'utilisateur a modifié le texte) peuvent être utiles dans certains cas.


QTextEdit : champ de texte à plusieurs lignes



Ce type de champ est similaire à celui qu'on vient de voir, à l'exception du fait qu'il gère l'édition sur plusieurs lignes et, en particulier, qu'il autorise l'affichage de texte enrichi (HTML).

Voici un QTextEdit :

QTextEdit


Il y a un certains nombre de choses que l'on pourrait voir sur les QTextEdit mais ce serait un peu trop long pour ce chapitre qui est plutôt là pour faire une revue rapide des widgets.

Notez les propriétés plainText et html qui permettent respectivement de récupérer & modifier le contenu sous forme de texte simple et sous forme de texte enrichi en HTML. Tout dépend de l'utilisation que vous en faites, normalement dans la plupart des cas vous utiliserez plutôt plainText.

Si vous vous intéressez à l'utilisation du HTML avec Qt, je vous invite à consulter la liste des balises HTML et propriétés CSS supportées. Vous remarquerez qu'un grand nombre d'éléments sont supportés.



QSpinBox : champ de texte de saisie d'entiers



Une QSpinBox est un champ de texte (type QLineEdit) qui permet d'entrer uniquement un nombre entier et qui dispose de petits boutons pour augmenter ou diminuer la valeur :

SpinBox


QSpinBox hérite de QAbstractSpinBox (tiens, encore une classe abstraite !). Vérifiez donc aussi la doc de QAbstractSpinBox pour connaître toutes les propriétés de la spinbox.

Voici quelques propriétés intéressantes :


QDoubleSpinBox : champ de texte de saisie de nombres décimaux



Le QDoubleSpinBox est très similaire au QSpinBox, à la différence près qu'il travaille sur des nombres décimaux (des double) :

DoubleSpinBox


On retrouve la plupart des propriétés de QSpinBox. On peut rajouter la propriété decimals qui gère le nombre de chiffres après la virgule affichés par le QDoubleSpinBox.


QSlider : un curseur pour sélectionner une valeur



Un QSlider se présente sous la forme d'un curseur permettant de sélectionner une valeur numérique :

QSlider


QSlider hérite de QAbstractSlider (damned, encore une classe abstraite !) qui propose déjà un grand nombre de fonctionnalités de base.

Beaucoup de propriétés sont les mêmes que QSpinBox, je ne les relisterai donc pas ici.
Notons la propriété orientation qui permet de définir l'orientation du slider (verticale ou horizontale).

Jetez un oeil en particulier à ses signaux, car on connecte en général le signal valueChanged(int) au slot d'autre widget pour répercuter la saisie de l'utilisateur.
Nous avions d'ailleurs manipulé ce widget lors du chapitre sur les signaux et les slots.


QComboBox : une liste déroulante



Une QComboBox est une liste déroulante :

QComboBox


On ajoute des valeurs à la liste déroulante avec la méthode addItem :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;

    QComboBox *liste = new QComboBox(&fenetre);
    liste->addItem("Paris");
    liste->addItem("Londres");
    liste->addItem("Singapour");
    liste->addItem("Tokyo");
    liste->move(30, 20);

    fenetre.show();

    return app.exec();
}


On dispose de propriétés permettant de contrôler le fonctionnement de la QComboBox :



La QComboBox émet des signaux comme currentIndexChanged() qui indique qu'un nouvel élément a été sélectionné et highlighted() qui indique l'élément survolé par la souris (ces signaux peuvent envoyer un int pour donner l'indice de l'élément ou un QString pour le texte).

A noter aussi le widget fils QFontComboBox qui permet de sélectionner une police parmi une liste proposant une prévisualisation de la police.

Les conteneurs

Normalement, n'importe quel widget peut en contenir d'autres.
Cependant, certains widgets ont été vraiment créés spécialement pour pouvoir en contenir d'autres :



Nous allons apprendre à les manipuler, en nous intéressant en particulier à celui qui propose des onglets qui est un petit peu délicat. ;)

QFrame : une bordure



QFrame est très proche de QWidget. En fait, la seule nouveauté c'est qu'il peut générer une bordure. C'est donc un QWidget basique avec une bordure.

QFrame


QFrame est une classe de base pour de nombreux widgets qui peuvent avoir une bordure, comme les QLabel. Tout ce que nous allons faire avec les QFrame ici, tous les widgets qui en héritent peuvent le faire aussi.

Dans la doc de QFrame, regardez au début le paragraphe "Inherited by...". C'est la liste des classes qui héritent de QFrame, et qui disposent donc aussi des fonctionnalités de QFrame.


Un QFrame possède quelques propriétés pour gérer la forme de la bordure :



Testons la propriété frameShape :

Code : C++
1
2
3
QFrame *frame = new QFrame(&fenetre);
frame->setFrameShape(QFrame::StyledPanel);
frame->setGeometry(30, 20, 120, 90);


Résultat :

Frame


Dans la pratique, le QFrame sert à contenir d'autres widgets (à moins que vous aimiez dessiner des rectangles partout pour le plaisir). Il est donc probable que vous définissiez un layout pour le QFrame et que vous placiez des widgets à l'intérieur.
Allez je me fends d'un petit exemple, ça vous rappellera le chapitre sur les layouts :

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
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;


    QFrame *frame = new QFrame(&fenetre);
    frame->setFrameShape(QFrame::StyledPanel);
    frame->setGeometry(30, 20, 120, 90);


    QLineEdit *lineEdit = new QLineEdit("Entrez votre nom");
    QPushButton *bouton1 = new QPushButton("Cliquez ici");
    QPushButton *bouton2 = new QPushButton("Ou là...");

    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(lineEdit);
    vbox->addWidget(bouton1);
    vbox->addWidget(bouton2);

    frame->setLayout(vbox);


    fenetre.show();

    return app.exec();
}


Frame et layout


On n'a rien fait de bien nouveau. Le QFrame contient un layout vertical qui organise ses widgets enfants : un QLineEdit et deux QPushButton.
Le QFrame lui-même est placé de manière absolue sur la fenêtre (ses coordonnées sont définies dans setGeometry). Je fais ça pour simplifier l'exemple, mais dans la pratique le QFrame devrait être lui-même placé dans un autre layout (le layout principal de la fenêtre).


QGroupBox : un groupe de widgets



Nous avons déjà utilisé QGroupBox en pratique plus haut, lorsque nous avons utilisé les boutons radio. Je ne vais donc pas trop m'éterniser dessus.

QGroupBox


Un QGroupBox propose de créer une bordure comme QFrame, mais il a en plus l'avantage de permettre de définir un titre pour le conteneur.

QGroupBox n'hérite pas de QFrame. On n'a donc pas le choix dans le type de bordure, mais on a une propriété flat qui permet d'aplatir la bordure.


Le titre du conteneur est défini via sa propriété title, ou directement lors de l'appel au constructeur :

Code : C++
1
QGroupBox *groupBox = new QGroupBox("Titre du QGroupBox", &fenetre);


Exercice : essayez d'adapter l'exemple précédent sur QFrame pour utiliser cette fois un QGroupBox. C'est facile, mais attention aux propriétés spécifiques au QFrame qui ne sont ici plus valables.

Le résultat devrait être le suivant :

QGroupBox et layout


Il est aussi possible d'ajouter une case à cocher devant le QGroupBox. Pour cela, mettez sa propriété checkable à true :

Code : C++
1
groupBox->setCheckable(true);


Lorsque la case est cochée, les widgets à l'intérieur sont activés. Lorsqu'elle est décochée, les widgets sont désactivés. Essayez.


QTabWidget : des pages d'onglets



Le QTabWidget propose une gestion de plusieurs pages de widgets, organisées sous forme d'onglets :

QTabWidget


Ce widget-conteneur est sensiblement plus difficile à utiliser que les autres. En effet, il ne peut contenir qu'un widget par page.

Quoi ? On ne peut pas afficher plus d'un widget par page ???
Mais c'est tout nul !


Sauf... qu'un widget peut en contenir d'autres ! :)
Et si on utilise un layout pour organiser le contenu de ce widget, on peut arriver rapidement à une super présentation. Le tout est de savoir combiner tout ce qu'on a appris jusqu'ici.


D'après le texte d'introduction de la doc de QTabWidget, ce conteneur doit être utilisé de la façon suivante :

  1. Créer un QTabWidget.
  2. Créer un QWidget pour chacune des pages (chacun des onglets) du QTabWidget, sans leur indiquer de widget parent.
  3. Placer des widgets enfants dans chacun de ces QWidget pour peupler le contenu de chaque page. Utiliser un layout pour positionner les widgets de préférence.
  4. Appeler plusieurs fois addTab() pour créer les pages d'onglets en indiquant l'adresse du QWidget qui contient la page à chaque fois.


Bon, c'est un peu plus délicat comme vous pouvez le voir, mais il faut bien un peu de difficulté, ce chapitre était trop facile. ;)

Si on fait tout dans l'ordre, vous allez voir que l'on n'aura pas de problème.
Je vous propose de lire ce code que j'ai créé qui montre un exemple d'utilisation du QTabWidget. Il est un peu long mais il est commenté et vous devriez arriver à le digérer. ^^

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <QApplication>
#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget fenetre;

    // 1 : Créer le QTabWidget
    QTabWidget *onglets = new QTabWidget(&fenetre);
    onglets->setGeometry(30, 20, 240, 160);

    // 2 : Créer les pages, en utilisant un widget parent pour contenir chacune des pages
    QWidget *page1 = new QWidget;
    QWidget *page2 = new QWidget;
    QLabel *page3 = new QLabel; // Comme un QLabel est aussi un QWidget (il en hérite), on peut aussi s'en servir de page


    // 3 : Créer le contenu des pages de widgets

        // Page 1

        QLineEdit *lineEdit = new QLineEdit("Entrez votre nom");
        QPushButton *bouton1 = new QPushButton("Cliquez ici");
        QPushButton *bouton2 = new QPushButton("Ou là...");

        QVBoxLayout *vbox1 = new QVBoxLayout;
        vbox1->addWidget(lineEdit);
        vbox1->addWidget(bouton1);
        vbox1->addWidget(bouton2);

        page1->setLayout(vbox1);


        // Page 2

        QProgressBar *progress = new QProgressBar;
        progress->setValue(50);
        QSlider *slider = new QSlider(Qt::Horizontal);
        QPushButton *bouton3 = new QPushButton("Valider");

        QVBoxLayout *vbox2 = new QVBoxLayout;
        vbox2->addWidget(progress);
        vbox2->addWidget(slider);
        vbox2->addWidget(bouton3);

        page2->setLayout(vbox2);


        // Page 3 (je ne vais afficher qu'une image ici, pas besoin de layout)

        page3->setPixmap(QPixmap("icone.png"));
        page3->setAlignment(Qt::AlignCenter);


    // 4 : ajouter les onglets au QTabWidget, en indiquant la page qu'ils contiennent
    onglets->addTab(page1, "Coordonnées");
    onglets->addTab(page2, "Progression");
    onglets->addTab(page3, "Image");


    fenetre.show();

    return app.exec();
}


Vous devriez retrouver chacune des étapes que j'ai mentionnées plus haut :

  1. Je crée d'abord le QTabWidget que je positionne ici de manière absolue sur la fenêtre (mais je pourrais aussi utiliser un layout).
  2. Ensuite, je crée les pages pour chacun de mes onglets. Ces pages sont matérialisées par des QWidget.
    Vous noterez que pour la dernière page je n'utilise pas un QWidget mais un QLabel. Ca revient au même et c'est compatible car QLabel hérite de QWidget. Sur la dernière page, je me contenterai d'afficher une image.
  3. Je crée ensuite le contenu de chacune de ces pages que je dispose à l'aide de layouts verticaux (sauf pour la page 3 qui n'est constituée que d'un widget). Là il n'y a rien de nouveau.
  4. Enfin, j'ajoute les onglets avec la méthode addTab(). Je dois indiquer le libellé de l'onglet ainsi qu'un pointeur vers le "widget-page" de cette page.


Résultat, on a un super système d'onglets à 3 pages avec tout plein de widgets dedans !


Onglet 1
Page 1

Onglet  2
Page 2

Onglet 3
Page 3


Je vous conseille de vous entraîner à créer vous aussi un QTabWidget. C'est un bon exercice, et c'est l'occasion de réutiliser la plupart des widgets que l'on a vus dans ce chapitre. ;)


Il faut pratiquer et pratiquer. Ce qu'on fait là ne devrait pas être bien compliqué si vous avez correctement suivi le cours jusqu'ici. Mais il faut quand même pratiquer pour faire des erreurs et se rendre compte qu'on n'avait pas parfaitement tout compris.
C'est à partir de ce moment-là seulement que vous commencerez à maîtriser les widgets. :)

Q.C.M.

Laquelle de ces classes ne permet pas d'ouvrir une fenêtre ?
Qu'est-ce qu'un widget qui n'a pas de parent ?
De quoi hérite QCheckBox ?
Je veux afficher une image. Quel widget utiliser ?
Quel widget correspond à une liste déroulante ?
Si ma propriété s'appelle value, comment puis-je la modifier ?
Un champ de texte QLineEdit sur une seule ligne peut-il afficher du texte formaté en HTML ?

Statistiques de réponses au QCM


Pfiou !

Il s'agit peut-être du chapitre le plus long que j'aie jamais écrit... mais je tenais à le faire. En fait, c'est un peu paradoxal car tout cela se trouve dans la doc et je vous ai déjà appris à lire la doc, mais j'estimais qu'il devait quand même forcément y avoir un tour d'horizon des principaux widgets dans mon tutoriel, sinon j'aurais eu l'impression d'avoir été incomplet.

Servez-vous de ce chapitre pour vous faire une idée de ce qui existe, mais ensuite je vous conseille très fortement de passer plus de temps sur la doc que sur ce chapitre. En effet, nous sommes loin d'avoir vu toutes les fonctionnalités de ces widgets, de même que nous n'avons pas vu tous les widgets qui existent !

J'ai réservé les widgets les plus complexes pour de futurs chapitres, qui introduiront pour l'occasion un concept de programmation important lorsqu'on programme des GUI : le modèle MVC.


Bon, vous pensez pas qu'il serait temps de pratiquer tout ce qu'on a appris avec un petit TP là ? :)
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

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 262 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0364s (0.0202s)