Gestion d'une interface graphique avec Qt

Les étapes 1 : Création d'une première application Qt
2 : Premier pas SIGNAL / SLOT
3 : Ajout d'un menu
4 : Ajout d'un nouveau SLOT
Cet exercice détaille pas à pas la prise en main de Qt, puis la modification
de l'interface graphique de DrawQt afin d'y ajouter les boutons qui
sélectionnent les différentes formes graphiques.

On s'attachera à appliquer systématiquement tous les outils que l'on
connaît désormais, c'est-à-dire:

Étape 1 : Création d'une première application Qt (5 minutes)

Cette première étape, variante de Hello World, permet de créer notre première application Qt et de se familiariser avec cet outil.

On commence par définir son environnement Svn (remplacer les <ens> par votre nom de binome. Ex ens2):

 $> cd ˜/Projet
 $> svn mkdir https://svn.lal.in2p3.fr/projects/Etudiants/<ens>/TpQt -m "Creation du dossier TpQt"
 $> svn checkout https://svn.lal.in2p3.fr/projects/Etudiants/<ens>/TpQt TpQt
      
On va avoir besoin du package Interfaces pour accéder aux libraries système et pour accéder à l'environnement Qt.Si le dossier Projet/Interfaces n'est pas présent, l'installer de la manière suivante:
 $> cd ˜/Projet
 $> svn export https://svn.lal.in2p3.fr/projects/Enseignement/LAL-Info/tags/head/Interfaces Interfaces
      
Puis on crée le nouveau projet avec l'outil Cmt :
 $> cd ˜/Projet
 $> cmt create TpQt v1
 $> cd TpQt/v1/cmt
      
On édite ensuite le fichier requirements :
package TpQt
 
# Utilisation de l'outil Doxygen
use Platform v1r* Interfaces
 
# Utilisation de l'environnement Qt
use Qt       v2r* Interfaces
 
# Package d'utilitaires de reconstruction
use dld      v2r* Interfaces
 
include_dirs $(TPQTROOT)/include
 
# Création du fichier moc permettant de générer le code pour les événements
document moc moc_myWindow   FROM=../include/myWindow.h TO=../src/moc_myWindow.cpp
 
# Définition d'une librarie contenant notre classe
library TpLib   \
   ../src/myWindow.cpp \
   ../src/moc_myWindow.cpp
 
# Arguments de compilation et de link de la classe myWindow
macro lib_TpLib_cflags   " ${Qt_cflags}"
macro lib_TpLib_cppflags " ${lib_TpLib_cflags}"
macro TpLib_shlibflags   " ${Qt_linkopts} ${dld_linkopts}"
macro TpLib_linkopts     " -L${TPQTROOT}/$(Platform_bin) -lTpLib" \
    WIN32                  " ${TPQTROOT}\$(Platform_bin)\TpLib.lib"
 
macro_append QtTestlinkopts " ${TpLib_linkopts}"
 
# Construction de l'application QtTest.exe
application QtTest QtTest.cpp 
 
# Empaquetage de l'application au format Mac
document darwin_app TpQt   FROM=QtTest TO=../app/QtTest
 
# Mise-à-jour de la variable d'environnement DYLD_LIBRARY_PATH
path_append DYLD_LIBRARY_PATH "" Darwin "${TPQTROOT}/$(Platform_bin)"
# création de la documentation
document doxygen doc -group=documentation TO=../doc 
      

Note: Vous noterez au début du fichier requirements l'utilisation des mots clés use Qt v2r* Interfaces, grace à cette ligne, CMT va aller cherche le package Qt/v2r* qui se trouve dans $CMTPATH (défini automatiquement au lancement de votre session de Terminal -dans le fichier .bashrc_profile-)

Puis, pour créer la classe de base, on crée ensuite le fichier d'entête de la classe ../include/myWindow.h :

 #ifndef MYWINDOW_H
 #define MYWINDOW_H
 
 #include <QtGui/QMainWindow>
 #include <QtGui/QPushButton>
 
 /**
  * @file   myWindow.h
  * @author LAL ens <ens@lal.in2p3.fr>
  * @date   Mars 2007
  * 
  * @brief  first class HelloWorld in Qt
  * 
  * 
  */
 class myWindow : public QMainWindow
 {
   // macro Q_OBJECT indispensable pour spécifier à Qt l'utilisation du mécanisme interne de 
   // communication entre les classes.
   Q_OBJECT
   public:
 
   /** @brief Constructeur de la classe principale
    * @param parent : Widget parent de la classe. Dans notre cas, ce sera
    * la fenêtre principale, donc le parent sera "NULL"
    * @param fl : Flags de création de la fenêtre. Cela permet par exemple de
    * créer une fenêtre non redimentionnable, sans bouton quitter...etc...
    */
   myWindow( QMainWindow* parent = 0, Qt::WFlags fl = Qt::Window );
 
   /** @brief Destructeur de la classe
    */
   virtual ~myWindow();
   
  private:
   /** @brief Notre PushButton Hello
    */
   QPushButton* hello;
   
 };
 
 #endif // MYWINDOW_H
      
Puis, on crée le fichier d'implémentation de la classe ../src/myWindow.cpp :
 /**
  * @file   myWindow.cpp
  * @author LAL ens <ens@lal.in2p3.fr>
  * @date   Mars 2007
  * 
  * @brief  first class HelloWorld in Qt
  * 
  * 
  */
 
 // Interface
 #include <myWindow.h>
 
 /** @brief Méthode de création de la classe myWindow 
* Notre classe, pour être une fenêtre affichage, doit hériter de * la classe QMainWindow de Qt. * @param parent : Widget parent de la classe. Dans notre cas, ce sera * la fenêtre principale, donc le parent sera "NULL" * @param fl : Flags de création de la fenêtre. Cela permet par exemple de * créer une fenêtre non redimentionnable, sans bouton quitter...etc... */ myWindow::myWindow( QMainWindow* parent, Qt::WFlags fl ) : QMainWindow( parent, fl ) { // Création de notre button poussoir hello = new QPushButton(...); Pour compléter cette ligne, référez vous à la documentation de Qt // La documentation des méthodes du QPushButton est disponible sur : QPushButton // Affichage de notre bouton en position centrale setCentralWidget( hello ); } /** * @brief Détruit l'objet myWindow et tous les objects contenus à l'intérieur.
* En particulier le bouton précédement créé. */ myWindow::~myWindow() { delete hello; }
Puis ajouter le main dans un nouveau fichier QtTest.cpp
 /**
  * @file   QtTest.cpp
  * @author LAL ens <ens@lal.in2p3.fr>
  * @date   Mars 2007
  * 
  * @brief  first HelloWorld en Qt
  * 
  * 
  */
 #include <QtGui/QApplication>
 #include <QtGui/QPushButton>
 
 #include <myWindow.h>
 
 int main(int argc, char *argv[])
 {
  QApplication app(argc, argv);
  myWindow * mw = new myWindow();
  mw->show();
  return app.exec();
 }
      
Lancer le script setup.sh qui va configurer les variables liées à notre Tp (environnement Qt, gestion des libraries...). Se placer dans le répertoire cmt puis lancer :
 $> cmt config
 $> source setup.sh
      
Avant de reconstruire, compléter le fichier myWindow.cpp non compilable en l'état ...
Puis :
 $> cmt make 
 $> cmt make doc
      
Important : lors de l'exécution de ces deux commandes durant tout le déroulement du TP, ne pas se contenter de les regarder passer sans analyser les messages qu'elles fournissent éventuellement. Ne pas oublier que si une erreur de syntaxe s'est glissée ou qu'il manque un fichier, ces commandes n'écraseront pas l'exécutable précédent (ou la documentation précédente), et on pourra croire à tort que les modifications faites n'ont pas été prises en compte.
Puis tester l'application (2 méthodes):
Dans le Finder (L'explorateur de fichier), lancer le fichier ../app/QtTest.app, ou bien :
 $> open ../app/QtTest.app
      
Attention : Les sorties textuelles effectuées par std::cout ne donnent aucun résultat dans une application graphique. En effet cette application travaille de façon indépendante du Terminal, elle n'a donc pas connaissance de celui-ci. Il est cependant possible de récupérer ces sorties textuelles grâce à la console système :
 open /Applications/Utilities/Console.app
        
Cette console sert pour toutes les sorties 'système', donc ne pas s'étonner d'y trouver un grand nombre de messages d'autres applications. Il est possible de filtrer les messages en haut à droite de la fenêtre (non case sensitif).
On peut alors sauvegarder la version dite de départ dans Svn, après avoir retiré les fichiers qu'on ne souhaite pas versionner :
 $> rm -r ../Darwin ../doc ../app
 $> rm Darwin.make Makefile *.csh *.sh
 $> cd ../../
 $> svn add v1
 $> svn commit -m "Version de depart" v1
      
On peut ensuite se positionner dans le répertoire de travail src, là où on modifie les fichiers source qui seront versionnés :
 $> cd ./v1/src
      
Valider cette étape  Coche

Étape 2 : Ajout d'un SIGNAL / SLOT sur le boutton (10 minutes)

Comme on l'a remarqué, aucune action n'est associée au bouton de son application. Afin d'y remédier, on va utiliser le mécanisme de SIGNAL/SLOT de Qt.
L'objet éméteur sera le bouton hello.
Le signal envoyé sera l'action du clic souris.
L'objet receveur sera la classe myWindow.
Pour finir, le slot sera la méthode close() de la classe QMainWindow.

Pour cela, il suffit simplement d'ajouter la ligne suivante, juste après la création du bouton, dans myWindow.cpp :

 // Connection du signal clic() du bouton vers la méthode close() de la QMainWindow
 connect( hello, SIGNAL(clicked()), this, SLOT(close()) );
      
Reste à construire, tester l'application puis la sauver dans Svn :
 $> cmt make
 $> cmt make doc
 $> open ../app/QtTest.app
 $> cd ..
 $> svn commit -m "Action sur bouton" .
      

Note : Il arrive parfois que le compilateur ne prenne pas en compte votre dernière modification. Dans ce cas, votre application compileras, vous obtiendrez un crash dès le lancement. En fait, ce qui se passe, c'est que le fichier moc_mywindow n'a pas éé recompilé, il vous suffit de l'effacer (dans src/) et de relancer la compilation.

Valider cette étape  Coche

Étape 3 : ajout d'un menu (15 minutes)

On va désormais ajouter un menu à notre fenêtre.
Attention : Sur un Mac, les menus ne s'affichent pas en haut de la fenêtre en question, mais en haut de l'écran.

Ajouter les includes suivants dans le fichier myWindow.cpp

 #include <QMenu>
 #include <QMenuBar>
            

Puis le code de la méthode principale est à mettre en lieu et place de l'ancienne fonction :

 /** @brief Méthode de création de la classe myWindow 
* Notre classe, pour être une fenêtre affichage, doit hériter de * la classe QMainWindow de Qt. Cette MainWindow possède un menu simple * et une fenêtre contenant un simple bouton. * @param parent : Widget parent de la classe. Dans notre cas, ce sera * la fenêtre principale, donc le parent sera "NULL" * @param fl : Flags de création de la fenêtre. Cela permet par exemple de * créer une fenêtre non redimentionnable, sans bouton quitter...etc... */ myWindow::myWindow( QMainWindow* parent, Qt::WFlags fl ) : QMainWindow( parent, fl ) { // Définition du bouton poussoir hello = new QPushButton( "Hello world!" ); connect( hello, SIGNAL(clicked()), this, SLOT(close()) ); // Affichage du bouton en position centrale setCentralWidget(hello); // Création du menuBar QMenuBar* menubar = new QMenuBar(this); // Création d'un menu déroulant "Fichier" QMenu * menuFichier = new QMenu( "Fichier" ); // et ajout du menu fichier à la barre de menu principale menubar->addMenu(menuFichier); // Maintenant, on ajoute nos items // Ajout d'un item "Ouvrir" menuFichier->addAction( "Ouvrir" ); // Ajout d'un séparateur menuFichier->addSeparator(); // Ajout d'un item "Quitter" que l'on connecte au slot "close" de la mainWindows menuFichier->addAction( "Fermer l'application",this,SLOT(close()) ); // Puis on affecte cette barre de menu à la fenêtre principale setMenuBar( menubar ); }
On reconstruit, on teste et on sauvegarde dans Svn
Valider cette étape  Coche

Étape 4 : Ajout d'un nouveau SLOT (15 minutes)

On désire maintenant ajouter notre propre action lors du clic sur un menu.

Pour cela, nous allons commencer par ajouter une action à notre menu "Ouvrir", que l'on va connecter à la fonction changer() définie plus loin. À la suite de la création du menu "Ouvrir" ajouter les lignes suivantes :

  // Ajout d'un nouvel item "Changer" que l'on connecte à la fonction changer()
  menuFichier->addAction( "Changer",this, SLOT(changer()) );
      
Puis, on ajoute la fonction "changer()" qui sera exécutée lors de l'appel du menu "Changer". Cette fonction étant appelée par un événement (clic sur un menu), c'est donc un nouveau SLOT que l'on va définir.
Dans le fichier myWindow.h, ajouter dans l'entête de la classe les lignes suivantes.
 public slots :
 void changer();
      
Puis, le corps de la fonction, à ajouter dans le fichier myWindow.cpp
 /** @brief Cette méthode change le texte du bouton hello.
* La documentation des méthodes du QPushButton est disponible sur : * QPushButton
* Le QPushButton est un type spécial de bouton. En effet, les boutons peuvent être * des PushButton, des CheckButton, des RadioButton...etc...Il hérite donc des * propriétés d'un type de bouton abstrait appellé QAbstractButton. * La documentation des méthodes du QAbstractButton est disponible sur : * QAbstractButton
*/ void myWindow::changer() { hello->setText(...); // Compléter cette ligne en fonction de la documentation }
On reconstruit, on teste et on sauvegarde dans Svn.
Valider cette étape  Coche
Valid XHTML 1.0! Valid CSS!
Enseignement Lal<ens@lal.in2p3.fr>
Last modified: Mon Feb 13 11:52:56 CET 2012