Traduction par Jean-luc Biord, du Site de la communauté
Qt francophone.
English TOC.
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.
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.
Name | Text | menuText |
aExit | Exit | E&xit |
aAddContact | Add Contact | &Add Contact |
aEditContact | Edit Contact | &Edit Contact |
aRemoveContact | Remove Contact | &Remove Contact |
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.
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).
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.
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.
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() |
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.
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.
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
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.
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();
}
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 ) );
}
}
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;
}
}
}
}
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;
}
}
}
}
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();
}
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.
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.
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.
This is a part of digitalfanatics.org and is valid XHTML.
Copyright (c) 2002-2004 by Johan Thelin (e8johan -at- digitalfanatics.org). This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 (local copy) or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder. Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.