|  | Home · All Classes · All Functions · Overviews

[Sommaire] [Next: Chapitre 2]

# Carnet d'adresses 1 - Conception de l'interface utilisateur

Files:

- tutorials/addressbook-fr/part1/addressbook.cpp
- tutorials/addressbook-fr/part1/addressbook.h
- tutorials/addressbook-fr/part1/main.cpp
- tutorials/addressbook-fr/part1/part1.pro

La première partie de ce tutoriel traite de la conception d'une interface graphique (GUI) basique, que l'on utilisera pour l'application Carnet d'adresses.

La première étape dans la création d'applications graphiques est la conception de l'interface utilisateur. Dans ce chapitre, nous verrons comment créer les labels et champs de saisie nécessaires à l'implementation d'un carnet d'adresses de base. Le résultat attendu est illustré par la capture d'écran ci-dessous.

Nous allons avoir besoin de deux objets QLabel, `nameLabel` et `addressLabel`, ainsi que deux champs de saisie: un objet QLineEdit, `nameLine`, et un objet QTextEdit, `addressText`, afin de permettre à l'utilisateur d'entrer le nom d'un contact et son adresse. Les widgets utilisés ainsi que leur placement sont visibles ci-dessous.

Trois fichiers sont nécessaires à l'implémentation de ce carnet d'adresses:

- `addressbook.h` - le fichier de définition (header) pour la classe `AddressBook`,
- `addressbook.cpp` - le fichier source, qui comprend l'implémentation de la classe `AddressBook`
- `main.cpp` - le fichier qui contient la méthode `main()` , et une instance de la classe `AddressBook`.

## Programmation en Qt - héritage

Lorsque l'on écrit des programmes avec Qt, on a généralement recours à l'héritage depuis des objets Qt, afin d'y ajouter des fonctionnalités. C'est l'un des concepts fondamentaux de la création de widgets personnalisés ou de collections de widgets. Utiliser l'héritage afin de compléter ou modifier le comportement d'un widget présente les avantages suivants:

- La possibilité d'implémenter des méthodes virtuelles et des méthodes virtuelles pures pour obtenir exactement ce que l'on souhaite, avec la possibilité d'utiliser l'implémentation de la classe mère si besoin est.
- Cela permet l'encapsulation partielle de l'interface utilisateur dans une classe, afin que les autres parties de l'application n'aient pas à se soucier de chacun des widgets qui forment l'interface utilisateur.
- La classe fille peut être utilisée pour créer de nombreux widgets personnalisés dans une même application ou bibliothèque, et le code de la classe fille peut être réutilisé dans d'autres projets

Comme Qt ne fournit pas de widget standard pour un carnet d'adresses, nous partirons d'une classe de widget Qt standard et y ajouterons des fonctionnalités. La classe `AddressBook` crée dans ce tutoriel peut être réutilisée si on a besoin d'un widget carnet d'adresses basique.

## La classe AddressBook

Le fichier `addressbook.h` permet de définir la classe `AddressBook`.

On commence par définir `AddressBook` comme une classe fille de QWidget et déclarer un constructeur. On utilise également la macro Q_OBJECT pour indiquer que la classe exploite les fonctionnalités de signaux et slots offertes par Qt ainsi que l'internationalisation, bien que nous ne les utilisions pas à ce stade.

     class AddressBook : public QWidget
     {
         Q_OBJECT

     public:
         AddressBook(QWidget *parent = 0);

     private:
         QLineEdit *nameLine;
         QTextEdit *addressText;
     };

La classe contient les déclarations de `nameLine` et `addressText`, les instances privées de QLineEdit et QTextEdit mentionnées précédemment. Vous verrez, dans les chapitres à venir que les informations contenues dans `nameLine` et `addressText` sont nécessaires à de nombreuses méthodes du carnet d'adresses.

Il n'est pas nécessaire de déclarer les objets QLabel que nous allons utiliser puisque nous n'aurons pas besoin d'y faire référence après leur création. La façon dont Qt gère la parenté des objets est traitée dans la section suivante.

La macro Q_OBJECT implémente des fonctionnalités parmi les plus avancées de Qt. Pour le moment, il est bon de voir la macro Q_OBJECT comme un raccourci nous permettant d'utiliser les méthodes tr() et connect().

Nous en avons maintenant terminé avec le fichier `addressbook.h` et allons passer à l'implémentation du fichier `addressbook.cpp`.

## Implémentation de la classe AddressBook

Le constructeur de la classe `AddressBook` prend en paramètre un QWidget, parent. Par convention, on passe ce paramètre au constructeur de la classe mère. Ce concept de parenté, où un parent peut avoir un ou plusieurs enfants, est utile pour regrouper les Widgets avec Qt. Par exemple, si vous détruisez le parent, tous ses enfants seront détruits égalament.

     AddressBook::AddressBook(QWidget *parent)
         : QWidget(parent)
     {
         QLabel *nameLabel = new QLabel(tr("Name:"));
         nameLine = new QLineEdit;

         QLabel *addressLabel = new QLabel(tr("Address:"));
         addressText = new QTextEdit;

à l'intérieur de ce constructeur, on déclare et instancie deux objets locaux QLabel, `nameLabel` et `addressLabel`, de même on instancie `nameLine` et `addressText`. La méthode tr() renvoie une version traduite de la chaîne de caractères, si elle existe; dans le cas contraire, elle renvoie la chaîne elle même. On peut voir cette méthode comme un marqueur `<insérer la traduction ici>`, permettant de repérer les objets QString à considérer pour traduire une application. Vous remarquerez, dans les chapitres à venir comme dans les exemples Qt, qu'elle est utilisée chaque fois que l'on utilise une chaîne susceptible d'être traduite.

Lorsque l'on programme avec Qt, il est utile de savoir comment fonctionnent les agencements ou layouts. Qt fournit trois classes principales de layouts pour contrôler le placement des widgets: QHBoxLayout, QVBoxLayout et QGridLayout.

On utilise un QGridLayout pour positionner nos labels et champs de saisie de manière structurée. QGridLayout divise l'espace disponible en une grille, et place les widgets dans les cellules que l'on spécifie par les numéros de ligne et de colonne. Le diagramme ci-dessus présente les cellules et la position des widgets, et cette organisation est obtenue à l'aide du code suivant:

         QGridLayout *mainLayout = new QGridLayout;
         mainLayout->addWidget(nameLabel, 0, 0);
         mainLayout->addWidget(nameLine, 0, 1);
         mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop);
         mainLayout->addWidget(addressText, 1, 1);

On remarque que le label `AddressLabel` est positionné en utilisant Qt::AlignTop comme argument optionnel. Ceci est destiné à assurer qu'il ne sera pas centré verticalement dans la cellule (1,0). Pour un aperçu rapide des layouts de Qt, consultez la section Layout Management.

Afin d'installer l'objet layout dans un widget, il faut appeler la méthode setLayout() du widget en question:

         setLayout(mainLayout);
         setWindowTitle(tr("Simple Address Book"));
     }

Enfin, on initialise le titre du widget à "Simple Address Book"

## Exécution de l'application

Un fichier séparé, `main.cpp`, est utilisé pour la méthode `main()`. Dans cette fonction, on crée une instance de QApplication, `app`. QApplication se charge de des ressources communes à l'ensemble de l'application, tel que les polices de caractères et le curseur par défaut, ainsi que de l'exécution de la boucle d'évènements. De ce fait, il y a toujours un objet QApplication dans toute application graphique en Qt.

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

         AddressBook addressBook;
         addressBook.show();

         return app.exec();
     }

On construit un nouveau widget `AddressBook` sur la pile et on invoque sa méthode show() pour l'afficher. Cependant, le widget ne sera pas visible tant que la boucle d'évènements n'aura pas été lancée. On démarre la boucle d'évènements en appelant la méthode exec() de l'application; le résultat renvoyé par cette méthode est lui même utilisé comme valeur de retour pour la méthode `main()`. On comprend maintenant pourquoi `AddressBook` a été créé sur la pile: à la fin du programme, l'objet sort du scope de la fonction `main()` et tous ses widgets enfants sont supprimés, assurant ainsi qu'il n'y aura pas de fuites de mémoire.

[Sommaire] [Next: Chapitre 2]