Résumé
Vous apprendrez ici à utiliser les zones de saisie, les boutons, les étiquettes, les boîtes listes et les grilles.
Table des matières
Dans ce TP nous demanderons à l'utilisateur du programme de saisir son nom dans un QLineEdit. Lors d'un clic sur le bouton "Prendre en compte", le nom saisi sera affiché dans un QLabel. Voici l'écran que nous souhaitons obtenir :
Le développement de cette application suit un protocole rigoureux:
Créer un répertoire pour le projet
Comme pour toute application il est nécessaire de créer un répertoire pour son projet. Seront stockés dans ce répertoire les fichiers sources ainsi que les fichiers compilés ou encore les fichiers xml décrivant l'interface utilisateur fichiers ".ui".
[gthom@bigserv qt4]$ mkdir prjHello [gthom@bigserv qt4]$ cd prjHello [gthom@bigserv prjHello]$
Création de l'interface graphique
La construction de l'interface graphique ce fait à l'aide d'un outil d'édition appelé designer. Si vous avez installé la librairie qt4 il doit se trouver dans le répertoire: /usr/local/Trolltech/Qt-4.0.0/bin/
Nous allons donc lancer designer: [gthom@bigserv prjHello]$ /usr/local/Trolltech/Qt-4.0.0/bin/designer
Vous devriez alors voir apparaître un écran comme ceci:
Dans la boîte de dialogue "New form" nous allons choisir "main window" qui comme son nom l'indique permet de créer la fenêtre principale de l'application.
Aprés avoir cliqué sur le bouton "Create" vous devriez obtenir l'écran suivant.
Nous allons maintenant grâce à l'éditeur de propriétés changer la valeur de deux des propriétés de notre QMainWindow: donnez la valeur "MWPremiere" à la propriété objectName, il s'agit de l'identifiant de l'instance créée et donnez la valeur "Ma super première application" à la propriété windowTitle c'est le titre affiché dans la barre de titre. Conformément à l'écran ci-dessous:
Nous allons maintenant procéder à l'enregistrement de notre fichier xml décrivant l'interface: Nous allons aller dans le menu File et choisir "Save Form" conformément à l'écran ci-dessous:
On donnera comme nom de fichier premiere.ui.
Nous allons ensuite rajouter des widgets à notre interface. Un widget est un composant visuel de la bibliothéque "qt" que l'on peut déposer dans un formulaire.
Il faut mettre une étiquette un "label" devant la zone de saisie pour indiquer à l'utilisateur ce qu'il doit taper. Ici ce label doit contenir: "Votre nom:".
Pour cela il faut faire un glisser déposer dans la fiche du widget label se trouvant dans la zone "display Widgets" de la fenêtre "qt Designer"
A l'issue de cette opération on obtient la fenêtre suivante:
Analyse on vient de déposer dans notre formulaire une instance de la classe QLabel. Si l'on observe attentivement l'éditeur de propriétés,ci dessous:
On voit que notre instance a pour nom label, que c'est une instance de la classe QLabel qui hérite de la classe Qframe qui hérite elle-même de la classe QWidget qui hérite elle-même de la classe QObject.
Dans les propriétés de la classe QLabel il y en a une qui nous intéresse particulièrement (elle est en gras, c'est la propriété text. On va lui donner comme valeur "Votre nom:". Pour effectuer cette opération il y a deux façons de faire:
A l'issue de la modification, vous devez obtenir l'écran suivant:
Ajoutons maintenant notre zone de saisie. Il s'agit d'un widget classé en tant que "input widget" nommé "Line Edit" et matérialisé par l'icône
. Par le même principe de glisser-déposer nous allons le placer sur la droite de notre label.Vous devriez maintenant disposer de l'écran suivant:
Modification des propriétés de la zone d'édition:
Lorsqu'on dépose un QLineEdit sur un formulaire, on crée une instance de la classe QLineEdit. Il y a une seule propriété à changer: le nom de l'instance propriétés name héritée de la classe QObject par défaut designer propose lineEdit nous allons choisir de l'appeler lineEditNom parce qu'elle contiendra le nom de l'utilisateur du programme. A noter aussi si vous souhaitez qu'un nom apparaisse par défaut, vous pouvez le rentrer dans la propriété text de notre instance de la classe QlineEdit nommée lineEditNom.
Soigner l'aspect de notre interface graphique:
Quoi qu'il arrive, le label doit être sur la même horizontale que le QLineEdit auquel il correspond. Nous allons donc utiliser une layout horizontale pour arranger l'alignement de nos deux widgets. Comment faire ? On sélectionne le QLabel et la QLineEdit puis on clique sur le petit bouton
layout horizontally dans la barre d'outils la plus basse sur la gauche de l'écran ci-dessous.
Vous obtenez ensuite une petite boîte rouge "la layout horizontale" dans laquelle les deux widgets sont parfaitement alignés. Comme dans l'écran ci-dessous.
Il va falloir maintenant ajouter un bouton . Le widget à utiliser est de la classe QPushButton et est naturellement classé dans les widgets de type "Buttons". Le rôle des boutons est de déclencher l'exécution de code quand l'utilisateur clique dessus. Ici l'objectif sera d'afficher dans un label le nom préalablement saisi l'utilisateur.
Par glisser-deposer, placer un QPushButton sur la fiche. Changez sa propriété name en pushButtonPrendreEnCompte, et sa propriété text en "&Prendre en Compte".
.
Pour parfaire l'aspect de notre interface graphique nous allons placer un spacer horizontal à gauche de notre bouton. Cela permettra au bouton de rester sur la droite de l'écran quoi qu'il arrive. Le spacer horizontal se trouve dans le groupe "Spacers". Aprés avoir glissé-déposé le spacer on rangera le spacer et le bouton dans une layout horizontale selon le même principe que précédemment. Nous obtiendrons alors l'écran suivant:
Nous allons maintenant rajouter à notre interface deux labels:
On donnera à la propriété text du premier la valeur "Votre nom est:".
En ce qui concerne le deuxième label, on videra sa propriété text. Puis on changera son name en labelNom.
Bien entendu les deux QLabels doivent être sur la même horizontale, il faut donc les mettre dans une layout horizontale.
Il nous reste maintenant à placer un bouton permettant à l'utilisateur de quitter l'application. Nous obtiendrons alors l'interface suivante:
Il est possible de prévisualiser notre interface. Nous allons pour cela activer le menu on s'aperçoit alors que l'on peut redimensionner la fenêtre mais que le résultat n'est pas à la hauteur de nos espérances, en effet, la taille de nos widgets ne s'adapte pas à la nouvelle géométrie de la fenêtre.Nous allons fermer la fenêtre de prévisualisation et modifier notre interface.
Il suffit de dire que notre fenêtre est une layout verticale dans laquelle nos quatres layouts sont placées les unes en dessous des autres.
Pour cela il suffit de cliquer sur une zone vierge de notre window et de cliquer sur l'icône
. Il ne nous reste plus ensuite qu'à réaliser une prévisualisation et à constater la différence: quelles que soient les dimensions de la fenêtre, les composants apparaissent correctement.
Il ne nous reste plus qu'à dire que lorsque l'on clique sur le bouton la fenêtre se ferme.
Notre fenêtre sait se fermer, elle dispose d'une méthode pour cela cette méthode s'appelle "close()".
Notre objectif est de dire que l'orsque l'évènement "clicked() se produit sur l'objet PushButtonQuitter il faut exécuter le slot "close()" de notre fenêtre MWPremiere. Nous allons donc utiliser la boîte de dialogue: "Signal/slot editor" et ajouter une connexion comme indiqué dans l'écran ci-dessous:
Maintenant notre interface est terminée, aprés avoir sauvé nous allons quitter designer.
Génération du code correspondant à notre interface
Nous avons disposons désormais dans notre répertoire prjHello d'un fichier XML
nommé premiere.ui que vous pouvez visualiser par exemple avec kate ou gvim.
Un outil nommé uic fourni avec la bibliothéque QT permet de générer le code correspondant à la description contenue dans le fichier .ui.
Assurez-vous tout d'abord d'avoir bien configuré les variables d'environnement PATH, LIB et QTDIR. Vous pouvez le faire de façon permanente en éditant le fichier ~/.bash_profile. Vous devez y déposer les lignes suivantes:
export QTDIR=/usr/local/Trolltech/Qt-4.0.0/ export PATH=$QTDIR/bin:$PATH:$HOME/bin export LIB=$QTDIR/lib:$LIB
Générons le fichier .h qui contiendra le code de la classe MWPremiere que nous avons pris tellement de soin à concevoir.
[gthom@bigserv prjHello]$ uic premiere.ui -o premiere.h [gthom@bigserv prjHello]$ ls premiere.ui premiere.h [gthom@bigserv prjHello]$
On voit que uic a créé un fichier premiere.h que l'on va s'empresser de visualiser. Dans ce fichier on voit l'interface de la classe Ui::MWPremiere où Ui est un espace de nom.
La classe Ui::MPremiere est une classe visuelle disposant en tant que propriétés public de divers pointeurs pointant vers des widgets qu'on y a disposé. Elle ne dispose en gros que d'une seule méthode: setupUi(...) permettant d'instancier les widgets et de faire pointer les pointeurs dessus.
à nous l'héritage ...
Nous allons créer une classe qui va hériter à la fois de la classe QMainWindow et de la classe Ui::MWPremiere. On lui attachera deux fichiers un .h qui contiendra l'interface de notre classe et un .cpp qui contiendra l'implémentation. Nous allons décider d'appeler notre classe <className>notOnlyAFenetre</className> ainsi appelée parce que c'est certe une fenêtre, mais elle va être dotée d'un comportement que l'on va programmer.
cela va nous donner un .h que voici:
#include "premiere.h" //IL FAUT BIEN CONNAITRE LA CLASSE Ui::MWPremiere PUISQU'ON EN HÉRITE
class notOnlyAFenetre : public QMainWindow, Ui::MWPremiere //HÉRITAGE MULTIPLE c'est une QMainWindow et une Ui::MWPremiere
{
Q_OBJECT //macro à ne pas oublier permettant l'inclusion de plein de choses lors de la compilation
public:
notOnlyAFenetre(); //le constructeur de la classe
public slots:
void afficheNom(); //un slot qui sera attaché à l'événement clicked du bouton "Prendre en compte"
};
et un .cpp que voilà
void notOnlyAFenetre::afficheNom()
{
//labelNom souvenez-vous était le nom du deuxième label à côté de "Votre nom est:"
//c'est en fait un pointeur sur instance de la classe QLabel
//Le QLabel possède une prorpiété "text" disposant de deux accesseurs: un en écriture: setText(QString nouveauTexte) et
//un accesseur de lecture QString text()
QString nomTapeParLUtilisateur; //vous connaissiez la gapString QT a créé la QString encore meilleure
nomTapeParLUtilisateur= lineEditNom->text();//appel de l'accesseur text() de la classe QLineEdit qui nous renvoie la valeur de la propriété "text"
labelNom->setText(nomTapeParLUtilisateur);//valorisation de la propriété text du labelNom
}
notOnlyAFenetre::notOnlyAFenetre()
{
setupUi(this);//appel de la méthode héritée setupUi en lui passant en paramètre nous-même: la mainWindow qui va bien
connect(pushButtonPrendreEnCompte,SIGNAL(clicked()),this, SLOT(afficheNom()));
//quand on clique sur le bouton pushButtonPrendreEnCompte il faut executer afficheNom()
}
Nous disposons donc maintenant de deux fichiers.h ainsi que d'un cpp et un ui. Il nous manque un main.cpp dans lequel on placerait notre fonction main().
Le main
Où je vous donne le main:
#include "notonlyafenetre.h"
#include <QtGui>
int main(int argc, char ** argv)
{
QApplication monApplication(argc,argv); //nécessaire
notOnlyAFenetre maFenetreDouee; //indispensable
maFenetreDouee.show(); //il vaut mieux en effet qu'elle se voit
return monApplication.exec(); //indispensable aussi
}
Nous disposons maintenant des fichiers suivant:
[gthom@bigserv prjHello]$ ls main.cpp notonlyafenetre.cpp notonlyafenetre.h premiere.h premiere.ui [gthom@bigserv prjHello]$
Génération d'un projet au sens QT du terme
QT dispose d'un outil permettant de créer un fichier projet (extension .pro). Cet outil s'appelle qmake et il faut lui passer comme option -project pour qu'il crée le projet.
[gthom@bigserv prjHello]$ qmake -project [gthom@bigserv prjHello]$ ls main.cpp notonlyafenetre.cpp notonlyafenetre.h premiere.h premiere.ui prjHello.pro [gthom@bigserv prjHello]$
On voit apparaître un nouveau fichier: prjHello.pro. Bien sûr la curiosité nous pousse à regarder ce qu'il contient. Et bien pas grand chose, on voit que QMake a trouvé nos fichiers dans le répertoire courant. Nous allons pouvoir passer à la suite.
Génération d'un fichier Makefile
Nous allons générer un fichier Makefile qui nous permet de ne pas tout recompiler à chaque fois.
[gthom@bigserv prjHello]$ qmake prjHello.pro [gthom@bigserv prjHello]$ ls main.cpp Makefile notonlyafenetre.cpp notonlyafenetre.h premiere.h premiere.ui prjHello.pro
Nous constatons qu'un fichier 'Makefile' a bien été créé.
Compiler notre application
Je sens que vous piaffez d'impatience de voir tourner ce programme dont vous réviez. Mais il faut d'abord le compiler.
[gthom@bigserv prjHello]$ make g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_CORE_LIB -DQT_GUI_LIB -DQT_SHARED -I/usr/local/Trolltech/Qt-4.0.0/mkspecs/default -I. -I/usr/local/Trolltech/Qt-4.0.0/include/QtGui -I/usr/local/Trolltech/Qt-4.0.0/include/QtCore -I/usr/local/Trolltech/Qt-4.0.0/include -I. -I. -I. -o main.o main.cpp g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_CORE_LIB -DQT_GUI_LIB -DQT_SHARED -I/usr/local/Trolltech/Qt-4.0.0/mkspecs/default -I. -I/usr/local/Trolltech/Qt-4.0.0/include/QtGui -I/usr/local/Trolltech/Qt-4.0.0/include/QtCore -I/usr/local/Trolltech/Qt-4.0.0/include -I. -I. -I. -o notonlyafenetre.o notonlyafenetre.cpp /usr/local/Trolltech/Qt-4.0.0/bin/moc -DQT_CORE_LIB -DQT_GUI_LIB -DQT_SHARED -I/usr/local/Trolltech/Qt-4.0.0/mkspecs/default -I. -I/usr/local/Trolltech/Qt-4.0.0/include/QtGui -I/usr/local/Trolltech/Qt-4.0.0/include/QtCore -I/usr/local/Trolltech/Qt-4.0.0/include -I. -I. -I. notonlyafenetre.h -o moc_notonlyafenetre.cpp g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_CORE_LIB -DQT_GUI_LIB -DQT_SHARED -I/usr/local/Trolltech/Qt-4.0.0/mkspecs/default -I. -I/usr/local/Trolltech/Qt-4.0.0/include/QtGui -I/usr/local/Trolltech/Qt-4.0.0/include/QtCore -I/usr/local/Trolltech/Qt-4.0.0/include -I. -I. -I. -o moc_notonlyafenetre.o moc_notonlyafenetre.cpp g++ -Wl,-rpath,/usr/local/Trolltech/Qt-4.0.0/lib -o prjHello main.o notonlyafenetre.o moc_notonlyafenetre.o -L/usr/local/Trolltech/Qt-4.0.0/lib -L/usr/local/src/qt-x11-opensource-desktop-4.0.0/lib -lQtGui_debug -L/usr/X11R6/lib -lpng -lSM -lICE -lXi -lXrender -lXrandr -lXcursor -lXinerama -lfreetype -lfontconfig -lXext -lX11 -lm -lQtCore_debug -lz -ldl -lpthread [gthom@bigserv prjHello]$ ls main.cpp Makefile moc_notonlyafenetre.o notonlyafenetre.h premiere.h prjHello* main.o moc_notonlyafenetre.cpp notonlyafenetre.cpp notonlyafenetre.o premiere.ui prjHello.pro [gthom@bigserv prjHello]$
Quel est donc ce moc_notonlyafenetre.cpp ? un fichier hyper nécessaire généré par notre macro QOBJECT à ne pas oublier.
Lancer l'application
Le moins qu'on puisse dire c'est qu'il faut en connaître des concepts pour savoir ce qu'on fait.
Dans ce TP nous ferons saisir deux noms et nous les permuterons s'ils ne sont pas classés par ordre alphabétique. Conformez-vous en ce qui concerne l'interface à l'écran ci-dessous:
Indications : Vous n'avez à connaître que le type renvoyé par la méthode "text()" de la classe "LineEdit" il s'agit du type "QString".
On récupérera les contenus des deux zones de texte, et dans le cas où les deux mots ne seraient pas bien rangés, on les remet dans l'ordre.
Modification de code : Version n°2 :
Reprendre le TP ci-dessus mais avec la contrainte supplémentaire suivante :
le bouton "Ranger" ne devient actif que lorsqu'il a des raisons de l'être.
Indications : Recopiez entièrement le répertoire contenant le projet précédent dans un nouveau répertoire (voir cp -R...). Pour rendre ou non un bouton actif on utilise la méthode: "setEnabled(bool unbooleen)" de la classe "QPushButton".
Dans l'inspecteur d'objet de QT on voit certaines propriétés des widgets. Ces propriétés sont encapsulées. Pour chaque propriété sont fournis deux accesseurs. L'un permet d'obtenir la valeur que contient la propriété. L'autre permet de donner une valeur à la propriété. ainsi sont associées à la propriété "text" deux accesseurs: méthode "text()" et méthode "setText(...)". propriété "width" méthode "width()" et méthode "setWidth(...)" propriété "ploum" méthode "ploum()" et méthode "setPloum(...)"