Traduction par Jean-luc Biord, du Site de la communauté Qt francophone.
English TOC.

7. Le Carnet d'Adresses

Ce sera notre première vraie application : un carnet d'adresses. Nous concevrons et mettrons en application les classes de base et l'interface utilisateur dans ce chapitre.

Commençons en créant un projet C++ vide dans Qt Designer. Je choisis d'appeler mon projet adbook.pro, mais vous pouvez choisir n'importe quel nom.

La première chose que nous voulons faire est d'ajouter une nouvelle fenêtre principale. La fenêtre que nous emploierons est censée être vide, ainsi assurez-vous de désélectionner dans l'assistant toutes les options comme représenté sur le schéma 7-1.

The Main Window Wizard with no options checked

Schéma 7-1 l'assistant de création de fenêtre principale sans option cochée.

Maintenant changez le nom de la forme en frmMain et modifez caption avec 'Address Book'.

Au lieu de connecter des boutons à des slots directement nous emploierons des groupes d'action. Cela nous fournit un signal pour manipuler des événements multiples tels qu'une entrée de menu, un accélérateur de clavier (c.-à-d. un raccourci) et un bouton de barre d'outils. Ajoutez quatre actions à l'aide du bouton new action (en clair sur le schéma 7-2) selon le tableau 7-1. Pour ce faire, cliquez simplement le bouton new action, puis changez l'action en utilisant l'éditeur de propriété. Les actions résultantes sont montrées sur le schéma 7-2.

Les caractères & indique à Qt de souligner le caractère suivant et de l'employer comme raccourci clavier.

The new action button

Schéma 7-2 le bouton new action.

Name Text menuText
aExit Exit E&xit
aAddContact Add Contact &Add Contact
aEditContact Edit Contact &Edit Contact
aRemoveContact Remove Contact &Remove Contact

Tableau 7-1

The actions

Schéma 7-3 Les actions.

Cliquez avec le bouton droit de la souris sur la forme pour afficher le menu contextuel. Dans celui-ci, choisissez l'entrée de menu add. Faites ceci deux fois de sorte que vous finissiez par avoir une forme contenant deux entrées de menu.

Maintenant il est temps de renommer les entrées de menu. Dans les versions antérieures à Qt 3.x ce doit être fait en cliquant avec le bouton droit de la souris sur chacune des entrées de menu nouvellement créées et en sélectionnant l'option de renommage du menu contextuel. Dans les versions postérieures, cliquez simplement sur l'entrée et utilisez l'éditeur de propriété pour changer le nom. D'une façon ou de l'autre, renommez les en "&File" et "&Contacts" de la gauche vers la droite.

Maintenant, glissez et déposez simplement les actions sur les menus. Placez l'action aExit dans le menu File et le reste des actions dans le menu contacts. Le schéma 7-4 montre le résultat.

The Contacts menu

Schéma 7-4 Le menu Contacts.

Maintenant, ajoutez une list view à la forme et appelez la lvContacts. Changez la propriété allColumnsShowFocus à True. Quand cela a été fait, cliquez droit sur la list view et choisissez de l'éditer. Effacez les entrées de la liste (schéma 7-5) et créez les colonnes "Name", "E-Mail" et "Phone Number" (schéma 7-6).

The list view items

Schéma 7-5 Les items de la list view.

The list view columns

Schéma 7-6 Les colonnes de la list view.

Pour faire de la list view le widget principal de la forme, choisissez la forme et appliquez une disposition de grille. Dans les propriétés, changez layoutMargin à zéro pour faire atteindre à la list view les bords de la forme. La forme résultante est montrée dans le mode de prévisualisation sur le schéma 7-7.

The main window in preview mode

Schéma 7-7 La main window dans le mode de prévisualisation.

Maintenant il est temps d'ajouter quelques fonctionnalités au projet. Nous commençons par ajouter les private slots suivants à la forme: init(), addContact(), editContact() et removeContact(). Le dialogue d'édition des slots est montré sur le schéma 7-8.

The slots

Schéma 7-8 Les slots.

Cliquez sur le bouton de connexion pour l'action courante dans l'éditeur d'action et reliez les signaux comme montré dans le tableau 7-2. Pour vérifier que ceci a été correctement fait, cliquez droit sur la forme (par exemple sur la barre de menu en dehors de toute entrée de menu) et choisisissez Connections du menu contextuel. Le dialogue s'affichant devrait produire quelque chose de ressemblant au schéma 7-9. (notez s.v.p. que l'allure de ce dialogue diffère légèrement entre différentes versions de Qt. Le contenu est le même dans toutes les versions que j'ai vues)..

Source Signal Slot (always of frmMain)
aExit activated() close()
aAddContact activated() addContact()
aEditContact activated() editContact()
aRemoveContact activated() removeContact()

Tableau 7-2

The connections

Schéma 7-9 Les connexions.

L'étape suivante est de créer un dialogue pour éditer de nouveaux et d'anciens contacts. Le schéma 7-10 montre à quoi ressemblera le dialogue quand il sera fini.

The contact editing dialog

Schéma 7-10 Le dialogue d'édition des contacts.

Les instructions pour la création de ce dialogue sont brèves car toutes les étapes ont été décrites dans le chapitre 5.

Commencez par ajouter un nouveau dialogue au projet. Nommez-le dlgContact et mettez Contact dans la propriété caption. Dans le dialogue, ajoutez deux boutons poussoir et appelez-les pbOk et pbCancel. Ajoutez alors un group box. Dans le group box, ajoutez trois labels et trois line edit. Nommez ces derniers leName, leEMail et lePhone. N'oubliez pas d'ajouter deux spacers comme représenté sur le schéma 7-11. En outre, modifiez le texte affiché des boutons et des labels comme montré dans la figure.

The widgets before applying the layouts

Schéma 7-11 Les widgets avant d'appliquer les dispositions.

Maintenant, nous continuerons avec les dispositions. Choisissez les line edit et appliquez une disposition verticale. Faites la même chose aux labels. Alors, sélectionnez les boutons et le spacer horizontal et appliquez une disposition horizontale. Maintenant, sélectionnez le cadre et appliquez une disposition horizontale. En conclusion, sélectionnez la forme et appliquez une disposition verticale. Tout le travail de disposition qui a été fait doit maintenant redimensionner le dialogue de sorte qu'il est un bel affichage.

Pour les signaux et les slots. Tout ce que nous devons faire est de relier les signaux clicked() des boutons aux slots accept et reject du dialogue. Après que vous ayez fait ceci, le dialogue est prêt. Cela montre comme il est rapide et facile de créer un dialogue simple.

Avant que nous puissions ajouter du code à la fenêtre principale nous avons besoin d'une structure pour stocker nos contacts. En fait, ce n'est pas nécessaire avec la fonctionnalité que nous allons présenter dans ce chapitre, mais il sera employé plus tard. Maintenant, ajoutez un fichier d'en-tête C++ au projet et mettez y le code de l'exemple 7-1 et sauvez-le comme contact.h.

#ifndef CONTACT_H
#define CONTACT_H

#include <qstring.h>

class Contact
{
public:
  QString name,
    eMail,
    phone;
};

#endif

Exemple 7-1

Afin de pouvoir employer cette structure dans notre fenêtre principale il doit être inclus. Nous mettrons les contacts dans un QValueList, ainsi nous devons déclarer la liste comme QValueList<Contact> m_contacts dans la liste des variables de la classe. Il y a trois fichiers supplémentaires qui doivent être inclus, le dialogue - dlgcontact.h, une boîte de message - qmessagebox.h et la classe line edit - qlineedit.h. Si le fichier des line edit est omis il ne sera possible d'accéder à aucun membre line edit dans le code dans la fenêtre principale puisque la fenêtre principale ne contient pas elle-même de line edit. Tous les widgets dans une fenêtre, un dialogue ou un widget composé seront automatiquement inclus. Toutes les inclusions et la déclaration sont montrées sur le schéma 7-12.

The file inclusions and declarations

Schéma 7-12 Les fichiers inclus et les déclarations.

Enfin le moment est venu pour un certain code "actif". Commencez en initialisant le dialogue dans le slot init(). Le code montré dans l'exemple 7-2 efface uniquement le list view. Ce n'est pas nécessaire, mais il montre où mettre le code d'initialisation.

void frmMain::init()
{
  // Clear the list
  lvContacts->clear();
}

Exemple 7-2

Après, nous ajoutons la capacité d'ajouter des contacts. L'exemple 7-3 affiche le code et montre comment des dialogues peuvent être employés efficacement. D'abord initialise, puis affiche, si le résultat est accepté, vérifie les données et lance les actions. Dans ce cas, nous ajoutons un nouveau contact à notre liste.

void frmMain::addContact()
{
  // Création d'un nouveau dialogue
  dlgContact dlg( this );

  // On l'affiche et on attend Ok ou Cancel
  if( dlg.exec() == QDialog::Accepted )
  {
    // Ok a été choisi
    Contact c;

    // Extraction des informations depuis le dialogue
    c.name = dlg.leName->text();
    c.eMail = dlg.leEMail->text();
    c.phone = dlg.lePhone->text();

    // Contrôle pour les noms dupliqués
    for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
    {
    if( (*it).name == c.name )
      {
        // Avertissement de la duplication et abandon
        QMessageBox::warning( this, tr("Duplicate"), tr("The person ") + (*it).name + tr(" allready exists.") );
        return;
      }
    }
    // Pas de doublon trouvé, ajout du contact dans la liste et affichage dans le listview
    m_contacts.append( c );
    lvContacts->insertItem( new QListViewItem( lvContacts, c.name , c.eMail, c.phone ) );
  }
}

Exemple 7-3

C'est pas mal de code d'un coup, commençons ici quelques explications. Les liens mènent à la documentation officielle de Qt ainsi vous pouvez les suivre pour plus de détails.

La première chose que nous faisons est d'instancier notre sous-classe de QDialog, dlg. L'appel à la méthode exec() attend la fermeture du dialogue et renvoie un statut. Nous sommes intéressés de savoir si le résultat du dialogue est accepté par l'utilisateur, d'où le test if.

Le Contact, c, est complété par l'information du dialogue. Nous obtenons le texte des line edit en utilisant la méthode text().

Ensuite un conteneur de Qt, QValueList, est utilisé. Qt est compatible avec la STL, mais les conteneurs de Qt sont souples d'emploi. Si un doublon est trouvé, un dialogue d'avertissement est affiché en utilisant la méthode warning() de QMessageBox.

Pour finir, le contact est ajouté à la liste de contacts et un nouveau QListViewItem est ajouté à la list view.

Nous continuons avec le code de l'exemple 7-4. Il nous permet d'enlever des contacts de la liste. Notez comme il est facile d'enlever des éléments d'une list view, effacez seulement l'élément. Il n'y a pas besoin de s'inquiéter de la libération de la mémoire grâce à Qt.

void frmMain::removeContact()
{
  // Affectation d'un pointeur vers l'élément sélectionné
  QListViewItem *item = lvContacts->currentItem();

  // Contrôle s'il est un élément sélectionné valide
  if( item )
  {
// Trouve l'élément correspondant dans la liste interne
    for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
    {
      if( (*it).name == item->text(0) )
      {
        // Nous l'avons trouvé, nous le supprimons de la liste et du listview
        m_contacts.remove( it );
        delete item;
        return;
      }
    }
  }
}

Exemple 7-4

Maintenant il nous manque le code pour un slot seulement. L'exemple 7-5 montre le code utilisé pour éditer les contacts. On peut voir comment mélanger les cas d'ajouts et de suppressions. Il localise d'abord l'entrée choisie, configure le dialogue, l'affiche et modifie l'entrée si tout est correct. L'algorithme de test dupliqué pourrait avoir besoin d'un petit commentaire. Si le nom a été changé, alors la liste ne peut avoir aucun doublon, mais si le nom est identique, alors nous permettons une occurrence qui est l'entrée d'origine.

void frmMain::editContact()
{
  // Affectation d'un pointeur vers l'élément sélectionné
  QListViewItem *item = lvContacts->currentItem();
  
  // Contrôle s'il est un élément sélectionné valide
  if( item )
  {
    // Trouve l'élément correspondant dans la liste interne
    for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
    {
      if( (*it).name == item->text(0) )
      {
        // Nous l'avons trouvé, nous affichons le dialogue avec lui
        dlgContact dlg( this );
        
        dlg.leName->setText( (*it).name );
        dlg.leEMail->setText( (*it).eMail );
        dlg.lePhone->setText( (*it).phone );
        
        // On l'affiche et on attend Ok ou Cancel
        if( dlg.exec() == QDialog::Accepted )
        {
          // Contrôle des noms dupliqués par comptage du nombre d'occurences
          int occurencies = 0;
          for( QValueList<Contact>::iterator i2 = m_contacts.begin(); i2 != m_contacts.end(); ++i2 )
            if( (*i2).name == dlg.leName->text() )
              occurencies++;
          
          // Si le nom est changé nous n'allouons aucune occurence, autrement une
          if( (dlg.leName->text() != (*it).name && occurencies > 0) || (occurencies > 1) )
          {
            // Avertissement de la duplication et abandon
            QMessageBox::warning( this, tr("Duplicate"), tr("The person ") + dlg.leName->text() + tr(" allready exists.") );
            return;
          }
          
          // Mise à jour de la liste interne
          (*it).name = dlg.leName->text();
          (*it).eMail = dlg.leEMail->text();
          (*it).phone = dlg.lePhone->text();
          
          // Mise à jour de la listview
          item->setText( 0, (*it).name );
          item->setText( 1, (*it).eMail );
          item->setText( 2, (*it).phone );
        }
        
        return;
      }
    }
  }
}

Exemple 7-5

Avant que nous puissions produire une application qui fonctionne, nous devons ajouter une fonction main() banale. Ajoutez un nouveau fichier source C++ appelé main.cpp au projet et mettez-y le code de l'exemple 7-6. Le code affiche une fenêtre principale et ensuite attend que la fenêtre principale soit fermée.

#include <qapplication.h>

#include "frmmain.h"

int main( int argc, char **argv )
{
  QApplication a( argc, argv );

  frmMain *m = new frmMain();
  m->show();

  a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );

  return a.exec();
}

Exemple 7-6

Le schéma 7-13 affiche l'application produite. Pour la construire, s.v.p. lancez qmake adbook.pro && make depuis une console, puis lancez l'application obtenue et appréciez.

The application in action

Schéma 7-13 L'application en action.

Fenêtre principale, dialogue et widget
Beaucoup de nouveaux utilisateurs de Qt sont désorientés par la variété de méthodes pour afficher une fenêtre avec quelques widgets à l'intérieur. Les premiers exemples de Qt emploient un widget directement dans la fonction main() tandis que d'autres utilisent des dialogues. Ajoutez à ceci l'option d'utiliser une fenêtre principale et la confusion est totale.

Pour résumer rapidement une longue histoire, employez les fenêtres principales dans des applications importantes et les dialogues dans de petites applications. Si vous voulez connaître mes raisons à cela, lisez ce qui suit.

La capacité d'employer des widgets directement dans une fonction main() est très utile pour tester des widgets et pour écrire des applications vraiment petites sans aucun code actif dedans. Dès que vous voudrez ajouter des slots pour des actions telles qu'ajouter une nouvelle entrée, effacer une liste ou effectuer une autre action spécifique d'application, l'approche widget devrait être évitée. Puisse que la plupart des applications utiles fournissent de réelles fonctionnalités, il peut être dit que vous ne devez pas utiliser de widget directement à moins que vous ne testiez le widget lui-même.

Les applications basées sur un dialogue sont puissantes puisqu'elles sont faciles à faire et peuvent être employées pour accomplir une tâche simple n'exigeant pas une fenêtre principale. Il n'est pas nécessaire de limiter une application basée sur un dialogue à un seul dialogue, mais dès que vous commencerez à avoir un dialogue affichant un "document" sur les autres dialogues fonctionnant, vous devrez vraiment considérer d'employer une fenêtre principale à la place.

Comme indiqué dans le paragraphe précédant une approche de fenêtre principale est recommandée dès que vous commencerez à travailler sur un "document". Cela ne signifie pas un document texte WYSIWYG (What You See Is What You Get (ce que vous voyez est ce que vous obtenez), comme n'importe quel traitement de texte moderne), mais plutôt une collection de données (comme dans le model - view - controller, MVC, modèle de conception). Un exemple d'un document est la liste de contacts utilisés dans le carnet d'adresses. Il y a plusieurs avantages à employer l'approche de fenêtre principale. Celui que je préfère c'est qu'il est facile de créer des applications permettant à l'utilisateur de travailler avec plusieurs documents, en créant simplement de nouvelles instances de votre fenêtre principale. Ceci signifie que chaque document obtient une fenêtre pour lui même et c'est l'approche que la recherche d'interaction humain-ordinateur recommande. Jusque récemment la plupart des applications fenêtrées telles que Office a employée une interface MDI (un bon nombre de documents dans une grande fenêtre principale), mais dans des versions postérieures une interface SDI (une fenêtre par document). Une interface SDI est ce que vous obtenez en employant l'approche de fenêtre principale.

Résumé

Le code d'exemple de ce chapitre peut être trouvé ici.

Ce chapitre a montré comment une application complète est construite en utilisant une fenêtre principale et un dialogue pour travailler avec le "document" présenté dans la fenêtre principale. Nous avons également clarifié le choix entre la fenêtre principale et le dialogue.

Exercices

  1. Comment ajoutez vous, modifiez et enlevez des articles dans un list view ?
  2. Pour quelle application choisissez-vous une fenêtre principale, un dialogue ou une approche widget ? Une unité de traitement de texte ? Une face avant de dictionnaire ? Une application de dessin ? Un renommeur de fichier ? Pourquoi ?
  3. Dans l'application carnet d'adresses, ajoutez un champ appelé ICQ# pour chaque contact et affichez-le dans le list view.

This is a part of digitalfanatics.org and is valid XHTML.