Introduction à Perl/Tk

Interfaces graphiques avec Perl

Nancy Walsh

Traducteur

Éric Jacoboni

Perl/TK est une extension de Perl grâce à laquelle l'interface utilisateur des programmes devient graphique. Avec Tk, les programmes Perl ne sont plus limités au mode texte, mais créent fenêtres graphiques, boutons, zones de saisie, boîtes de liste, menus et barres de défilement. Tk libère le développeur du confinement de la ligne de commande et lui permet de construire des applications graphiques répondant à des événements, sous MS-Windows comme sous Unix.

Cet ouvrage est destiné aux novices comme aux experts. Il présente les concepts de la programmation événementielle et détaille le meilleur mode de conception d'applications à interfaces graphiques.

Il montre, étape par étape, comment implanter et configurer les éléments graphiques de Perl/Tk. Une attention particulière est portée aux gestionnaires d'espace, responsables du placement de chaque bouton, menu, étiquette et boîte de liste dans une fenêtre. Pour chacun des widgets de Perl/Tk couramment utilisés, vous trouverez dans ce livre :

- une présentation générale et un exposé des utilisations courantes, avec des exemples ;

- les options de configuration ;

- les méthodes permettant de le manipuler ou de peaufiner son comportement ;

- des exercices vous permettant de vous familiariser avec ses applications.

Bien que ce livre n'enseigne pas les bases du langage, toute personne ayant écrit même le plus simple programme Perl devrait pouvoir apprendre Tk grâce à lui. Le style d'écriture, agréable et aéré, va droit au but. Ce livre est riche en illustrations montrant l'aspect de chaque élément et l'effet des options de configuration sur sa présentation.

Il a été écrit pour tous les programmeurs Perl souhaitant implanter des interfaces graphiques simples et faciles à utiliser. « Cet excellent ouvrage traite au mieux de Perl/Tk. Le paragraphe de la préface faisant référence aux 'gourous' apprenant eux aussi grâce à lui s'est avéré vrai, même pour moi ». - Nick Ing-Simmons, créateur de Perl/Tk.

Nous remercions le traducteur de ce livre, Éric Jacoboni, ainsi que la maison d'édition O'Reilly Media, Inc. de nous avoir aimablement autorisés à republier en ligne ce livre devenu introuvable. Merci aussi à Dominique Buraud pour son assistance dans la préparation de ce projet.
Nous remercions également djibril, winjerome et lolo78 pour le travail de reformatage et Claude Leloup pour la relecture orthographique.

Ce livre a été publié à l'origine en 2000, mais il reste la « bible » du Perl/Tk en français. Néanmoins, Perl/Tk ayant évolué, de nouvelles options sont apparues depuis. N'hésitez pas à regarder également la documentation officielle de Perl/Tk pour voir s'il y a eu du nouveau. Dans tous les cas, ce livre couvre toujours la version de Perl à ce jour (03/05/2018) et tout ce qui y est présenté fonctionne parfaitement.

N'hésitez pas à laisser vos commentaires : Commentez .

- Titre : Introduction à Perl/Tk

- Auteur : Nancy Walsh

- Traducteur : Éric Jacoboni

- Parution : 18 mai 2018

- Mise à jour : 18 mai 2018

- Public visé :

- Licence : Le contenu de cet article est rédigé par Nancy Walsh et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Voir également :

- Installer Tk sur une distribution de Perl récente

- FAQ Perl/Tk

Sommaire

- Préface

- Ce que vous devriez déjà savoir

- Contenu de ce livre

- Progression dans la lecture

- Novices en Perl/Tk

- Des initiés aux gourous

- Conventions typographiques

- Donnez-nous votre avis

- Remerciements

- I. Introduction à Perl/Tk

- I-A. Perl (et Tk)  : un peu d'histoire

- I-B. Perl/Tk pour Unix et Windows 95/NT

- I-C. Versions

- I-D. Pourquoi utiliser une interface graphique ?

- I-E. Pourquoi utiliser Perl/Tk ?

- I-F. Installation du module Tk

- I-G. Création des widgets

- I-G-1. Définitions succinctes des widgets Toplevel, MainWindow et Frame

- I-H. Style de programmation

- I-I. Affichage d'un widget

- I-J. Anatomie d'une boucle d'événement

- I-K. Un exemple : « Bonjour tout le monde »

- I-L. Utilisation de exit ou de destroy

- I-M. Conventions pour les noms des widgets

- I-N. Utilisation de print pour les diagnostics et les recherches d'erreur

- I-O. Conception de vos fenêtres (une approche rapide)

- II. Gestion de l'espace

- II-A. Pack

- II-A-1. Options de pack

- II-A-2. Positionnement des widgets

- II-A-3. Rectangles alloués

- II-A-4. Remplissage du rectangle alloué

- II-A-5. Agrandissement du rectangle alloué

- II-A-6. Ancrage d'un widget dans son rectangle alloué

- II-A-7. Ordonnancement des widgets dans une fenêtre

- II-A-8. Compléter la taille d'un widget avec des espaces de remplissage

- II-A-9. Distances d'écran

- II-A-10. Affichage dans un autre parent

- II-A-11. Méthodes associées à pack

- II-A-12. Suppression d'un widget

- II-A-13. Obtention d'informations sur les emplacements

- II-A-14. Désactivation et activation du changement de taille automatique

- II-A-15. Énumération des widgets

- II-B. Grid

- II-B-1. Caractères spéciaux

- II-B-2. Traverser des colonnes

- II-B-3. Cellules vides

- II-B-4. Options de Grid

- II-B-5. Désignation explicite des lignes et colonnes

- II-B-6. Traverser explicitement des lignes et des colonnes

- II-B-7. Forcer un widget à remplir une cellule

- II-B-8. Remplissage d'un widget

- II-B-9. Utilisation d'un autre parent

- II-B-10. Configuration des colonnes et des lignes

- II-B-11. Poids d'une ligne ou d'une colonne

- II-B-12. Taille minimale d'une cellule

- II-B-13. Remplissage

- II-B-14. Boîte de dimensionnement (Bounding box)

- II-B-15. Suppression d'un widget

- II-B-16. Obtenir des informations

- II-B-17. Emplacement d'un widget

- II-B-18. Propagation

- II-B-19. Combien de colonnes et de lignes ?

- II-B-20. Énumération des widgets

- II-C. Place

- II-C-1. 0ptions de place

- II-C-2. Coordonnées absolues

- II-C-3. Coordonnées relatives

- II-C-4. Ancrage d'un widget

- II-C-5. Largeur et hauteur

- II-C-6. Options de prise en compte des contours

- II-C-7. Méthodes associées à place

- II-C-7-a. Suppression d'un widget

- II-C-7-b. Informations sur les emplacements

- II-C-7-c. Énumération des widgets

- II-D. Résumé sur la gestion de l'espace

- III. Le bouton de base

- III-A. Le widget Button

- III-A-1. Création d'un bouton

- III-A-2. Options d'un bouton standard

- III-A-3. Affichage de texte

- III-A-4. Afficher une image ou un bitmap à la place du texte

- III-A-5. Assignation d'une fonction de rappel

- III-A-6. Définition d'une fonction de rappel

- III-A-7. Fonctions anonymes

- III-A-8. Désactiver un bouton

- III-A-9. Manipulation du texte

- III-A-10. Modification du style d'un bouton

- III-A-11. Changer la taille d'un bouton

- III-A-12. Ajouter un raccourci clavier

- III-A-13. Options de couleur

- III-A-14. Modification du curseur de la souris

- III-A-15. Options du focus

- III-A-16. Modification du rectangle de focus

- III-A-17. Configuration d'un bouton

- III-A-18. Clignotement d'un bouton

- III-A-19. Invocation d'un bouton

- III-B. Essayez et amusez-vous !

- IV. Cases à cocher et boutons radio

- IV-A. Le widget Checkbutton

- IV-A-1. Options des cases à cocher

- IV-A-2. Stockage de l'état de l'indicateur

- IV-A-3. Affectation d'une fonction de rappel

- IV-A-4. Valeurs de cochage

- IV-A-5. Couleur de l'indicateur

- IV-A-6. Masquage de l'indicateur

- IV-A-7. Affichage d'une image à la place du texte

- IV-A-8. Style d'une case à cocher

- IV-A-9. Configuration d'une case à cocher

- IV-A-10. Cocher ou décocher une case

- IV-A-11. Clignotement d'une case à cocher

- IV-A-12. Invocation d'une case à cocher

- IV-B. Le widget Radiobutton

- IV-B-1. Création de boutons radio

- IV-B-2. Options des boutons radio

- IV-B-3. Utilisation de l'option -variable

- IV-B-4. Configuration de la valeur d'un bouton radio

- IV-B-5. Style d'un bouton radio

- IV-B-6. Configuration d'un bouton radio

- IV-B-7. Sélection et désélection d'un bouton radio

- IV-B-8. Clignotement d'un bouton radio

- IV-B-9. Invocation d'un bouton radio

- IV-C. Essayez et amusez-vous !

- V. Les widgets étiquette et de saisie

- V-A. Le widget Label

- V-A-1. Création d'une étiquette

- V-A-2. Options des étiquettes

- V-A-3. Différences entre les étiquettes et les autres widgets

- V-A-4. Styles d'étiquettes

- V-A-5. Exemple de message d'état

- V-A-6. Configuration d'une étiquette

- V-B. Le widget Entry

- V-B-1. Création d'un widget de saisie

- V-B-2. Options d'un widget de saisie

- V-B-3. Assignation du contenu de la saisie à une variable

- V-B-4. Styles d'une zone de saisie

- V-B-5. Indices de saisie

- V-B-6. Options de sélection du texte

- V-B-7. Le point d'insertion

- V-B-8. Saisies de mots de passe

- V-B-9. Utilisation d'une barre de défilement

- V-B-10. Configuration d'un widget de saisie

- V-B-11. Suppression de texte

- V-B-12. Déterminer le contenu d'un widget de saisie

- V-B-13. Déplacement du point d'insertion

- V-B-14. Obtenir la valeur d'un indice

- V-B-15. Insertion de texte

- V-B-16. Parcours du texte

- V-B-17. Utilisation du texte sélectionné

- V-B-18. Changer la vue d'un widget de saisie

- V-C. Essayez et amusez-vous !

- VI. Barres de défilement

- VI-A. Composantes d'une barre de défilement

- VI-B. La méthode Scrolled

- VI-B-1. Configuration de barres créées avec Scrolled

- VI-C. Le widget Scrollbar

- VI-C-1. Création d'un widget barre de défilement

- VI-C-2. Options des barres de défilement

- VI-C-3. Couleurs d'une barre de défilement

- VI-C-4. Style des barres de défilement

- VI-C-5. Orientation d'une barre de défilement

- VI-C-6. Utilisation des flèches et de l'ascenseur

- VI-C-7. Assignation d'une fonction de rappel

- VI-C-8. Communication entre une barre de défilement et les autres widgets

- VI-C-9. Configuration d'une barre de défilement

- VI-C-10. Définir ce qui est visible

- VI-C-11. Connaître la vue courante

- VI-C-12. Activer les éléments d'une barre de défilement

- VI-C-13. Calcul des modifications en pixels

- VI-C-14. Localiser un point de la zone de défilement

- VI-C-15. Identification des éléments

- VI-D. Exemples

- VI-D-1. Zone de saisie

- VI-D-2. Widgets liste, texte et canevas

- VI-D-3. Une seule barre de défilement, plusieurs widgets

- VI-E. Essayez et amusez-vous !

- VII. Le widget boîte de liste

- VII-A. Création et remplissage d'une boîte de liste

- VII-B. Options des boîtes de liste

- VII-C. Modes de sélection

- VII-C-1. Modes de sélection

- VII-C-2. Différences entre systèmes d'exploitation

- VII-D. Couleurs

- VII-E. Style d'une boîte de liste

- VII-E-1. Style des éléments sélectionnés

- VII-E-2. Changement de taille spécifique aux boîtes de liste

- VII-F. Configuration d'une boîte de liste

- VII-G. Indices de liste

- VII-H. Insertion d'éléments

- VII-I. Suppression d'éléments

- VII-J. Récupération d'éléments

- VII-K. Méthodes de sélection

- VII-K-1. Sélection des éléments

- VII-K-2. Annulation de sélections

- VII-K-3. Tester la sélection

- VII-K-4. Ancrage de la sélection

- VII-L. Aller à un indice précis

- VII-M. Conversion d'indices

- VII-N. Compter les éléments

- VII-O. Élément actif vs élément sélectionné

- VII-P. Boîte de dimensionnement

- VII-Q. Recherche d'un indice par sa coordonnée y

- VII-R. Méthodes de défilement

- VII-S. Exemple Listbox

- VII-T. Essayez et amusez-vous !

- VIII. Le widget texte

- VIII-A. Création et utilisation d'un widget texte

- VIII-B. Options du widget texte

- VIII-B-1. Fontes

- VIII-B-2. Taille du widget

- VIII-B-3. Style d'un widget texte

- VIII-B-4. Hauteur de l'interligne

- VIII-B-5. Taquets de tabulation

- VIII-C. Une courte pause pour un bref exemple

- VIII-D. Indices de texte

- VIII-D-1. Valeurs de l'indice de base

- VIII-D-2. Modificateurs d'indices

- VIII-D-3. Exemples d'indices de texte

- VIII-E. Marqueurs de texte

- VIII-E-1. Options des marqueurs

- VIII-E-2. Un exemple de marqueur simple

- VIII-E-3. Utilisation du marqueur "sel" pour manipuler la sélection

- VIII-E-4. Création et configuration des marqueurs

- VIII-E-5. Ajout d'un marqueur à du texte existant

- VIII-E-6. Utilisation de bind avec les marqueurs

- VIII-E-7. Suppression de toutes les instances d'un marqueur

- VIII-E-8. Annulation de l'effet d'un marqueur

- VIII-E-9. Priorités des marqueurs

- VIII-E-10. Obtention des noms de marqueurs

- VIII-E-11. Déterminer où s'applique un marqueur

- VIII-F. Insertion de texte

- VIII-G. Suppression de texte

- VIII-H. Capture de texte

- VIII-I. Conversion des valeurs d'indices

- VIII-J. Comparaison d'indices

- VIII-K. Affichage du texte situé à un indice

- VIII-L. Obtention de la taille d'un caractère

- VIII-M. Obtention d'informations sur une ligne

- VIII-N. Recherche dans un widget texte

- VIII-O. Barres de défilement

- VIII-P. Marques

- VIII-P-1. Configuration et obtention du pôle d'attraction d'une marque

- VIII-P-2. Noms des marques

- VIII-P-3. Création et suppression de marques

- VIII-Q. Imbrication de widgets

- VIII-Q-1. Les méthodes window

- VIII-R. Test du code

- VIII-R-1. Recherche

- VIII-S. Essayez et amusez-vous !

- IX. Le widget canevas

- IX-A. Création d'un canevas

- IX-B. Système de coordonnées

- IX-C. La région de parcours

- IX-D. Utilisation de bind avec un canevas

- IX-E. Options d'un canevas

- IX-E-1. Nouvelles options

- IX-E-2. Nouvelles options de défilement

- IX-E-3. Options pour le texte

- IX-E-4. Liste des options du widget canevas

- IX-F. Création d'éléments dans un canevas

- IX-F-1. Arcs

- IX-F-2. Bitmaps

- IX-F-3. Images

- IX-F-4. Lignes

- IX-F-5. Ovales

- IX-F-6. Polygones

- IX-F-7. Rectangles

- IX-F-8. Textes

- IX-F-9. Indices des éléments texte

- IX-F-10. Suppression de caractères

- IX-F-11. Positionnement du point d'insertion

- IX-F-12. Informations sur les indices

- IX-F-13. Ajout de texte

- IX-F-14. Sélection de texte

- IX-F-15. Widgets

- IX-G. Configuration du widget canevas

- IX-H. Configuration des éléments d'un canevas

- IX-I. Marqueurs

- IX-I-1. Liaison d'éléments à l'aide des marqueurs

- IX-I-2. Recherche de marqueurs

- IX-I-3. Obtention des marqueurs d'un élément donné

- IX-J. Coordonnées de la boîte de dimensionnement

- IX-K. Conversion de coordonnées

- IX-L. Déplacement des éléments

- IX-M. Modification de la liste d'affichage

- IX-N. Suppression d'éléments

- IX-O. Suppression de marqueurs

- IX-P. Déterminer le type d'un élément

- IX-Q. Focus clavier

- IX-R. Représentation PostScript d'un canevas

- IX-S. Modification de l'échelle d'un canevas

- IX-T. Recherche

- IX-T-1. Méthodes de parcours

- IX-U. Exemple de programme de dessin

- IX-V. Essayez et amusez-vous !

- X. Le widget barre de progression

- X-A. Création d'une barre de progression

- X-B. Assignation d'une fonction de rappel

- X-C. Orientation

- X-D. Valeurs minimum et maximum

- X-E. Valeur affichée vs valeur stockée

- X-F. Ajout d'une étiquette

- X-G. Affichage des pas de progression

- X-H. Modification de la taille de la barre

- X-I. Options rarement utilisées

- X-J. Configuration d'une barre de progression

- X-K. Obtenir la valeur d'une barre de progression

- X-L. Configuration de la valeur d'une barre

- X-M. Obtention des coordonnées

- X-N. Identifier les parties d'une barre de progression

- X-O. Essayez et amusez-vous !

- XI. Menus

- XI-A. Différents types de menus

- XI-B. Le widget Menubutton

- XI-B-1. Création d'un bouton de menu

- XI-B-2. Options d'un bouton de menu

- XI-B-3. Options propres au bouton

- XI-B-4. Éléments détachables

- XI-B-5. Options de couleurs

- XI-B-6. Indicateur du bouton

- XI-B-7. Ajout d'éléments dans un menu

- XI-B-8. Entrées de type commande

- XI-B-9. Entrées de type bouton radio

- XI-B-10. Entrées de type case à cocher

- XI-B-11. Entrées de type cascade

- XI-B-12. Entrées de type séparateur

- XI-B-13. Accélérateurs

- XI-B-14. Assignation d'un menu différent

- XI-B-15. Configuration d'un bouton de menu

- XI-B-16. Configuration des entrées d'un bouton de menu

- XI-B-17. Ajout d'entrées à un bouton de menu

- XI-B-18. Création d'une entrée de type case à cocher

- XI-B-19. Création d'une entrée de type bouton radio

- XI-B-20. Création d'une entrée de type séparateur

- XI-B-21. Création d'un menu en cascade

- XI-B-22. Référence à un menu

- XI-C. Exemples complets d'utilisation des boutons de menu

- XI-C-1. Création d'une barre de menu

- XI-C-2. Liste dynamique de documents

- XI-D. Le widget Menu

- XI-D-1. Création d'un menu de base

- XI-D-2. Options de création d'un menu

- XI-D-3. Styles de menu

- XI-D-4. Fontes et curseurs

- XI-D-5. Appel d'une fonction avant l'affichage du menu

- XI-D-6. Ajout d'entrées

- XI-D-7. Indices de menu

- XI-D-8. Configuration d'un widget menu

- XI-D-9. Configuration des entrées de menu

- XI-D-10. Ajout d'entrées dans un menu

- XI-D-11. Insertion d'entrées dans un menu

- XI-D-12. Suppression d'entrées d'un menu

- XI-D-13. Appel d'entrées du menu

- XI-D-14. Obtention du type d'une entrée

- XI-D-15. Conversion d'indices

- XI-D-16. Affichage d'un menu

- XI-D-17. Affichage d'un menu en cascade

- XI-D-18. Fermer un menu

- XI-D-19. Obtention de la position d'une entrée

- XI-D-20. Exemple de menu contextuel

- XI-E. Le widget Optionmenu

- XI-E-1. Création et configuration d'un menu d'options

- XI-F. Essayez et amusez-vous !

- XII. Cadres

- XII-A. Création d'un cadre

- XII-B. Styles des cadres

- XII-B-1. Ajout d'une étiquette à un cadre

- XII-C. Les cadres ne sont pas interactifs

- XII-D. Problèmes avec les palettes de couleurs

- XII-D-1. L'option magique -class

- XII-E. Méthodes du widget Frame

- XII-F. Essayez et amusez-vous !

- XIII. Widgets de premier niveau

- XIII-A. Création d'un widget de premier niveau

- XIII-B. Méthodes des widgets de premier niveau

- XIII-B-1. Configuration d'un widget de premier niveau

- XIII-B-2. Donner une taille à un widget de premier niveau

- XIII-B-3. Taille maximale

- XIII-B-4. Taille minimale

- XIII-B-5. Limitation des changements de taille

- XIII-B-6. Utilisation de contraintes sur la taille

- XIII-B-7. Configuration du titre

- XIII-B-8. Affichage d'un widget de premier niveau

- XIII-B-9. Effacement d'un widget de premier niveau

- XIII-B-10. Mettre en icône un widget de premier niveau

- XIII-B-11. Spécification du bitmap de l'icône

- XIII-B-12. Utilisation d'un masque d'icône

- XIII-B-13. Configuration du nom de l'icône

- XIII-B-14. Configuration de la position de l'icône

- XIII-B-15. Utilisation d'une fenêtre à la place d'une icône

- XIII-B-16. État d'un widget de premier niveau

- XIII-B-17. Configuration de la machine cliente

- XIII-B-18. Propriétés d'une fenêtre

- XIII-B-19. Définition de la palette de couleurs

- XIII-B-20. La propriété commande

- XIII-B-21. Mode de gestion du focus

- XIII-B-22. Parent d'un widget de premier niveau

- XIII-B-23. Maillage de l'application

- XIII-B-24. Devenir le chef d'un groupe

- XIII-B-25. Suppression des décors des fenêtres

- XIII-B-26. Qui a placé la fenêtre ?

- XIII-B-27. Qui a modifié sa taille ?

- XIII-B-28. Une fenêtre irréelle

- XIII-C. Résumé

- XIII-D. Essayez et amusez-vous !

- XIV. Liaisons d'événements

- XIV-A. La méthode bind

- XIV-B. Paramètres passés à la fonction de rappel

- XIV-C. Définition de séquences d'événements

- XIV-C-1. Modificateurs

- XIV-C-2. Types d'événements (avec détails optionnels)

- XIV-D. Informations sur un événement

- XIV-D-1. Coordonnées

- XIV-D-2. Numéro du bouton de la souris

- XIV-D-3. Hauteur et largeur

- XIV-D-4. Informations sur le clavier

- XIV-D-5. Type de l'événement

- XIV-E. S'échapper d'une fonction de rappel créée avec bind

- XIV-F. La méthode bindtags

- XIV-G. Utilisations de bind

- XV. Widgets composites

- XV-A. Détour par un exemple

- XV-B. Emplacement des fichiers

- XV-C. Création d'un widget composite à partir d'un cadre

- XV-C-1. Populate détaillé

- XV-C-2. Appel de Advertise

- XV-C-3. Appel de Delegates

- XV-C-4. Appel de ConfigSpecs

- XV-C-4-a. Création d'un synonyme

- XV-C-4-b. Définition d'options

- XV-C-4-c. Exemple d'appel à ConfigSpecs

- XV-C-5. Résumé sur les widgets construits à partir d'un cadre

- XV-D. Widgets composites construits à partir d'un widget de premier niveau

- XVI. Méthodes communes à tous les widgets

- XVI-A. Construction d'une généalogie

- XVI-A-1. Fils d'un widget

- XVI-A-2. Nom d'un widget

- XVI-A-3. Parent d'un widget

- XVI-A-4. Premier niveau d'un widget

- XVI-A-5. Gestionnaire d'espace d'un widget

- XVI-A-6. Classe d'un widget

- XVI-A-7. Identificateur d'un widget

- XVI-A-8. Chemin d'un widget

- XVI-B. Méthodes concernant la couleur

- XVI-B-1. La palette de couleurs est-elle saturée ?

- XVI-B-2. Nombre de cellules

- XVI-B-3. Profondeur de couleur

- XVI-B-4. Conversion en valeur RGB

- XVI-B-5. Configuration des couleurs

- XVI-B-6. Configuration de couleur prédéfinie

- XVI-C. Bases de données des options

- XVI-D. Le nom de l'application

- XVI-E. Existence d'un widget

- XVI-F. Le widget est-il affiché ?

- XVI-G. Conversion des distances d'écran

- XVI-H. Taille d'un widget

- XVI-H-1. Géométrie d'un widget

- XVI-H-2. Hauteur demandée

- XVI-H-3. Largeur demandée

- XVI-H-4. Largeur réelle

- XVI-H-5. Hauteur réelle

- XVI-I. Position d'un widget

- XVI-I-1. Position relative à la fenêtre racine

- XVI-I-2. Coordonnées relatives au parent

- XVI-I-3. Coordonnées relatives à la fenêtre racine

- XVI-I-4. Coordonnées du bureau virtuel

- XVI-I-5. Coordonnées du pointeur de la souris relativement au bureau

- XVI-J. Informations sur l'écran

- XVI-J-1. Nom de l'écran

- XVI-J-2. Hauteur et largeur de l'écran

- XVI-J-3. Nombre de cellules

- XVI-J-4. Profondeur de l'écran

- XVI-J-5. Type de couleur

- XVI-J-6. Type de serveur

- XVI-J-7. Le widget est-il visible ?

- XVI-K. Méthodes applicables aux atomes

- XVI-L. Émettre un bip

- XVI-M. Méthodes pour le presse-papiers

- XVI-N. Méthodes de sélection

- XVI-N-1. Annuler la sélection

- XVI-N-2. Obtenir le texte sélectionné

- XVI-N-3. Assignation d'une fonction de rappel

- XVI-N-4. Connaître le propriétaire du texte sélectionné

- XVI-N-5. Attribuer un propriétaire à une sélection

- XVI-O. Suppression d'un widget

- XVI-P. Méthodes de gestion du focus clavier

- XVI-Q. Méthodes de capture

- XVI-R. Communication entre les applications

- XVI-S. Attente d'événements

- XVI-T. Événements concernant les fichiers

- XVI-U. Traitement des options de la ligne de commande

- XVI-V. Temporisations

- XVII. Configuration des widgets avec configure et cget

- XVII-A. La méthode configure

- XVII-A-1. La méthode cget

- XVII-B. Valeurs par défaut, pour chaque widget

- XVII-B-1. Widget Button

- XVII-B-2. Widget Canvas

- XVII-B-3. Widget Checkbutton

- XVII-B-4. Widget Entry

- XVII-B-5. Widget Frame

- XVII-B-6. Widget Label

- XVII-B-7. Widget Listbox

- XVII-B-8. Widget Menu

- XVII-B-9. Widget Radiobutton

- XVII-B-10. Widget Scale

- XVII-B-11. Widget Scrollbar

- XVII-B-12. Widget Text

- XVII-B-13. Widget Toplevel

- XVIII. Différences entre systèmes d'exploitation

- XVIII-A. Unix

- XVIII-B. Windows NT et 95/98

- XVIII-B-1. Problèmes avec Windows 95

- XVIII-B-2. Sélections

- XIX. Fontes

- XIX-A. Description d'une fonte

- XIX-A-1. Déterminer les fontes disponibles

- XIX-A-2. Méthodes pour les fontes

- XIX-A-2-a. Création

- XIX-A-2-b. Configuration

- XIX-A-2-c. Informations sur la fonte réelle

- XIX-A-2-d. Suppression

- XIX-A-2-e. Taille du texte

- XIX-A-2-f. Détails d'une fonte

- XIX-A-2-g. Familles et noms

- XX. Glossaire

Préface

Perl est un langage particulièrement adapté au traitement des fichiers, aux connexions avec les bases de données, et à de nombreuses autres tâches pénibles à réaliser sans aucun code existant. Longtemps, les programmes Perl furent limités à une interface en ligne de commande. L'interface Tk a changé tout cela.

L'extension Tk de Perl vous permet de créer des interfaces graphiques pour vos programmes. En utilisant les modules inclus dans la distribution de Tk, vous pouvez créer des fenêtres avec boutons, listes, texte et autres types de widgets afin de faciliter l'utilisation de votre application.

Ce que vous devriez déjà savoir

Pour tirer le meilleur parti de ce livre, vous devez déjà connaître les bases de Perl (sa version 5, plus précisément). Mieux vaut être suffisamment à l'aise en Perl pour être au moins capable de lire un code et de savoir ce qu'il fait. Il n'est pas nécessaire d'être un gourou Perl ou un hacker Perl pour apprendre à utiliser Perl/Tk, mais le fait de connaître le langage vous aidera. Voici la liste des choses qui devraient au moins vous être familières : hachages, tableaux, sous-programmes, les versions anonymes des trois précédents, les variables $_ et @_.

Perl/Tk utilise les caractéristiques orientées objet de Perl 5 ; même si vous ne les comprenez pas toutes, vous devriez pouvoir les reconnaître. La seule autre chose dont vous aurez besoin est d'une connaissance préalable d'autres interfaces utilisateur graphiques (GUI) et de ce que vous y appréciez ou non. Cela vous aidera pour décider des caractéristiques de vos propres applications. Examinez le traitement de texte que vous utilisez sur votre ordinateur personnel, votre navigateur web, ou tout autre programme utilisant des boutons, des barres de défilement et acceptant à la fois des saisies via la souris et le clavier.

Ces applications sont plutôt imposantes ; nous débuterons avec des exemples bien plus simples et progresserons en les étoffant. Nous couvrirons ainsi en détail chacun des widgets de base et toutes les options associées. Vous apprendrez comment conférer à une fenêtre l'aspect que vous voulez lui donner, et comment la rendre conviviale et attrayante pour l'utilisateur.

Si vous désirez en apprendre plus sur Perl en général, lisez Learning Perl, Programming Perl, Advanced Perl Programming et Perl Cookbook, également publiés par O'Reilly & Associates, Inc(1). Sur le Web, vous trouverez aussi de nombreuses FAQ et documentations. Ce livre traite exclusivement de l'extension Tk de Perl, qui en constitue une partie assez spécifique.

Contenu de ce livre

Chapitre 1, Introduction à Perl/Tk

1. Le premier chapitre contient un historique des étapes significatives du développement de Perl et du module Tk. Il vous permet aussi de débuter avec un simple programme Bonjour et présente rapidement la programmation par événements.

Chapitre 2, Gestion de l'espace

1. La gestion de l'espace est probablement le concept le plus important de Perl/Tk. Elle détermine l'apparence de vos widgets à l'écran (ou, dans certains cas, ce que l'on n'y verra pas). Les trois gestionnaires d'espace – pack, grid et place – sont étudiés dans ce chapitre. La plupart des exemples de ce livre utilisent pack.

Chapitre 3, Le bouton de base

1. Le bouton est le premier widget que nous étudierons et vous trouverez de nombreux détails dans ce chapitre ainsi que de nombreux de bouts de code et des copies d'écrans montrant les différentes façons de manipuler et de déformer le widget bouton. La plupart des options étudiées ici sont communes aux autres widgets standards.

Chapitre 4, Cases à cocher et boutons radio

1. Les cases à cocher et les boutons radio ressemblent aux boutons classiques, mais ont un autre aspect et sont habituellement programmés de façon différente.

Chapitre 5, Les widgets étiquette et de saisie

1. Une étiquette (label) est le plus simple des widgets, on l'utilise généralement avec un widget de saisie (entry), ce qui explique pourquoi ces deux éléments sont présentés dans le même chapitre Le widget Entry vous permet de récupérer des informations entrées par l'utilisateur.

Chapitre 6, Barres de défilement

1. En Perl/Tk, certains widgets peuvent être parcourus par défilement, ce qui signifie qu'ils peuvent contenir plus d'informations que vous ne pouvez en voir à l'écran. Les barres de défilement (scrollbars) servent à naviguer à travers les données de ces widgets. Ce chapitre explique comment les barres de défilement communiquent avec les widgets et présente ce que vous devez faire pour les créer et les utiliser.

Chapitre 7, Le widget boîte de liste

1. Une boîte de liste (listbox) peut contenir n'importe quel type de donnée, mais elle contient habituellement une liste d'options parmi lesquelles l'utilisateur peut choisir. Dans ce chapitre, vous apprendrez comment créer une boîte de liste, la remplir avec des éléments, et comment modifier la façon dont l'utilisateur pourra y sélectionner.

Chapitre 8, Le widget texte

1. Le widget texte est un widget à tout faire, utilisable dans de nombreuses situations, pas seulement pour afficher du texte. Ce chapitre présente les différentes choses que vous pouvez placer dans un tel widget (du texte ou d'autres widgets) et comment en tirer le meilleur parti.

Chapitre 9, Le widget canevas

1. Un canevas (canvas) peut afficher des objets comme des cercles, des rectangles, du texte et même d'autres widgets. Ce chapitre présente toutes les options et méthodes disponibles et la façon de les utiliser.

Chapitre 10, Le widget barre de progression

1. Le widget barre de progression (scale) est très pratique lorsqu'il s'agit de donner à l'utilisateur un intervalle de nombres dans lequel choisir afin de lui interdire d'entrer une valeur en dehors de cet intervalle ou de taper accidentellement des lettres. Ce chapitre contient des exemples de ce widget et présente toutes les méthodes disponibles pour le configurer et l'utiliser.

Chapitre 11, Menus

1. Lorsqu'une application deviendra suffisamment complexe, vous aurez besoin d'y placer un menu. Ce chapitre présente les différentes façons de créer des menus et comment les utiliser au mieux dans une application.

Chapitre 12, Cadres

1. Le widget Frame sert à organiser vos autres widgets à l'écran pour obtenir l'aspect recherché. Ce chapitre vous explique comment utiliser des cadres (frames) avec un gestionnaire d'espace (expliqué au chapitre 2) afin que vos fenêtres soient présentées selon vos goûts.

Chapitre 13, Widgets de premier niveau

1. Une application a souvent besoin de contenir plus d'une fenêtre. Vous pouvez utiliser un widget de premier niveau (toplevel) pour créer une seconde fenêtre. Ce chapitre vous explique comment en créer un et l'afficher. Il présente aussi les nombreuses méthodes disponibles pour manipuler les widgets de premier, niveau.

Chapitre 14, Liaisons d'événements

1. L'une des meilleures façons d'ajouter des fonctionnalités à votre application est d'ajouter des liaisons supplémentaires aux widgets. Ce chapitre vous explique ce qu'est une liaison, comment en créer une, et comment l'utiliser.

Chapitre 15, Widgets composites

1. Vous pouvez combiner des widgets pour créer un widget bien plus utile et réutilisable. La plupart des widgets supplémentaires que vous pouvez utiliser avec Perl/Tk sont créés de cette façon. Ce chapitre présente un exemple de widget composite et vous donne quelques idées pour créer les vôtres.

Chapitre 16, Méthodes communes à tous les widgets

1. En Perl/Tk, plusieurs méthodes sont communes à tous les widgets. Nous les présentons dans ce chapitre, ainsi que la façon de les utiliser.

Annexe A, Configuration des widgets avec configure et cget

1. L'annexe A décrit les méthodes configure et cget, utilisées avec n'importe quel widget. Elle présente également un tableau des options des widgets et les valeurs par défaut de chacune d'elles.

Annexe B, Différences entre systèmes d'exploitation

1. L'annexe B présente les différences que vous constaterez lorsque vous utiliserez Perl/Tk sur différents systèmes d'exploitation : Unix et Win32, plus précisément.

Annexe C, Fontes

1. L'annexe C présente l'utilisation de fontes avec Tk, pour les systèmes Unix et Win32. Elle couvre aussi la nouvelle syntaxe des fontes utilisée par Tk8.

Progression dans la lecture

Ce livre a été, conçu et écrit pour deux catégories principales de lecteurs : les novices en Perl/Tk et ceux l'ayant déjà pratiqué.

Novices en Perl/Tk

Si vous ne savez pas par où commencer, partez du début. Ce livre est conçu pour vous conduire à travers les sujets en construisant une base de connaissances. Nous débuterons simplement avec le widget bouton dans le chapitre 3, Le bouton de base, et progresserons jusqu'aux widgets plus complexes. L'utilisation de Perl/Tk n'est pas vraiment compliquée dès lors que l'on a compris les bases de son fonctionnement.

Des initiés aux gourous

Vous avez développé de nombreux programmes avec Perl/Tk et pensez savoir comment procéder. Il y a des chances pour que vous ayez trouvé une « méthode qui marche » et que vous vous y teniez. Je conseille de parcourir le chapitre 2, Gestion de l'espace, afin de mieux comprendre le fonctionnement des gestionnaires d'espace. Passez ensuite directement aux sections concernant les widgets qui vous intéressent. J'ai inclus des bouts de codes utiles (et parfois des programmes complets) qui vous donneront des idées sur la façon d'utiliser les widgets de différentes manières. Les listes des options de chaque widget sont des aide-mémoire utiles pour toutes ces damnées options et la façon dont elles affectent chaque widget.

Conventions typographiques

Ce livre utilise les conventions typographiques suivantes :

Italique

1. pour les noms de fichiers, de commandes, les URL et pour mettre en évidence certains termes. Dans les lignes de syntaxe, les italiques sont utilisés pour identifier les valeurs remplaçables.

Taillefixe

1. pour les noms des fonctions, des méthodes et leurs arguments, ainsi que pour mettre en évidence du code littéral dans le texte.

Gras

1. pour indiquer les valeurs par défaut dans les lignes de syntaxe.

Donnez-nous votre avis

Nous avons testé et vérifié les informations de ce livre du mieux qu'il nous est possible, mais vous trouverez peut-être que certaines caractéristiques ont changé (ce qui peut ressembler à des erreurs, en fait). Faites-nous part de toutes les erreurs que vous trouvez, et envoyez vos suggestions pour les futures éditions de ce livre en écrivant à :

info@editions-oreilly.fr

Ce livre n'étant plus édité chez O'Reilly, inutile d'écrire à cette adresse courriel. Vous pouvez toujours nous faire part de vos commentaires sur le forum et nous corrigerons ce tutoriel. Commentez .

Nous avons mis en place un site web pour ce livre où nous énumérerons les erreurs et les prévisions pour les éditions suivantes. Vous pourrez également y télécharger le code source des programmes, ce qui vous évitera de les saisir

http://www.editions-oreilly.fr/

Ce livre n'étant plus édité chez O'Reilly, inutile d'écrire à cette adresse courriel. Commentez .

Remerciements

Ce livre a occupé une petite partie de ma vie, et je voudrais remercier les personnes qui m'ont aidé à le rendre possible et m'ont soutenue alors que j'étais en train de l'écrire : mon mari Mike, d'abord, qui m'a aidée de tant de petites façons qu'il m'est impossible de toutes les énumérer ; nos chiens, Brandy et Théo, pour avoir gardé mes pieds au chaud ; nos chats, Thumper et Sasha, pour avoir gardé l'écran et le clavier sur le bureau (ils sont entièrement responsables de toutes les fautes d'orthographe) ; mon collègue, Kreg WEBB, qui m'a suggéré cette idée complètement folle ; mon éditeur, Linda MUI, pour avoir toujours eu des choses positives à dire en plus de celles un peu moins plaisantes.

Je voudrais aussi remercier tous les relecteurs du livre : Stephen LIDIE, Achim BOHNET, Peter PRYMMER, Nick ING-SIMMONS, et Phivu NGUYEN.

Le traducteur souhaite remercier son éditeur, Nat Makarévitch, pour sa confiance et le suivi de son travail. Merci également à Olivier Tharan, pour sa relecture assidue et ses corrections judicieuses.

I - Introduction à Perl/Tk

De nombreux et divers modules étendent les fonctionnalités de Perl. Ce livre traite du module Tk qui nous permet d'ajouter facilement une interface graphique à nos scripts Perl tout en continuant à utiliser toutes les possibilités qui font la grandeur de ce langage. Au lieu de nécessiter la saisie d'une commande plus ou moins compliquée, votre programme Perl/Tk est invoqué à l'aide d'une icône ou d'une simple commande et l'interface gère tout à partir de là.

L'extension Tk ne fait pas partie de la distribution standard de Perl(2). Vous devrez l'obtenir séparément à partir d'un site CPAN et l'installer. Lorsque ce sera fait, il vous suffira d'ajouter use Tk; au début de vos scripts Perl.

I-A - Perl (et Tk)  : un peu d'histoire

Au départ, Perl a été écrit comme une solution rapide à un problème que Larry WALL avait avec son travail. Comme toutes les personnes qui s'estiment paresseuses, il trouva un moyen meilleur et plus facile de procéder : Perl était né. Il a, depuis, évolué pour devenir un langage très répandu et bien utilisé. Perl existe pour de nombreuses plates-formes différentes, est bien documenté et, cerise sur le gâteau, est gratuit. Évidemment, la raison pour laquelle vous lisez ce livre est que vous êtes déjà converti à Perl et que vous voulez savoir comment l'utiliser au mieux.

L'extension Tk de Perl gère tous les widgets, et toutes les petites choses qui, ensemble, créent une interface graphique. Elle a été adaptée par Nick ING-SIMM0NS à partir de Tcl/Tk afin d'être utilisable avec Perl. Une erreur classique consiste à croire que l'on doit installer Tcl/Tk en plus de Perl et Tk pour que tout fonctionne, alors que tout ce dont vous avez besoin est Perl et son extension Tk. Grâce au travail de beaucoup d'autres personnes, les paresseux autoproclamés comme moi peuvent télécharger le binaire pour leur machine puis l'installer en moins de dix minutes. Vous pouvez aussi le compiler pour votre machine à partir des sources.

I-B - Perl/Tk pour Unix et Windows 95/NT

Alors que j'écrivais ce livre et que je me rendais compte que je n'avais pas assez de machines chez moi pour l'écrire avec MS Word et tester les exemples de code sur ma machine Linux sans devoir redémarrer sans cesse pour passer d'un système d'exploitation à l'autre, un miracle survint. L'extension Tk pour Perl fut adaptée pour Windows. Windows désigne ici le système d'exploitation super-influent installé par défaut sur la plupart des ordinateurs personnels de nos jours, Microsoft Windows. La plupart des gens n'ont pas de compilateur C pour leur machine Windows, mais Gurasamy SARATHY a réalisé une très bonne distribution binaire de Perl et une sélection judicieuse d'extensions, dont Tk. Il vous suffit de la télécharger puis de lancer l'installation, et vous êtes prêt à travailler.

La façon d'écrire des applications Perl/Tk ne varie pas, que l'on emploie une machine sous Unix ou sous Windows. Vous pouvez utiliser n'importe quel éditeur de texte simple. Une petite différence existe quant à la manière de lancer les applications reportez-vous à l'annexe B, Différences entre systèmes d'exploitation, pour plus de détails. Pour le moment, je me contenterai de dire que je préfère lancer mes applications Perl sous Windows NT 4.0 (Service Pack 3) plutôt que sous Windows 95.

I-C - Versions

Lorsque j'ai commencé à écrire ce livre, les versions les plus récentes de Perl et de Tk étaient, respectivement, 5.003 et 400.202. Depuis, une version Win32 du module Tk a été développée et mise à disposition. Perl a aussi subi quelques changements. Juste avant l'impression de ce livre, l'adaptation de Tk800.007 était en phase bêta et Perl atteignait le numéro de version 5.004_68 (version bêta, également). J'ai tâché d'inclure les informations concernant les nouvelles versions de Tk et Perl et pour tester les exemples avec elles. Dans certains cas (les fontes, par exemple), certaines fonctionnalités significatives ont été ajoutées dans la nouvelle version de Tk. J'ai essayé de noter tous les changements là où ils s'appliquent mais, la plupart du temps, vous n'aurez pas à vous soucier de la version que vous utilisez(3).

I-D - Pourquoi utiliser une interface graphique ?

Vous avez évidemment acheté ce livre (ou vous êtes en train de penser à le faire) car vous avez quelques idées sur les raisons pour lesquelles vous pourriez vouloir utiliser une interface graphique pour l'un ou plusieurs de vos scripts. Dans le cas contraire, continuez à lire.

Comme vous êtes habitué à écrire des scripts Perl, vous connaissez les moyens d'y faire entrer et sortir des informations. Cela implique généralement une combinaison de lecture/écriture de fichiers, d'options de la ligne de commande et, éventuellement, de données lues ou écrites lors de l'exécution de l'application (STDIN/STDOUT, utilisation de tubes (|) ou de l'opérateur <>). Certaines applications peuvent s'exécuter sans nécessiter d'entrées et d'autres, comme les scripts d'installation, ont constamment besoin d'informations fournies par l'utilisateur Voulez-vous installer ce fichier ? Puis-je remplacer cette DLL ? Voulez-vous créer ce répertoire ? Voulez-vous installer les fichiers d'aide ? Parfois, vous pouvez configurer un ensemble de valeurs par défaut pour que l'utilisateur n'ait plus qu'à presser la touche 'Entrée' pour accepter, mais il doit quand même rester rivé sur son clavier pour attendre la question suivante. Ne serait-ce pas plus agréable de rassembler d'abord toutes ces informations puis de permettre à l'utilisateur de presser un bouton « Démarrer » pour exécuter toutes les étapes une fois que les décisions ont été prises ?

Une interface graphique donne l'impression qu'une application dispose d'un peu plus de potentialités et est plus professionnelle. Cependant, dans certains cas, ce serait du gâchis d'ajouter une telle interface à un script. Si tout ce que vous devez faire consiste à lire un fichier, faire quelques modifications ne nécessitant pas d'intervention de l'utilisateur, et recracher le tout dans un autre fichier, un GUI serait stupide et déplacé. Les interfaces graphiques sont adaptées aux cas où vous avez besoin d'un grand nombre de décisions et de saisies de l'utilisateur, comme dans notre exemple d'installation du paragraphe précédent.

Voici quelques exemples d'applications où l'utilisation d'une interface utilisateur graphique est justifiée :

- un mini client web se connectant à un serveur-dictionnaire et vous permettant de rechercher des mots ;

- une application prenant une expression rationnelle en entrée et affichant la liste des correspondances trouvées dans une fenêtre que l'on peut faire défiler ;

- une application interrogeant une base de données et affichant les résultats des requêtes dans plusieurs widgets, avec des étiquettes indiquant ce que représentent les données affichées ;

- un lecteur de courrier interrogeant votre boîte de réception et pouvant également envoyer des messages électroniques ;

- parfois, votre patron dit « rendez-le simple à utiliser ! » et cela consiste habituellement soit à envelopper un script, soit à écrire une interface qui facilite la compréhension des utilisateurs quant aux décisions qu'ils doivent prendre. Vos utilisateurs peuvent aussi être plus à l'aise avec une interface graphique qu'avec une interface en ligne de commande.

I-E - Pourquoi utiliser Perl/Tk ?

Avez-vous déjà essayé de tracer une fenêtre en utilisant les possibilités « natives » ? Si vous le faites en C, il vous faudra développer à peu près 100 lignes de code, simplement pour créer le classique programme « Bonjour tout le monde »(4), que vous utilisiez Windows ou X Window. Ces lignes ne comprennent même pas l'ajout d'un bouton de sortie vous permettant de quitter proprement l'application.

Tout au long de ma vie de programmeur, j'ai utilisé plusieurs méthodes différentes pour tracer des fenêtres et créer des applications dotées d'interfaces utilisateur graphiques. Utiliser les fonctions X Window de base (comme X_Create_Line_from_x_to_y), est vraiment un fardeau. Elles offrent, il est vrai, un contrôle total sur tous les petits détails mais, là encore, vous devez gérer chacun d'eux. Parfois, j'apprécie de ne pas savoir exactement comment le bouton s'est tracé ; il me suffit de savoir qu'il l'a fait (je sais conduire une voiture sans pour autant comprendre exactement les détails intimes du moteur à explosion et j'apprécie de pouvoir tourner la clé de contact et réussir dans ma mission consistant à conduire jusqu'à mon travail).

Vous avez probablement vu plusieurs livres sur Tcl/Tk. Le problème de Tcl est que vous devez programmer en respectant les contraintes de ce langage. Je préfère vraiment en utiliser un que je connais déjà plutôt bien et y parfaire mes compétences.

Perl/Tk vous fait cadeau de tous les petits détails ennuyeux. Il gère la boucle des événements. Il gère le tracé des bords en 3D de vos boutons (si vous n'êtes pas certain de comprendre ce que je dis, patience : j'expliquerai tout cela en temps voulu). Vous pouvez simplement utiliser le langage Perl pour « placer un bouton ici » ce qui, traduit en véritable Perl, se dit

$mw->Button(-text => "quelque chose")->pack();

De plus, grâce à la merveilleuse communauté dont dispose Perl, il existe une multitude de types de widgets composés différents et prêts à l'emploi. Si vous ne pouvez pas trouver le widget parfait (tel qu'une sélection-à-listes-multiples avec son canevas associé, par exemple), il est assez simple de le créer en utilisant une combinaison de widgets et de constructions, soit de base, soit moins courants.

Du point de vue d'un programmeur, l'utilisation de Perl/Tk pour écrire une interface utilisateur graphique est quelque chose d'agréable : c'est le nirvana du développeur !

Avec simplement quelques lignes de code, vous pouvez instantanément afficher un bouton et plusieurs autres widgets qui ressembleront à une application complète. Bien sûr, cela prend un peu plus de temps pour coder le corps de tout cela, mais c'est presque aussi plaisant.

Tandis que vous progresserez dans la lecture de ce livre, le meilleur moyen de comprendre ce qui se passe sera d'essayer les nombreux exemples différents. II y a des tonnes de bouts de code inclus pour cette seule raison. Commencez par le programme de base « Bonjour tout le monde » et modifiez les options du bouton lorsque vous passerez aux chapitres 2 et 3. Examinez les résultats sur votre propre écran.

Vous pouvez aussi tester les outils suivants (que nous ne traitons pas dans ce livre, mais qui sont pratiques à utiliser) : tkpsh et ptksh (ils sont apparus avec Tk800.007, la dernière version de Tk pour Perl). Vous pouvez les télécharger à partir du site http://www.monmouth.com/~beller. Ces deux programmes vous permettent de taper du code sur STDIN et d'évaluer chaque instruction (un peu comme wish).

À ce jour (15/04/2018), La version de TK la plus à jour n'est évidemment plus la version TK8000.007 et le lien ci-dessus n'est plus fonctionnel.

I-F - Installation du module Tk

Avant d'aller plus loin dans les détails d'utilisation de Perl/Tk, nous devons traiter de son installation. Il existe plusieurs façons d'obtenir Perl et Tk et de les installer sur votre machine. Vous pouvez récupérer les sources et les compiler (c'est facile avec Unix, moins avec Windows), ou obtenir une distribution binaire et l'installer. Certaines de ces distributions peuvent ne pas inclure tous les composants voulus, aussi assurez-vous de lire tous les fichiers README contenus dans le paquetage.

Les deux principales distributions binaires de Perl pour Win32 sont disponibles auprès d'ACTIVESTATE (https://www.activestate.com) et de CPAN (https://www.perl.com). La distribution binaire de CPAN contient le module Tk et c'est donc elle que nous décrirons ici(5).

Vous devez d'abord installer Perl.

Pour savoir si le module Tk est déjà installé, utilisez cette commande(6) :

Perl -e 'use Tk'

Si vous n'obtenez pas d'erreur, vous pouvez continuer. Sinon, l'erreur ressemblera à ce message(7) :

Can't locate Tk.pm in @INC (@INC contains: C:\PERL\lib\site.
C:\PERL\lib c:\perl\lib c:\perl\lib\site
c:\perl\lib\site .) at mon-script une 1.

Vous devrez rechercher le module Tk sur un site CPAN en commençant par : https://www.cpan.org/modules/by-module/Tk/

À partir de ce répertoire, recherchez les fichiers Tk*readine et Tk*tar.gz (essayez toujours de récupérer les dernières versions le * représente ici le numéro de version). Faites attention lorsque vous téléchargez le fichier .gz, car certains systèmes tentent de renommer celui-ci en .tar.tar : si c'est le cas, renommez simplement le fichier afin qu'il ait une extension .tar.gz et il se décompactera correctement. Suivez les instructions du fichier README afin de vous assurer de disposer de la bonne version de Perl. Après le téléchargement de Tk*tar.gz, vous devrez le décompacter en utilisant Winzip pour Windows, ou gunzip et tar -xvf pour Unix(8). Suivez ensuite les instructions contenues dans le fichier Install. Cela ressemble beaucoup à l'installation de Perl lui-même.

Recommencez le test perl -e 'use Tk' (ou perl -e "use Tk") afin de vous assurer que tout a fonctionné correctement. Que ce soit pour Windows ou pour Unix, vérifiez que le répertoire des binaires de Perl a été ajouté à votre variable d'environnement PATH. Vous pouvez alors utiliser le script de démonstration nommé widget pour connaître les types de widget disponibles.

I-G - Création des widgets

Tous les widgets sont créés selon le même principe de base, à part quelques exceptions. Chaque widget doit avoir un widget parent qui le surveille alors qu'il est créé et en garde une trace durant son existence. Lorsque vous créez une application, une fenêtre centrale contiendra tous les autres widgets. Habituellement, cette fenêtre sera la mère de tous les widgets qu'elle contient et de toutes les autres fenêtres que vous créerez dans votre application. Vous créez un ordre des widgets pour que les communications entre les widgets fils et parents puissent avoir automatiquement lieu sans intervention de votre part dès lors que vous avez tout mis en place.

En supposant que le widget $parent existe déjà, l'instruction classique de création d'un widget Widgettype sera:

$fils = $parent->Widgettype( [-option => valeur, . . . ]);

Remarquez que les variables qui stockent les widgets sont des scalaires. En fait, ce sont des références vers des objets widgets, mais vous n'avez pas besoin de le savoir pour l'instant. Si vous ne connaissez pas bien la programmation orientée objet avec Perl, sachez seulement que l'utilisation du signe -> entre $parent et Widgettype appelle la méthode Widgettype de l'objet $parent. Elle relie $parent au fils $fils. Comme vous pouvez vous en douter, $parent devient le parent du widget créé. Un parent peut avoir plusieurs fils, mais un fils n'a qu'un seul parent. Il n'y a presque rien à faire de plus ici que d'assigner des fils à leurs parents.

Lorsque vous appelez la méthode Widgettype, il y a habituellement des paramètres de configuration à passer pour configurer le widget et les interactions avec l'application. Les paramètres de configuration apparaissent par paires : une option et sa valeur associée. Vous verrez des options comme -text, -state ou -variable. Remarquez que les options commencent toutes par un tiret et que, même avec ce tiret, ce ne sont que des chaînes de caractères représentant des étiquettes qui indiquent la valeur suivante apparaissant dans la liste. Habituellement, il n'est pas nécessaire de mettre les options entre apostrophes, car Perl est suffisamment astucieux pour reconnaître qu'il s'agit de chaînes. Toutefois, si vous utilisez le commutateur -w, Perl peut se plaindre à propos d'une option dont il pense qu'il ne s'agit pas de texte. Pour éviter cela, vous pouvez mettre systématiquement toutes vos options entre apostrophes, mais ce ne devrait pas être nécessaire. Les noms des options sont toujours tout en minuscules (sauf dans quelques cas très rares, qui seront indiqués lorsque nous les aborderons).

Les options sont données sous la forme d'une liste identique à celle-ci :

(-option => valeur, -option => valeur, -option => valeur)

Ne soyez pas troublé par le curieux signe => ; c'est juste une autre façon de dire « virgule ». En fait, vous pourriez vous contenter d'utiliser celles-ci :

(-option, valeur, -option, valeur, -option, valeur)

Cependant, il est alors plus difficile de dire quelles sont les paires option/valeur. Si l'on considère les instructions suivantes comme syntaxiquement équivalentes (chacune d'elles crée un widget bouton de 10 pixels sur 10, affichant « Sortie » et réalisant l'action de quitter l'application lorsqu'il est pressé) :

$bouton = $parent->Button( -text, "Sortie",
-command, sub { exit },
-width, 10, -height, 10 );
$bouton = $parent->Button( -text => "Sortie",
-command => sub { exit },
-width    => 10, -height => 10);

Les appariements des paramètres sont bien plus évidents dans la deuxième ligne. L'option doit être placée immédiatement avant la valeur qui lui est associée : -text est appariée avec "Sortie", -command a la valeur sub{exit}, et -width et -height ont toutes deux la valeur 10.

Bravo, nous n'avons pas encore terminé le premier chapitre que vous savez déjà lire une ligne typique de code Perl/Tk !

I-G-1 - Définitions succinctes des widgets Toplevel, MainWindow et Frame

Le chapitre suivant présente la gestion de l'espace, et plusieurs des exemples utilisent des widgets dont vous ne savez encore rien. Dans la plupart des cas, leurs rôles sont faciles à deviner, mais nous proposerons parfois une courte description.

Un widget MainWindow (« fenêtre principale », en français) est une version spéciale de widget Toplevel (« premier niveau », en français). MainWindow et Toplevel sont tous les deux des fenêtres contenant d'autres widgets. La seule différence entre eux est qu'un MainWindow est la première fenêtre créée dans votre application. Ces deux widgets sont traités plus en détail dans le chapitre chapitre 13, Widgets de premier niveau.

L'autre type de widget que vous devez connaître est le widget Frame (« cadre », en français). Un cadre est un conteneur pouvant aussi contenir d'autres widgets. Habituellement, il est invisible et ne sert qu'à arranger les widgets selon vos désirs. Le chapitre 12, Cadres, est consacré au traitement du widget Frame.

Voici à quoi ressemble la création de chacun de ces widgets

$mw    = new MainWindow;       # ou $mw    = MainWindow->new();
$top   = $mw->Toplevel();
$frame = $mw->Frame( -borderwidth => 2, -relief => "groove" );

Pour le moment, il vous suffira de vous rappeler les significations générales des widgets MainWindow, Toplevel, et Frame.

I-H - Style de programmation

Les lignes de code d'un script Perl/Tk peuvent rapidement devenir lourdes à cause de toutes les paires option/valeur utilisées pour la définition et la configuration de chaque widget. Il y a plusieurs façons de formater le code pour traiter les problèmes de lisibilité (et, dans certains cas, d' « éditabilité »). Dans la majeure partie des cas, il suffit d'ajouter des espaces ou des tabulations supplémentaires pour mettre en évidence les différentes parties du code. Lorsque vous aurez l'habitude de lire du code, il ne vous apparaîtra plus aussi mystérieux et pesant.

Un des styles d'écriture place chaque paire option/valeur sur une ligne distincte (c'est celui que je préfère et que j'utilise tout le temps)

$bouton = $parent->Button(
-text    => "mon texte",
-command => sub { exit },
-width   => 10,
-height  => 10);

Avec ce style de codage, les paires sont évidentes, ainsi que les valeurs associées à chaque option (vous pouvez aussi pousser à l'extrême et aligner chaque => pour faire de belles colonnes, selon le temps que vous mettez à presser la barre d'espace). Certains préfèrent commencer les paires option/valeur sur la ligne suivante et placer le ); final sur sa propre ligne, après la dernière paire, qui conserve la virgule pour faciliter le formatage :

$bouton = $parent->Button(
-text    => "mon texte",
-command => sub { exit },
-width   => 10,
-height  => 10
);

Cela rend le code plus facile à modifier ; on peut ajouter ou supprimer une paire option/valeur sans devoir se bagarrer avec les parenthèses, les points-virgules ou les virgules. Cela permet aussi de garder les lignes suivantes plus près du côté gauche de la page afin d'éviter d'avoir un code partant trop à droite lorsque l'on a plusieurs niveaux d'indentation.

Parfois, lorsqu'il n'y a qu'une ou deux paires option/valeur, il semble naturel de les laisser toutes sur la même ligne et de garder un peu d'espace

$bouton = $parent->Button(-text => "mon texte", -command => sub { exit });

Vous finirez sûrement par utiliser un style adapté à la façon dont vous lisez et modifiez le code. Quel que soit celui que vous choisissez, essayez simplement d'être cohérent tout au long de vos scripts au cas où quelqu'un d'autre s'occuperait de la maintenance de votre code (cela pourrait même être vous, un an ou plus après l'avoir écrit).

I-I - Affichage d'un widget

On utilise deux méthodes distinctes pour créer et pour afficher un widget, bien qu'elles soient parfois confondues dans la même ligne, ce qui les fait ressembler à une méthode unique. Dans les exemples vus jusqu'à présent, nous avons utilisé Button pour créer le bouton, mais rien ne s'affiche en utilisant cette méthode seule. Au lieu de cela, vous devez utiliser un gestionnaire d'espace pour provoquer l'affichage du widget dans son widget père ou dans un autre widget. Le gestionnaire le plus souvent utilisé est pack et il suffit d'appeler la méthode pack sur l'objet $widget de la façon suivante :

$widget->pack();

Par exemple :

$bouton->pack();

On peut passer des paramètres à la méthode pack, mais nous les aborderons au chapitre 2, Gestion de l'espace.

Il n'est pas nécessaire d'appeler pack sur une ligne distincte. La notation ->pack peut être ajoutée à la création du widget :

$parent->Button(
-text    => "Au revoir !",
-command => sub { exit })->pack();

Les autres gestionnaires d'espace disponibles sont grid et place. Tous trois se comportent différemment et le choix de l'un plutôt qu'un autre dépend de l'aspect que vous voulez donner à votre application. Encore une fois, les informations sur les gestionnaires d'espace se trouvent dans le chapitre 2.

I-J - Anatomie d'une boucle d'événement

Lorsque vous programmez une application utilisant une interface graphique plutôt qu'en mode texte, il y a beaucoup de choses différentes à considérer. Dans une application en mode texte, vous pouvez lire sur l'entrée standard (STDIN), analyser les options de la ligne de commande, lire des fichiers ou demander des informations précises à l'utilisateur. Le clavier est le seul moyen d'entrée laissé à l'utilisateur. Dans une interface utilisateur graphique, les entrées peuvent ne pas venir que de là, mais aussi de la souris et du gestionnaire de fenêtres (comme c'est le cas de l'ordre de fermeture d'une fenêtre venant d'un gestionnaire comme mwm ou MS Windows). Bien que ces entrées supplémentaires rendent vos applications plus souples, elles compliquent aussi votre travail. Pourvu qu'on lui indique ce qu'il faut faire, Perl/Tk nous aide à gérer proprement toutes ces entrées supplémentaires.

Dans une interface utilisateur graphique, les entrées sont définies par des événements. Ceux-ci sont typiquement des combinaisons d'actions sur le clavier ou la souris, effectuées simultanément ou non. Si l'utilisateur presse le bouton gauche de la souris sur le bouton « B », il s'agit d'un certain type d'événement. Presser le bouton droit de la souris sur le bouton « C » serait un autre événement. Presser sur la touche 'a' du clavier en serait encore un autre. Un autre, enfin, serait de maintenir la touche 'Ctrl' pressée tout en cliquant avec le bouton central de la souris. Vous voyez le principe…

Les événements sont traités par une boucle de gestion des événements (event loop). Cette boucle fait exactement ce que son nom implique – elle gère les événements en boucle. Elle détermine les sous-programmes à appeler selon le type de l'événement survenu. Voici l'algorithme d'une telle boucle :

Tant Que non fini Faire
obtenir des informations sur l'événement

Si l'événement est un clic gauche de la souris Alors
appeler process_left_mouse_click
Sinon Si l'événement est un clic droit de la souris Alors
appeler process_right_mouse_click
Sinon Si l'événement est une frappe au clavier Alors
appeler type-it
Sinon
gérer les événements d'affichage, de changement de taille, etc.
Fin Si
Fin Tant Que

Évidemment, il s'agit ici d'une approche simplissime d'une boucle d'événement, bien qu'elle fasse quand même apparaître l'idée principale. Cette boucle est un processus de filtrage permettant de déterminer le type de l'entrée fournie à l'application. La fonction process_left_mouse_click, par exemple, pourrait déterminer où se trouvait le pointeur lorsque le clic souris est survenu, puis appeler d'autres fonctions en utilisant cette information.

En Perl/Tk, la boucle d'événement est lancée par l'appel d'une fonction nommée MainLoop (« boucle principale »). Tout ce qui se trouve avant cet appel concerne simplement la configuration de l'interface. Tout le code placé après elle ne sera pris en compte que lors de la sortie de l'interface via l'appel $mw- >destroy(9).

Si l'on oublie d'inclure l'instruction MainLoop, le programme semblera « réfléchir » pendant un moment, puis reviendra à l'invite de l'interpréteur de commande. Aucune fenêtre, aucun bouton ou widget ne sera tracé. L'une des premières choses qui se passent après l'appel à MainLoop est le dessin de l'interface et le lancement de la boucle principale.

Avant d'aller plus loin dans la boucle d'événement et dans ce qu'elle fait (et dans ce que vous devez faire pour qu'elle fonctionne correctement), examinons un programme réel en fonctionnement, « Bonjour tout le monde » (vous attendiez autre chose ?).

I-K - Un exemple : « Bonjour tout le monde »

Tout langage de programmation passe par l'exemple « Bonjour tout le monde ». C'est un bon exemple car il montre comment faire quelque chose de simple mais utile. Dans cet exemple, le titre de notre fenêtre affiche « Bonjour tout le monde » et nous utilisons un bouton permettant de quitter l'application :

1. #!/usr/bin/perl

2. use Tk;

3. my $mw = MainWindow->new;

4. $mw->title("Bonjour tout le monde");

5. $mw->Button(-text => "Fin", -command => sub { exit; })->pack;

6. MainLoop;

Bien qu'il ne fasse que six lignes, il se passe un certain nombre de choses dans notre petit programme. La première ligne, comme tout programmeur Perl le sait, appelle Perl (uniquement sous Unix ; avec Win32, vous devez taper perl hello.pI pour l'invoquer). La deuxième indique à Perl que nous souhaitons utiliser le module Tk.

La troisième ligne

my $mw = MainWindow->new;

crée une fenêtre. Elle aura les mêmes attributs de base du gestionnaire de fenêtres que toutes vos autres fenêtres. Que ce soit dans un environnement Unix ou Windows, elle ressemblera à n'importe quelle autre fenêtre.

Son titre est modifié en utilisant la méthode title. Si l'on n'avait pas utilisé cette méthode, le texte apparaissant sur la barre de titre serait le même que le nom du fichier contenant le code. Ainsi, si notre script était stocké dans un fichier nommé bonjour, la chaîne « Bonjour » s'inscrirait dans la barre de titre de notre application (Tk met automatiquement la première lettre en majuscule). On n'est pas obligé d'utiliser la méthode title mais elle donne à l'application un aspect plus fini.

Toute chaîne passée en paramètre devient le titre de la fenêtre. Si l'on voulait que le titre soit « Hé ! Visez mon super programme! », ce serait là qu'il faudrait le préciser. Cela ressemble à l'utilisation de l'option -title au lancement de n'importe quelle application X standard. Il existe encore d'autres méthodes pour les objets MainWindow ; nous les traiterons plus tard, aux chapitres 12 et 13.

La ligne suivante crée un widget Button, configure ses propriétés de base, et le place (voir le chapitre 3, Le bouton de base, pour connaître toutes les options de configuration possibles).

Le bouton est configuré pour afficher le texte « Fin » puis appeler la fonction exit de Perl lorsqu'il est pressé. Enfin, la dernière ligne est l'appel de la fonction MainLoop. Celle-ci met en marche le gestionnaire d'événements et, à partir de là, l'application ne fera que ce qu'on lui a demandé de faire : si l'utilisateur clique sur le bouton, elle se terminera. Tout ce que l'utilisateur fait d'autre – mise en icône, modification de la taille, passer à une autre application – sera traité par le gestionnaire de fenêtres et ignoré par notre application. La figure 1.1 représente un affichage possible de la fenêtre « Bonjour tout le monde ».

Figure 1.1 : Fenêtre « Bonjour tout le monde »

I-L - Utilisation de exit ou de destroy

Dans tous les exemples de ce livre, on utilise sub { exit; } pour quitter une application Perl/Tk. Cela fonctionne bien tant que vous employez use Tk; dans le fichier contenant le sub { exit; }. Perl/Tk définit sa propre fonction exit, qui réalise certaines tâches de nettoyage et autres choses diverses et importantes pour lui. Une autre façon de quitter la partie Tk de l'application est d'appeler $mw->destroy(), qui détruit la fenêtre principale et passe au code situé après l'instruction MainLoop. Ce code ne sera pas exécuté si l'on utilise sub { exit; }. Gardez cela à l'esprit si vous comptez faire quelque chose après la fin de la partie interface.

I-M - Conventions pour les noms des widgets

Des conventions pour les noms ? Qu'est-ce que cela peut être ennuyeux ! En fait, nos programmes deviennent parfois si gros et difficiles à manier que l'on ne peut mémoriser ce que désigne cette fichue variable $bouton. Si notre programme comprenait plus de dix boutons, ce serait plutôt difficile de se rappeler auquel correspond $bouton3 sans devoir se plonger dans un paquet de lignes de code.

Je suggère simplement une convention de nommage et, si elle vous plaît, utilisez-la ! Sinon, utilisez la vôtre ou priez pour avoir une très bonne mémoire.

Pour les boutons, j'aime bien utiliser b_, btn_ ou Bouton pour qualifier le nom de la variable. Ainsi, j'appellerai mon bouton de l'exemple « Bonjour tout le monde » $b_fin, $btn_fin ou $BoutonFin.

Un widget spécial est la première fenêtre que nous créons avec la méthode MainWindow. J'utilise toujours $mw comme nom de variable pour celle-ci. Vous verrez d'autres programmes l'appeler $main ou $mainwindow.

Le tableau 1-1 contient une liste des types de widget et ce que je suggère comme noms pour chacun d'eux. Remplacez « truc » par quelque chose de plus descriptif (exit, par exemple). Si vous utilisez cette convention, vous saurez toujours avec quel type de widget vous êtes en train de travailler.

Tableau 1-1 : Conventions de noms par type de widget

Type de widget

Suggestions de noms

Exemples

Button

$b_truc (ou $btn_truc, $Bouton_Truc)

$b_fin, $b_appliquer, $BoutonNouveau

Canvas

$canevas_truc ou $Canevas-Truc

$canevas_principal, $CanevasPetit

Checkbutton

$cc_truc ou $CaseCocherTruc

$cc_majuscule, $CaseCocherMinuscule

Entry

$s_truc ou $SaisieTruc

$s_nom, $SaisieAdresse

Frame

$c_truc ou $CadreTruc

$c_principal, $c_gauche, $CadreCanevas

Label

$e_truc ou $EtiquetteTruc

$e_nom, $EtiquetteAdresse

Listbox

$bl_truc ou $BoiteListeTruc

$bl_equipes, $BoiteListeEquipes

Menu

$m_truc ou $Menu_Truc

$m_fichier, $m_editer, $MenuAide

Radiobutton

$br_truc ou $BoutonRadio-Truc

$br_bleu, $br_gris, $BoutonRadioRouge

Scale

$curseur_truc ou $CurseurTruc

$curseur_age, $CurseurRevenus

Scrollbar

$defil_truc ou $DefilTruc

$defil_x, $DefilY

Text

$t_truc (ou $txt_truc) ou $TexteTruc

$txt_fichier, $TexteCommentaire

Toplevel

$f_truc ou $FenetreTruc

$f_principale, $FenetreOuvrirFichier

J'avoue ne pas toujours respecter mes propres règles. Tout au long de ce livre, vous me verrez simplement utiliser $bouton dans les exemples de code. J'utiliserai $bouton1 et $bouton2 s'il y en a deux dans l'exemple. Dans tout ce qui dépasse quelques lignes, j'essaierai (parole de scout ?) de respecter ma convention : j'utiliserai toujours un nom indiquant le type du widget auquel je fais référence.

I-N - Utilisation de print pour les diagnostics et les recherches d'erreur

Normalement, vous lancerez vos programmes Perl/Tk en tapant le nom du programme à l'invite de l'interpréteur de commande :

% bonjour

ou

C:\> perl bonjour

Quand vous invoquez ainsi le programme, toute sortie produite par une instruction print (ou printf) est envoyée à cette fenêtre du terminal. Parfois, vous ne verrez pas l'information réellement affichée tant que vous n'aurez pas quitté l'application. C'est probablement dû au fait que vous avez oublié de placer un \n à la fin de la chaîne à afficher, qui provoque un vidage automatique du tampon de sortie. Pendant l'exécution de votre application, si vous pensez que vous ne voyez pas apparaître le résultat d'une instruction print alors que cela devrait être le cas, assurez-vous que ce print contient bien un \n.

I-O - Conception de vos fenêtres (une approche rapide)

Avant de décider quels sont les événements à gérer, cela vaut la peine de passer un peu de temps à esquisser quelques fenêtres sur papier et de décider ce qui devrait se passer (du point de vue de l'utilisateur) lorsque l'on clique sur un bouton ou que l'on choisit une option d'un menu.

L'une des choses les plus importantes à mémoriser lorsque l'on conçoit les fenêtres de ses applications est que rien ne se passe tant que l'on n'est pas entré dans la boucle d'événement. Tout ce qui précède l'appel à MainLoop ne sert qu'à préparer cette boucle.

Une interface utilisateur graphique donne souvent à une application un aspect plus achevé et plus complet que ne le ferait une interface en ligne de commande. De plus, avec un GUI, il est souvent plus facile de manipuler de nombreux types différents d'entrées utilisateur.

Voici certains points à considérer lorsque vous décidez de l'aspect de l'interface graphique :

- chaque widget doit avoir un rôle. Il doit être intuitif et donner des informations ;

- pensez à la façon dont un utilisateur se servira d'une application, et concevez-la en fonction de cela ;

- n'essayez pas de fourrer tout ce que fait l'application dans une seule fenêtre ;

- n'essayez pas de toujours tout séparer en plusieurs fenêtres. Parfois, une application est si simple qu'une seule fenêtre suffit ;

- les couleurs c'est bien, mais il existe beaucoup de personnes insensibles aux couleurs autour de vous. Si vous tenez tant à utiliser des couleurs, faites en sorte qu'elles soient personnalisables via un fichier ou par l'application elle-même ;

- certains widgets font mieux leur travail que d'autres. Utilisez le widget qui convient le mieux à ce que vous voulez faire.

C'est tout pour la lecture. Maintenant, préparez-vous à apprendre les ficelles.

II - Gestion de l'espace

Les widgets sont placés à l'écran par un gestionnaire d'espace. Celui-ci détermine leur position et taille sur la fenêtre d'affichage. Il existe plusieurs gestionnaires d'espace en Perl/Tk : pack, place et grid.

Ces trois gestionnaires sont appelés comme des méthodes sur le widget, mais ils ont tous leurs propres méthodologies et arguments pour modifier l'emplacement et la façon, dont les widgets sont placés à l'écran

$widget1->pack(); $widget2->place(); $widget3->grid();

Lorsqu'on organise des widgets, il est souvent nécessaire de distinguer des groupes de widgets pour obtenir un look and feel particulier. Quand on utilise pack, par exemple, il est difficile d'empiler horizontalement et verticalement les widgets sans les grouper d'une façon ou d'une autre. On rassemble les widgets en utilisant un widget cadre dans une fenêtre ou en utilisant une autre fenêtre (un widget de premier niveau).

On crée notre première fenêtre en appelant MainWindow. Une MainWindow est une forme spéciale de widget de premier niveau. Pour plus de détails sur la création et la configuration des widgets cadre et de premier niveau, voir le chapitre 12, Cadres, et le chapitre 13, Widgets de premier niveau.

Par suite des différences existant entre les trois gestionnaires d'espace, il est difficile (pas complètement impossible, mais vraiment pas recommandé) d'utiliser plus d'un gestionnaire dans la même zone. Dans notre $mw, on peut afficher plusieurs types de widgets mais, si l'on commence à utiliser pack, il faut continuer à le faire pour tous les widgets directement contenus dans $mw. On ne pourrait utiliser grid au milieu de tout cela. Une fenêtre pouvant contenir un cadre qui, à son tour, contient d'autres widgets, nous utilisons pack pour placer le cadre dans la fenêtre principale, puis nous pouvons utiliser grid pour gérer les widgets dans le cadre.

Figure 2.1 : Cadre dans une fenêtre, utilisant un gestionnaire d'espace différent

Bien que les différents gestionnaires d'espace aient chacun leurs forces et faiblesses, pack est le plus utilisé et c'est donc lui que je traiterai en premier et de façon détaillée. grid était en cours de développement pendant l'écriture de ce livre, et a beaucoup évolué avec la sortie de Tk8.0 et son portage en Perl. Le gestionnaire place est le plus difficile à utiliser car vous devez connaître les coordonnées exactes de chaque widget.

II-A - Pack

Vous vous rappelez, lorsque vous étiez enfant et que vous deviez assembler ces puzzles en bois ? Ils arboraient souvent de jolies images d'animaux et chaque pièce ne pouvait se trouver que dans un seul emplacement. Les chevauchements entre pièces n'étaient pas possibles.

Avec le gestionnaire d'espace pack, nos fenêtres ressemblent à ces puzzles en bois car les widgets ne peuvent pas non plus se chevaucher ou se recouvrir (partiellement ou complètement). Voyez la figure 2.2.

Figure 2.2 : Erreur de chevauchement

Si un bouton est placé à un certain endroit de la fenêtre, le bouton suivant (ou tout autre widget) devra se placer à côté du bouton déjà présent. Heureusement, nos fenêtres ne s'occuperont que de formes rectangulaires, et pas de pièces de puzzle aux formes exotiques.

L'ordre selon lequel on place ses widgets est très important car il affecte directement ce que l'on verra à l'écran. Chaque widget cadre, ou fenêtre, gère une liste des objets qui s'affichent chez lui. Cette liste est ordonnée : si le widget A précède le widget B, A sera prédominant. Cela deviendra évident lorsque nous passerons plusieurs exemples en revue. Nous obtiendrons souvent un aspect très différent pour notre fenêtre, simplement en plaçant les widgets dans un autre ordre.

Si vous ne vous souciez pas de ce à quoi peut ressembler la fenêtre, ni de comment sont placés les widgets, vous pouvez utiliser pack sans argument et ne pas lire le reste de ce chapitre. Voici à nouveau comment faire :

$widget->pack();

Pour donner un plus bel aspect à vos fenêtres et les rendre plus faciles à utiliser (et plus conviviales), vous pouvez passer des paramètres à la méthode pack afin de modifier la façon dont les widgets et la fenêtre apparaîtront. Comme toujours en Perl/Tk, les arguments sont organisés par paires. Une utilisation plus sophistiquée serait donc :

$widget->pack( [option => valeur, ...] );

Voici un exemple de code créant une fenêtre en n'utilisant aucune des options de pack. La figure 2.3 présente le résultat obtenu (je sais que je n'ai pas encore traité tous les widgets utilisés dans cet exemple mais patientez, c'est très simple).

Figure 2.3 : Fenêtre avec des widgets gérés par pack

#!/usr/bin/perl -w
use Tk;

my $mw = MainWindow->new;
$mw->title("Fenêtre moche");
$mw->Label( -text => "Voici un exemple de fenêtre moche\nobtenue sans passer d'option à pack" )->pack;

$mw->Checkbutton( -text => "J'aime ça !" )->pack;
$mw->Checkbutton( -text => "J'ai horreur de ça !" )->pack;
$mw->Checkbutton( -text => "Je m'en moque" )->pack;
$mw->Button(
-text    => "Fin",
-command => sub {
exit;
}
)->pack;
MainLoop;

Vous pouvez modifier le code précédent et ajouter quelques options aux appels à pack afin de donner un plus bel aspect à votre fenêtre (examiner la figure 2.4) :

#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new;
$mw->title("Belle fenêtre");
$mw->Label( -text => "Exemple de fenêtre moins disposée au hasard et obtenue en passant des options à pack" )->pack;
$mw->Button(
-text    => "Fin",
-command => sub {
exit;
}
)->pack(
-side   => 'bottom',
-expand => 1,
-fill   => 'x'
);
$mw->Checkbutton( -text => "J'aime ça !" )->pack(
-side   => 'left',
-expand => 1
);
$mw->Checkbutton( -text => "J'ai horreur de ça !" )->pack( -side => 'left', -expand => 1 );
$mw->Checkbutton( -text => "Je m'en moque" )->pack(
-side   => 'left',
-expand => 1
);

MainLoop;

L'utilisation de pack vous permet de contrôler :

- la position dans la fenêtre par rapport aux bords du cadre ou de celle-ci ;

- la taille des widgets, relativement aux autres widgets ou de façon absolue ; l'espacement entre les widgets ;

- la position dans la liste des widgets de la fenêtre ou du cadre.

Figure 2.4 : Fenêtre avec des widgets gérés par pack et certaines de ses options

Les options et valeurs par défaut sont énumérées et étudiées dans la section suivante.

II-A-1 - Options de pack

La liste suivante énumère toutes les options disponibles lors d'un appel à pack. Les valeurs par défaut sont mises en gras (ce qui signifie que, si vous n'utilisez pas cette option, vous obtiendrez les effets de cette valeur pour cette option).

-side => 'left' | 'right' | 'top' | 'bottom'

1. Place le widget contre le bord spécifié de la fenêtre ou du cadre.

-fill => 'none' | 'x' | 'y' | 'both'

1. Force le widget à remplir dans la direction spécifiée le rectangle qui lui a été alloué.

-expand => 1 | 0

1. Force le rectangle alloué au widget à remplir l'espace disponible restant dans la fenêtre ou le cadre.

-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'

1. Ancre le widget dans le rectangle qui lui a été alloué.

-after => $autrewidget

1. Met $widget après $autrewidget dans l'ordre de placement.

-before => $autrewidget

1. Met $widget avant $autrewidget dans l'ordre de placement.

-in => $autrefenetre

1. Place $widget dans $autrefenetre au lieu de le mettre dans le parent de $widget, ce qui est le cas par défaut.

-ipadx => montant

1. Augmente la dimension horizontale du widget par montant x 2.

-ipady => montant

1. Augmente la dimension verticale du widget par montant x 2.

-padx => montant

1. Place des espaces de remplissage à gauche et à droite du widget.

-pady => montant

1. Place des espaces de remplissage au-dessus et en dessous du widget.

II-A-2 - Positionnement des widgets

Chaque fenêtre (ou cadre) a quatre côtés haut, bas, gauche et droite. Le placeur utilise ces bords comme points de référence pour les widgets. Par défaut pack place les widgets contre le haut de la fenêtre ou du cadre.

Vous pouvez choisir le bord contre lequel sera placé un widget en utilisant l'option -side :

-side => 'left' | 'right' | 'top' | 'bottom'

Si, par exemple, vous souhaitez placer votre bouton contre le bord gauche de la fenêtre, vous pouvez préciser -side => 'left'.

En utilisant notre exemple « Bonjour tout le monde » comme base, voyons ce qui se passe lorsque l'on place notre bouton contre les différents bords (figure 2.5).

Figure 2.5 : top (par défaut), bottom, left et right

La seule chose qui changera sera la partie ->pack de la ligne de création du bouton. Nous modifierons aussi la chaîne « Bonjour tout le monde » dans l'instruction $mw->title afin de montrer l'option de pack utilisée.

La forme employée est :

$mw->Button(
-text    => 'Fin',
-command => sub {
exit;
}
)->pack( -side => 'POSITIONNEMENT' );

où POSITIONNEMENT peut être top, bottom, left ou right.

Les fenêtres montrées ici ont été légèrement agrandies pour mettre en évidence les différences résultant de l'utilisation des diverses valeurs de -side. Normalement, la fenêtre sera seulement de la taille requise pour afficher le bouton. Lorsque vous décidez de la façon de placer les widgets, il est toujours préférable de voir ce qui se passe lorsque vous agrandissez ou réduisez la fenêtre. Assurez-vous que le comportement obtenu est celui recherché.

Pour l'instant pack semble assez simple, mais qu'en est-il si vous souhaitez placez plus d'un bouton dans votre application ? Que se passe-t-il si l'on ajoute simplement d'autres boutons ?

$mw->Button( -text => 'Fin 1', -command => sub { exit; } )->pack;
$mw->Button( -text => 'Fin 2', -command => sub { exit; } )->pack;
$mw->Button( -text => 'Fin 3', -command => sub { exit; } )->pack;
$mw->Button( -text => 'Fin 4', -command => sub { exit; } )->pack;

Comme la valeur par défaut de -side est top, on s'attend à ce qu'ils soient tous entassés contre le haut de la fenêtre ; n'est-ce pas ? C'est un peu ça. Le placeur alloue de l'espace pour chaque widget, puis manipule le widget dans cet espace, et cet espace dans la fenêtre.

La figure 2.6 montre à quoi ressemble la fenêtre avec les quatre boutons « Fin » ; la section suivante explique pourquoi ils sont placés ainsi.

Figure 2.6 : Quatre boutons places avec les valeurs par défaut

II-A-3 - Rectangles alloués

Lorsqu'il doit placer un widget, le placeur recherche d'abord le bord (haut, bas, droite, gauche) à utiliser. Puis, il réserve une zone rectangulaire invisible sur toute la longueur de ce bord pour ne l'utiliser qu'avec ce widget.

Dans la figure 2.7, le rectangle en trait continu représente notre fenêtre (ou cadre) vide et le rectangle en pointillé est la zone rectangulaire que le placeur réserve pour le premier bouton. En réalité, il occupe toute la largeur ou la hauteur de la fenêtre, mais pour mieux le mettre en évidence, nous l'avons un peu indenté.

Figure 2.7 : Zones rectangulaires avec -side => 'top' et -side => 'left'

Les dimensions de la boîte en pointillé, que nous appellerons le rectangle alloué, sont calculées d'après la taille du widget. Pour les bords haut et bas, le rectangle alloué est aussi large que la fenêtre et aussi haut que le widget qui y sera placé, mais pas plus. Pour les bords droit et gauche, ce rectangle est aussi haut que la fenêtre, et aussi large qu'il est nécessaire pour convenir au widget, mais pas plus.

Jusqu'à maintenant, nos exemples ont utilisé des boutons dans lesquels le texte du bouton détermine sa largeur. Si nous créons un bouton contenant le texte « Fin », et un autre avec le texte « Fin, Terminé, Stop », ce second bouton sera beaucoup plus large que le premier. Lorsque ces deux widgets seront placés contre le bord gauche ou droit de la fenêtre, le second bouton aura un rectangle alloué plus large que celui du premier. Si nous les plaçons tous les deux contre les bords haut et bas, les rectangles alloués seront de la même hauteur et de la même largeur car cette dernière est déterminée par la fenêtre, pas par le widget.

Lorsque la taille du rectangle alloué a été déterminée, le widget est placé à l'intérieur selon les options utilisées et/ou les valeurs par défaut de ces options. Je passerai plus tard en revue ces options et la façon dont elles affectent le rectangle alloué.

Une fois que le premier widget a été placé dans la fenêtre, la taille de la zone disponible pour les rectangles suivants est plus petite car le premier rectangle alloué a utilisé une partie de cet espace (voir la figure 2.8).

Figure 2.8 : Deuxième rectangle alloué avec le bord 'top' par défaut

Quand plusieurs boutons sont placés contre les différents bords de la même fenêtre, les résultats dépendent de l'ordre utilisé.

Nous commencerons par placer un bouton en haut, un autre en bas, et les autres à droite et à gauche :

$mw->Button( -text => "HAUT",   -command => sub { exit } )->pack( -side => 'top' );
$mw->Button( -text => "BAS",    -command => sub { exit } )->pack( -side => 'bottom' );
$mw->Button( -text => "DROIT",  -command => sub { exit } )->pack( -side => 'right' );
$mw->Button( -text => "GAUCHE", -command => sub { exit } )->pack( -side => 'left' );

Les rectangles alloués pour cette fenêtre ressembleront à ceux du schéma de la figure 2.9.

Figue 2.9 : Rectangles alloués pour quatre boutons

Figure 2.10 : Quatre boutons placés autour des bords, taille normale et après agrandissement

II-A-4 - Remplissage du rectangle alloué

Normalement, le widget est laissé à sa taille par défaut, qui est habituellement plus petite que le rectangle qui lui a été alloué. Si l'option -fill est utilisée, le widget changera lui-même de taille pour remplir le rectangle alloué en fonction de la valeur indiquée. Les valeurs possibles sont :

1. -fill => 'none' | 'x' | 'y' | 'both'

L'utilisation de la valeur 'x' modifiera la taille du widget selon l'axe des x. De même, 'y' fera que le widget s'agrandira selon l'axe des y. -f1ll => 'both' est un bon moyen de voir exactement la taille et l'emplacement donné au rectangle car 'both' modifie la taille du widget dans les directions x et y. Reprenons notre exemple des quatre boutons, en précisant -fill => 'both' :

$mw->Button( -text => "HAUT",   -command => sub { exit } )->pack( -side => 'top',    -fi11 => 'both' );
$mw->Button( -text => "BAS",    -command => sub { exit } )->pack( -side => 'bottom', -fi11 => 'both' );
$mw->Button( -text => "DROIT",  -command => sub { exit } )->pack( -side => 'right',  -fill => 'both' );
$mw->Button( -text => "GAUCHE", -command => sub { exit } )->pack( -side => 'left',   -fi11 => 'both' );

La figure 2.11 montre la fenêtre que l'on obtient.

Figure 2.12 : Quatre boutons placés sur chaque bord dans un ordre différent avec -fill => 'both

Si nous inversons le bouton que nous créons en premier, nous obtenons un résultat différent. La fenêtre de la figure 2.12 a été créée en plaçant les widgets dans l'ordre droit, gauche, haut et bas.

Figure 2.12 : Quatre boutons placés sur chaque bord dans un ordre différent avec -fill => 'both'

La figure 2.13 est le résultat d'encore un autre ordonnancement, qui montre bien que les rectangles alloués changent de taille selon ce qui est placé en premier.

Une utilisation classique de -fill est avec les widgets utilisant des barres de défilement : les boîtes de liste, les canevas et les textes. Habituellement, les barres de défilement se trouvent sur le côté de la fenêtre et l'on souhaite que la boîte de liste remplisse la zone restante. Voyez le chapitre 6, Barres de défilement, et le chapitre 7, Le widget boîte de liste, pour plus d'informations.

Figure 2.13  Quatre boutons placés dans l'ordre haut, droit, bas et gauche

II-A-5 - Agrandissement du rectangle alloué

L'option -expand manipule le rectangle alloué, non le widget qu'il contient. La valeur associée à -expand est un booléen.

1. -expand => 1 | 0

Avec une valeur vraie, le rectangle alloué s'agrandira dans tout l'espace disponible restant dans la fenêtre selon le bord sur lequel le widget a été placé.

Les widgets placés sur les bords droit ou gauche s'agrandiront selon l'axe horizontal. Ceux placés sur les bords haut ou bas s'agrandiront dans la direction verticale. Si plus d'un widget est placé avec l'option -expand positionnée, l'espace supplémentaire dans la fenêtre est divisé à parts égales entre tous les rectangles alloués qui le veulent.

Dans les figures 2.10 ou 2.11, nous avons constaté qu'il restait un peu de place au centre de la fenêtre et qu'il n'était occupé par aucun widget. Si nous modifions le code et que nous ajoutons -expand => 1 à la liste des options de pack pour chaque bouton, le résultat sera celui affiché par la figure 2.14.

Figure 2.14 : Quatre boutons utilisant les options -expand => 1 et -fill => 'both'

Remarquez que la figure 2.14 laisse l'option -fill => 'both' dans le code. Si nous omettons l'option -fill, les boutons garderont leur taille originale, mais les rectangles alloués (invisibles) s'étendront sur l'espace restant dans la fenêtre (voir la figure 2.15).

Figure 2.15 : Quatre boutons utilisant les options -expand => 1 et -fill => 'none'

Dans la figure 2.15, les boutons sont centrés dans leurs rectangles alloués à cause de la valeur par défaut de l'option -anchor, qui est 'center'.

II-A-6 - Ancrage d'un widget dans son rectangle alloué

L'option -anchor manipule le widget à l'intérieur du rectangle alloué en l'ancrant à l'endroit indiqué par la valeur de l'option. Elle utilise les points cardinaux comme références.

1. -anchor => 'e' | 'w' | 'n' | 's' | 'ne' | 'nw' | 'se' | 'sw' | 'center'

La figure 2.16 montre ces emplacements dans un exemple de rectangle alloué.

Figure 2.16 : Rectangle alloué avec indication des points d'ancrage-expand => 1

La valeur par défaut de -anchor est 'center', qui place le widget au centre du rectangle qui lui a été alloué. À moins que l'option -expand n'ait une valeur vraie, cela ne changera pas grand-chose à quoi que ce soit dans la fenêtre. Ainsi que nous l'avons vu dans la figure 2.17, qui montre l'effet de l'utilisation de l'option -expand => 1, il est évident que le widget colle à cette position centrale lorsque la fenêtre change de taille.

La figure 2.18 montre ce que donnent -anchor => 'e' et -anchor => 'w' si toutes les autres valeurs par défaut sont utilisées pour placer le widget.

Rappelez-vous que le rectangle alloué est créé en fonction du bord contre lequel est placé le widget, certaines combinaisons sembleront donc n'avoir aucun effet. Par exemple :

$mw->Button( -text => "Fin", -command => sub { exit } )

Figure 2.17 : Comportement par défaut de -anchor avec -expand valant 1

Figure 2.18 : Exemples de -anchor => 'e' et -anchor => 'w'

->pack(-side => 'top', -anchor => 'n');

Ce bout de code laissera le widget exactement au même endroit qu'il aurait été si l'option -anchor n'avait pas été précisée car le rectangle alloué ne change pas du tout de taille. Si l'option -expand est aussi utilisée, le widget collera au côté nord de la fenêtre lorsque la taille de celle-ci sera modifiée. Si -anchor => 's' avait été utilisée, le widget aurait collé au côté sud de la fenêtre lors du changement de taille de celle-ci.

L'option -anchor est le plus souvent utilisée pour aligner plusieurs widgets d'un coup. Les figures 2.19 et 2.20 montrent deux exemples classiques.

Figure 2.19 : Fenêtre avec trois boutons placés avec -side => 'top', -anchor => 'w'

Figure 2.20 : Fenêtre avec trois boutons placés avec -side => 'left', -anchor => 'n'

Parfois, lorsque -side et -anchor sont utilisées ensemble, les résultats ne semblent pas être ceux attendus. Gardez toujours à l'esprit ce rectangle alloué invisible et la façon dont il affecte ce que vous voyez à l'écran.

II-A-7 - Ordonnancement des widgets dans une fenêtre

Chaque fenêtre contenant des widgets garde la trace de ceux-ci dans une liste ordonnée. L'ordre de cette liste est normalement déterminé par celui dans lequel les widgets ont été placés. Le dernier placé est le dernier de la liste. En utilisant l'option -after, on peut modifier l'ordre par défaut en spécifiant après quel widget sera placé notre nouveau widget. À l'opposé, si l'on utilise l'option -before, on peut placer le nouveau widget avant un autre déjà placé :

-after  => $autrewidget
-before => $autrewidget

Créons, par exemple, quatre boutons ($widget1, $widget2, $widget3, $widget4) et

plaçons-en trois pour commencer. L'appel de pack pour $widget4 pourrait alors être :

$widget4->pack(-after => $widget1);

La figure 2.21 montre deux fenêtres : l'une avant que $widget4 ne soit placé, l'autre après.

Figure 2.21 : À gauche : trois boutons placés dans l'ordre. À droite : « Fin 4 » placé avec -after => $widget1

Pour placer $widget4 devant $widget1, on utilisera l'instruction :

$widget4->pack(-before => $widget1);

Nous en constatons les effets sur la figure 2.22.

Figure 2.22 : « Fin 4 » placé avec -before => $widget1

II-A-8 - Compléter la taille d'un widget avec des espaces de remplissage

Le dernier moyen de forcer pack à modifier la taille du widget est d'utiliser les options de remplissage. Le premier ensemble de ces options affecte le widget lui-même en ajoutant quelque chose à sa taille par défaut. Des montants différents ou identiques peuvent être ajoutés dans les directions x et y. Pour préciser le remplissage voulu dans la direction x, on utilise l'option -ipadx :

-ipadx => montant

On spécifie le remplissage dans la direction y de cette façon :

-ipady => montant

montant est un nombre représentant une distance d'écran. Je traiterai de la définition d'une telle distance dans la section suivante.

Les options -ipadx et -ipady changent toutes les deux la taille du widget avant que le rectangle alloué ne soit calculé. -ipadx ajoute le montant spécifié à gauche et à droite du widget. La largeur totale du widget augmentera donc de (montant x 2). La figure 2.23 montre les effets des options -ipadx et -ipady sur un bouton.

Figure 2.23 : Le bouton « Fin 1 » a été créé avec les options -ipadx => 10, -ipady => 10

L'autre type de remplissage est effectué entre le bord du widget et celui du rectangle alloué. Il est réalisé avec les options -padx et -pady :

-padx => montant
-pady => montant

L'utilisation de -padx et -pady n'affecte pas la taille du widget mais celle du rectangle alloué. Ces options agissent comme un tampon placé autour du widget, l'empêchant de toucher les autres widgets. La figure 2.24 montre les effets de leur utilisation.

Figure 2.24 : Le bouton « Fin I » a été créé avec -padx => 10 et -pady => 10

Un bon moyen de se rappeler la différence entre -ipadx/y et -padx/y est que le « i » signifie « intérieur du widget » ou « remplissage interne ».

II-A-9 - Distances d'écran

Vous verrez souvent des options, comme -ipadx et -ipady, nécessitant des valeurs indiquées en unités d'écran (ce que l'on appelle une distance d'écran). Vérifiez toujours la valeur que demande réellement l'option. Une unité d'écran est un nombre suivi de l'unité utilisée (par défaut : le pixel).

Tableau 2-1 : Unités d'écran

Désignation

Signification

Exemples

(aucune)

Pixels (défaut)

20, 30, "20", "40"

c

Centimètres

'3c', '4c', "3c"

i

Pouces (Inches)

'2i', "3i"

m

Millimètres

'4m', "4m"

p

Points imprimante (1/72 pouce)

"72p", '40p'

Pour utiliser ces désignations, il faut mettre la valeur entre apostrophes (simples ou doubles). Voici quelques exemples :

$bouton->pack( -ipadx => 20 );       # 20 pixels
$bouton->pack( -ipadx => '20' );     # encore 20 pixels
$bouton->pack( -ipadx => "1i" );     # 1 pouce
$bouton->pack( -ipadx => '1m' );     # 1 millimètre
$bouton->pack( -ipadx => 1 );        # 1 pixel
$bouton->pack( -ipadx => "20p" );    # 20 points imprimante

Rappelez-vous que la désignation "p" ne signifie ni « pixel », ni « pouce », mais « point imprimante ». Je vous recommande de toujours utiliser les pixels comme unité de mesure car des écrans différents ont des résolutions différentes ; un écran donné peut afficher un vrai pouce et un autre peut afficher autre chose.

II-A-10 - Affichage dans un autre parent

Par défaut, lorsqu’un widget est placé, il l'est dans la région qui l'a créé, mais il est parfois nécessaire de l'afficher dans une autre région. Pour ce faire, on utilise l'option -in :

-in => $autrefenêtre

Celle-ci place le nouveau widget à la fin de la liste de placement d'$autrefenêtre et l'affiche en fonction de cela. Toutes les autres options précisées lors de l'appel à pack s'appliquent quand même.

II-A-11 - Méthodes associées à pack

Quelques méthodes sont utilisées en conjonction avec le gestionnaire d'espace pack. Elles permettent au programmeur d'obtenir des informations sur le widget placé, ou sur le widget parent contenant d'autres widgets.

II-A-12 - Suppression d'un widget

Pour supprimer un widget d'une fenêtre ou d'un cadre, on utilise la méthode packForget :

$widget->packForget();

PackForget fait comme si le widget disparaissait. Il n'est pas détruit, mais n'est plus géré par pack. Il est supprimé de la liste de placement et, s'il doit être replacé plus tard, il sera mis à la fin de cette liste.

II-A-13 - Obtention d'informations sur les emplacements

Pour récupérer la liste de toutes les informations sur la configuration de l'emplacement d'un widget placé par pack, on utilise packInfo :

@liste = $widget->packInfo();

Cette liste est une suite de paires option/valeur. La première paire est -in avec la fenêtre contenant le widget (habituellement le parent). Voici un exemple d'informations retournées par packInfoo :

-in MainWindow=HASH(Ox8l8dcf4) -anchor n -expand O -fill none
-ipadx 0 -ipady 0 -padx 10 -pady 10 -side left

On peut en déduire que nous avons placé notre $widget dans la fenêtre principale plutôt que dans un cadre. Comme la liste est appariée, nous pourrions aisément stocker le résultat de packlnfo dans un hachage et faire référence aux différentes options en utilisant une clé de ce hachage :

%infos_placement = $widget->packInfo;
print "Bord utilisé    : ", $infos_placement{-side}, "\n";

II-A-14 - Désactivation et activation du changement de taille automatique

Lorsque l'on place un widget dans une fenêtre, celle-ci (ou le cadre) modifiera elle-même sa taille pour qu'elle convienne au widget. Si vous placez dynamiquement des widgets dans votre fenêtre au cours de l'exécution du programme, la fenêtre semblera sauter de taille en taille. Vous pouvez désactiver ce comportement en utilisant la méthode packPropagate sur le cadre ou le widget de premier niveau :

$widget->packPropagate(0);

Si on lui passe la valeur 0 ou 'off', packPropagate modifie le comportement du widget pour qu'il ne modifie pas sa taille afin de tenir compte de widgets qu'il contient. Lorsqu'une valeur fausse est passée à packPropagate avant que les widgets ne soient placés, le changement automatique de taille n'est pas assuré ; vous ne pouvez donc pas voir les widgets placés dans le parent tant que vous n'avez pas modifié explicitement la taille de ce dernier. Si vous appelez packPropagate après que les widgets ont été placés, votre widget ignorera toutes les modifications de taille venant de ses fils.

II-A-15 - Énumération des widgets

On peut déterminer quels sont les widgets contenus dans notre cadre ou fenêtre avec la méthode packSlaves :

@liste = $widgetparent->packSlaves();

packSlaves renvoie une liste ordonnée de tous les widgets ayant été placés dans $widgetparent. Si ce dernier ne contient aucun widget placé par pack, cette méthode renvoie une chaîne (ou une liste) vide.

La liste renvoyée par packSlaves ressemble à celle-ci :

Tk::Button=HASH(0x81b2970) Tk::Button=HASH(0x8116ccc)
Tk::Button=HASH(0x8lbcdd4)

Chaque élément est une référence à un widget placé par pack et peut être utilisé pour configurer celui-ci. On peut, par exemple, augmenter la taille de chaque widget de 20 pixels dans les deux directions en itérant sur chacun des éléments et en les « plaçant » avec cette nouvelle information. En reprenant notre exemple de « Belle fenêtre » de la figure 2.4, on peut ajouter un bouton qui appellerait packSlaves lorsqu'il est pressé :

#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new;
$mw->title("Belle fenêtre");
$mw->Label( -text => "Exemple de fenêtre moins disposée au hasard et obtenue en passant des options à pack" )->pack;
$mw->Button(
-text    => "Agrandir",
-command => \&replace_fils
)->pack(
-side   => 'bottom',
-anchor => 'center'
);
$mw->Button(
-text    => "Fin",
-command => sub { exit; }
)->pack(
-side   => 'bottom',
-expand => 1,
-fill   => 'x'
);
$mw->Checkbutton( -text => "J'aime ça !" )->pack(
-side   => 'left',
-expand => 1
);
$mw->Checkbutton( -text => "J'ai horreur de ça !" )->pack(
-side   => 'left',
-expand => 1
);
$mw->Checkbutton( -text => "Je m'en moque" )->pack(
-side   => 'left',
-expand => 1
);

MainLoop;

sub replace_fils {
my @fils = $mw->packSlaves;
foreach (@fils) {
$_->pack( -ipadx => 20, -ipady => 20 );
}
}

La figure 2.25 montre la fenêtre qui en résulte.

Voyons ce qui se passe lorsque l'on presse le bouton « Agrandir ». Comme le montre la figure 2.26, tous les widgets sont maintenant replacés avec les paramètres additionnels -ipadx => 20, -ipady => 20. Ces nouvelles options s'ajoutent à celles déjà utilisées lors du précédent placement des widgets. Si une option est répétée, la dernière prend le pas sur les précédentes.

La fenêtre devient subitement énorme ! Des pressions supplémentaires sur le bouton « Agrandir » n'auraient aucun effet sur la fenêtre, car tous les widgets ont déjà -ipadx

Figure 2.25 : Fenêtre avant d'avoir pressé le bouton « Agrandir »

Figure 2.26 : Fenêtre après avoir pressé le bouton « Agrandir »

et -ipady à 20. Si l'on voulait toujours ajouter 20 aux valeurs d'-ipadx et -ipady, il faudrait récupérer les valeurs actuelles et leur ajouter 20. Voici le code réalisant cela :

sub replace_fils {
my @fils = $mw->packSlaves;
foreach (@fils) {
%infos_placement = $_->packInfo();
$_->pack(
-ipadx => 20 + $infos_placement{"-ipadx"},
-ipady => 20 + $infos_placement{"-ipady"}
);
}
}

Nous utilisons packInfo pour récupérer la configuration courante, et nous ajoutons 20 aux valeurs concernées.

II-B - Grid

Le gestionnaire d'espace grid divise la fenêtre en une grille composée de lignes et colonnes débutant à 0,0 dans le coin supérieur gauche La figure 2.27 montre un exemple de grille.

Figure 2.27 : Diagramme montrant une fenêtre divisée en grille

Plutôt que d'utiliser les bords de la fenêtre comme points de référence, grid divise l'écran en lignes et colonnes Cela ressemble beaucoup à un tableur, n'est-ce pas ? Chaque widget est affecte à une cellule de la grille en utilisant les options de grid.

La méthode grid prend une liste de widgets au lieu de n'agir que sur un seul à la fois(10). En voici l'utilisation générique :

$widget1->grid( [ $widget2, ... , ][option => valeur ] );

Voici un exemple réel :

$widget1->grid($widget2, $widget3);

Au lieu d'utiliser trois appels distincts, on peut n'utiliser qu'un seul appel à grid pour afficher les trois widgets. Vous pouvez aussi appeler indépendamment grid sur chaque widget, exactement comme avec pack. Chaque appel à grid créera une nouvelle ligne dans la fenêtre, ce qui fait que, dans notre exemple, $widget1, $widget2 et $widget3 seront sur la première ligne. Un autre appel à grid aurait créé une seconde ligne ; c'est ce qui se passe lorsqu'on ne précise pas d'autres options à l'appel de grid.

Pour contrôler encore mieux vous pouvez préciser les options explicites -row et -column pour chaque widget de la fenêtre. Ces options seront traitées plus tard.

Lorsqu'aucune option supplémentaire n'est spécifiée, on suppose que :

- le premier widget de la ligne ($widget1 dans notre exemple) appelle la méthode grid ;

- tous les autres widgets de cette ligne seront spécifiés en paramètres de grid ;

- chaque appel supplémentaire de grid ajoutera une nouvelle ligne d'affichage ;

- des caractères spéciaux peuvent être utilisés pour changer les valeurs des options -columnspan et -rowspan du widget, sans utiliser ces options explicitement.

Quelques exemples nous aideront à comprendre. Chaque appel à grid crée une nouvelle ligne, nous obtiendrons donc deux lignes dans l'exemple suivant :

# Crée deux lignes, contenant chacune quatre widgets
$widget1->grid($widget2, $widget3, $widget4);
$widget5->grid($widget6, $widget7, $widget8);

Dans cet exemple, les quatre lignes ne contiennent chacune qu'un widget :

# Crée quatre lignes, chacune contenant un seul widget
$widgetl->grid();
$widget2->grid();
$widget3->grid();
$widget4->grid();

Nous pouvons aussi créer à mesure les widgets :

$mw->Button( -text => 'Bouton 1', -command => \&fonctionl )->grid(
$mw->Button( -text => 'Bouton 2', -command => \&fonction2 ),
$mw->Button( -text => 'Bouton 3', -command => \&fonction3 ),
$mw->Button( -text => 'Bouton 4', -command => \&fonction4 )
);

Faites particulièrement attention au fait que les second, troisième et quatrième appels à Button se trouvent dans l'appel à grid. Les quatre boutons seront tous placés dans la première ligne. Si nous exécutions à nouveau la même instruction, les nouveaux widgets seraient placés dans la ligne suivante.

II-B-1 - Caractères spéciaux

Différents caractères spéciaux peuvent servir à modifier la façon dont les widgets sont placés avec grid dans la fenêtre. Chacun d'eux sert de marqueur de place précisant que faire de cette position dans la grille :

"-" (le signe moins)

1. Indique à grid que le widget spécifié juste avant ce signe dans la liste devra aussi s'étendre sur cette colonne. Pour s'étendre sur plus d'une colonne, mettez un « - » à chaque position étendant le widget. Un « - » ne peut suivre un« ^ » ou un « x ».

"x"

1. Laisse un espace vide à l'endroit où un widget aurait normalement dû être placé.

"^"

1. Un widget de la ligne x s'étendra sur les lignes x et x + 1 lorsque ce caractère est situé dans la liste des paramètres de grid pour la ligne s + 1 dans cette position ligne/colonne. Le nombre de caractères "^" doit correspondre au nombre de colonnes sur lesquelles s'étend le widget dans la ligne x. Cela ressemble au fonctionnement de "-", mais en vertical(11).

Les sections suivantes contiennent des exemples illustrant les effets de ces caractères spéciaux.

II-B-2 - Traverser des colonnes

Le code suivant crée trois rangées de boutons. Les deux premières sont normales et, dans la troisième, le second bouton s'étend sur trois colonnes. Chaque caractère "-" incrémente le nombre de colonnes utilisées par le bouton qui est par défaut, de 1. Par conséquent, la colonne de départ et deux tirets ("-", "-") indiquent qu'il y a trois colonnes à traverser. L'option -sticky est nécessaire pour que le bouton colle aux bords des cellules qu'il traverse. Si cette option avait été omise, le bouton aurait été centré par rapport aux trois cellules traversées.

$mw->Button(
-text    => "Bouton 1",
-command => sub {
exit;
}
)->grid(
$mw->Button( -text => "Bouton 2", -command => sub { exit } ),
$mw->Button( -text => "Bouton 3", -command => sub { exit } ),
$mw->Button( -text => "Bouton 4", -command => sub { exit } )
);
$mw->Button(
-text    => "Bouton 5",
-command => sub {
exit;
}
)->grid(
$mw .->Button( -text => "Bouton 6", -command => sub { exit } ),
$mw->Button( -text => "Bouton 7", -command => sub { exit } ),
$mw->Button( -text => "Bouton 8", -command => sub { exit } )
);
$mw->Button(
-text    => "Bouton 9",
-command => sub {
exit;
}
)->grid( $mw->Button( -text => "Bouton 10", -command => sub { exit } ), -sticky => "nsew" );

La fenêtre résultante est celle de la figure 2.28.

II-B-3 - Cellules vides

Le caractère "x" signifie « sauter cet espace » et laisser un trou dans la grille. Dans le code qui suit, on a supprimé la ligne qui créait « Bouton 6 » et on l'a remplacée par un "x". La cellule est encore présente, mais elle ne contient aucun widget.

Figure 2.28 : Exemple de colonnes traversées avec le caractère « - »

$mw->Button(
-text    => "Bouton 1",
-command => sub {
exit;
}
)->grid(
$mw->Button( -text => "Bouton 2", -command => sub { exit } ),
$mw->Button( -text => "Bouton 3", -command => sub { exit } ),
$mw->Button( -text => "Bouton 4", -command => sub { exit } )
);

$mw->Button( -text => "Bouton 5", -command => sub { exit; } )->grid(
"x",
$mw->Button( _text => "Bouton 7", -command => sub { exit } ),
$mw->Button( -text => "Bouton 8", -command => sub { exit } )
);

Le résultat apparaît à la figure 2.29.

Figure 2.29 : Laisser une cellule vide entre des widgets

II-B-4 - Options de Grid

Les options, autres que celles citées ci-dessous, fonctionnent comme celles de pack :

"-"

1. Caractère spécial utilisé dans la liste de widgets de grid. Il augmente la valeur de l'option -colunnspan du widget qui se trouve avant lui dans la liste des widgets.

"x"

1. Caractère spécial utilisé dans la liste de widgets de grid. Laisse un espace vide dans la grille.

"^"

1. Caractère spécial utilisé dans la liste de widgets de grid. Il augmente la valeur de l'option -rowspan du widget qui se trouve directement au-dessus de lui dans la grille.

-column => n

1. Indique la colonne où placer le widget (n ≥ 0).

-row => m

1. Indique la ligne où placer le widget (m ≥ 0).

-columnspan => n

1. Indique le nombre de colonnes à occuper par le widget à partir de -column.

-rowspan => m

1. Indique le nombre de lignes à occuper par le widget à partir de -row.

-sticky => chaîne

1. La chaîne contient les caractères n (« nord »), s (« sud »), e (« est ») ou w (« ouest ») Le widget collera à ces bords

-in => $autrefenetre

1. Indique que le widget sera placé par grid dans $autrefenetre au lieu de l'être dans son parent.

-ipadx => montant

1. $widget s'agrandit de 2 x montant dans la direction x.

-ipady => montant

1. $widget s'agrandit de 2 x montant dans la direction y.

-padx => montant

1. Un espace tampon, égal à montant, est placé à gauche et à droite du widget.

-pady => montant

1. Un espace tampon, égal à montant, est placé au-dessus et en dessous du widget.

II-B-5 - Désignation explicite des lignes et colonnes

Plutôt que de laisser grid faire des suppositions, il est parfois nécessaire de préciser explicitement la ligne et la colonne dans lesquelles placer le widget. Les options -row et -column sont faites pour ça. Chacune d'elles prend un entier positif comme paramètre :

-column => n, -row => m

Avec ces options, il n'est pas nécessaire de construire ou de placer les widgets dans un ordre logique quelconque (sauf pour votre propre confort lorsque vous rechercherez des erreurs). Vous pouvez placer votre premier widget en colonne 10 et ligne 5, si vous le désirez. Toutes les autres cellules se trouvant aux lignes et colonnes précédentes resteront vides.

II-B-6 - Traverser explicitement des lignes et des colonnes

Il est également possible de préciser explicitement qu'un ou plusieurs widgets doivent traverser certaines colonnes ou lignes. Pour traverser des colonnes, on utilise l'option -columnspan et, pour traverser des lignes, l'option -rowspan. Toutes les deux prennent en paramètre un entier supérieur ou égal à 1. Cette valeur précise le nombre de lignes ou colonnes à traverser, elle comprend la ligne ou colonne dans laquelle le widget est placé.

Dans cet exemple, on utilise la façon simple de placer des widgets en ne spécifiant pas explicitement les options -row et -column. Remarquez que le second appel à grid s'applique aux deux widgets : en conséquence, l'option -columnspan s'applique aux deux boutons créés ici.

$mw->Button(
-text    => "Bouton 1",
-command => sub {
exit;
}
)->grid(
$mw->Button( -text => "Bouton 2", -command => sub { exit } ),
$mw->Button( -text => "Bouton 3", -command => sub { exit } ),
$mw->Button( -text => "Bouton 4", -command => sub { exit } ),
-sticky => "nsew"
);

# Bouton 5 occupera les colonnes 0-1, et Bouton 6 les colonnes 2-3
$mw->Button(
-text   => "Bouton 5",
-command => sub {
exit;
}
)->grid(
$mw->Button( -text => "Bouton 6", -command => sub { exit } ),
-sticky     => "nsew",
-columnspan => 2
);

La fenêtre obtenue est celle de la figure 2.30.

Figure 2.30 : Exemple d'utilisation de -columnspan

Cette fenêtre pourrait aussi avoir été créée en utilisant le caractère spécial « - » afin d'indiquer l'extension de colonne de la façon suivante :

$mw->Button(
-text    => "Bouton 1",
-command => sub {
exit;
}
)->grid(
$mw->Button( -text => "Bouton 2", -command => sub { exit } ),
$mw->Button( -text => "Bouton 3", -command => sub { exit } ),
$mw->Button( -text => "Bouton 4", -command => sub { exit } ),
-sticky => "nsew"
);

# Bouton 5 occupera les colonnes 0-1, et Bouton 6 les colonnes 2-3
$mw->Button( -text => "Bouton 5", -command => sub { exit } )
->grid( "-", $mw->Button( -text => "Bouton 6", -command => sub { exit } ), "-", -sticky => "nsew" );

L'exemple suivant illustre l'utilisation explicite des options -row et -column en plus de l'option -rowspan :

$mw->Button( -text => "Bouton 1", -command => sub { exit } )
->grid( -row => 0, -column => 0, -rowspan => 2, -sticky => 'nsew' );
$mw->Button( -text => "Bouton 2", -command => sub { exit } )
->grid( -row => 0, -column => 1 );
$mw->Button( -text => "Bouton 3", -command => sub { exit } )
->grid( -row => 0, -column => 2 );
$mw->Button( -text => "Bouton 4", -command => sub { exit } )
->grid( -row => 0, -column => 3 );
$mw->Button( -text => "Bouton 5", -command => sub { exit } )
->grid( -row => 1, -column => 1 );
$mw->Button( -text => "Bouton 6", -command => sub { exit } )
->grid( -row => 1, -column => 2 );
$mw->Button( -text => "Bouton 7", -command => sub { exit } )
->grid( -row => 1, -column => 3 );

La fenêtre obtenue est celle de la figure 2.31.

Figure 2.31 Utilisation explicite de -rowspan

II-B-7 - Forcer un widget à remplir une cellule

Lorsque l'on utilise pack, il faut utiliser les deux options -fill et -expand pour que le widget change de taille dans son rectangle alloué. grid n'a pas de rectangle alloué à remplir mais une cellule dans la grille. L'utilisation de l'option -sticky avec grid est similaire à l'utilisation de -fill et -expand avec pack.

La valeur associée à -sticky est une chaîne contenant les points cardinaux sur lesquels doit se « coller » le widget. Si le widget doit toujours « coller » au sommet de la cellule, on utilise -sticky => "n". Pour le forcer à remplir complètement la cellule, utilisez -sticky => "nsew". Pour qu'il soit aussi haut que la cellule, mais de largeur normale, utilisez -sticky => "ns". La chaîne peut contenir des virgules et des espaces, ils seront ignorés. Ces deux lignes sont donc équivalentes :

-sticky => "nsew"
-sticky => "n, s, e, w" # Même chose

Si vous utilisez -sticky avec vos widgets, puis que vous modifiez la taille de la fenêtre, vous noterez que leur taille ne varie pas comme vous l'attendez. Cela est dû au fait que le changement de taille des cellules et des widgets qu'elles contiennent est pris en charge par les méthodes gridColumnconfigure et gridRowconfigure, étudiées plus loin dans ce chapitre.

II-B-8 - Remplissage d'un widget

grid accepte aussi les quatre options suivantes : -ipadx, -ipady, -padx et -pady. Elles fonctionnent exactement comme avec pack mais, au lieu de modifier la taille du rectangle alloué, elles changent celle de la cellule dans laquelle se trouve le widget.

Dans l'exemple suivant, les options -ipadx et -ipady s'appliquent à la ligne de boutons supérieure, non à celle du bas. Remarquez comment les boutons 5 à 8 de la figure 2.32 sont aussi plus larges qu'ils ont réellement besoin de l'être. C'est parce que l'on utilise l'option -sticky => "nsew".

$mw->Button( -text => "Bouton1", -command => sub { exit } )->grid(
$mw->Button( -text => "Bouton 2", -command => sub { exit } ),
$mw->Button( -text => "Bouton 3", -command => sub { exit } ),
$mw->Button( -text => "Bouton 4", -command => sub { exit } ),
-sticky => "nsew",
-ipadx  => 10,
-ipady  => 10
);

$mw->Button( -text => "Bouton 5", -command => sub { exit } )->grid(
$mw->Button( -text => "Bouton 6", -command => sub { exit } ),
$mw->Button( -text => "Bouton 7", -command => sub { exit } ),
$mw->Button( -text => "Bouton 8", -command => sub { exit } ),
-sticky => "nse"
);

Figure 2.32 : Exemple d'utilisation des options -ipadx et -ipady

Dans l'exemple suivant, les options -padx et -ipady sont appliquées à la rangée supérieure de boutons, pas à celle du bas. Le résultat est donné par la figure 2.33.

$mw->Button( -text => "Bouton 1", -command => sub { exit } )->grid(
$mw->Button( -text => "Bouton 2",  -command => sub { exit } ),
$mw->Button( -text => "Bouton 3",  -command => sub { exit } ),
$mw->Button( -text => "Bouton 4",, -command => sub { exit } ),
-sticky => "nsew",
-padx   => 10,
-pady   => 10
);
$mw->Button( -text => "Bouton 5", -command => sub { exit } )->grid(
$mw->Button( -text => "Bouton 6", -command => sub { exit } ),
$mw->Button( -text => "Bouton 7",  -command => sub { exit } ),
$mw->Button( -text => "Bouton 8",  -command => sub { exit } ),
-sticky => "nsew"
);

Figure 2.33 : Exemple d'utilisation des options -padx et -pady

II-B-9 - Utilisation d'un autre parent

L'option -in fonctionne comme avec pack. $widget sera placé dans $autrefenetre au lieu de l'être dans son parent par défaut. Voici comment utiliser cette option :

-in => $autrefenetre

II-B-10 - Configuration des colonnes et des lignes

Comme avec tous les gestionnaires d'espace, certaines méthodes sont associées à grid. Chacune d'elles est appelée via un widget placé sur l'écran à l'aide de grid. Il est parfois nécessaire de modifier les options du groupe de cellules formant notre grille.

On peut contrôler le changement de taille et la taille minimale d'une cellule avec les méthodes gridColumnconfigure et gridRowconfigure. Chacune d'elles prend un numéro de colonne ou de ligne comme premier paramètre et, éventuellement, d'autres paramètres modifiant la configuration de cette colonne ou ligne.

gridColumnconfigure et gridRowconfigure fonctionnent presque comme la méthode configure utilisée sur les widgets. À la différence de cette dernière, cependant, les options utilisables avec gridColumnconfigure et gridRowconfigure ne peuvent être utilisées avec la méthode grid. Les options possibles pour ces deux méthodes sont -weïght, -minsize et -pad.

Si vous ne passez qu'un numéro de ligne ou de colonne en paramètre, ces méthodes renvoient un tableau contenant les options courantes et leurs valeurs pour la méthode.

@config_co1onne = $mw->gridColumnconfigure(0);
@config_ligne = $mw->gridRowconfigure(0);

Dans l'exemple précédent, on récupère les valeurs des options de la première ligne et de la première colonne. Les résultats obtenus en utilisant les valeurs par défaut ressembleraient à ceci :

-minsize 0 -pad 0 -weight 0
-minsize 0 -pad 0 -weight 0

On peut récupérer la valeur d'une seule des options en précisant cette option en deuxième paramètre :

print $mw->gridColumnconfigure(0, -weight), "\n";
print $mw->gridRowconfigure(0, -weight), "\n";

Et les résultats seraient :

0
0

Pour changer la valeur d'une option, spécifiez-la avec la valeur que vous voulez lui associer ; par exemple :

$mw->gridColumnconfigure( 0, -weight => 1 );
$mw->gridRowconfigure( 0, -weight => 1 );

Vous pouvez également fournir plusieurs options dans le même appel :

$mw->gridColumnconfigure( 0, -weight => 1, -pad => 10 );
$mw->gridRowconfigure( 0, -weight => 1, -pad => 10 );

Maintenant que nous savons comment appeler gridColumnconfigure et gridRowconfigure, nous devons nous intéresser à ce que font les trois options de ces méthodes.

II-B-11 - Poids d'une ligne ou d'une colonne

L'option -weight permet de préciser l'espace qui sera alloué à cette colonne ou à cette ligne lorsque la fenêtre est divisée en cellules. Rappelez-vous qu'il faut utiliser -sticky => "nsew" dans votre appel à grid si vous voulez que le widget change de taille en même temps que la cellule. La valeur par défaut de -weight. est 0, qui indique que la largeur de la colonne ou la hauteur de la ligne dépendront du widget le plus grand de la colonne ou de la ligne. Chaque valeur de -weight est en relation avec les autres -weight des lignes et colonnes.

Si une colonne ou une ligne a une option -weight valant 2, elle est deux fois de la taille d'une colonne ou d'une ligne ayant une valeur de 1 pour cette option. Les colonnes ou lignes ayant l'option -weight à 0 n'auront pas leur taille modifiée. Si vous voulez que tous vos widgets changent de taille en même temps que la fenêtre, ajoutez ceci à votre code avant d'appeler MainLoop :

( $colonnes, $lignes ) = $mw->gridSize();
for ( $i = 0 ; $i < $colonnes ; $i++ ) {
$mw->gridColumnconfigure( $i, -weight => 1 );
}
for ( $i = 0 ; $i < $lignes ; $i++ ) {
$mw->gridRowconfigure( $i, -weight => 1 );
}

Ce code mettra un -weight de 1 pour chaque ligne et colonne de la grille, quelle que soit sa taille. Bien sûr, cette méthode ne fonctionne que si vous souhaitez mettre la même taille à toutes les lignes et à toutes les colonnes, mais cela vous donne une idée.

Voici un exemple du fonctionnement de l'option -weight (le résultat est donné par la figure 2.34) :

$mw->Button( -text => "Bouton 1", -command => sub { exit } )->grid(
$mw->Button( -text => "Bouton 2", -command => sub { exit } ),
$mw->Button( -text => "Bouton 3", -command => sub { exit } ),
$mw->Button( -text => "Bouton 4", -command => sub { exit } ),
-sticky => "nsew"
);
$mw->Button( -text => "Bouton 5", -command => sub { exit } )->grid(
"x",
$mw->Button( -text => "Bouton 7", -command => sub { exit } ),
$mw->Button( -text => "Bouton 8", -command => sub { exit } ),
-sticky => "nsew"
);
$mw->gridColumnconfigure( 1, -weight => 1 );
$mw->gridRowconfigure( 1, -weight => 1 );

Figure 2.34 : Exemple d'utilisation de gridRowconfigure et gridColumnconfigure

En donnant à la ligne et à la colonne 1 un poids de 1, (alors que toutes les autres lignes et colonnes ont un poids de 0), celles-ci récupèrent tout l'espace supplémentaire rendu disponible par une augmentation de la taille de la fenêtre. Notez que les colonnes 0, 2 et 3 n'ont que la largeur suffisante pour afficher les boutons et leur texte, mais que la colonne 1 a rempli l'espace supplémentaire. Le même effet a lieu pour la ligne 0, qui a un poids de 0, et la ligne 1, qui a un poids de 1 (la fenêtre a été agrandie pour mettre en évidence les effets de -weight).

II-B-12 - Taille minimale d'une cellule

L'option -minsize permet d'indiquer la largeur minimale d'une colonne ou la hauteur minimale d'une ligne. Sa valeur doit être une distance d'écran. Dans l'exemple qui suit,

la taille minimale des cellules de la ligne 0 et de celles de la colonne 0 est fixée à 10 pixels

$mw->gridColumnconfigure(0, -minsize => 10);
$mw->gridRowconfigure(0, -minsize => 10);

Si la colonne ou la ligne occupait théoriquement moins de 10 pixels, elle aura en fait au moins cette taille.

II-B-13 - Remplissage

On peut ajouter des espaces de remplissage (padding) autour du widget, ainsi qu'au widget lui-même en utilisant les options -padx/y et -ipadx/y. Vous pouvez aussi en ajouter en utilisant l'option -pad avec les méthodes gridColumnconfigure et gridRowconfigure. Le remplissage est ajouté autour du widget, pas au widget lui-même. Lorsque vous appelez gridColumnconfigure, l'option -pad ajoutera des espaces à gauche et à droite du widget. L'appel de gridRowconfigure avec -pad ajoutera des espaces au-dessus et en dessous du widget. Voici deux exemples :

$mw->gridColumnconfigure( 0, -pad => 10 );
$mw->gridRowconfigure( 0, -pad => 10 );

II-B-14 - Boîte de dimensionnement (Bounding box)

La méthode gridBbox permet de récupérer les dimensions d'une cellule :

($offset_x, $offset_y, $largeur, $hauteur) = $cellule->gridBbox(0, 2);

Cet exemple récupère la boîte de dimensionnement de la cellule située à la colonne 0, ligne 2. Toutes les valeurs renvoyées sont exprimées en pixels. La boîte de dimensionnement modifiera sa taille en même temps que celle de la fenêtre. Les quatre valeurs renvoyées représentent le décalage sur l'axe des x, le décalage sur l'axe des y, la largeur de la cellule et sa hauteur (les décalages sont relatifs à la fenêtre ou au cadre dans lequel le widget est placé).

II-B-15 - Suppression d'un widget

Tout comme packForget, gridForget met le(s) widget(s) hors de vue. Cela peut, ou ne pas, forcer la fenêtre à changer de taille ; cela dépend de la taille du widget et de l'endroit où il se trouvait dans la fenêtre. Voici quelques exemples :

$mw->gridForget();                # Rien ne se passe
$widget->gridForget();            # $widget disparaît
$widget->gridForget($widget1);    # $widget et $widget1 disparaissent
$widget->gridForget( $w1, $w3 );  # $widget, $w1 et $w3 disparaissent

Les widgets disparaissent de la fenêtre, mais les cellules qu'ils occupaient continuent d'exister.

II-B-16 - Obtenir des informations

La méthode gridlnfo renvoie des informations sur $widget sous la forme d'une liste. Tout comme packInfo, les deux premiers éléments indiquent où a été placé le widget :

@liste = $widget->gridlnfo();

Voici un exemple de résultats produits par gridInfo :

-in Tk::Frame=HASH(0x81abc44) -column 0 -row 0 -columnspan 1
-rowspan 2 -ipadx 0 -ipady 0 -padx 0 -pady 0 -sticky nesw

II-B-17 - Emplacement d'un widget

La méthode gridLocation renvoie la colonne et la ligne du widget le plus près des coordonnées (x, y) spécifiées :

($colonne, $ligne) = $mw->gridLocation($x, $y)

$x et $y sont exprimés en unités d'écran relatives à la fenêtre maître ($mw, dans nos exemples). La valeur -1 est renvoyée pour les emplacements au-dessous ou à gauche de la grille.

Lorsqu'on lui passe les paramètres (0, 0), gridLocation retourne : 0 0 qui indique la cellule en colonne 0 et ligne 0.

II-B-18 - Propagation

Il existe une méthode gridPropagate qui ressemble à packPropagate :

$mw->gridPropagate( 0 );

Lorsqu'on lui passe une valeur fausse, gridPropagate désactive la propagation d'espace, ce qui signifie que les informations de taille ne sont pas remontées au parent de $mw. Par défaut, la propagation est activée. Sans paramètre, gridPropagate renvoie sa valeur courante.

II-B-19 - Combien de colonnes et de lignes ?

Pour savoir la taille que la grille a atteinte après y avoir placé de nombreux widgets, on peut utiliser la méthode gridSize qui renvoie le nombre de colonnes et de lignes de la grille :

($colonnes, $lignes) = $mw->gridSize();

La liste obtenue contient le nombre de colonnes puis de lignes. Dans beaucoup des exemples précédents, nous avions une taille de grille de 4 colonnes par 2 lignes.

($colonnes, $lignes) = $fenetre->gridSize();

Il n'est pas nécessaire qu'un widget soit placé dans une colonne/ligne pour que cette paire soit considérée comme une combinaison colonne/ligne correcte. Si l'on place un widget en colonne 4 et ligne 5 en utilisant -row => 5, -column => 4 et que le seul autre widget se trouve en colonne 0 et ligne 0, gridSize renverra 5 et 6.

II-B-20 - Énumération des widgets

Il y a deux moyens de trouver quels sont les widgets qui ont été placés avec grid dans une fenêtre ou dans un cadre. On utilise gridSlaves sans paramètre pour obtenir une liste complète ou bien l'on donne une ligne et une colonne. Voici des exemples de ces deux façons de faire :

@esclaves = $mw->gridSlaves();
print "@esclaves\n";

Ce code afficherait quelque chose comme :

Tk::Button=HASH(0x81b6fb8) Tk::Button=HASH(0x81ba454)
Tk::Button=HASH(0x81ba4cc) Tk::Button=HASH(0x81ba538)
Tk::Button=HASH(0x81ba6dc) Tk::Button=HASH(0x81ba748)

Nous aurions pu spécifier le widget de la colonne 0, ligne 0 :

$widget = $mw->gridSlaves( -row => 0, -column => 0 );
print "$widget\n";
# Cela afficherait : Tk::Button=HASH(0x81b6fb8)

Si l'on ne précise que l'option -row, on obtient une liste ne contenant que les widgets de cette ligne. La même chose se passe si l'on ne spécifie que l'option -column : la liste ne contiendra que les widgets de cette colonne.

II-C - Place

Le gestionnaire d'espace place diffère de grid et de pack. Au lieu de prendre comme référence un emplacement de cellule ou le bord d'une fenêtre, vous utiliserez la plupart du temps des coordonnées x et y relatives. Vous pouvez aussi utiliser place pour faire se chevaucher des parties de widgets, ce qui est impossible avec grid et pack.

L'appel de place ressemble à celui des autres gestionnaires d'espace :

$widget->place( [ option => valeur, . . . ] );

Les options spécifiées lors de l'appel de place affectent la façon dont sont placés les widgets à l'écran.

II-C-1 - 0ptions de place

-anchor => 'n' | 'ne' | 'e' | 'se' 1 's' | 'sw' | 'w' | 'nw' | 'center'

1. Configure la position dans le widget qui sera placée aux coordonnées précisées.

-bordermode => 'inside' | 'outside' | 'ignore'

1. Détermine si la partie frontière du widget sera incluse ou non dans le système de coordonnées.

-height => montant

1. Configure la hauteur absolue du widget.

-in => $fenetre

1. Indique que le widget fils sera placé dans $fenetre au lieu du parent qui l'a créé. Toutes les coordonnées ou tailles relatives font quand même référence au parent.

-relheight => ratio

1. Indique que la hauteur du widget a un rapport de ratio avec celle du widget parent.

-relwidth => ratio

1. Indique que la largeur du widget a un rapport de ratio avec celle du widget parent.

-relx => xratio

1. Indique que le widget sera déplacé de xratio, relativement à son widget parent.

-rely => yratio

1. Indique que le widget sera déplacé de yratio, relativement à son widget parent.

-width => montant

1. Indique que la largeur du widget sera de montant.

-x => x

1. Indique que le widget sera placé en z. z est une distance d'écran.

-y => y

1. Indique que le widget sera placé en y. y est une distance d'écran.

II-C-2 - Coordonnées absolues

La fenêtre (ou le cadre) parent a un système de coordonnées standard où 0,0 représente le coin supérieur gauche. Les valeurs x augmentent vers la droite, tandis que les valeurs y augmentent vers le bas. C'est représenté par la figure 2.35.

Figure 2.35 : Système de coordonnées de la fenêtre parent en coordonnées absolues

On utilise les options -x et -y pour utiliser des coordonnées absolues afin de préciser l'emplacement du widget :

-x => x, -y => y

Les valeurs acceptées pour x et y sont des distances d'écran (par exemple, 5, qui est exprimé en pixels). Ces coordonnées seront celles du point d'ancrage (contrôlé par -anchor) du widget. L'ancrage par défaut est "nw", le coin supérieur gauche du widget.

Une autre différence majeure entre place et les autres gestionnaires d'espace est que l'appel de place nécessite au moins deux paramètres. Il n'y a pas de valeurs par défaut pour les options -x et -y et vous obtiendrez une erreur si vous essayez d'appeler place sans paramètre (en faisant, par exemple, $widget->place()).

L'exemple le plus simple de l'utilisation de -x et -y consiste à placer un widget aux coordonnées 0,0 :

$mw->Button( -text => "Fin", -command => sub { exit } )->place( -x => 0, -y => 0 );

Comme l'on pourrait s'y attendre, le widget se place dans le coin supérieur gauche de la fenêtre, ainsi que le montre la figure 2.36. Peu importe la taille de la fenêtre : notre widget restera positionné en 0,0. Même lorsque la fenêtre prend la plus petite taille possible, le widget ne bouge pas.

Figure 2.36 : Bouton placé avec -x => 0, -y => 0

Voici un exemple d'utilisation de -x et -y pour créer des widgets qui se chevauchent (figure 2.37)

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->place( -x => 10, -y => 10 );
$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->place( -x => 20, -y => 20 );

Figure 2.37 : Chevauchement de boutons avec place()

II-C-3 - Coordonnées relatives

Avec place, il existe un autre système de coordonnées, défini pour le widget parent, et qui permet un placement relatif dans celui-ci. Ce système de coordonnées est montré par la figure 2.38.

Le coin supérieur gauche a les coordonnées (0.0, 0.0) et le coin inférieur droit, les coordonnées (1.0, 1.0). Le centre de la fenêtre serait donc (0.5, 0.5). Ces coordonnées sont exprimées sous une forme en virgule flottante afin de permettre à place de gérer n'importe quelle taille de fenêtre. Cela permet au widget de rester à sa position (au centre, par exemple), quels que soient les changements de taille de la fenêtre.

Il est permis de spécifier des coordonnées inférieures à 0.0 ou supérieures à 1.0. Cependant, il y a fort à parier que le widget ne sera pas complètement visible dans la fenêtre si vous utilisez des coordonnées en dehors de cet intervalle.

Le code suivant produit le bouton montré à la figure 2.39 :

$bouton = $mw->Button(-text => "Fin", -command => sub { exit });
$bouton->place(-relx => 0.5, -rely => 0.5);

Bien que le bouton de la figure 2.39 soit placé au centre de la fenêtre, il semble en être décalé car c'est le coin supérieur gauche du widget, et non son centre, qui a été placé au milieu de la fenêtre. On peut changer ceci en utilisant -anchor, dont je parlerai dans un instant. Si nous changeons la taille de cette fenêtre, le bouton reste quand même au milieu (voir la figure 2.40).

L'exemple suivant crée deux boutons, placés dans la fenêtre à l'aide de coordonnées relatives.

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->place(
-relx => 0.2,
-rely => 0.2
);
$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->place( -relx > 0.5, -rely => 0.5 );

Figure 2.38 : Le système de coordonnées relatives

Figure 2.39 : Utilisation de place() avec -relx => 0.5, -rely => 0.5

Figure 2.40 : La fenêtre précédente, élargie

Quelle que soit la taille de la fenêtre ou l'emplacement des autres widgets, les deux boutons resteront à ces coordonnées relatives (voir la figure 2.41).

2.41 : Deux boutons placés en coordonnées relatives à la fenêtre parent

La fenêtre gauche de la figure 2.41 a la taille qui lui a été donnée lors de sa création. Celle de droite est cette même fenêtre dont on a modifié la taille pour qu'elle soit bien plus petite. Notez que le second bouton reste au-dessus. C'est parce que l'ordre de la liste des widgets de la fenêtre est conservé ; le second bouton « Fin » (placé en 0.5, 0.5) est dessiné en dernier, il est donc tracé au-dessus de l'autre bouton.

On peut aussi combiner les systèmes de coordonnées absolues et de coordonnées relatives en utilisant les deux dans la liste des paramètres. Le système de coordonnées relatives est le premier pris en compte, puis les valeurs des options -x et -y sont ajoutées à cette position. Les options -relx => 0.5, -x => -10 indiquent de placer le widget 10 pixels à gauche du centre de la fenêtre.

II-C-4 - Ancrage d'un widget

Pensez au widget fils comme à un morceau de papier que vous voudriez mettre sur un tableau de liège (le tableau est le widget parent). Vous utiliseriez une punaise pour maintenir le papier sur le tableau. Vous pouvez placer cette punaise au centre du papier, dans son coin supérieur gauche (« nw »), ou dans le coin inférieur droit (« se »). Le point où la punaise maintiendra le papier au tableau est le point d'ancrage. Le point spécifié par -anchor est « punaisé » aux coordonnées données par -x et -y, ou par -relx, -rely. La valeur par défaut d'-anchor est « nw ». La figure 2.38 montre ces points d'ancrage dans le widget fils.

Il est important de savoir où est le point d'ancrage car cela affectera la présentation du widget dans son parent.

Figure 2.42 : Des valeurs différentes de -anchor affectent le placement du widget dans la fenêtre

Des instructions de placement quasiment identiques ont été utilisées pour placer le bouton « Fin » dans les fenêtres de la figure 2.42. La différence réside en la modification de l'option -anchor. Le bouton de la fenêtre de gauche a été créé ainsi :

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->place(
-relx => 0.5,
-rely => 0.5
);

La fenêtre de droite a utilisé le placement suivant :

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->place(
-relx   => 0.5,
-anchor => "center",
-rely   => 0.5
);

Comme avec pack et grid, les valeurs possibles de -anchor sont : 'n', 'e', 's', 'w', 'center', 'nw', 'sw', 'ne' et 'se'. Cependant, la valeur s'applique maintenant au widget fils plutôt qu'à la position dans le rectangle alloué.

II-C-5 - Largeur et hauteur

Lorsque l'on utilise place, on peut indiquer la largeur et la hauteur du widget en utilisant l'une de ces trois possibilités :

- autoriser le widget à déterminer sa propre taille ;

- préciser la largeur et/ou la hauteur en mesures absolues ;

- préciser la largeur et/ou la hauteur en mesures relatives (au widget parent).

Pour laisser les widgets déterminer leur propre taille, on ne spécifie aucune option. Les autres solutions impliquent d'utiliser les options -width, -height et -relwidth, -relheight, respectivement.

Les options -width et -height permettent d'indiquer la largeur et la hauteur exactes du widget, exprimées en distances d'écran :

-width => montant, -height => montant

Chaque montant est une distance d'écran (ce terme a été introduit plus haut, avec pack). Le widget gardera ces tailles, même s'il coupe les bords des éléments qu'il contient. Notre bouton apparaît plutôt bizarrement sur l'écran lorsque l'on utilise une largeur de 40 pixels (voir la figure 2.43).

$mw->Button(
-text    => "Ce bouton provoquera la fin du programme",
-command => sub { exit }
)->place(
-x     => 0,
-y     => 0,
-width => 40
);

Figure 2.43 : Utilisation de -width avec place()

Les deux autres options, -relwidth et -relheight, déterminent la largeur et la hauteur du widget par rapport à celles du widget parent.

-relwidth => ratio, -relheight => ratio

ratio est un nombre en virgule flottante (comme les valeurs utilisées par -relx ou -rely). Une valeur de 1.0 rendra le widget aussi large (ou aussi haut) que son parent. Une valeur de 0.5 le réduira de moitié par rapport à son parent (voir la figure 2.44).

Figure 2.44 : Le même bouton, celui de gauche utilise -relwidth => 0.5, -relheight => 0.5

Lorsqu'elles sont utilisées ensemble, les options -width et -relwidth s'ajoutent, c'est aussi le cas de -height et -relheight.

II-C-6 - Options de prise en compte des contours

NorSmalement, le contour du widget est utilisé comme frontière de l'espace possible dans la fenêtre, ce qui signifie que tout widget placé avec les systèmes de coordonnées absolues ou relatives sera placé à l'intérieur de ce contour. Ce comportement peut être modifié en utilisant l'option -bordermode.

1. -bordermode => 'inside' | 'outside' | 'ignore'

L'utilisation de 'outside' permettra au système de coordonnées d'utiliser aussi l'espace occupé par le contour. 'ignore' fera que le système de coordonnées utilisera la zone X Window officielle. Cependant, cette option est peu utile, ainsi que vous pouvez vous en rendre compte en visualisant les différences induites par ces trois valeurs dans l'exemple de la figure 2.45.

Figure 2.45 : Exemples d'utilisation de -bordermode

Si l'on y regarde de très près (prenez votre loupe), on peut constater que la version utilisant "outside" est plus haute et plus large de deux pixels que la version utilisant "inside". Cela est dû à mon gestionnaire de fenêtre (fvwm) : mes contours ont une largeur de deux pixels.

II-C-7 - Méthodes associées à place

Les méthodes associées à place sont simples et ne permettent pas de beaucoup manipuler les widgets.

II-C-7-a - Suppression d'un widget

Comme pack et grid, place dispose d'une méthode permettant de supprimer un widget.

$widget->placeForget();

Si l'on utilise cette méthode sur un widget, celui-ci sera supprimé de l'écran. Il sera aussi ôté de la liste gérée par le widget parent.

II-C-7-b - Informations sur les emplacements

La méthode placeInfo renvoie une liste d'informations sur le widget :

@infos = $widget->placeInfo();
print "@infos";
## Résultats produits (il y a des espaces aux endroits sans valeur)
-x 0 -relx 0 -y 0 -rely 0 -width -relwidth -height -relheight -anchor nw

II-C-7-c - Énumération des widgets

@widgets = $parent->placeSlaves();

La méthode placeSlaves renvoie une liste des widgets fils contenus dans $parent. Cette liste ressemble à celle que produisent les méthodes packSlaves et gridSlaves.

II-D - Résumé sur la gestion de l'espace

Vous en savez maintenant plus sur les trois différents gestionnaires d'espace que vous n'aurez jamais besoin d'en savoir pour écrire des applications Perl/Tk correctes. Voici quelques conseils pratiques pour décider du gestionnaire à utiliser :

- pack convient bien à une utilisation générale, et vous le choisirez dans 95 % des cas ;

- grid est parfait lorsque vous souhaitez créer une disposition en colonnes identique à celle d'une feuille de calcul d'un tableur ;

- place est le plus utile lorsque vous souhaitez que vos widgets conservent une position ou une taille relatives au widget qui les a créés. Utilisé correctement, il est très puissant ;

- quel que soit le gestionnaire utilisé, prenez votre temps afin d'avoir des widgets sur la fenêtre à laquelle ils appartiennent (ou, plus probablement, à l'endroit où vous le souhaitez). Il n'y a rien de plus déconcertant qu'un bouton semblant ne pas appartenir à la bonne fenêtre.

Au fur et à mesure de la lecture de ce livre, vous noterez que certains noms d'options des gestionnaires d'espace sont aussi des noms d'options utilisés pour créer ou configurer un type de widget. On peut, par exemple, spécifier l'option -width d'un bouton sans utiliser place. Rappelez-vous toujours le contexte dans lequel l'option est utilisée : la différence de fonctionnement est parfois très subtile.

III - Le bouton de base

III-A - Le widget Button

De tous les widgets disponibles en Perl/Tk, le bouton est l'un des plus souvent utilisés : il n'est qu'à voir tous les exemples du chapitre 2, Gestion de l'espace. Lorsqu'un bouton est pressé, quelque chose se passe. Cela peut aller de la sortie du programme (comme dans notre exemple « Bonjour tout le monde »), au début d'une longue suite d'opérations, comme l'ouverture d'un fichier ou le lancement d'un autre processus. Typiquement, un bouton affiche un texte court : Terminé, Appliquer, Sauver, OK, Fin.

D'autres widgets sont aussi considérés comme des boutons : les boutons radio, les cases à cocher et les boutons de menu. Ce chapitre traite des boutons traditionnels. Le chapitre 4, Cases à cocher et boutons radio, et le chapitre 11, Menus, traiteront des autres types, car ils ne se présentent pas de la même façon à l'écran et se comportent différemment.

Nous traitons d'abord du widget bouton, car il est facile de constater comment les différentes options affectent sa présentation. La plupart des autres widgets de Perl/Tk utilisent les mêmes options. Normalement, si un nom d'option est le même, la modification que celle-ci apportera au widget sera aussi (plus ou moins) la même.

III-A-1 - Création d'un bouton

On utilise la syntaxe suivante pour créer un bouton :

$bouton = $widget_parent->Button( [ option => valeur, ... ] ) ;

Nous avons déjà vu des exemples de boutons dans notre programme « Bonjour tout le monde » du chapitre 1, ainsi que dans tous les exemples de gestion de l'espace du chapitre 2. Dans ceux-ci, un bouton est créé et placé sur l'écran avec une instruction comme :

$mw->Button(-text => "Terminé", -command => sub { exit })->pack;

Vous pouvez faire en sorte de conserver une référence au bouton de la façon suivante :

$bouton = $mw->Button(-text => "Terminé", -command => sub { exit })->pack;

Dans « Bonjour tout le monde », nous n'avions pas besoin de faire ensuite référence au bouton et cela n'aurait donc pas eu d'intérêt d'en conserver une. Cependant, dans la plupart des exemples de ce chapitre, nous supposerons que nous avons sauvegardé une référence au widget bouton au moment de sa création.

III-A-2 - Options d'un bouton standard

Le reste de ce chapitre traite des options vous permettant de changer l'aspect du bouton et de lui faire faire ce que vous voulez.

-activebackground => couleur

1. Configure la couleur que devra prendre le fond lorsque le curseur de la souris est au-dessus du bouton. Une couleur est exprimée sous la forme d'une chaîne : "red", par exemple.

-activeforeground => couleur

1. Configure la couleur que devra prendre le texte lorsque le curseur de la souris est au-dessus du bouton.

-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' |'nw' | 'center'

1. Permet d'ancrer le texte à la position spécifiée du bouton.

-background => couleur

1. Configure la couleur de fond du bouton.

-bitmap => nom_bitmap

1. Précise le bitmap par défaut, ou l'emplacement d'un fichier bitmap (avec @ devant son chemin d'accès).

-borderwidth => montant

1. Change la largeur du bord tracé autour du bouton (met en évidence l'option -relief).

-command => fct_rappel

1. Donne un pointeur vers une fonction qui sera appelée lorsque le bouton sera pressé.

-cursor => nom_curseur

1. Indique que le curseur de la souris se changera en nom_curseur lorsqu'il sera au-dessus du bouton.

-disabledforeground => couleur

1. Configure la couleur que devra prendre le texte lorsque le bouton sera désactivé.

-font => nom_fonte

1. Change la fonte de tout le texte du bouton.

-foreground => couleur

1. Change la couleur du texte en couleur.

-height => montant

1. Configure la hauteur du bouton : en caractères si du texte y est affiché, ou en distances d'écran s'il contient une image ou un bitmap.

-highlightbackground => couleur

1. Configure la couleur de la zone derrière le rectangle de focus (qui apparaît lorsque le widget n'a pas le focus).

-highlightcolor => couleur

1. Configure la couleur du rectangle de focus (qui apparaît lorsque le widget a le focus).

-highlightthickness => montant

1. Configure l'épaisseur de la boîte noire située autour du bouton ; indiquant qu'il a le focus.

-image => ptr_img

1. ptr_img est un pointeur vers un objet Image, créé à partir d'un fichier GIF ou PPM/PGM .

-justify => 'left' | 'right' | 'center'

1. Indique le bord du bouton contre lequel s'alignera un texte de plusieurs lignes.

-padx => montant

1. Ajoute des espaces à gauche et à droite, à l'intérieur du contour du bouton.

-pady => montant

1. Ajoute des espaces au-dessus et en dessous, à l'intérieur du contour du bouton.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie le type du contour tracé autour du bouton.

-state => 'normal' | 'disabled' | 'active'

1. Indique l'état de réactivité du bouton. Si cette option vaut "disabled" , le bouton ne répondra pas.

-takefocus => 0 | 1 | undef

1. Indique que le bouton n'obtiendra jamais le focus (0), l'obtiendra toujours (1), ou laisse l'application décider (undef).

-text => texte

1. Indique le texte affiché dans le bouton.

-textvariable => \$variable

1. Pointeur vers une variable contenant le texte à afficher dans le bouton. Ce texte changera en même temps que ce que contient $variable.

-underline => n

1. Souligne le nième caractère du texte. Permet d'utiliser ce caractère au clavier pour cliquer lorsque le bouton a le focus.

-width => montant

1. Configure la largeur du bouton : en caractères si du texte y est affiché, ou en distances d'écran s'il contient une image ou un bitmap.

-wraplength => montant

1. Configure, en distances d'écran, le montant maximal de texte affiché sur une seule ligne. Cette option vaut 0 par défaut.

III-A-3 - Affichage de texte

Pour que l'utilisateur sache ce que fait le bouton lorsqu'il est pressé, on doit indiquer le rôle de ce bouton à l'aide d'un texte. L'option réalisant cette opération est -text :

-text => 'texte'

Au moment de décider du texte descriptif, préférez un commentaire simple et court si vous ne souhaitez pas que le bouton occupe toute la fenêtre à cause d'un texte trop long.

Le texte peut contenir n'importe quoi : des caractères alphanumériques, un ou plusieurs retours à la ligne, des variables. Si elle est mise entre apostrophes simples, la chaîne sera interprétée littéralement ; entre apostrophes doubles, elle sera interpolée, exactement comme n'importe quelle chaîne en Perl. L'interpolation n'a lieu qu'une seule fois (la première fois que l'option est analysée). Si la variable est modifiée plus tard dans le programme, cela n'aura pas d'effet sur le texte du bouton. La seule façon de changer la chaîne après la création est d'utiliser la méthode configure pour la modifier (en faisant, par exemple, $bouton->configure(-text => "nouveau texte");) ou d'utiliser l'option -textvariable.

L'option -text n'a pas de valeur par défaut ; le bouton n'aura simplement pas de texte si vous ne spécifiez pas de valeur pour cette option.

L'autre moyen d'afficher du texte sur le bouton est d'utiliser l'option -textvariable. Celle-ci permet d'associer une variable scalaire au bouton ; le contenu de cette variable y sera affiché. On la spécifie de la façon suivante :

-textvariable => \$variable

L'utilisation de cette option implique que le texte du bouton changera en même temps que le contenu de $variable. Lorsque le texte change, le bouton ajuste sa taille à celui-ci, et la taille de la fenêtre entière peut donc être modifiée.

Ce morceau de code montre comment utiliser l'option -textvariable :

$cpteur = 0;
$mw->Button(
-text    => "+ 1",
-command => sub { $cpteur++ }
)->pack( -side => 'left' );
$mw->Button( -textvariable => \$cpteur )->pack( -side => 'left' );
$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack( -side => 'left' );

La figure 3.1 montre deux fenêtres : la première représente la fenêtre juste après sa création, la deuxième, la même fenêtre après avoir cliqué vingt fois sur le bouton « + 1 ».

Figure 3.1 : Exemple d'utilisation de l'option -textvariable

III-A-4 - Afficher une image ou un bitmap à la place du texte

Au lieu d'afficher une chaîne sur le bouton, on peut utiliser l'option -image pour afficher une image.

-image => $ptr_img

Les formats d'images admis sont GIF et PPM/PGM. Les images JPEG sont reconnues en utilisant un module séparé (Tk::JPEG), disponible sur CPAN. D'autres types d'images sont également reconnus à mesure que de nouveaux modules comme Tk::JPEG sont développés. Vérifiez ceux qui sont actuellement disponibles sur CPAN.

Lorsque l'on utilise une image, seule celle-ci sera affichée, car le bouton ne peut afficher qu'une chaîne de texte ou une image, pas les deux. Pour créer une variable $ptr_img, on utilise la méthode Photo (en lui fournissant le nom et le chemin de l'image si elle ne se trouve pas dans le répertoire courant). La variable $ptr_img est ensuite donnée comme valeur de l'option -image :

$image = $mw->Photo( -file => "Xcamel.gif" );
$mw->Button(
-text    => 'Fin',
-command => sub { exit },
-image   => $image
)->pack;

La figure 3.2 montre un exemple de bouton contenant un fichier GIF(12).

Figure 3.2 : Bouton contenant une image à la place du texte

Pour afficher un bitmap, on utilise l'option -bitmap et on fournit son nom :

-bitmap => 'nom_bitmap'

Il existe plusieurs bitmaps par défaut : error, gray12, gray25, gray50, gray75, hourglass, info, questhead, question, et warning (voir la figure 3.3). On les précise en donnant à l'option leur nom entre apostrophes :

$mw->Button(-bitmap => 'error', -command => \&gestion_erreur)->pack;

Pour utiliser un fichier bitmap, on doit placer un @ devant son nom complet :

$mw->Button(
-bitmap  => '@/usr/share/bitmap/monbitmap',
-command => sub { exit }
)->pack;

Notez que, si vous utilisez des apostrophes doubles, il faut protéger le @ avec un antislash (ici, "\@/usr/share/bitmap/monbitmap").

Figure 3.3 : Fenêtre affichant tous les bitmaps par défaut

III-A-5 - Assignation d'une fonction de rappel

Tout comme l'option -text, l'option -command est quasiment toujours utilisée lors de la création d'un bouton. Pour que ce dernier fasse quelque chose lorsqu'il est pressé, il faut lui associer une fonction de rappel (callback) en utilisant l'option -command. Cette fonction sera exécutée lorsque le bouton 1 de la souris est pressé au-dessus du bouton(13). Si l'on clique sur le bouton, mais que l'on déplace le curseur à l'extérieur de celui-ci avant de le relâcher, rien ne se passera, car le clic souris aura été annulé.

Dans le programme « Bonjour tout le monde », nous avons utilisé la fonction exit comme fonction de rappel :

$mw->Button(-text => "Fin", -command => sub { exit })->pack;

Il y a plusieurs façons d'associer une fonction, ou un ensemble d'instructions, à un bouton. Ce qui suit s'applique à tous les widgets disposant d'une option -command, vous la verrez donc souvent revenir.

III-A-6 - Définition d'une fonction de rappel

Une fonction de rappel peut être définie de plusieurs façons :

- comme une fonction anonyme : par exemple, sub { faire quelque chose } ;

- comme une référence à une fonction : par exemple, \&mafonction ;

- comme une liste anonyme dont le premier élément est une référence à une fonction, et les autres les paramètres passés à cette fonction :

[ \&mafonction, $arg0, $arg1, \@arg2...].

Le bouton créé dans notre programme « Bonjour tout le monde » utilisait une fonction anonyme. Voici à nouveau le code :

$mw->Button(-text => "Fin", -command => sub { exit; })->pack;

Nous aurions aussi pu créer la fonction anonyme avant de créer le bouton et lui passer la référence à celle-ci :

$ref_fonction = sub { exit };
$mw->Button(-text => "Fin", -command => $ref_fonction)->pack;

C'est utile dans le cas où notre fonction anonyme réalise plusieurs choses et que cela serait affreux de la mettre dans la liste des paramètres du bouton.

Nous aurions aussi pu créer une fonction classique pour gérer le exit, puis passer une référence à celle-ci :

sub sortir {
&faire_autre_chose;
exit;
}
$mw->Button(-text => "Fin", -command => \&sortir )->pack;

Il est conseillé d'utiliser une fonction comme celle-ci si vous prévoyez plus d'une possibilité de quitter votre application. Vous pourriez, par exemple, configurer votre programme pour que l'on puisse en sortir via un menu, un bouton ou la commande Close du gestionnaire de fenêtres.

Si vous devez passer des paramètres à votre fonction exit, utilisez la forme de liste anonyme :

sub sortir {
my ($arg1, $arg2) = @_;
&faire_autre_chose if ($arg1 = 12);
exit;
}
$mw->Button(-text => "Fin",
-command => [ \&sortir, 12, 'ciao' ])->pack;

Il est important de se rappeler comment les différentes façons d'indiquer une fonction de rappel affectent la portée des variables.

III-A-7 - Fonctions anonymes

Les fonctions anonymes sont « mises de côté » pour être appelées plus tard, à partir de la boucle des événements, MainLoop. Les commandes contenues dans la fonction ne seront pas analysées, et toutes les variables qu'elle utilise ne seront pas non plus évaluées tant que cette fonction n'est pas appelée.

foreach (@noms) {
$mw->Button(
-text    => $_,
-command => sub { print "$_ a été pressé !\n"; }
)->pack;
}

Le code précédent utilise la variable $_ dans la boucle foreach. Le texte du bouton vaudra ce qui est attendu, car $_ est évalué quand le bouton est créé. Cependant, le $_ dans la portée de la fonction anonyme ne sera pas évalué jusqu'à ce que le bouton soit vraiment pressé. À ce moment-là, $_ pourrait ne pas être défini et des erreurs commenceraient à s'afficher à chaque fois que vous cliqueriez sur le bouton.

Une référence à une fonction et une liste anonyme sont très similaires, hormis le fait que la liste permet de passer des paramètres supplémentaires à la fonction (notamment ceux dans la portée courante) :

foreach (@noms) {
$mw->Button(
-text    => $_,
-command => [ \&affiche_nom, $_ ]
)->pack;
}

sub affiche_nom {
print "$_[0] a été pressé !\n";
}

La liste anonyme est créée pendant l'appel de création du bouton. Cela signifie que le $_ de cette liste est évalué dans le contexte de la boucle foreach et vaudra la même valeur que celle utilisée par l'option -text.

Ceux d'entre vous, qui ont l'habitude de créer des fonctions anonymes au vol, peuvent aussi faire la même chose de cette façon :

foreach (@noms) {
$mw->Button(
-text    => $_,
-command => [ sub { print " $_[0] a été pressé !\n"; }, $_ ]
)->pack;
}

La fonction anonyme est le premier élément de la liste de l'option -command, le second élément est le paramètre $_. Pensez au fait que vous voudrez parfois beaucoup plus qu'une simple instruction print dans votre fonction et que, pour des raisons de lisibilité, il est préférable de la placer dans une fonction.

Ce qui précède n'est qu'un survol rapide des fonctions anonymes et de leur fonctionnement avec Perl/Tk : le Camel Book(14) contient toutes les informations nécessaires, et plus encore.

III-A-8 - Désactiver un bouton

Lorsqu'un bouton est créé, il se présente par défaut comme étant prêt à fonctionner. Il changera de couleur lorsque la souris passe au-dessus de lui et exécutera la fonction de rappel associée lorsqu'il est pressé. Vous pouvez changer cela à l'aide de l'option -state.

1. -state => "normal" | "disabled" | "active"

L'état "normal" vient juste d'être décrit. L'état "active" correspond au cas où le curseur de la souris se trouve au-dessus du bouton, il est utilisé de façon interne. L'état "disabled" fait que le bouton apparaîtra « grisé » (ou avec des couleurs spécifiées par les options -disabledforeground et -disabledbackground) et qu'il ne répondra à aucune action.

Un bouton ne devrait être sélectionnable que s'il sert à quelque chose dans l'application ; on peut, par exemple, envisager un bouton qui, une fois pressé, en désactiverait un autre.

Le code ressemblerait à :

my $b_fin = $mw->Button(
-text    => 'Sortie',
-command => sub { exit }
)->pack;
$var = "Empêcher la sortie";
$mw->Button(
-textvariable => \$var,
-command      => sub {
my $etat = $b_fin->configure( -state );
if ( $etat eq "disabled" ) {
$b_fin->configure( -state => 'normal' );
$var = "Empêcher la sortie";
}
else {
$b_fin->configure( -state => 'disabled' );
$var = "Autoriser la sortie";
}
}
)->pack;

Dans cet exemple, on sauvegarde une référence au bouton « Sortie », car on l'utilisera plus tard pour changer l'état de ce bouton. Notez aussi que $b_fln est utilisé en dehors de la portée de la fonction anonyme. Cela ne fonctionnera que si $b_fln reste dans la portée globale du programme afin que cette variable soit définie lorsque la fonction anonyme est exécutée. Faites attention à ne pas affecter autre chose à, $b_fln car, si vous le faites, la fonction anonyme, lorsqu'elle sera appelée fera référence à la nouvelle valeur de $b_fln, pas à celle que vous souhaitez.

La figure 3.4 montre la fenêtre après que l'on a cliqué une fois sur le bouton « Empêcher la sortie ».

Figure 3.4 : Fenêtre avec un bouton désactivé (« Sortie ») et un bouton normal

La méthode configure est expliquée plus loin dans ce chapitre ; ne vous souciez pas de son fonctionnement pour l'instant.

Le fait de désactiver des widgets lorsqu'ils ne servent pas permet de donner des indications visuelles aux utilisateurs quant à ce qu'ils peuvent ou ne peuvent pas faire dans l'application.

III-A-9 - Manipulation du texte

En plus d'afficher du texte sur le bouton, on peut modifier l'apparence et l'emplacement de ce texte dans le bouton. Pour ce faire, la façon la plus simple consiste à utiliser l'option -font afin de changer la fonte :

-font => 'nom_fonte'

Il y a plusieurs moyens de spécifier une fonte. Si vous utilisez Tk4 (qui est la version courante au moment où ce livre est écrit), suivez les instructions des paragraphes suivants. Si vous utilisez la dernière version de Perl/Tk, qui utilise Tk8.0(15), lisez l'annexe C, Fontes : elle présente les nouvelles méthodes à utiliser avec les fontes.

La nouvelle fonte est indiquée sous la forme d'une chaîne contenant son nom. Il y a une différence entre les façons d'indiquer les fontes pour des systèmes Win32 et pour des systèmes Unix. Les fontes disponibles sous votre système X Window peuvent être connues grâce à la commande xlsfonts ou en utilisant le module Tk::Fonts. La fonte par défaut pour le widget bouton, sur mon système Unix est(16) :

"-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"

Bien que vous verrez cette fonte par défaut sur un système Win32, vous devrez passer un autre type de chaîne comme valeur de l'option -font. Voici un exemple :

-font => "{Times New Roman} 12 {normal}"

Vous pouvez aller voir dans le Panneau de configuration de Windows 95/NT, sous la rubrique « Fontes » pour connaître les fontes disponibles. Si vous double-cliquez sur une fonte, une fenêtre s'ouvrira et affichera celle-ci dans ses différentes tailles disponibles. Utilisez le nom tel qu'il est mentionné dans le répertoire Fonts pour former la première partie du nom entre accolades. Le nombre qui suit est la taille de la fonte en points. La troisième partie est son type, habituellement il s'agit de normal, italiques, ou gras.

Il ne peut y avoir qu'une fonte par bouton et le texte ne peut donc pas en changer au milieu d'un mot, mais chaque bouton (ou widget) d'une application peut utiliser une fonte différente. Voici un exemple de deux boutons dans une même fenêtre, l'un utilise la fonte par défaut, l'autre « lucidasans-14 » (une fonte Unix) :

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack(
-side   => 'left',
-fill   => 'both',
-expand => 1
);
$mw->Button(
-text    => "Fin",
-font    => "lucidasans-14",
-command => sub { exit }
)->pack(
-side   => 'left',
-fill   => 'both',
-expand => 1
);

La figure 3.5 montre le résultat :

Figure 3.5 : Boutons avec des fontes différentes

En plus de modifier la fonte, on peut aussi déplacer le texte dans le bouton. On peut changer la position par rapport à laquelle le texte s'alignera de lui-même, comme avec un traitement de texte, grâce à l'option -justify :

1. -justify => 'left' | 'right' | 'center'

La valeur par défaut de -justify est 'center'. Normalement, le texte affiché dans un bouton est un texte court d'un ou deux mots : « Fin », « Terminé », « Oui », « Non » ou « Annuler », par exemple. L'alignement du texte ne saute pas aux yeux, sauf lorsqu'il s'étale sur plusieurs lignes et, par défaut, le bouton n'affichera plusieurs lignes que si la chaîne contient un caractère \n. On peut aider le programme à décider où couper les lignes en utilisant l'option -wraplength :

-wraplength => 'longueurmax'

'longueurmax' indique la longueur maximale de la ligne en distances d'écran (voir le chapitre 2). Si la longueur du texte du bouton excède ce montant, le texte sera coupé et ira à la ligne. La valeur par défaut de -wraplength est 0.

Voici un exemple utilisant les deux options, -justify et -wraplength :

foreach (qw(left center right)) {
$b = $mw->Button(
-text       => "Ce bouton est aligné avec $_",
-command    => sub { exit },
-wraplength => 53,
-justify    => $_
)->pack(
-side   => 'left',
-fill   => 'both',
-expand => 1
);
}

La figure 3.6 montre ce que donnent ces trois boutons. Bien que cet exemple ne le montre pas, il est possible de couper le texte au milieu d'un mot.

Le dernier ajustement possible du texte (ou du bitmap) est celui de sa position dans le bouton. Celle-ci est contrôlée par l'option -anchor, qui fonctionne comme l'option -anchor des gestionnaires d'espace :

Figure 3.6 : Effets des options -justify et -wraplength dans un bouton

1. -anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'

Tout comme une fenêtre, un bouton a des points cardinaux définissant des emplacements dans celui-ci. La figure 3.7 montre ces différents points.

Figure 3.7 : Points d'ancrage dans un bouton

La position par défaut du texte est 'center'. Lorsque la position est modifiée, l'effet de cette option n'apparaît pas évident, sauf si le bouton s'agrandit. Dans la figure 3.8, le bouton est celui qui a été créé dans l'exemple sur l'option -justify (figure 3.6) sauf que -anchor => 'nw' a été ajouté à la liste de ses options.

Figure 3.8 : L'ancre du bouton est positionnée à 'nw'

Comme cela a été évoqué plus haut, cette option est similaire à l'option -anchor de la méthode pack. Il est important de noter que cette option modifie la position du texte dans le bouton ; celle de pack modifie la position du widget dans la fenêtre.

III-A-10 - Modification du style d'un bouton

Par défaut, un bouton se présente comme s'il était légèrement surélevé par rapport à la surface de la fenêtre. En utilisant l'option -relief, on peut modifier le style des bords du bouton :

1. -relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

Chaque valeur modifie légèrement l'aspect du bouton, comme vous pouvez le constater avec la figure 3.9.

flat

1. Le bouton apparaît comme s'il n'y avait que du texte dans la fenêtre. L'utilisation de 'flat' (qui signifie « plat », en français) n'est pas conseillée pour les boutons, car l'utilisateur n'a aucune information visuelle et ne peut savoir que le bouton peut être pressé (il ressemble à une étiquette).

groove

1. Donne un aspect légèrement enfoncé (groove signifie « rainure », en français) aux bords du bouton (comme s'il y avait un fossé autour du texte).

raised

1. C'est la valeur par défaut (« surélevé », en français) ; elle donne un aspect 3D avec une ombre sur les bords inférieur et droit, ce qui fait apparaître le bouton plus haut que la surface de la fenêtre.

ridge

1. L'opposé de groove (ridge signifie « ride ») ; fait apparaître une ride autour du texte.

sunken

1. L'opposé de raised (sunken signifie « en contrebas ») ; donne un effet 3D au bouton, le faisant apparaître en dessous de la surface de la fenêtre.

solid

1. Le bouton est sur le même plan que la fenêtre (comme 'flat'), mais son contour est matérialisé par un trait plein.

Quelle que soit la valeur choisie pour l'option -relief, l'aspect du bouton sera celui de 'sunken' lorsqu'il est pressé.

Figure 3.9 : Les différents types de relief d'un bouton

En plus de changer le type des bords tracés autour d'un bouton, on peut aussi modifier l'épaisseur de ces bords avec l'option -borderwidth :

-borderwidth => montant

La valeur par défaut de -borderwidth est 2. Plus les bords sont larges, plus les effets de l'option relief sont marqués. La figure 3.10 montre ce que donne un -borderwidth de 10 pour chaque type de relief.

Figure 3.10 : Les différents reliefs avec l'option -borderwidth à 10

On peut aussi spécifier -borderwidth en utilisant l'abréviation -bd. Bien que cette dernière donne les mêmes résultats, l'utilisation de -borderwidth rend le code plus facile à relire. De plus, -bd n'est pas reconnue par tous les widgets et compter sur elle peut s'avérer dangereux.

Je ne conseille pas l'utilisation de valeurs supérieures à 4 pour -borderwidth, car cela rend les widgets vraiment laids. Dans tous les chapitres sur les widgets, il y aura une copie d'écran montrant les effets d'une valeur de -borderwidth plus grande pour chacune des valeurs possibles de relief. La meilleure utilisation de -borderwidth est de faire ressortir un widget par rapport aux autres durant la phase de développement (je l'utilise souvent avec des cadres pour matérialiser leurs emplacements, car, normalement, ils sont invisibles : voir le chapitre 12).

III-A-11 - Changer la taille d'un bouton

Normalement, la taille d'un bouton est automatiquement déterminée par l'application et dépend du texte ou de l'image que ce bouton affiche. La largeur et la hauteur peuvent, toutefois, être précisées explicitement avec les options -width et/ou -height :

-width => x, -height => y

Les valeurs spécifiées par x et y diffèrent selon que le bouton affiche un bitmap/image ou un texte. Dans le premier cas, ces valeurs représentent des distances d'écran, dans le deuxième, elles indiquent des nombres de caractères.

L'exemple suivant a un bouton avec sa taille par défaut et un autre, tracé avec des valeurs de 10 pour les deux options -width et -height (il n'est pas nécessaire que les valeurs de ces deux options soient les mêmes, ni d'utiliser les deux) :

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack( -side => 'left' );
$mw->Button(
-text    => "Fin",
-width   => 10,
-height  => 10,
-command => sub { exit }
)->pack( -side => 'left' );

Dans la figure 3.11, le second bouton est bien plus haut qu'il n'est large, car les caractères du texte sont plus hauts que larges.

Figure 3.11 : Bouton par défaut et avec -width => 10, -height => 10

La valeur spécifiée par -width et -height est un nombre de caractères, car le bouton affiche du texte. Lorsque ces options sont utilisées avec un bitmap, la valeur est exprimée en distances d'écran. Voici un exemple d'utilisation de -width et -height avec un bitmap :

$mw->Button(
-bitmap  => 'error',
-width   => 10,
-height  => 10,
-command => sub { exit }
)->pack( -side => 'left' );
$mw->Button(
-bitmap  => 'error',
-command => sub { exit }
)->pack( -side => 'left' );
$mw->Button(
-bitmap  => 'error',
-width   => 50,
-height  => 50,
-command => sub { exit }
)->pack( -side => 'left' );

Le premier bouton est créé en réduisant sa largeur et sa hauteur à 10 pixels. Le bouton du milieu a une taille normale et le troisième est créé avec une largeur et une hauteur de 50 pixels. La figure 3.12 montre le résultat obtenu.

Figure 3.12 : Boutons affichant le même bitmap, avec des valeurs différentes de -width et -height

La valeur par défaut de -width et -height est 0, qui demande au programme de décider dynamiquement de la largeur et de la hauteur du bouton.

La largeur totale d'un bouton de texte est égale à la largeur du texte plus un montant de 2 x -padx. Sa hauteur est la hauteur du texte, plus 2 x -pady. La largeur et la hauteur d'un bouton avec bitmap sont égales à la largeur et la hauteur du bitmap. Toute option -padx ou -pady est ignorée lorsqu'un bitmap est affiché.

Une autre possibilité pour préciser explicitement une largeur ou une hauteur consiste à augmenter la taille du bouton en utilisant les options -padx et/ou -padx pour ajouter des espaces de remplissage entre le texte et les bords du bouton :

-padx => montant, -pady => montant

Le montant spécifié pour -padx est ajouté à gauche et à droite du texte du bouton. Celui spécifié pour -pady est ajouté au-dessus et en dessous du texte bouton. La figure 3.13 montre le résultat obtenu.

En utilisant ces options, vous demandez au bouton d'être plus grand qu'il ne le serait normalement, mais vous n'avez pas à vous demander s'il sera plus petit, comme c'est le cas lorsque l'on utilise -width et -height.

Figure 3.13 : Exemples de boutons avec -padx => 20, -pady => 20

N'oubliez pas que -padx et -pady sont ignorées lorsqu'un bitmap est affiché.

III-A-12 - Ajouter un raccourci clavier

Un bouton est normalement employé par l'utilisateur via un clic du bouton 1 de la souris lorsque le curseur se trouve au-dessus de lui. Il peut aussi être activé en pressant la touche de tabulation jusqu'à ce que le bouton reçoive le focus, puis en appuyant sur la barre d'espace. Les effets sont les mêmes : la fonction de rappel associée au bouton est appelée et la valeur de l'option -relief est temporairement modifiée. Le focus clavier est matérialisé par un fin rectangle noir, tracé autour du widget (voir la figure 3.20, plus loin dans ce chapitre).

Pour activer le bouton en utilisant une autre touche du clavier, on peut utiliser l'option -underline dans un bouton affichant du texte :

-underline => N

Cela soulignera le nième caractère de la chaîne, le premier ayant la position 0. Avec la chaîne « Quitter », underline => 0 soulignera le premier caractère, le « Q » (voir la figure 3.14).

La valeur par défaut de underline est -1, ce qui signifie qu'aucun caractère ne sera souligné dans la chaîne.

Figure 3.14 : Exemple avec -underline => 0

III-A-13 - Options de couleur

Les options -background, -foreground, -activebackground, -activeforeground, et -disabledforeground permettent de modifier les couleurs d'un bouton. Chacune d'elles a pour valeur une chaîne identifiant une couleur. Cette dernière peut être le nom de la couleur, comme "blue", ou une valeur hexadécimale, comme "#d9d9d9", dénotant aussi une couleur, mais de façon beaucoup plus sibylline.

Sous Win32, comme avec les systèmes Unix, vous pouvez exécuter le script de démonstration widget, livré avec le module Perl/Tk. Si le répertoire des binaires Perl se trouve dans la liste de vos chemins par défaut, il suffit de taper widget sur la ligne de commande DOS ou Unix. Dans la section consacrée aux boîtes de liste se trouve un exemple affichant les noms des couleurs. Vous pouvez double-cliquer sur les noms de cette liste pour constater les changements de couleur de l'application. Les valeurs admises pour le nom de la couleur sont énumérées dans le fichier rgb.txt sur votre système Unix. Normalement, ce fichier se trouve dans le répertoire des bibliothèques X11. Sur mon système Linux, il se trouve dans /usr/X11R6/lib/X11. Sous X Window, vous pouvez également utiliser les applications xcolors ou showrgb. Consultez les pages de manuel de ces commandes pour les utiliser au mieux.

Le répertoire de votre distribution Perl est également un bon endroit pour rechercher les noms de couleurs (et cela s'applique aussi à Win32). Recherchez le fichier xcolors.h : il s'agit d'un fichier texte contenant les valeurs et les noms RGB d'un bon nombre de couleurs. Sur ma machine Windows 95, ce fichier se trouve dans le répertoire C: \Perl\lib\site\Tk\ptk.

La couleur d'un bouton dépend de son état courant. Lorsque celui-ci est 'normal', les couleurs affectées aux options -foreground et -background s'appliquent. Le fond du bouton est la zone située derrière la chaîne de texte, dans les limites du bouton ; sa couleur est indiquée de la façon suivante :

-background => couleur

La couleur par défaut du fond est gris clair ("#d9d9d9" en représentation RGB hexadécimale). La figure 3.15 montre ce que cela donne de colorer en bleu(17) le fond du second bouton « Fin ».

Figure 3.15 : Exemple avec -background => 'blue'

Le premier plan d'un bouton est le texte (ou le bitmap) affiché. Sa couleur est précisée ainsi :

-foreground => couleur

Par défaut, -foreground vaut 'black'. Quelle que soit la couleur choisie, si vous voulez qu'elle soit visible, assurez-vous qu'elle soit assez contrastée par rapport à celle du fond. Dans l'exemple de la figure 3.15, la couleur du texte était celle par défaut et elle ne se détachait pas très bien de la couleur de fond du bouton. Si l'on met la valeur de -foreground à 'white', le texte sera bien plus lisible, comme vous pouvez le constater avec la figure 3.16. -fg est un raccourci pour -foreground qui peut ne pas fonctionner avec certains autres types de widgets. Pour cette raison, je vous conseille de conserver -foreground comme nom d'option.

Figure 3.16 : Exemple avec -background => 'blue' et -foreground => 'white'

Lorsque l'on utilise les options -foreground et -background avec un bitmap, le premier plan et le fond de celui-ci seront des couleurs spécifiées. Les effets de ces couleurs dépendent du bitmap. Un exemple est donné à la figure 3.17.

Figure 3.17 : Bitmap 'error' avec -foreground => 'white' et -background => 'black'

Les options -foreground et -background contrôlent les couleurs du bouton lorsqu'il est dans un état 'normal'. Lorsque le curseur de la souris est au-dessus de lui, les couleurs -activeforeground et -activebackground sont utilisées.

-activebackground => couleur, -activeforeground => couleur

Ces couleurs sont différentes, car nous voulons que l'utilisateur puisse se rendre compte qu'il peut presser le bouton. En faisant légèrement varier les couleurs lorsque le curseur de la souris passe au-dessus du bouton, il sait qu'il peut cliquer dessus pour faire quelque chose. La valeur par défaut de -activebackground est un gris légèrement plus foncé ("#ececec").

La dernière option de couleur, -disabledforeground, est la couleur du texte lorsque l'état du bouton est 'disabled'.

-disabledforeground => couleur

Lorsque le bouton est désactivé, il ne répondra pas lorsque le curseur de la souris se trouve au-dessus de lui, ou lorsqu'il est pressé. La couleur par défaut du texte (ou du bitmap) est "#a3a3a3". La figure 3.18 montre la différence entre les couleurs du texte d'un bouton désactivé et d'un bouton normal (nous avons déjà vu cet exemple avec la figure 3.4, retournez voir le code ayant permis de créer cette fenêtre).

Figure 3.18 : Exemple avec -disabledforeground

III-A-14 - Modification du curseur de la souris

Le curseur de la souris ressemble normalement à une flèche(18). Il peut être modifié pour chaque widget avec l'option -cursor :

-cursor => nom_curseur

Lorsque la souris est au-dessus du bouton, le curseur changera pour prendre l'aspect spécifié. Cette modification aura lieu, que le bouton soit désactivé ou non. Voici une liste de l'ensemble des curseurs disponibles.

X_cursor

arrow

based_arrow_down

based_arrow_up

boat

bogosity

bottom_left_corner

bottom_right_corner

bottom_side

bottom_tee

box_spiral

center_ptr

circle

clock

coffee_mug

cross

cross_reverse

crosshair

diamond_cross

dot

dotbox

double_arrow

draft_large

draft_small

draped_box

exchange

fleur

gobbler

gumby

hand1

hand2

heart

icon

iron_cross

left_ptr

left_side

left_tee

leftbutton

ll_angle

lr_angle

man

middlebutton

mouse

pencil

pirate

plus

question_arrow

right_ptr

right_side

right_tee

rightbutton

rtl_logo

sailboat

sb_down_arrow

sb_h_double_arrow

sb_left_arrow

sb_right_arrow

sb_up_arrow

sb_v_double_arrow

shuttle

sizing

spider

spraycan

star

target

tcross

top_left_arrow

top_left_corner

top_right_corner

top_side

top_tee

trek

ul_angle

umbrella

ur_angle

watch

xterm

Voici un programme interactif permettant de visualiser les différents curseurs :

Figure 3.19 : Les curseurs standards

#!/usr/bin/perl -w
use Tk;

## Crée les éléments de la fenêtre
$mw = MainWindow->new;
$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack(
-side => "bottom",
-fill => "x"
);
$defilement = $mw->Scrollbar;
$liste      = $mw->Listbox(
-selectmode     => 'single',
-yscrollcommand => [ set => $defilement ]
);

$defilement->configure( -command => [ yview => $liste ] );

$defilement->pack( -side => 'right', -fill => 'y' );
$liste->pack( -side => 'left', -fill => 'both' );

## Ouvre le fichier contenant tous les curseurs disponibles
## Vous pouvez devoir le modifier si votre cursorfont.h est ailleurs
## Sur les systèmes Win32, recherchez C:\Perl\lib\site\Tk\X11\cursorfont.h
open( CURSEURS, "/usr/X11R6/include/X11/cursorfont.h" )
|| die "Impossible d'ouvrir le fichier des curseurs.\n";

while (<CURSEURS>) {
push( @curseurs, $1 ) if (/\#define XC_(\w+) /);
}

close(CURSEURS);

$liste->insert( 'end', sort @curseurs );
$liste->bind(
'<Button-1>',
sub {
$mw->configure( -cursor => $liste->get( $liste->curselection ) );
}
);

MainLoop;

Bien que ce programme puisse sembler un peu compliqué à ce moment du livre, regardez comment il fonctionne. Si vous ne comprenez pas tout tout de suite, c'est normal. Continuez à lire quelques chapitres et revenez le consulter jusqu'à ce que vous le compreniez. Les boîtes de liste sont traitées au chapitre 7, Le widget boîte de liste, et bind est expliquée au chapitre 14, Liaisons d'événements.

III-A-15 - Options du focus

On peut tabuler entre les widgets d'une application pour les rendre accessibles à des entrées effectuées via le clavier. L'application indique qu'un widget est disponible pour une saisie au clavier en traçant autour de lui une bordure noire, appelée « rectangle de focus » (highlight rectangle, en anglais), comme montré par la figure 3.20. Si un widget est entouré de cette bordure, on dit qu'il a le focus de l'application (on peut forcer une application à donner le focus à un widget spécifique lorsqu'elle démarre en faisant $widget->focus;). Lorsqu'un bouton a le focus, on peut le presser avec la barre d'espace du clavier au lieu d'utiliser la souris.

Figure 3.20 : Le premier bouton a le focus d'entrée

On peut faire en sorte que l'application empêche un bouton de recevoir le focus clavier à l'aide de l'option -takefocus :

1. -takefocus => 0 | 1 | undef

L'option -takefocus vaut normalement la chaîne vide (undef), ce qui permet à l'application de décider dynamiquement si le widget acceptera le focus ou non. Si un widget est dans l'état 'disabled', il sera ignoré lors des tabulations de widget en widget. Pour qu'une application ignore toujours le widget lors de ces tabulations, on utilise -takefocus => 0. Pour qu'elle autorise toujours le focus pour celui-ci, on utilise -takefocus => 1.

III-A-16 - Modification du rectangle de focus

Le rectangle de focus(19) est normalement affiché avec une épaisseur de 2 pixels. On peut modifier cela en utilisant l'option -highlightthickness :

-highlightthickness => montant

Le montant spécifié est une distance d'écran quelconque. Le bouton « Fin », à droite de la figure 3.21, a -highlightthickness à 10 et possède le focus.

Figure 3.21 : Exemple avec -highlightthickness => 10

Lorsqu'un bouton n'a pas le focus clavier, il est entouré d'un petit espace. Si cela vous ennuie, vous pouvez mettre -highlightthickness à 0, et cet espace ne s'affichera plus, même si ce widget a le focus. C'est une mauvaise façon de programmer que de mettre -highlightthickness à 0 si vous n'avez pas aussi mis -takefocus à 0.

La couleur du rectangle de focus peut également être modifiée. Pour ce faire, on utilise deux valeurs : la couleur du rectangle de focus lorsque le bouton le détient, et celle qu'il aura sinon. L'option -highlightcolor est la couleur que prend le rectangle de focus lorsque le bouton le détient :

-highlightcolor => couleur

La figure 3.22 montre le bouton droit avec le focus et -highlightcolor à 'yellow'. Comparez-la à la figure 3.21 pour constater la différence.

Figure 3.22 : Exemple de bouton avec -highlightcolor => 'yellow'

Pour changer la couleur de l'espace autour du bouton lorsqu'il n'a pas le focus, on utilise l'option -highlightbackground :

-highlightbackground => couleur

Normalement, le rectangle de focus a la même couleur que le fond de la fenêtre, ce qui permet de le confondre avec le fond de la fenêtre ou du cadre contenant le bouton.

La figure 3.23 montre un exemple où les deux boutons ont la configuration suivante :

-highlightcolor => 'blue', -highlightbackground => 'yellow'

Le bouton de droite est celui qui possède le focus.

Figure 3.23 : Boutons avec -highlightcolor => 'blue' et -highlightbackground => 'yellow'

III-A-17 - Configuration d'un bouton

Après avoir créé le widget et sauvegardé une référence vers celui-ci dans un scalaire (comme $bouton), on peut appliquer des méthodes sur ce bouton.

Deux méthodes permettent de configurer un bouton après sa création et d'obtenir des informations sur sa configuration courante : configure et cget. Elles fonctionnent avec tous les widgets et sont décrites dans l'annexe A, Configuration des widgets avec configure et cget. Voici quelques exemples courants pour vous mettre sur les rails :

$etat = $bouton->cget( -state );         # Obtient la valeur courante de -state
$etat = $bouton->configure( -state );    # Obtient la valeur courante de -state
$bouton->configure( -text => "Nouveau texte" );    # Modifie le texte
$texte = $bouton->cget( -text );                   # Obtient la valeur courante de -text
@tout  = $bouton->configure();                     # Obtient des infos sur toutes
# les options du bouton

III-A-18 - Clignotement d'un bouton

La méthode flash fait « clignoter » le bouton à l'écran. Il passe sans cesse de la couleur correspondant à l'état normal à la couleur correspondant à l'état actif, et réciproquement.

$bouton->flash();

III-A-19 - Invocation d'un bouton

La méthode invoke appelle la fonction sur laquelle pointe l'option -command. Dès lors que vous avez utilisé -command pour assigner la fonction de rappel, vous pouvez utiliser invoke à chaque fois que vous avez besoin de réaliser la même opération :

$bouton->invoke();

III-B - Essayez et amusez-vous !

L'un des meilleurs moyens de comprendre comment fonctionne Perl/Tk est de l'essayer. Lorsque les bases ont été comprises, vous passerez le plus gros de votre temps à essayer les options et les fonctions de rappel pour obtenir les résultats voulus. Voici quelques voies à explorer pour mieux comprendre le widget bouton :

- créez une fenêtre contenant trois boutons et faites en sorte que chaque bouton affiche quelque chose de différent lorsqu'il est cliqué ;

- créez une fenêtre contenant trois boutons et faites en sorte que les deux premiers changent mutuellement leur texte lorsqu'ils sont pressés. Le dernier bouton doit vous permettre de quitter le programme ;

- créez de très gros, et de très petits, boutons dans la même fenêtre.

IV - Cases à cocher et boutons radio

Ce chapitre traite des widgets cases à cocher (checkbutton) et des boutons radio (radiobutton). Bien qu'ils se ressemblent beaucoup, on les utilise dans des buts différents.

Les cases à cocher servent à sélectionner autant d'éléments que nécessaire, comme on le fait, par exemple, avec une liste de courses. Les boutons radio servent dans des situations où l'on doit faire un choix dans un groupe d'éléments, comme dans le cas d'un examen de type QCM (questionnaire à choix multiples) :

Q1 : En quelle année Christophe Colomb a-t-il découvert l'Amérique ?
A) 1400
B) 1470
C) 1472
D) 1492
E) Aucune des dates précédentes

Comme les boutons radio sont regroupés, on ne peut sélectionner qu'un et un seul bouton par groupe. Si le choix par défaut est toujours A, et que l'on clique sur D, A sera automatiquement désélectionné.

Les deux sections de ce chapitre traitent de l'utilisation de ces deux types de widgets et de la façon de les configurer.

IV-A - Le widget Checkbutton

Au chapitre 3, Le bouton de base, nous avons vu les options associées au widget bouton. Une case à cocher est considérée comme un bouton particulier (et elle utilise quasiment les mêmes options), bien que la façon dont elle est utilisée dans une application diffère de celle d'un bouton standard.

Au lieu de cliquer sur une case à cocher pour que quelque chose se passe immédiatement, on l'utilise pour indiquer une réponse de type « oui/non » : si la case est cochée, cela signifie « oui », si elle n'est pas cochée, cela signifie « non ». On pourrait, par exemple, utiliser des cases à cocher pour énumérer les options d'impression d'un document : les textes de ces cases pourraient être « Imprimer un en-tête de page », « Pages paires uniquement », « Pages impaires uniquement » et « Numérotation des pages ». Le bas de la fenêtre ne contiendrait qu'un bouton « Imprimer » et, lorsque l'on cliquerait dessus, le programme rechercherait les cases cochées et soumettrait la demande d'impression avec les options choisies.

On peut utiliser des cases à cocher pour des fenêtres énumérant plusieurs travaux à exécuter (c'est le cas des gestionnaires de traitement par lot) afin de demander à l'utilisateur quelles sont les tâches concernées. Si la case placée à côté de la tâche est cochée, celle-ci sera lancée, sinon elle ne sera pas prise en compte.

À chaque fois qu'une case à cocher est utilisée, l'application demande à l'utilisateur de répondre à une question de type « oui/non ». Les cases formant un groupe ont, typiquement, un rapport les unes avec les autres (comme dans l'exemple d'impression d'un document), mais cela n'est pas obligatoire, car la réponse à chaque case est indépendante de tous les autres widgets ou cases à cocher de l'écran.

Une case à cocher ressemble à un bouton ; elle affiche une chaîne de texte, mais possède aussi un indicateur dans sa partie gauche. Par défaut, les bords extérieurs de la case ne sont pas en relief comme ceux d'un bouton standard, mais l'indicateur (le petit carré à gauche) apparaît, lui, en trois dimensions.

Une case à cocher fonctionne de la même façon qu'un bouton de base : on clique dessus avec le bouton gauche de la souris. Un bouton changera la valeur de son option -relief (la façon dont ses bords sont tracés) afin de faire comme s'il avait été enfoncé, tandis qu'une case à cocher ne modifiera que l'état de son indicateur. Si la case est cochée, l'indicateur sera représenté comme s'il avait été enfoncé dans la fenêtre et rempli d'une couleur plus sombre(20).

La terminologie est parfois confuse entre l'état (ou la valeur) de l'indicateur et l'état de la case elle-même. Si l'indicateur ressemble à un petit bouton surélevé sans couleur, la case n'est pas cochée (case de gauche de la figure 4.1). S'il est coloré, elle est cochée (case de droite de la figure 4.1). L'état de la case complète (dont l'indicateur) peut être normal, actif ou désactivé. Les deux cases de la figure 4.1 sont dans l'état normal.

Comme pour toute création de widgets, une case à cocher est créée en utilisant une méthode portant le nom du widget avec sa première lettre en majuscule, Checkbutton, et appelée par le widget parent. L'utilisation classique en est :

$cc = $widget_parent->Checkbutton( [ option => valeur, . . . ] )->pack;

En plus de posséder un indicateur avec un état, une case à cocher a aussi une fonction de rappel associée à son option -command. Cette fonction est appelée lorsque la case est cochée (quel que soit l'état de l'indicateur). Cependant, il n'est pas toujours nécessaire d'associer une fonction de rappel aux boutons radio et aux cases à cocher, car on peut vérifier leur état plus tard dans le programme.

L'état booléen d'une case à cocher est stocké dans une variable que l'on indique via l'option -variable à la création de la case. Chaque case à cocher doit utiliser sa propre variable pour stocker son propre état et, lorsqu'elle est cochée, cet état est mis à jour. Les options pouvant modifier le comportement d'une case à cocher sont énumérées ci-dessous et expliquées en détail plus loin.

IV-A-1 - Options des cases à cocher

Les options suivantes fonctionnent exactement comme celles des boutons de base, je ne les détaillerai donc pas. Reportez-vous au chapitre 3, Le bouton de base, pour la description complète des options suivantes : -activebackground, -activeforeground, -anchor, -background, -borderwidth, -cursor, -disabledforeground, -font, -foreground, -height, -highlightbackground,-highlightcolor,-highlightthickness, -justify, -padx, -pady, -state, -takefocus, -text, -textvariable, -underline, -width, et -wraplength.

Les autres options se comportent un peu différemment, ou n'existent que pour le widget Checkbutton. Elles sont décrites dans la liste suivante. Certaines options (comme -selectimage) ne concernent que l'indicateur. Rappelez-vous que l'option -state concerne le widget dans son ensemble, et que l'état de l'indicateur est géré par les options -onvalue, -offvalue, -indicatoron et -variable.

-activebackground => couleur

1. Configure la couleur que devra prendre le fond du widget lorsque le curseur de la souris est au-dessus de lui.

-activeforeground => couleur

1. Configure la couleur que devra prendre le texte lorsque le curseur de la souris sera au-dessus du widget.

-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' |'nw' | 'center'

1. Permet de positionner le texte à la position spécifiée dans le widget. Plus le widget est grand, plus cet effet est notable.

-background => couleur

1. Configure la couleur de fond du widget (derrière le texte).

-bitmap => nom_bitmap

1. Affiche nom_bitmap à la place du texte.

-borderwidth => montant

1. Change la largeur du bord tracé autour du widget. Modifie aussi la largeur de l'indicateur. La valeur par défaut est 2.

-command => fct_rappel

1. Associe une fonction au bouton, qui sera appelée lorsque le bouton sera pressé.

-cursor => nom_curseur

1. Indique que le curseur de la souris se changera en nom_curseur lorsqu'il sera au-dessus du widget.

-disabledforeground => couleur

1. Configure la couleur que devra prendre le texte lorsque -state sera 'disabled'.

-font => nom_fonte

1. Change la fonte du texte du widget.

-foreground => couleur

1. Change la couleur du texte en couleur.

-height => montant

1. Configure la hauteur du bouton : montant est exprimé en distances d'écran.

-highlightbackground => couleur

1. Configure la couleur du rectangle de focus autour du widget lorsque celui-ci n'a pas le focus.

-highlightcolor => couleur

1. Configure la couleur du rectangle de focus autour du widget lorsque celui-ci a le focus.

-highlightthickness => montant

1. Configure l'épaisseur du rectangle de focus.

-image => ptr_image

1. Affiche l'image ptr_image à la place du texte.

-indicatoron => 0 | 1

1. Précise s'il faut afficher l'indicateur.

-justify => 'left' | 'right' | 'center'

1. Indique le bord du widget contre lequel s'alignera le texte.

-offvalue => valeur

1. Donne la valeur utilisée lorsque la case n'est pas cochée. Par défaut, la valeur, qui doit être un scalaire, est 0.

-onvalue => valeur

1. Donne la valeur utilisée lorsque la case est cochée. Par défaut, la valeur, qui doit être un scalaire, est 1.

-padx => montant

1. Précise l'espace à laisser entre le texte/indicateur et les bords gauche/droit du widget.

-pady => montant

1. Précise l'espace à laisser entre le texte/indicateur et les bords haut/bas du widget.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie le type du contour tracé autour du widget.

-selectcolor => couleur

1. Indique la couleur de l'indicateur lorsqu'il est coché.

-selectimage => ptr_image

1. Indique l'image à afficher à la place du texte lorsque la case est cochée. Cette option est ignorée si -image n'est pas utilisée.

-state => 'normal' | 'disabled' | 'active'

1. Indique l'état de réactivité du widget. Si cette option vaut "disabled", il ne répondra jamais.

-takefocus => 0 | 1 | undef

1. Indique si le widget peut ou non prendre le focus.

-text => texte

1. Indique le texte affiché dans le widget.

-textvariable => \$variable

1. Pointeur vers une variable contenant le texte à afficher dans le widget.

-underline => n

1. Souligne le nième caractère du texte.

-variable => \$valeur

1. Affecte la valeur coché/non coché de l'indicateur à $valeur.

-width => montant

1. Configure la largeur du widget. montant est exprimé en distances d'écran.

-wraplength => montant

1. Indique que le texte passera à la ligne lorsqu'il dépassera ce montant.

IV-A-2 - Stockage de l'état de l'indicateur

L'option -variable associe une variable à l'état de l'indicateur en passant une référence comme valeur. Pour utiliser le scalaire $valeur, il faut ajouter le code suivant à la liste des options de l'appel à la méthode Checkbutton :

-variable => \$valeur

Tout comme l'option -textvariable indique la variable associée au texte de la case à cocher, cette option indique la variable associée à l'indicateur. Lorsque la case sera cochée, $valeur contiendra l'état de l'indicateur (la valeur placée dans $valeur est définie par -onvalue et -offvalue, et vaut 1 et 0, respectivement).

En plus d'utiliser la souris pour modifier l'état de l'indicateur, vous pouvez également modifier directement $valeur. Si votre code contient $valeur = 1 à un endroit donné, l'indicateur sera coché. Vous pouvez écrire $valeur = 1 avant de créer la case à cocher afin de cocher l'indicateur de celle-ci lors de sa création. Si vous changez la valeur de $valeur après la création de la case, celle-ci se modifiera pour refléter cette nouvelle valeur. La fonction associée à -command (s'il y en a une) n'est pas appelée lorsque la valeur de $valeur est modifiée.

L'utilisation de -variable est habituellement la façon la plus simple de positionner l'état de l'indicateur de la case. Voici un exemple comportant deux boutons permettant de changer la valeur contenue dans $cc_valeur :

$cc_valeur = 0;
$cc        = $mw->Checkbutton(
-text     => "Case à cocher",
-variable => \$cc_valeur,
-command  => sub { print "Cochée ! $cc_valeur\n" }
)->pack( -side => 'top' );
$mw->Button(
-text    => "Case cochée",
-command => sub { $cc_valeur = 1 }
)->pack( -side => 'left' );
$mw->Button(
-text    => "Case non cochée",
-command => sub { $cc_valeur = 0 }
)->pack( -side => 'left' );

La figure 4.2 montre le résultat obtenu :

Figure 4.2 : Boutons modifiant la valeur d'une case à cocher

La valeur stockée dans $cc_valeur peut être modifiée de trois façons différentes : en cliquant sur la case à cocher, en cliquant sur le bouton « Case non cochée », ou en cliquant sur le bouton « Case cochée ». Vous ne verrez s'afficher « Cochée ! » dans la fenêtre de terminal à partir de laquelle vous avez lancé le script que si vous cliquez sur la case à cocher. Il existe d'autres façons de changer la valeur associée à une case : étudiez les méthodes invoke, select, deselect et toggle plus loin dans ce chapitre.

IV-A-3 - Affectation d'une fonction de rappel

L'option -command fonctionne exactement comme elle le fait avec un bouton standard. Cependant, la fonction de rappel associée à une case à cocher réalise, habituellement, des tâches moins évidentes. Souvent, il n'y a pas de fonction associée à une case à cocher, car l'information importante est l'état de cette case, plutôt que la détection du cochage de celle-ci.

L'une des choses que peut faire une case à cocher est de modifier l'apparence de la fenêtre. Une telle case pourrait être celle de la figure 4.3.

Lorsque l'utilisateur coche la case, la fenêtre se transforme comme par magie pour ressembler à celle de la figure 4.4.

Voici le code qui réalise ce tour de magie :

Figure 4.3 : Case qui affichera les autres widgets si elle est cochée

Figure 4.4 : La fenêtre, après avoir coché la case

#!/usr/bin/perl -w
use Tk;

my $mw = MainWindow->new;

$mw = MainWindow->new;
$mw->title("Case à cocher");

## On crée les autres widgets, mais on ne les place pas encore !
for ( $i = 1 ; $i <= 5 ; $i++ ) {
push( @boutons, $mw->Button( -text => "Bouton $i" ) );
}

$mw->Checkbutton(
-text     => "Affiche tous les widgets",
-variable => \$cc_valeur,
-command  => sub {
if ($cc_valeur) {
foreach (@boutons) {
$_->pack( -side => 'left' );
}
}
else {
foreach (@boutons) {
$_->pack('forget');
}
}
}
)->pack( -side => 'top' );

MainLoop;

Pour pouvoir afficher des widgets plus tard dans le programme, on les crée d'abord et on stocke leurs références dans le tableau @boutons. Les boutons de cet exemple ne sont pas très utiles, car ils n'ont même pas de -command associée. Normalement, ils devraient avoir chacun une tâche spécifique à réaliser lorsqu'ils sont cliqués ; dans notre exemple, nous voulons simplement qu'ils existent.

Puis, nous créons notre case à cocher magique. Lorsque cette case est cochée (quel que soit l'état de son indicateur), elle appelle la fonction pointée par -command. Cette fonction examine la valeur courante de $cc_valeur, affiche les boutons si la case est cochée et les cache si elle ne l'est pas. La valeur de $cc_valeur est modifiée avant que cette fonction ne soit appelée. Lorsque la case est à nouveau cochée, les autres boutons seront supprimés de la fenêtre et celle-ci se réduira pour retrouver la taille qu'elle avait précédemment.

Ce type de configuration est très pratique lorsque l'on souhaite conserver une fenêtre de base tout en gardant la possibilité d'afficher des widgets supplémentaires si l'utilisateur désire utiliser les fonctions avancées proposées par ceux-ci. On peut, par exemple, créer une fenêtre de recherche disposant d'un emplacement pour entrer du texte, d'un bouton pour lancer la recherche et d'une case à cocher pour les recherches plus élaborées. Cocher cette case ajoutera d'autres widgets en bas de la fenêtre afin de permettre de traiter les différences entre majuscules et minuscules, les expressions rationnelles, et d'utiliser d'autres mécanismes de recherche plus élaborés.

IV-A-4 - Valeurs de cochage

Si vous n'aimez la valeur de cochage par défaut, 1, vous pouvez utiliser l'option -onvalue pour la modifier :

-onvalue => valeur ## la valeur par défaut est 1

Même chose pour la valeur associée à une case non cochée :

-offvalue => valeur ## la valeur par défaut est 0

Ces options changeront les valeurs stockées dans $variable. Selon la façon dont vous voulez que la case à cocher interagisse avec le reste de votre application, il est parfois préférable d'utiliser des valeurs différentes. La nouvelle valeur peut être n'importe quoi, pourvu que ce soit une valeur scalaire : cela signifie que l'on peut utiliser des références à des tableaux et à des hachages, si nécessaire.

Il est préférable de conserver l'opposition des significations de -onvalue et -offvalue. Si -onvalue contient, à un instant donné, la chaîne « ON », -offvalue devrait logiquement valoir « OFF ». Bien sûr, si le but de cette case à cocher est d'utiliser une valeur plus précise de Π , -onvalue pourrait contenir « 3.14159265359 », et -offvalue « 3.14 ».

Prenez garde à l'utilisation de valeurs inhabituelles pour -onvalue et -offvalue. Si vous configurez -variable avec quelque chose qui n'est égal ni à l'une ni à l'autre, la case sera considérée comme non cochée, même si la valeur associée à -variable est différente de celle associée à -offvalue. Si, par exemple, on utilise la configuration -onvalue => 1, -offvalue => 0, -variable => \$variable, avec $variable valant 3, la case sera considérée comme non cochée.

IV-A-5 - Couleur de l'indicateur

L'option -selectcolor permet de modifier la couleur que prend l'indicateur lorsque la case est cochée :

-selectcolor => couleur

La valeur par défaut est "#b03060" (rose foncé). La modification de cette valeur changera aussi la couleur du fond de la case lorsqu'elle est cochée et que l'option -indicatoron vaut 0.

IV-A-6 - Masquage de l'indicateur

L'un des signes distinctifs entre une case à cocher et un bouton standard est la présence d'un indicateur. On utilise l'option -indicatoron pour demander à Perl/Tk de ne pas tracer cet amusant petit carré :

1. -indicatoron => 0 | 1

Ainsi que nous l'avons vu dans les exemples précédents, la valeur par défaut associée à -indicatoron est 1 (afficher l'indicateur). Si l'on remplace cette valeur par 0, la case à cocher ressemblera presque à un bouton normal (sans autant d'espace autour du texte). Malgré cette ressemblance, son comportement lors d'un clic (pour cocher l'indicateur, qui est caché) est entièrement différent (voir la figure 4.5). Notez que l'option -relief est complètement ignorée lorsque -indicatoron est positionnée à 0.

Figure 4.5 : Case à cocher avec -indicatoron => 0. La fenêtre gauche n'est pas cochée. Celle de droite est cochée.

Dans cet exemple, la couleur du fond de la case cochée est celle de -selectcolor, pas de -backgroundcolor. On peut utiliser une configuration sans indicateur si l'on change le texte de la case afin de refléter son nouvel état. Par exemple, lorsque l'on passe de « Connexion autorisée » à « Connexion non autorisée ».

IV-A-7 - Affichage d'une image à la place du texte

Comme avec un bouton normal, on peut utiliser l'option -image afin de remplacer le texte d'une case à cocher par une image. Une autre option, -selectimage, permet d'afficher une image différente lorsque la case a été cochée :

-image => $ptr_img1 [ , -selectimage => $ptr_img2 ]

L'option -selectimage est tributaire de l'utilisation de -image, d'où la notation utilisée dans cette ligne.

Les $ptr_img peuvent être créés en utilisant les mêmes méthodes que celles utilisées au chapitre 3 avec l'option -image :

$fleche = $mw->Photo(-file => "nextart.gif");

L'image sera mise sur la case à la place du texte. Ces options ont priorité sur -text ; si -text et -image sont utilisées, -text sera ignorée.

L'image affichée dépend du fait que la case soit cochée ou non. Si seule l'option -image est spécifiée, cette image sera affichée quel que soit l'état de la case. Si -selectimage est aussi spécifiée, l'image qui lui est associée s'affichera lorsque la cache est cochée. La figure 4.6 montre un exemple utilisant les deux options, dont les cases à cocher ont été créées avec le code suivant :

$img1 = $mw->Bitmap( -file => "/usr/X11R6/include/X11/bitmaps/woman" );
$img2 = $mw->Bitmap( -file => "/usr/X11R6/include/X11/bitmaps/xlogo32" );

$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack( -side => 'bottom' );

$mw->Checkbutton(
-text        => "Case à cocher",
-image       => $img1,
-selectimage => $img2,
-variable    => \$cc_valeur
)->pack( -side => 'top' );

Figure 4.6 : La même fenêtre avec la case non cochée (à gauche) et cochée (à droite)

L'utilisation de deux images différentes pour indiquer qu'une case est cochée ou non a sûrement plus de sens si l'on configure aussi l'option -indicatoron à 0. On peut, par exemple, vouloir indiquer qu'un document est verrouillé (en lecture seule) ou déverrouillé en utilisant l'image d'un cadenas fermé pour -image et d'un cadenas ouvert pour -selectimage.

Pour l'exemple précédent, j'ai choisi d'utiliser des fichiers bitmap comme images. Au lieu d'utiliser l'option -image pour afficher un bitmap, on peut utiliser directement l'option -bitmap. Celle-ci est rigoureusement la même que celle du bouton standard ; elle remplace le texte de la case par le bitmap spécifié (voir la figure 4.7).

Figure 4.7 : Case à cocher avec -bitmap => 'warning'

À la différence des options -image et -selectimage, l'utilisation de -bitmap ne modifiera pas l'image lorsque la case sera cochée ou décochée.

On pourrait penser que l'utilisation d'images à la place de textes rend une application plus facile à comprendre pour les utilisateurs ne parlant pas notre langue. Cependant, si l'on utilise trop de cases avec des images, on peut arriver à rendre les choses plus confuses encore pour les utilisateurs. Quelques icônes aisément compréhensibles sont préférables à un ensemble important d'icônes peu parlantes.

IV-A-8 - Style d'une case à cocher

Bien qu'un bouton et une case à cocher partagent tous deux les options -relief et -borderwidth, et qu'elles signifient exactement la même chose pour ces deux types de widget, leurs effets diffèrent lorsqu'elles sont utilisées avec une case à cocher : cela à cause de l'indicateur. Rappelons les valeurs possibles de ces deux options :

1. -relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

2. -borderwidth => montant

La figure 4.8 montre les différents types de contours lorsque l'on utilise la valeur par défaut de -borderwidth. La valeur par défaut de -relief est 'flat', car le contour extérieur d'une case à cocher n'est pas modifié lorsque l'on clique sur la case ; seul l'indicateur change. La figure 4.8 montre aussi que les bords de la case à cocher sont beaucoup plus près du texte ; les valeurs par défaut de -padx et -pady sont inférieures à celles d'un bouton. L'option -relief n'affecte pas l'indicateur.

L'option -borderwidth affecte à la fois le contour de la case et l'indicateur se trouvant dans celle-ci. L'indicateur lui-même reste de la même taille, quelle que soit la valeur de -borderwidth de la case, mais ses bords changent d'épaisseur. Lorsque l'on utilise une grande valeur, on obtient des résultats intéressants, montrés par la figure 4.9.

Figure 4.8 : Exemple de toutes les valeurs possibles de -relief

Figure 4.9 : Exemple avec -borderwidth => 4

Nous avons utilisé une valeur de 4 pour -borderwidth, et l'on peut constater que les contours sont devenus un peu plus épais, de même pour les bords de l'indicateur. Avec une valeur plus importante de -borderwidth, il y a beaucoup moins de place pour afficher la couleur de l'indicateur lorsqu'il est coché : voyez-vous le minuscule carré, au milieu de ces indicateurs ?

Figure 4.10 : Exemple avec -borderwidth => 10

Dans la figure 4.10, -borderwidth vaut 10. Remarquez la différence ! On ne peut plus voir du tout l'indicateur, bien qu'il y ait encore suffisamment de place pour lui. Que ces cases soient cochées ou non, il n'y a aucun moyen de connaître leur état courant, car l'indicateur est invisible.

Je recommande fortement de ne pas utiliser l'option -borderwidth avec une case à cocher à cause de cet effet de bord.

IV-A-9 - Configuration d'une case à cocher

Comme le widget bouton, une case à cocher dispose de méthodes permettant de la manipuler après sa création. Celles-ci peuvent être appelées à tout moment après la création de la case à cocher, même avant qu'elle ne soit affichée à l'écran.

Les méthodes configure et cget peuvent également être utilisées, elles sont décrites à l'annexe A, Configuration des widgets avec configure et cget.

IV-A-10 - Cocher ou décocher une case

On peut forcer une case à passer de l'état coché à l'état non coché, et vice versa, à l'aide des méthodes deselect et select.

La méthode deselect positionnera toujours l'indicateur comme étant non coché et la variable assignée par l'option -variable recevra la valeur de l'option -offvalue :

$cc->deselect();

L'opposé de deselect, select, forcera l'indicateur dans l'état coché et la variable assignée par -variable à la valeur de -onvalue :

$cc->select();

Ces deux méthodes sont ignorées si l'option -state vaut 'disabled'.

On peut aussi commuter l'indicateur d'un état à l'autre à l'aide de la méthode toggle :

$cc->toggle();

L'appel de toggle ne provoque pas celui de la fonction de rappel associée à l'option -command.

IV-A-11 - Clignotement d'une case à cocher

On peut faire clignoter l'indicateur en utilisant la méthode flash. Les couleurs basculeront entre la couleur définie par -background et celle définie par -foreground :

$cc->flash();

IV-A-12 - Invocation d'une case à cocher

Pour réaliser la même action qu'en cochant la case avec le bouton gauche de la souris, il suffit d'appeler la méthode invoke :

$cc->invoke();

Cela forcera l'appel de la fonction de rappel associée à -command ; cela passera aussi l'indicateur d'un état à l'autre.

IV-B - Le widget Radiobutton

Un bouton radio ressemble à une case à cocher, car il contient aussi un indicateur sur son côté gauche. Cet indicateur est un losange plutôt qu'un carré : comme avec une case à cocher, il est en 3D et apparaît légèrement surélevé lorsque le bouton n'est pas sélectionné.

La différence principale entre un bouton radio et une case à cocher concerne les rôles qu'ils jouent dans une application. Un bouton radio sert à sélectionner un choix parmi plusieurs possibilités :

- dans un questionnaire à choix multiples, les réponses A, B, C, D ou E ;

- la version d'un utilitaire que vous souhaitez utiliser ;

- le montant de vos revenus annuels : 0-120 000, 120 001-180 000, 180 001-240 000, 240 001 et plus ;

- votre type de nourriture préférée : bœuf, poulet, ou végétarienne.

Dans chaque exemple, une seule réponse convient. Cela n'aurait aucun sens, par exemple, d'avoir à la fois un salaire de 100 000 et de 200 000 francs. Lorsque vous répondez à un questionnaire à choix multiples, vous ne pouvez pas choisir toutes les réponses et espérer que le correcteur vous suive : vous ne devez en choisir qu'une.

Comme les boutons radio servent à faire un choix parmi plusieurs, il faut toujours en créer au moins deux(21). Cela n'aurait aucun sens de poser une question s'il n'y avait qu'une réponse possible. Les boutons radio sont toujours créés par groupes de deux ou plus.

IV-B-1 - Création de boutons radio

Jusqu'à maintenant, nous avons appris à ne créer qu'un seul widget à la fois. Comme les boutons radio font toujours partie d'un groupe, on a généralement besoin d'en créer plusieurs en même temps. On peut donc être plus efficace et créer tous les widgets aussi rapidement et simplement que possible. Je montrerai quelques moyens rapides pour créer un groupe de boutons radio.

Pour montrer comment tirer le meilleur parti des widgets, les exemples vont commencer à devenir un peu plus compliqués. Vous devriez maintenant connaître les bases de leur création et comment spécifier des options lors de cette phase. Je dirai souvent qu'une option fonctionne exactement comme avec le widget X et je vous renverrai au chapitre qui concerne ce dernier pour des explications plus complètes.

Les boutons radio ressemblent aux cases à cocher : comme elles, ils ont aussi une variable associée à l'état de l'indicateur (à l'aide de l'option -variable). Lorsque vous créez un groupe de boutons radio, utilisez la même variable pour tous les boutons du groupe. La valeur placée dans celle-ci changera en fonction du bouton sélectionné. Pour créer un autre groupe de boutons, il suffit d'associer une variable différente à celui-ci.

Comme chaque bouton radio du groupe pointe vers la même variable, les options -onvalue et -offvalue n'ont pas de raison d'être : -offvalue vaudrait ce que tout autre bouton voudrait qu'elle soit. Pour régler ce problème, les boutons radio utilisent l'option -value au lieu de -onvalue et -offvalue.

Comme premier exemple, nous allons créer un groupe de boutons radio indiquant la couleur de fond de notre fenêtre. Nous devons utiliser des couleurs correctes pour l'instruction $mw->configure(-background => couleur). En général, les noms simples de couleurs fonctionnent ; nous utiliserons donc les couleurs "red","yellow","green", "blue" et "gray" (qui sont les valeurs correspondant, respectivement, aux couleurs rouge, jaune, vert, bleu et gris).

Comme toujours, la création d'un bouton radio est réalisée par :

$br = $widgetparent->Radiobutton( [ option => valeur, . . . ] )->pack;

Voici le code permettant de créer le groupe de boutons radio contrôlant la couleur du fond :

# configure la valeur par défaut souhaitée
$br_valeur = "blue";
$mw->configure( -background => $br_valeur );

# hachage pour traduire les couleurs en français
%us_2_fr = (
red    => 'rouge',
yellow => 'jaune',
green  => 'vert',
blue   => 'bleu',
grey   => 'gris'
);

# crée les boutons radio qui nous permettront de changer le fond
foreach ( keys %us_2_fr ) {
$mw->Radiobutton(
-text     => $us_2_fr{$_},
-value    => $_,
-variable => \$br_valeur,
-command  => \&change_fond
)->pack( -side => 'left' );
}

# fonction changeant la couleur du fond selon $br_valeur
sub change_fond {
print "La couleur du fond est maintenant : $us_2_fr{$br_valeur}\n";
$mw->configure( -background => $br_valeur );
}

Nous stockons l'état du groupe des boutons radio dans $br_valeur. Celle-ci est initialisée à « blue », ce qui correspond au premier bouton créé. Lorsque l'un des boutons est sélectionné, y compris celui qui est déjà coché, la fonction change_fond est appelée. Celle-ci affichera la nouvelle valeur de $br_valeur, puis modifiera en conséquence la couleur de fond de notre fenêtre principale. Afin d'afficher le nom des couleurs en français, nous utilisons le hachage %us_2_fr afin de mettre en relation la valeur qui sera utilisée pour l'option -background de configure (qui est le nom de la couleur en anglais : « blue », par exemple) avec la valeur de l'option -text du bouton radio (qui sera en français : « bleu », par exemple). C'est aussi le texte français qui sera affiché par l'instruction print.

Une chose à noter : bien que nous ayons positionné la valeur par défaut de notre groupe de boutons à « blue », cela ne signifie pas que le fond de la fenêtre ait été aussi coloré en bleu. Pour ce faire, nous appelons la méthode configure en lui passant le contenu de $br_valeur. On aurait aussi pu faire la même chose en appelant explicitement la fonction change_fond, ou en coloriant la fenêtre lors de sa création.

La fenêtre obtenue est celle de la figure 4.11.

Figure 4.11 : Boutons radio modifiant la couleur de fond de la fenêtre

La meilleure façon de comprendre comment fonctionne cette fenêtre est de taper le code et de l'exécuter (ce conseil s'applique à beaucoup d'exemples de ce livre). Lorsque l'on clique sur chacun des boutons radio, on constate que les bandes de fenêtre, en haut et en bas, changent de couleur. On ne voit que ces petites bandes, car on a seulement modifié la couleur de fond de $mw, pas celles des boutons radio ou du bouton « Fin ».

Maintenant que nous avons vu une application de base des boutons radio, nous pouvons passer en revue chacune de leurs options.

IV-B-2 - Options des boutons radio

Comme pour les cases à cocher, les options suivantes sont les mêmes pour les trois types de boutons : -activebackground, -activeforeground, -anchor, -background, -borderwidth, -cursor, -disabledforeground, -font, -foreground, -height, -highlightbackground, -highlightcolor,-highlightthickness,-padx, -pady, -state, -takefocus, -text, -textvariable, -underline, et -width.

De plus, les options suivantes sont les mêmes, que ce soit pour une case à cocher ou pour un bouton radio : -command, -indicatoron, -image, -selectimage, -bitmap, -wraplength, -justify et -selectcolor.

Je détaillerai les autres, car elles se comportent un peu différemment, du fait du contexte dans lequel on les utilise.

-activebackground => couleur

1. Configure la couleur de fond qu'aura le widget lorsque la souris est au-dessus de lui.

-activeforeground => couleur

1. Configure la couleur qu'aura le texte du widget lorsque la souris est au-dessus de lui.

-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'

1. Configure la position du texte dans le widget. L'effet de cette option est d'autant plus notable que le widget est agrandi.

-background => couleur

1. Configure la couleur de fond du widget (derrière le texte).

-bitmap => nom_bitmap

1. Affiche ce bitmap à la place du texte.

-borderwidth => montant

1. Configure l'épaisseur des bords du widget. Modifie aussi l'épaisseur de l'indicateur. La valeur par défaut est 2.

-command => fct_rappel

1. Associe une fonction au bouton. Appelée lorsque le bouton est cliqué.

-cursor => nom_curseur

1. Indique que le curseur se transformera en nom_curseur lorsqu'il sera au-dessus du widget.

-disabledforeground => couleur

1. Précise la couleur du texte lorsque -state est 'disabled'.

-font => nom_fonte

1. Configure la fonte à utiliser pour afficher le texte dans le widget.

-foreground => couleur

1. Précise la couleur du texte.

-height => montant

1. Configure la hauteur du bouton. montant est une distance d'écran.

-highlightbackground => couleur

1. Configure la couleur du rectangle situé autour du widget lorsque celui-ci n'a pas le focus.

-highlightcolor => couleur

1. Configure la couleur du rectangle situé autour du widget lorsque celui-ci a le focus.

-highlightthickness => montant

1. Configure la largeur du rectangle de focus.

-image => ptr_img

1. Indique qu'une image sera mise à la place du texte.

-indicatoron => 0 | 1

1. Précise l'état de l'indicateur : 0 signifie que celui-ci ne sera pas affiché.

-justify => 'center'| 'left' | 'right'

1. Configure l'alignement du texte dans le widget.

-padx => montant

1. Configure l'espace qui sera laissé entre le texte/indicateur et les bords gauche/droit du widget.

-pady => montant

1. Configure l'espace qui sera laissé entre le texte/indicateur et les bords haut/bas du widget.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie l'aspect du contour du widget.

-selectcolor => couleur

1. Précise la couleur que prendra l'indicateur lorsqu'il est coché.

-selectimage => ptr_img

1. Précise qu'une image devra être affichée à la place du texte lorsque le bouton est activé. Cette option est sans effet si -image n'est pas utilisée.

-state => 'normal' | 'active' | 'disabled'

1. Configure l'état du widget. Si cette option vaut 'disabled', le bouton radio ne répondra à aucune entrée.

-takefocus => 0 | 1 | undef

1. Précise si le widget sera disponible pour prendre le focus ou non.

-text => chaîne

1. Indique le texte qui sera affiché dans le widget.

-textvariable => \$variable

1. Précise que le texte contenu dans $variable sera affiché dans le widget.

-underline => n

1. Souligne le nième caractère du texte du widget.

-value => valeur

1. Met valeur dans la variable spécifiée par -variable lorsque ce bouton radio est sélectionné (la valeur par défaut est 1).

-variable => \$variable

1. Précise la variable à utiliser lorsque le bouton radio est cliqué.

-width => montant

1. Donne la largeur du widget. montant est une distance d'écran.

-wraplength => montant

1. Force un retour à la ligne du texte lorsqu'il dépasse ce montant.

IV-B-3 - Utilisation de l'option -variable

L'option -variable aura le même aspect dans toutes les instructions de création de boutons radio sauf, qu'évidemment, on ne l'utilise pas de la même façon que pour les autres widgets. Plusieurs boutons radio peuvent partager la même variable plutôt que chacun ait sa propre variable distincte. L'exemple de la figure 4.11 montre comment cela fonctionne.

IV-B-4 - Configuration de la valeur d'un bouton radio

Une case à cocher avait deux options, -onvalue et -offvalue, car l'on devait se soucier de l'état de chacune des cases en particulier. Dans le cas des boutons radio, on ne s'occupe que de l'état d'un groupe entier. Chaque bouton radio doit avoir une valeur différente pour son option -value afin que l'examen de la variable désignée par -variable nous indique quel est le bouton sélectionné.

La valeur par défaut de -value est 1 (rappelez-vous : lorsque vous avez un groupe ne contenant qu'un seul bouton radio, celui-ci est toujours sélectionné).

Pour nous assurer que vous avez compris la différence existant entre les options -variable et -value, prenons un court exemple.

$mw->Radiobutton(
-text     => "Boeuf",
-value    => "Boeuf",
-variable => \$plat
);
$mw->Radiobutton(
-text     => "Poulet",
-value    => "Poulet",
-variable => \$plat
);
$mw->Radiobutton(
-text     => "Végétarien",
-value    => "Végétarien",
-variable => \$plat
);

Nous avons ainsi créé trois boutons radio utilisant tous la variable $plat pour y stocker leur valeur. Si l'utilisateur choisit « Bœuf », $plat contiendra la valeur « Bœuf », s'il choisit « Poulet », $plat contiendra la valeur « Poulet ». Plus tard, lorsque nous construirons le menu du jour afin de l'imprimer, nous pourrons consulter $plat afin de savoir ce que l'utilisateur souhaite commander.

IV-B-5 - Style d'un bouton radio

Bon, d'accord… l'option -relief fait la même chose qu'avec une case à cocher, mais cela vaut la peine de voir une copie d'écran montrant ce qui se passe lorsque l'on utilise les différents types de contours (voir la figure 4.12).

Figure 4.12 : Les différents types de contours d'un bouton radio

Comme pour une case à cocher, la modification de -borderwidth peut changer radicalement l'aspect du bouton (voir la figure 4.13).

Figure 4.13 : Boutons radio avec -borderwidth => 4

Vous vous rappelez comment l'indicateur disparaissait complètement d'une case à cocher lorsque l'on utilisait une valeur de 10 pour -borderwidth ? Eh bien, avec un bouton radio, cela le fait un peu ressembler à un cerf-volant (voir la figure 4.14). Vous ne pourrez pas non plus dire quel bouton est sélectionné ou pas, aussi je vous conseille de ne pas utiliser cette option.

Figure 4.14 : Boutons radio avec -borderwidth => 10

IV-B-6 - Configuration d'un bouton radio

Exactement comme avec nos autres widgets, on peut utiliser configure et cget pour obtenir, ou configurer, les valeurs des options de chaque bouton radio. Voir l'annexe A pour plus de détails sur l'utilisation de ces deux méthodes.

IV-B-7 - Sélection et désélection d'un bouton radio

Un bouton radio dispose aussi des méthodes select et deselect :

$br->deselect();
$br->select();

L'utilisation de select sélectionne le bouton radio et deselect le désélectionne (elle affecte une chaîne vide à la variable associée à -variable : si vous utilisez cette méthode, assurez-vous d'en tenir compte dans toutes les lignes de code évaluant cette variable). La commande associée à l'option -command sera aussi appelée, que ce soit avec select ou deselect.

IV-B-8 - Clignotement d'un bouton radio

La méthode flash fera clignoter la couleur de fond et la couleur du texte du bouton radio. À part ça, elle ne fait rien d'intéressant.

$br->flash();

IV-B-9 - Invocation d'un bouton radio

Pour sélectionner un bouton radio par programme, on utilise la méthode invoke :

$br->invoke();

Cela sélectionnera le bouton radio et appellera aussi la fonction de rappel associée à celui-ci via l'option -command. Cela fait la même chose que lorsque l'on clique sur le bouton radio avec la souris.

IV-C - Essayez et amusez-vous !

- créez un ensemble de cases à cocher et un bouton « Go », énumérant les états de chaque case ;

- réalisez un questionnaire utilisant des cases à cocher pour les questions pouvant avoir une ou plusieurs réponses, et des boutons radio pour les questions n'en admettant qu'une ;

- créez trois groupes de cases à cocher : « Couleur favorite », « Musique préférée » et « Pointure ». Puis, créez un bouton radio pour représenter chacun de ces groupes. Le bouton sélectionné décidera des cases à cocher que l'utilisateur pourra voir et utiliser.

V - Les widgets étiquette et de saisie

Vous souhaitez parfois que l'utilisateur saisisse des informations précises, telles que son nom, adresse, ou même un numéro de série. Le moyen le plus simple de s'acquitter de cette tâche consiste à utiliser des widgets de saisie (entry widget). On peut utiliser un widget étiquette (label widget) avec un widget de saisie afin d'indiquer clairement à l'utilisateur ce qu'il doit y mettre. Vous verrez que les combinaisons de widgets étiquette et de saisie sont le plus souvent utilisées dans une fenêtre de type « saisie dans une base de données », où se trouvent de nombreux éléments d'information que l'utilisateur doit fournir.

V-A - Le widget Label

Jusqu'à maintenant, nous n'avons parlé que de boutons, de boutons et encore de boutons. Que faire si l'on veut uniquement placer un simple texte d'information à l'écran ? Le widget étiquette fait exactement cela : une étiquette est comme un bouton qui ne ferait rien. C'est un widget non interactif qui, par défaut, ne peut avoir le focus clavier (ce qui signifie que l'on ne peut le sélectionner en utilisant une tabulation) et qui ne fait rien lorsque l'on clique dessus.

Le widget étiquette est probablement le widget le plus simple. Il ressemble à un bouton en ce qu'il peut afficher du texte (ou un bitmap), être mis en relief (il est plat, par défaut), afficher plusieurs lignes de texte, avoir une fonte différente, etc. La figure 5.1 montre une simple fenêtre, possédant un bouton et une étiquette, et créée à l'aide du code suivant :

use Tk;

$mw = MainWindow->new();
$mw->Label( -text => "Étiquette" )->pack();
$mw->Button( -text => "Fin", -command => sub { exit } )->pack();

MainLoop;

Figure 5.1 : Une simple fenêtre, avec une étiquette et un bouton

Voici quelques utilisations typiques d'une étiquette :

- on place une étiquette à gauche d'un widget de saisie pour que l'utilisateur sache quel est le type de donnée attendu ;

- on place une étiquette au-dessus d'un groupe de boutons radio, afin de préciser leur rôle (« Couleur de fond », par exemple). On peut faire la même chose avec des cases à cocher si celles-ci ont un rapport les unes avec les autres ou traitent du même thème ;

- on utilise une étiquette pour indiquer aux utilisateurs l'erreur qu'ils ont faite : « Le nombre saisi doit être compris entre 10 et 100 » (on utilisera généralement un widget de dialogue pour afficher ce type de messages, mais ce n'est pas obligatoire) ;

- on place une ligne d'information en bas d'une fenêtre. Tous les autres widgets devraient avoir un dispositif permettant d'afficher un texte décrivant leur rôle.

V-A-1 - Création d'une étiquette

La méthode permettant de créer une étiquette est Label : en voici une utilisation classique :

$etiquette = $parent->Label( [ option => valeur . . . ] )->pack();

Bien sûr, maintenant que vous avez l'habitude des méthodes de création d'un widget, vous savez que, lorsque vous créez une étiquette, vous pouvez préciser certaines options permettant de modifier son apparence et son comportement.

V-A-2 - Options des étiquettes

La liste suivante présente de manière exhaustive les options des étiquettes :

-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'

1. Permet de positionner le texte à la position spécifiée dans le widget. Plus le widget est grand, plus cet effet est notable.

-background => couleur

1. Configure la couleur de fond de l'étiquette.

-bitmap => bitmap

1. Affiche le bitmap bitmap à la place du texte.

-borderwidth => montant

1. Change la largeur des bords de l'étiquette.

-cursor => nom_curseur

1. Indique que le curseur de la souris se changera en nom_curseur lorsqu'il se trouvera au-dessus du widget.

-font => nom_fonte

1. Change la fonte du texte du widget.

-foreground => couleur

1. Change la couleur du texte (ou du bitmap) en couleur.

-height => montant

1. Configure la hauteur de l'étiquette : montant est exprimé en distances d'écran.

-highlightbackground => couleur

1. Configure la couleur du rectangle de focus autour du widget lorsque celui-ci n'a pas le focus.

-highlightcolor => couleur

1. Configure la couleur du rectangle de focus autour du widget lorsque celui-ci a le focus.

-highlightthickness => montant

1. Configure l'épaisseur du rectangle de focus. La valeur par défaut pour une étiquette est de 0.

-image => $ptr_img

1. Affiche l'image pointée par $ptr_img à la place du texte.

-justify => 'left' | 'right' | 'center'

1. Indique le bord de l'étiquette contre lequel s'alignera un texte de plusieurs lignes.

-padx => montant

1. Ajoute des espaces à gauche et à droite du texte dans l'étiquette.

-pady => montant

1. Ajoute des espaces au-dessus et en dessous du texte dans l'étiquette.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie le type du contour tracé autour de l'étiquette.

-takefocus => 0 | 1 | undef

1. Indique si le widget peut prendre le focus ou non.

-text => texte

1. Indique le texte affiché par l'étiquette.

-textvariable => \$variable

1. Pointeur vers une variable contenant le texte à afficher par l'étiquette. L'étiquette se modifiera automatiquement en même temps que $variable.

-underline => n

1. Souligne le nième caractère du texte. Permet à cette touche d'invoquer le widget lorsqu'il a le focus. La valeur par défaut est - (pas de caractère souligné).

-width => montant

1. Configure la largeur de l'étiquette à montant.

-wraplength => montant

1. Indique que le texte de l'étiquette passera à la ligne lorsqu'il dépassera ce montant.

Cette liste décrit brièvement chaque option. Certaines d'entre elles ont des valeurs par défaut différentes de celles déjà vues avec les différents widgets boutons, une étiquette se comporte donc un peu différemment de ceux-ci.

V-A-3 - Différences entre les étiquettes et les autres widgets

Lorsque nous avons créé des widgets boutons, nous pouvions cliquer dessus avec la souris, ou les sélectionner avec la touche de tabulation et les presser en utilisant le clavier. Un widget étiquette, par contre, n'interagit pas avec l'utilisateur : il est là dans le seul but d'informer et n'a donc pas d'option -command. On ne peut pas tabuler vers un widget étiquette, car rien ne se passerait.

La valeur par défaut de -takefocus, 0, rend l'étiquette non interactive. Lorsque l'on passe en tabulant sur les widgets de l'écran, le rectangle de focus nous indique quel est le widget qui a le focus clavier à un instant donné. Comme nous ne permettons pas à une étiquette d'avoir ce focus (rappelez-vous la valeur de 0 pour -takefocus), cela n'aurait pas de sens qu'elle ait un rectangle de focus visible. La valeur par défaut de l'option -highlightthickness d'une étiquette est 0. On peut matérialiser un rectangle autour d'une étiquette en mettant une valeur supérieure pour -highlightthickness et en configurant -highlightbackground avec une couleur comme bleu ou rouge.

Le widget étiquette n'a pas non plus d'option -state :comme on ne peut pas cliquer sur une étiquette, il n'y a aucune raison de la désactiver.

V-A-4 - Styles d'étiquettes

La figure 5.2 montre les effets de la modification de l'option -relief d'une étiquette. Notez que le contour du widget est très près du texte. À la différence d'un bouton, on ne souhaite généralement pas d'espace supplémentaire autour de l'étiquette (cet espace est contrôlé par les options -padx et -pady). Normalement, on souhaite qu'une étiquette se trouve le plus près possible du, ou des, widget(s) qu'elle décrit.

Vous remarquerez que j'aime voir ce que donnent les widgets lorsqu'ils ont des contours différents.

Cela aide parfois à déterminer l'endroit où un widget se termine, notamment dans le cas des widgets ayant une valeur par défaut égale à 'flat'. De plus, je change souvent le contour des différents widgets pour m'assurer de l'emplacement de chacun d'eux à l'écran. Lorsque l'on a créé 10 entrées et étiquettes ayant des noms abscons, il est facile de perdre le fil. De même, changer la largeur du bord d'un widget le fait ressortir. Bien sûr, je reconfigure toujours le contour et sa largeur avec des valeurs plus supportables avant de laisser quelqu'un d'autre exécuter le programme ! La couleur est aussi un bon moyen de diagnostiquer les interfaces créées.

Figure 5.2 : Étiquettes avec des contours différents. La fenêtre du bas a -borderwidth => 10

V-A-5 - Exemple de message d'état

J'utilise souvent les reliefs 'groove' ou 'ridge' lorsque je crée une étiquette d'aide ou d'état en bas d'une fenêtre. Je place cette étiquette à l'aide du gestionnaire d'espace pack en utilisant les options -side => 'bottom' et -fill => 'x'. Il y a deux façons d'utiliser une étiquette d'état :

- on configure la variable qui lui est associée pour qu'elle change au fur et à mesure que le programme progresse, et qu'elle prévienne l'utilisateur que le travail est en cours, ou que quelque chose se passe ;

- on fait en sorte qu'une étiquette d'aide donne des informations sur chacun des widgets de l'application lorsqu'ils ont le focus. Pour cela, on utilise la méthode bind.

Ces deux types d'utilisation sont montrés dans l'exemple de code suivant :

Ce code affiche une étiquette d'aide de type « Action en cours » :

$mw->Label(
-textvariable => \$message,
-borderwidth  => 2,
-relief       => 'groove'
)->pack(
-fill => 'x',
-side => 'bottom'
);
$mw->Text()->pack(
-side   => 'top',
-expand => 1,
-fill   => 'both'
);
$message = "Chargement du fichier index.html...";
...
$message = "Terminé";

L'étiquette est créée en bas de l'écran. On la place d'abord, car on veut qu'elle reste à l'écran si l'on modifie la taille de la fenêtre (rappelez-vous que les derniers widgets placés avec pack auront une priorité inférieure si la fenêtre manque de place). Au fur et à mesure que le programme s'exécute (ce que l'on représente par la ligne …), il modifie l'étiquette en conséquence.

Ce code donne un exemple d'utilisation d'une étiquette d'aide pour l'utilisation des widgets :

$mw->title("Exemple d'étiquette d'aide");

$mw->Label(
-textvariable => \$message,
-relief       => 'groove'
)->pack(
-side => 'bottom',
-fill => 'x'
);

$b1 = $mw->Button(
-text    => "Fin",
-command => \&exit
)->pack( -side => 'left' );
&lier_message( $b1, "Cliquez pour quitter l'application" );

$b2 = $mw->Button( -text => "Ne rien faire" )->pack( -side => 'left' );
&lier_message( $b2, "Ce bouton ne fait absolument rien !" );

$b3 = $mw->Button(
-text    => "Quelque chose",
-command => sub { print "Quelque chose\n"; }
)->pack( -side => 'left' );
&lier_message( $b3, "Affiche le texte 'Quelque chose'" );

sub lier_message {
my ( $widget, $msg ) = @_;
$widget->bind( '<Enter>', [ sub { $message = $_[1]; }, $msg ] );
$widget->bind( '<Leave>', sub { $message = ""; } );
}

Cet exemple est un peu plus long, car nous avons utilisé la méthode bind (expliquée en détail au chapitre 14, Liaisons d'événements). On veut associer un message d'aide à chaque widget créé : pour ce faire, on ajoute des liaisons à chaque widget afin de changer le contenu de la variable $message en une chaîne donnée lorsque la souris passe sur le widget, et en une chaîne vide lorsque la souris s'en éloigne. On utilise une fonction afin d'éviter de répéter à chaque fois les mêmes lignes de liaison. La figure 5.3 montre à quoi ressemble notre fenêtre lorsque la souris est au-dessus du bouton du milieu.

La figure 5.3 montre que le texte d'exemple est centré dans le widget étiquette. Lorsque l'on utilise des étiquettes de plusieurs lignes, et que l'on élargit le widget sur toute la largeur de l'écran, le texte reste centré, même si l'on utilise l'option -justify => 'left'. On peut contourner ce problème en créant un cadre (frame) conteneur, en lui donnant le contour voulu, en élargissant le cadre (au lieu de l'étiquette) à la largeur de l'écran et en plaçant le widget étiquette dans ce cadre :

$cadre = $mw->Frame(
-relief      => 'groove',
-borderwidth => 2
)->pack(
-side => 'bottom',
-fill => 'x'
);
$cadre->Label( -textvariable => \$message, )->pack( -side => 'left' );

Figure 5.3 : Fenêtre avec étiquette d'aide

Cela permet à l'étiquette de s'agrandir et de se réduire autant que nécessaire dans le cadre, tout en collant le texte sur le bord gauche. Si vous tapez ce court exemple, et que vous jouez avec les chaînes liées à chaque widget, vous remarquerez que la fenêtre changera elle-même de taille si le texte assigné à $message est trop long pour s'afficher sur l'étiquette. Cela peut être ennuyeux si la fenêtre est d'abord assez petite. Il y a deux manières de gérer cela : vous pouvez faire en sorte de toujours utiliser des messages courts, ou demander à la fenêtre de ne pas changer de taille lorsque celle de l'étiquette est modifiée.

Les inconvénients de chaque approche ne sont pas trop terribles et celle que vous choisirez dépendra simplement de l'application sur laquelle vous travaillez. Si vous pouvez faire des phrases vraiment courtes et ayant un sens, c'est parfait. Demander à la fenêtre de ne pas modifier sa taille est presque aussi simple, il suffit d'ajouter une seule ligne à votre programme :

$mw->packPropagate(0);

L'utilisation de packPropagate fait que votre fenêtre ne changera pas de taille lorsqu'un widget y est placé (nous avons déjà parlé de packPropagate au chapitre 2, Gestion de l'espace). Cela signifie que votre fenêtre peut ne pas montrer tous les widgets tout de suite. Vous pouvez gérer cela en continuant avec la fenêtre telle quelle est jusqu'à ce que tous vos widgets soient placés, puis en trouvant une taille de départ convenable pour votre fenêtre et en utilisant $mw->geometry(taille) pour demander cette taille initiale (voir le chapitre 13, Widgets de premier niveau, pour les informations concernant la méthode geometry).

V-A-6 - Configuration d'une étiquette

Une étiquette est un widget assez ennuyeux et il n'y a donc que deux méthodes permettant de modifier ou d'obtenir des informations sur elle : cget et configure. Ces deux méthodes fonctionnent pour le widget Label de la même façon que pour le widget Button. Reportez-vous à l'annexe A pour les détails sur les paramètres et les valeurs de retour de ces méthodes.

V-B - Le widget Entry

Jusqu'à maintenant, le seul moyen dont on disposait pour permettre à l'utilisateur d'intervenir était un clic de souris sur un bouton (widgets Button, Checkbutton, ou Radiobutton), géré via l'option -command. Bien qu'il soit utile de pouvoir cliquer avec la souris, c'est aussi limité. Le widget de saisie (entry widget) permet à l'utilisateur de taper du texte pouvant ensuite être utilisé d'une façon ou d'une autre par l'application. Voici quelques exemples de situations où l'on peut utiliser un widget de saisie :

- dans un formulaire de base de données nécessitant une saisie par champ (prénom, nom, adresse, par exemple) ;

- dans une fenêtre d'enregistrement de la licence d'un logiciel, nécessitant un numéro de série ;

- dans une fenêtre d'ouverture de session, nécessitant un nom d'utilisateur et un mot de passe ;

- dans une fenêtre de configuration, afin de donner le nom d'une imprimante ;

- dans une fenêtre d'ouverture de fichier, nécessitant de préciser le chemin et le nom d'un fichier.

Normalement, on ne se soucie pas de ce que tapent les utilisateurs dans un widget de saisie tant qu'ils n'ont pas fini cette saisie et tout traitement se fera « après coup », lorsque l'utilisateur cliquera sur un bouton de type « Appliquer ». On peut s'amuser à traiter chaque caractère au fur et à mesure qu'il est tapé en configurant une liaison compliquée, mais cela n'en vaut probablement pas la peine.

L'utilisateur peut taper n'importe quoi dans un widget de saisie ; c'est au programmeur de décider si le texte entré est correct ou non. Avant d'utiliser l'information saisie, on doit réaliser quelques tests afin de détecter d'éventuelles erreurs : si l'on attend un entier et que l'on obtient des caractères alphabétiques, on doit produire un message d'avertissement ou d'erreur afin de prévenir l'utilisateur.

Un widget de saisie est bien plus complexe qu'il n'y parait au premier abord : c'est, en fait, un éditeur de texte simplifié et réduit à une seule ligne. On peut y taper du texte, le sélectionner à la souris, l'effacer ou en ajouter. Je classerais un tel widget comme un widget de « milieu de gamme », car il est plus compliqué qu'un bouton, mais bien moins que les widgets texte ou canevas.

V-B-1 - Création d'un widget de saisie

Sans surprise :

$saisie = $parent->Entry( [ option => valeur . . . ] )->pack;

Lorsque le widget de saisie est créé, il est initialement vide de tout texte, et le point d'insertion (si le widget a le focus clavier) se trouve à son extrême gauche.

V-B-2 - Options d'un widget de saisie

La liste suivante contient une brève description de chacune des options de configuration d'un widget de saisie. Plusieurs d'entre elles sont traitées de façon plus détaillée dans la suite de ce chapitre.

-background => couleur

1. Configure la couleur de fond du widget de saisie (zone derrière le texte).

-borderwidth => montant

1. Configure l'épaisseur du contour du widget. La valeur par défaut est 2.

-cursor => nom_curseur

1. Indique que le curseur se transformera en nom_curseur lorsqu'il sera au-dessus du widget.

-exportselection => 0 | 1

1. Si la valeur booléenne est vraie, tout texte sélectionné sera exporté vers le bloc-notes (clipboard) du système de fenêtrage.

-font => nom_fonte

1. Change la fonte affichée dans la zone de saisie.

-foreground => couleur

1. Change la couleur du texte.

-highlightbackground => couleur

1. Configure la couleur qu'aura le rectangle de focus lorsque le widget n'a pas le focus clavier.

-highlightcolor => couleur

1. Configure la couleur qu'aura le rectangle de focus lorsque le widget détiendra le focus clavier.

-highlightthickness => montant

1. Configure la largeur du rectangle de focus autour du widget. La valeur par défaut est 2.

-insertbackground => couleur

1. Configure la couleur du point d'insertion.

-insertborderwidth => montant

1. Configure la largeur du contour du point d'insertion. Cette option est normalement utilisée avec les options -ipadx et -ipady du gestionnaire d'espace.

-insertofftime => millisecondes

1. Configure la durée pendant laquelle le point d'insertion disparaît du widget de saisie (cadence de clignotement).

-insertontime => millisecondes

1. Configure la durée pendant laquelle le point d'insertion apparaît dans le widget de saisie (cadence de clignotement).

-insertwidth => montant

1. Configure la largeur du point d'insertion. La valeur par défaut est 2.

-justify => 'left' | 'right' | 'center'

1. Configure l'alignement du texte dans le widget de saisie (à gauche, par défaut).

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie l'aspect du contour du widget de saisie.

-selectbackground => couleur

1. Configure la couleur de fond du texte sélectionné dans le widget de saisie.

-selectborderwidth => montant

1. Indique la largeur du contour de la mise en évidence de la sélection.

-selectforeground => couleur

1. Configure la couleur du texte sélectionné dans le widget de saisie.

-show => caractère

1. Indique quel est le caractère qui sera affiché à la place du texte réellement tapé.

-state => 'normal' | 'active' | 'disabled'

1. Configure l'état du widget. La valeur par défaut est 'normal'.

-takefocus => 0 | 1 | undef

1. Précise si le widget pourra prendre le focus clavier ou non.

-textvariable => \$variable

1. Précise la variable associée à l'information tapée dans le widget de saisie.

-width => montant

1. Indique la largeur de la saisie en nombre de caractères.

-xscrollcommand => fct_rappel

1. Assigne une fonction de rappel à utiliser lorsque l'on circule dans le widget.

Les options suivantes se comportent comme on peut s'y attendre, et ne méritent pas plus de précisions : -background, -cursor, -font, -highlightbackground, -highlightcolor, -highlightthickness, -foreground, -justify, -takefocus, et -state. Pour des explications supplémentaires quant à leurs effets sur un widget, voir le chapitre 3 33.

V-B-3 - Assignation du contenu de la saisie à une variable

L'option -textvariable permet de savoir ce que l'utilisateur a entré dans le widget de saisie :

-textvariable => \$variable

Cette option devrait déjà vous être familière grâce aux différents exemples donnés avec le widget bouton. Toute entrée de texte dans le widget de saisie sera assignée à cette variable. L'inverse est aussi vrai : toute chaîne assignée à la variable s'affichera dans le widget de saisie.

Il est important de se rappeler que tout ce que saisit l'utilisateur sera affecté à cette variable. Cela signifie que, même si l'on attend une entrée numérique (314, par exemple), on peut obtenir quelque chose comme 3s14 si l'utilisateur a pressé accidentellement (ou à dessein) la ou les mauvaises touches. Avant d'utiliser les informations obtenues à partir d'un widget de saisie, il est préférable de vérifier celles-ci afin de s'assurer qu'elles soient ce que l'on attendait ou, vraiment au pire, qu'elles soient dans un format correct. Essayer d'utiliser « 3s14 » dans une équation donnerait, à coup sûr, des résultats inattendus.

L'autre moyen de récupérer ce qui se trouve dans le widget de saisie consiste à utiliser la méthode get :

$valeur = $saisie->get();

On peut utiliser get, que l'option -textvariable soit positionnée ou non.

V-B-4 - Styles d'une zone de saisie

Comme pour tous les widgets, on peut modifier le tracé des contours en utilisant les options -relief et/ou -borderwidth :

1. -relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

2. -borderwidth => montant

Pour un widget de saisie, la valeur par défaut est 'sunken', ce qui est différent de ce que nous avons vu jusqu'à maintenant. La figure 5.4 montre les différents types de contours avec différentes valeurs de -borderwidth : celle par défaut (2), 4 et 10.

Voici le code ayant servi à créer les cinq zones de saisie. On utilise le nom du type de contour comme texte du widget.

$largeur = 2;    # puis 4, puis 10
foreach (qw/flat groove raised ridge sunken solid/) {
$saisie = $mw->Entry(
-relief      => $_,
-borderwidth => $largeur
)->pack( -expand => 1 );
$saisie->insert( 'end', $_ );    # place du texte dans la zone de saisie
}

Figure 5.4 : Les différents contours d'un widget de saisie : -borderwidth de 2 (défaut), 4 et 10

V-B-5 - Indices de saisie

Afin de manipuler le texte dans une zone de saisie, il faut un moyen d'identifier des parties ou des positions précises de ce texte. En fait, l'exemple ci-dessus utilisait un indice : la ligne $saisie->insert('end', $_) utilise l'indice 'end'. Comme la méthode insert (étudiée plus loin dans ce chapitre), toutes les méthodes nécessitant des informations sur une position donnée ont besoin d'un indice (ou de deux, si l'information concerne un ensemble de caractères). Celui-ci peut être aussi simple que 0, qui indique le tout début du texte, ou quelque chose de plus compliqué, comme 'insert'.

Voici les différentes formes d'indices et leur signification :

n (un entier quelconque)

1. Position ordinale d'un caractère. 0 est la position du premier caractère de la chaîne. Si la zone de saisie contient « Ta mère, son mot de passe c'est 'toto' ! » et que l'on utilise un indice de 13, le caractère ainsi désigné sera le « m » du mot « mot ».

'insert'

1. Désigne le caractère placé immédiatement après le point d'insertion, cette petite barre rigolote qui s'affiche dans la zone de saisie lorsque l'on y tape du texte. On peut déplacer ce point à l'aide des touches fléchées ou en cliquant sur un autre emplacement du widget.

'sel.first'

1. Le premier caractère de la chaîne sélectionnée. Une erreur se produira s'il n'existe pas de sélection. La chaîne sélectionnée est celle qui est créée à l'aide de la souris ou de la combinaison 'Maj-flèche' : elle apparaît légèrement surélevée par rapport au fond du widget.

2. Si le texte sélectionné dans cette chaîne est le mot « passe » (montré ici en gras)

Ta mère, son mot de passe c'est 'toto' !

3. 'sel.first' désignera le caractère « p ».

'sel.last'

1. Le caractère situé immédiatement après le dernier caractère de la chaîne sélectionnée. Une erreur se produira s'il n'existe pas de sélection dans la zone de saisie. Avec l'exemple précédent, cela désignerait l'espace après le « e » de « passe ».

'anchor'

1. Cet indice évolue selon ce qui se passe avec le texte sélectionné dans la zone de saisie. Par défaut, il débute à l'extrême gauche du widget (0). Il est modifié si l'on clique à n'importe quel endroit de la zone de saisie : sa nouvelle valeur sera alors la position sur laquelle on a cliqué. L'indice 'anchor' sera également modifié lorsque l'on fait une nouvelle sélection -- avec la souris ('anchor' sera à l'endroit où vous avez cliqué) ou en faisant la combinaison `Maj'-clic gauche ('anchor' sera positionné au début de la sélection). La plupart du temps, cet indice est utilisé de façon interne et l'on trouve rarement des cas où il serait utile à une application.

'end'

1. Désigne le caractère placé immédiatement après le dernier de la chaîne. Sa valeur est la même que si l'on avait spécifié la longueur totale de la chaîne avec un indice numérique.

@x

1. Cette forme utilise une coordonnée x, présente dans la zone de saisie. Le caractère situé à cette coordonnée sera utilisé. @0 désigne le caractère le plus à gauche (le premier) du widget de saisie. Vous utiliserez rarement cette forme d'indice.

V-B-6 - Options de sélection du texte

Lorsque l'on sélectionne du texte dans un widget de saisie, plusieurs choses se passent. Les indices 'sel.first' et 'sel.last' pointent, respectivement, sur le début et la fin du texte sélectionné. Sous Unix, on peut aussi copier la sélection dans le bloc-notes à l'aide de l'option -exportselection :

1. -exportselection => 0 | 1

Cette option indique si le texte sélectionné dans la zone de saisie doit, ou non, être aussi placé dans le tampon de sélection en plus d'être stocké de façon interne par le widget. La valeur par défaut fait que le texte sélectionné peut être copié-collé dans d'autres applications.

Les options -selectbackground, -selectforeground et -selectborderwidth permettent d'intervenir sur les couleurs utilisées par la sélection :

-selectbackground => couleur
-selectforeground => couleur
-selectborderwidth => montant

-selectbackground et -selectforeground modifient les couleurs du texte et de la zone derrière lui lorsque ce texte est mis en évidence. Le mot « texte » de la figure 5.5 est sélectionné.

Figure 5.5 : Zone de saisie avec -selectbackground => `red' et -selectforeground => `yellow'

On peut changer la largeur du contour de la boîte de sélection à l'aide de -selectborderwidth. Si l'on conserve la taille du widget de saisie, les effets ne seront pas visibles, car le widget coupe la boîte de sélection. Pour réellement voir les résultats de l'augmentation de la valeur de -selectborderwidth, il faut utiliser cette option au moment de la création du widget, ainsi que les options -ipadx et -ipady du gestionnaire d'espace utilisé.

Figure 5.6 : Zone de saisie avec -selectborderwidth => 10

On modifie généralement la valeur de -selectborderwidth afin d'ajouter un peu d'espace supplémentaire autour du texte ou pour vraiment mettre en évidence ce qui est sélectionné. Voici le code qui a permis de créer le widget de saisie de la figure 5.6 :

$saisie = $mw->Entry( -selectborderwidth => 10 )->pack(
-expand => 1,
-fill   => 'x',
-ipadx  => 10,
-ipady  => 10
);
$saisie->insert( 'end', 'Sélectionnez le mot texte de cette phrase' );

Remarquez bien les options -ipadx et -ipady de la méthode pack.

V-B-7 - Le point d'insertion

Le point d'insertion est cette petite barre clignotante, placée dans le widget de saisie lorsqu'il a le focus clavier. Si un autre widget a le focus, le point d'insertion reste là, mais est invisible (il en va de même lorsqu'aucun widget n'a le focus). Dans la figure 5.7, le point est situé tout de suite après le premier « n » du mot « insertion ».

Figure 5.7 : Point d'insertion par défaut

On peut modifier l'épaisseur, la largeur du bord et la couleur du point d'insertion à l'aide des options suivantes :

-insertbackground => couleur
-insertborderwidth => montant
-insertwidth => montant

L'option -insertwidth modifie simplement la largeur du point d'insertion afin qu'il apparaisse plus gros ; -insertbackground en change la couleur générale. La figure 5.8 montre un exemple de modification du point d'insertion à l'aide de ces deux options :

Figure 5.8 : Point d'insertion avec -insertbackground => 'green' et -insertwidth => 10

Le point d'insertion, quelle que soit la largeur, est toujours centré entre deux caractères. Le point d'insertion de la figure 5.8 se trouve au même endroit que dans la figure 5.7. Cela peut distraire les utilisateurs et rendre les choses confuses inutilement ; il est donc préférable de ne pas modifier l'option -insertwidth.

On peut donner un aspect tridimensionnel au point d'insertion en utilisant l'option -insertborderwidth (comme dans la figure 5.9). Cette option, tout comme -insertwidth n'a pas beaucoup d'intérêt pratique.

On peut aussi modifier la cadence de clignotement du point à l'aide des options suivantes :

Figure 5.9 : Point d'insertion avec -insertborderwidth => 5, -insertbackground => 'green' et -insertwidth => 10

-insertofftime => millisecondes
-insertontime => millisecondes

La valeur par défaut de -insertofftime est de 300 millisecondes, celle de -insertontime est de 600 millisecondes. Ces durées font que le point reste visible deux fois plus longtemps qu'il n'est caché. Les valeurs de ces options doivent être positives ou nulles.

Pour disposer d'un point d'insertion vraiment frétillant, il suffit d'utiliser des valeurs beaucoup plus petites pour ces deux options. Pour un point calme et tranquille, on double les durées. Si l'on ne veut pas que le point clignote, il suffit de mettre -insertofftime à 0.

V-B-8 - Saisies de mots de passe

Dans certains cas, l'information saisie par l'utilisateur ne doit pas s'afficher à l'écran. Pour afficher autre chose que le texte réellement tapé, on utilise l'option -show :

-show => caractère

La valeur de cette option est un caractère simple, qui sera affiché à la place des caractères tapés. Pour une saisie de mot de passe, par exemple, on peut utiliser des astérisques (voir la figure 5.10). Si l'on donne une chaîne comme valeur, seul le premier caractère de celle-ci sera utilisé. Par défaut, cette option n'est pas définie et tout ce que tape l'utilisateur s'affichera tel quel.

Figure 5.10 : Zone de saisie affichant un mot de passe

Lorsque l'on utilise l'option -show, l'information stockée dans la variable référencée par -textvariable sera l'information réelle, pas les astérisques.

Si cette option est utilisée, l'utilisateur ne peut couper et coller le mot de passe (quelle que soit la valeur de -exportselection). S'il est coupé et collé vers un autre écran, ce que l'utilisateur voyait (les astérisques, par exemple) est vraiment collé, mais pas l'information qui se cache derrière. On pourrait se dire qu'en configurant ensuite le widget de saisie en faisant $saisie->configure(-show => ""), les mots entrés par l'utilisateur réapparaîtraient soudainement. Heureusement qu'il n'en est rien : un ensemble de \x0 (du charabia, quoi) s'affichera à la place. Toute variable associée à la saisie via l'option -textvariable contiendra quand même l'information correcte. Si vous faites $saisie->get(), cette information correcte (non déformée) sera renvoyée. La méthode get est décrite plus bas dans ce chapitre.

V-B-9 - Utilisation d'une barre de défilement

Si l'information demandée à l'utilisateur est susceptible de devenir plutôt longue, celui- ci peut alors utiliser les touches fléchées pour faire défiler le texte. Pour faciliter ce déplacement, on peut créer une barre de défilement horizontale et l'affecter au widget de saisie via l'option -xscrollcommand :

-xscrollcommand => [ 'set' => $barre_defil ]

Pour le moment, je ne présenterai que le moyen le plus simple d'affecter une barre de défilement à un widget de saisie. Le chapitre 6, Barres de défilement, donnera plus de détails.

Pour créer une barre de défilement et l'associer à un widget de saisie, on écrit :

# crée la barre de défilement
$defil = $mw->Scrollbar( -orient => "horizontal" );

# crée la zone de saisie
$saisie = $mw->Entry( -xscrollcommand => [ 'set' => $defil ] )->pack( -expand => 1, -fill => 'x' );
$defil->pack( -expand => 1, -fill => 'x' );

# on les lie
$defil->configure( -command => [ $saisie => 'xview' ] );
$saisie->insert( 'end', "Chaîne vraiment très très très très très très très très longue" );

La figure 5.11 montre le résultat : à gauche, la fenêtre après sa création et, à droite, après s'être déplacé complètement à droite à l'aide de la barre de défilement.

Figure 5.11 : Barre de défilement avec un widget de saisie

Les cas où vous devrez utiliser une barre de défilement avec un widget de saisie sont très rares. Une barre double l'espace utilisé et l'on peut obtenir les mêmes avantages en utilisant simplement les touches fléchées lorsque le widget a le focus. Si l'utilisateur doit saisir plusieurs lignes de texte, il est préférable d'utiliser plutôt un widget texte. Le chapitre 8, Le widget texte, donne plus d'informations sur ce qu'un tel widget peut faire.

V-B-10 - Configuration d'un widget de saisie

Les méthodes configure et cget fonctionnent avec le widget de saisie de la même façon qu'avec tous les autres. Leurs options par défaut pour ce widget sont énumérées à l'annexe A, Configuration des widgets avec configure et cget.

V-B-11 - Suppression de texte

On peut utiliser la méthode delete pour supprimer une partie, ou la totalité, du texte contenu dans la zone de saisie. Pour ôter plus de deux caractères, on utilisera un intervalle d'indices, et un indice simple pour n'en ôter qu'un.

$saisie->delete( indice_debut [ , indice_fin] )

Pour supprimer tout le texte, on peut écrire $saisie->delete(0, 'end'). Si l'on utilise l'option -textvariable, on peut aussi détruire ce texte en réaffectant une chaîne vide à la variable : $variable = "".

Voici d'autres exemples d'utilisation de la méthode delete :

$saisie->delete(0); # ne supprime que le premier caractère
$saisie->delete(1); # supprime le second caractère
$saisie->delete('sel.first', 'sel.last') # supprime le texte sélectionné
if ($saisie->selectionPresent());        # s'il y a une sélection...

V-B-12 - Déterminer le contenu d'un widget de saisie

Il y a deux moyens de déterminer le contenu d'un widget de saisie : utiliser la méthode get ou se servir de la variable associée à -textvariable.$texte_saisi = $saisie->get(), par exemple, mettra dans $texte_saisi le contenu complet du widget.

La façon de récupérer ce contenu dépend de ce vous comptez en faire. Si vous voulez simplement l'utiliser pour l'écrire dans un fichier ou l'insérer dans une base de données, il ne serait pas raisonnable de gaspiller de la mémoire en le stockant dans une variable : dans ce cas, il suffit d'utiliser la méthode get dans l'instruction print (ou dans toute instruction concernée). Si l'information saisie est une valeur qui sera souvent utilisée (un nombre pour un calcul mathématique, par exemple), il est alors conseillé de la stocker dès le départ dans une variable afin de pouvoir y accéder plus tard.

V-B-13 - Déplacement du point d'insertion

La méthode icursor placera le point à l'indice spécifié :

$saisie->icursor(indice);

Par défaut, le point est à l'emplacement de la dernière insertion. Pour forcer celui-ci à aller ailleurs, on peut écrire quelque chose comme :

$s_texte = "Texte saisi";
$saisie = $mw->Entry(-textvariable => \$s_texte)->pack();
$saisie->focus;
$saisie->icursor(1); # Place le point à cet indice

On utilise la méthode focus (qui n'est pas spécifique aux widgets de saisie, mais à tous les widgets) pour que l'application donne le focus à notre widget de saisie dès qu'elle démarre. Puis, nous plaçons le point d'insertion entre le premier et le deuxième caractère (d'indices 0 et 1). Voir le chapitre 16, Méthodes communes à tous les widgets, pour plus d'informations sur la notion de focus.

Si l'on commence le texte par une chaîne précise, on peut faire en sorte de déplacer la position de début de la saisie à un emplacement donné. On peut, par exemple, initialiser $s_texte avec la chaîne « http:// », puis utiliser l'instruction $saisie->icursor('end').

V-B-14 - Obtenir la valeur d'un indice

La méthode index convertit un indice nommé en indice numérique :

$numindex = $saisie->index(indice);

L'une des applications de index est de rechercher le nombre de caractères dans la zone de saisie : $longueur = $saisie->index('end'). On aurait pu, évidemment, obtenir le même résultat en faisant $longueur = length($variable), si $variable est la variable utilisée avec l'option -textvariable.

On utilisera le code suivant afin de rechercher le début du texte sélectionné :

$indice_debut = $saisie->selectionPresent() ?
$saisie->index('sel.first') : -1;

Nous traiterons de selectionPresent plus loin dans ce chapitre.

V-B-15 - Insertion de texte

La méthode insert permet d'insérer une chaîne quelconque à partir de l'indice spécifié :

$saisie->insert(indice, chaîne);

Voici une application simple, utilisant insert :

#!/usr/bin/perl
use Tk;

$mw = MainWindow->new;
$mw->title("Saisie");

$s_texte = "Texte saisi";     # Crée une saisie avec un texte initial
$saisie = $mw->Entry( -textvariable => \$s_texte )->pack(
-expand => 1,
-fill   => 'x'
);
$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack( -side => 'bottom' );

# Crée un bouton qui insérera un compteur à la position du point
$i = 1;
$mw->Button(
-text    => "Insertion # ",
-command => sub {
if ( $saisie->selectionPresent() ) {
$saisie->insert( 'sel.last', "$i" );
$i++;
}
}
)->pack;

MainLoop;

On initialise la zone de saisie avec un texte par défaut : « Texte saisi ». Puis, on crée deux boutons : le premier est le célèbre bouton « Fin » qui nous permet de quitter l'application, le second est un peu plus compliqué. Lorsqu'il est pressé, il vérifie si du texte est sélectionné dans le widget $saisie : si c'est le cas, il insérera un nombre représentant le nombre de fois où l'on a cliqué sur le bouton « Insertion # ».

Figure 5.12 : Utilisation de la méthode insert

Dans la figure 5.12, nous avons d'abord sélectionné le mot « Texte », puis nous avons cliqué quatre fois sur le bouton « Insertion # ». À chaque fois qu'il est pressé, celui-ci insère un nombre à l'indice 'sel.last'. Cet indice ne change pas entre deux clics sur le bouton, on a donc l'impression de compter à l'envers !

V-B-16 - Parcours du texte

Deux méthodes, scanMark et scanDragto, permettent un parcours rapide d'une zone de saisie. Un appel à scanMark mémorise simplement la coordonnée x qui lui est passée afin de l'utiliser plus tard avec scanDragto. Elle retourne une chaîne vide.

$saisie->scanMark(x1);
$saisie->scanDragto(x2);

scanDragto est la compagne de scanMark : elle attend aussi une coordonnée x et la compare à celle passée à scanMark. La partie visible de la zone de saisie est ajustée de dix fois la différence entre les deux coordonnées.

V-B-17 - Utilisation du texte sélectionné

La méthode selection accepte plusieurs listes de paramètres différentes. Si l'on consulte la documentation en ligne, il y est écrit que l'on peut utiliser :

$saisie->selectionAdjust(indice);

Vous verrez aussi la forme $saisie->selection('adjust', indice ), où 'adjust' est le premier paramètre. Sachez que ces deux formes signifient exactement la même chose et que vous pouvez rencontrer l'une ou l'autre.

On peut positionner la sélection à un indice donné en utilisant la méthode selectionAdjust :

$saisie->selectionAdjust(indice);

Cette instruction aura pour effet d'étendre le texte sélectionné en direction de l'indice (vers quelque extrémité la plus proche qui soit).

Pour annuler la sélection :

$saisie->selectionClear();

Cela supprimera du widget de saisie tout indicateur de sélection, et les indices 'sel.first' et 'sel.last' seront alors indéfinis. Le texte sélectionné n'est pas supprimé.

Pour affecter un indice donné à l'indice 'anchor', on utilise la méthode selectionFrom :

$saisie->selectionFrom(indice);

Cette instruction n'affecte pas le texte sélectionné ni les indices 'sel.first' et 'sel.last'.

Le seul moyen de vérifier si une sélection a été faite dans le widget de saisie consiste à utiliser la méthode selectionPresent :

if ($saisie->selectionPresent()) {
# traitement de la sélection
}

Cette méthode renvoie 1 si du texte est sélectionné, on peut donc utiliser sans problème les indices 'sel.first' et 'sel.last' (s'il n'y a pas de sélection, une erreur se produira lorsque l'on voudra utiliser l'un de ces indices). selectionPresent renvoie 0 si rien n'est sélectionné.

On peut modifier l'intervalle de sélection à l'aide de la méthode selectionRange :

$saisie->selectionRange(indice_début, indice_fin);

Les deux indices précisent l'étendue souhaitée de la sélection. Si indice_début est supérieur ou égal à indice_fin, la sélection est annulée, ce qui place 'sel.first' et 'sel.last' dans un état indéfini. Sinon, 'sel.first' et 'sel.last' vaudront respectivement la même chose que indice_début et indice_fin.

La méthode selectionTo forcera la nouvelle sélection à commencer au point 'anchor' courant pour se terminer à l'indice spécifié :

$saisie->selectionTo(indice);

V-B-18 - Changer la vue d'un widget de saisie

Le rôle de la méthode xview dépend des paramètres qui lui sont passés.

Sans paramètre, elle renvoie une liste de deux éléments contenant des nombres de 0 à 1. Ceux-ci définissent ce qui est actuellement visible dans le widget de saisie. Le premier nombre indique le pourcentage de caractères laissés à gauche du texte et qui ne seront pas visibles : s'il vaut 0.3, 30 % du texte est à gauche de la zone de saisie. Le deuxième nombre est le pourcentage de texte non visible à gauche de la zone de saisie, plus le pourcentage de texte visible dans cette zone. Ici, 50 % du texte est réellement visible dans le widget (voir la figure 5.13).

($gauche, $droite) = $saisie->xview();

Figure 5.13 : Signification de $gauche et $droite

Lorsqu'on passe un indice à xview, le texte du widget de saisie se décalera à cette position pour que le caractère à l'indice spécifié soit visible à l'extrême gauche de la zone :

$saisie->xview(indice);

Les autres formes de xview concernent directement le défilement du texte et sont expliquées en détail au chapitre 6 :

$saisie->xviewMoveto(fraction);
$saisie->xviewScroll(nombre, quoi);

V-C - Essayez et amusez-vous !

On ne peut pas faire grand-chose d'excitant avec les widgets étiquette, mais il est conseillé de s'exercer à manipuler le widget de saisie :

- créez une zone de saisie et une étiquette, et affichez la même information dans les deux. Lorsque vous placez un nouveau texte dans la zone de saisie, l'étiquette doit afficher ce texte en même temps ;

- créez un formulaire de saisie d'une base de données, placez une étiquette Nom, Adresse, Ville, Code Postal, Téléphone, pour chaque zone de saisie. Ajoutez un bouton « Appliquer » qui réalisera certaines vérifications sur la cohérence des informations saisies en se fondant sur les informations attendues ;

- créez une fenêtre comportant un widget de saisie et plusieurs boutons, chacun d'eux réalisant une action différente sur le widget de saisie. Quelques suggestions : « Remise à zéro », « Supprimer la sélection », « Défaut » (remplacement par la chaîne initiale) ;

- créez un widget de saisie et entrez-y du texte. Placez un bouton qui, lorsqu'il est pressé, renversera la chaîne saisie.

VI - Barres de défilement

On utilise des barres de défilement (scrollbars, en anglais) avec des widgets lorsque l'espace d'affichage est plus petit que ce que l'on veut montrer. La présence d'une ou deux barres permet à l'utilisateur de faire défiler horizontalement et/ou verticalement le contenu d'un widget. Vous avez déjà rencontré des barres de défilement dans différents types d'applications : tout traitement de texte digne de ce nom, les logiciels de dessin, et même votre navigateur web, en possèdent. Ce chapitre vous expliquera comment utiliser les barres de défilement avec certains widgets Perl/Tk.

VI-A - Composantes d'une barre de défilement

La figure 6.1 présente les différentes parties d'une barre de défilement.

Figure 6.1 : Les différents composants d'une barre de défilement

La zone de défilement (trough) est la partie creusée, située entre les deux flèches. Elle est divisée en deux parties par l'ascenseur (slider), zone 1 et zone 2. L'ascenseur est le rectangle indiquant de manière proportionnelle à quel niveau l'on se trouve dans le widget. Si l'on se trouvait à la moitié de celui-ci, l'ascenseur serait au milieu de la zone de défilement. Les flèches aux deux extrémités sont, ici, appelées arrow1 et arrow2 . Si la barre de défilement avait été verticale (tournée de 90 degrés dans le sens des aiguilles d'une montre), arrow1 aurait été la flèche du haut.

Si l'on clique sur l'une ou l'autre des deux flèches, l'information contenue dans le widget se déplacera unité par unité. La valeur de celle-ci dépend du type de widget auquel est associée la barre de défilement : dans le cas d'une zone de saisie, l'unité est le caractère, dans le cas d'une boîte de liste disposant d'une barre de défilement verticale, l'unité est la ligne. Si l'on clique dans l'une des deux zones de défilement qui se trouvent de part et d'autre de l'ascenseur, l'information contenue dans le widget défilera d'une hauteur ou d'une largeur de fenêtre, dans cette direction. On peut aussi cliquer directement sur l'ascenseur et, tout en maintenant le bouton de la souris enfoncé, le déplacer « manuellement ».

Les barres de défilement peuvent être horizontales ou verticales. Normalement, elles se trouvent en bas et/ou à droite du widget qu'elles permettent de parcourir, mais ce n'est pas toujours le cas.

Les widgets de Perl/Tk pouvant être configurés pour utiliser des barres de défilement sont les éditeurs de texte, les boîtes de liste, les canevas, les zones de saisie, les visualiseurs PostScript , les listes arborescentes et les damiers. Seuls les quatre premiers (éditeur de texte, boîte de liste, canevas et zone de saisie) sont traités dans ce livre. Les figures 6.2 à 6.5 contiennent des exemples de barres de défilement associées à chacun de ces widgets.

Figure 6.2 : Zone de saisie avec barre de défilement

Figure 6.3 : Boîte de liste avec barre de défilement

Figure 6.4 : Widget texte avec barre de défilement. Le fichier affiché est Scrollbar.pm

Figure 6.5 : Canevas avec barre de défilement

Il y a deux façons de créer et configurer des barres de défilement pour les associer à des widgets. On peut utiliser la méthode Scrollbar afin de créer un widget barre de défilement, ou Scrolled afin de créer un widget et ses barres de défilement associées. Chacune d'elles a ses avantages et ses inconvénients. L'utilisation de Scrolled nécessite beaucoup moins de travail et de lignes de programme, mais ne vous permettra pas de créer n'importe quelle fioriture, comme associer la même barre de défilement à deux widgets différents. Créer vous-même les barres de défilement implique plus de code, mais vous permet de vous amuser beaucoup plus avec elles, car, si nécessaire, vous contrôlerez directement leur emplacement et les widgets auxquels elles seront associées. Ce chapitre présente ces deux méthodes de création.

VI-B - La méthode Scrolled

On utilise la méthode Scrolled pour créer en même temps un widget et ses barres de défilement. Elle renvoie un pointeur vers le widget créé. Il s'agit de la façon la plus simple d'ajouter des barres de défilement à un widget pouvant être parcouru. Cette méthode crée un cadre contenant le widget et la ou les barres de défilement. Tout cela est créé en un seul appel.

L'utilisation de Scrolled est la suivante :

$widget = $parent->Scrolled('Widget', -scrollbars => 'chaîne' [, options ] );

Le premier paramètre est le widget à créer : « Listbox » ou « Canvas », par exemple. L'autre paramètre nécessaire est l'option -scrollbars, qui prend pour valeur une chaîne indiquant les barres de défilement à créer et leurs emplacements.

Les valeurs possibles de -scrollbars sont "n", "s", "e", "w", "on", "os", "oe", "ow", ou toute combinaison de celles-ci comprenant n ou s avec e ou w. Le "n" demande de placer une barre de défilement horizontale au-dessus du widget. Un "s" demande de la placer en dessous. Un "e" placera une barre de défilement verticale à droite du widget et "w" la placera à gauche. Le lecteur perspicace aura compris que chacune des lettres "n", "s", "e" et "w" désigne un point cardinal (nord, sud, est, ouest).

En tout et pour tout, on ne peut placer que deux barres de défilement par widget, et elles doivent être perpendiculaires. On peut créer, par exemple, une seule barre de défilement sur le bord "n" du widget. On peut aussi en créer deux avec "nw" : une au-dessus, et l'autre à gauche du widget. Il n'est pas possible d'utiliser la valeur "ns", par exemple, car l'axe de parcours de "n" et "s" est le même.

Un "o" devant une direction rend cette barre de défilement optionnelle : elle ne s'affichera que si la taille du widget impose de faire défiler l'information qu'il contient. Mettez toujours les valeurs nord ou sud en premier (si vous les utilisez) afin d'éviter les messages émis par la fonction. Voici quelques exemples qui rendront tout cela plus clair :

# Crée une barre de défilement optionnelle
# à l'est (à droite) du widget
$bl = $mw->Scrolled("Listbox", -scrollbars => 'oe')->pack;

# Crée des barres de défilement au sud (en dessous)
# et à l'est (à droite) du widget
$bl = $mw->Scrolled("Listbox", -scrollbars => 'se')->pack;

# Créer des barres de défilement optionnelles au sud (en dessous)
# et à l'est (à droite) du widget
$bl = $mw->Scrolled("Listbox", -scrollbars => 'osoe')->pack;

# Crée des barres de défilement au nord (au-dessus)
# et à l'ouest (à gauche) du widget
$bl = $mw->Scrolled("Listbox", -scrollbars => 'nw')->pack;

VI-B-1 - Configuration de barres créées avec Scrolled

Toutes les autres options utilisées avec la méthode Scrolled ne servent qu'à configurer le widget créé. Si l'on veut configurer les barres de défilement, on utilisera la méthode Subwidget du widget de référence. Cette méthode peut être utilisée, car un widget Scrolled est en fait un widget composite. Ces widgets sont traités au chapitre 15, Widgets composites.

Pour colorer le fond de la barre de défilement horizontale en vert, on peut utiliser cette instruction :

$bl->Subwidget("xscrollbar")->configure(-background => "green");

Pour configurer une barre verticale, on utilisera le paramètre "yscrollbar" au lieu de "xscrollbar". Si l'on tente de configurer une barre de défilement qui n'a pas été créée (si, par exemple, on a utilisé l'option -scrollbars => "e" et que l'on utilise "xscrollbar" pour la configurer), rien ne se passera.

Pour ne configurer que le widget, on utilisera $widget->configure() après avoir appelé Scrolled, mais on peut aussi écrire :

$widget->Subwidget("widget")->configure(...);

Cette utilisation de Subwidget est idiote, car on pourrait se contenter de $widget. La chaîne « widget » est la même que le premier paramètre passé à Scrolled, sauf qu'il est entièrement en minuscules. Dans les exemples précédents, par exemple, nous avons appelé Scrolled en lui passant le paramètre "Listbox", mais nous utiliserions "listbox" avec la méthode Subwidget.

VI-C - Le widget Scrollbar

Au lieu de créer automatiquement une ou plusieurs barres de défilement avec la méthode Scrolled, on peut utiliser la méthode de création Scrollbar et configurer ce widget nous-même. Cette façon de procéder est préférable lorsque l'on doit faire des choses non classiques, comme utiliser une seule barre de défilement pour parcourir deux boîtes de liste.

VI-C-1 - Création d'un widget barre de défilement

Pour créer la barre, on appelle la méthode Scrollbar à partir du widget parent. Elle renvoie une référence à la barre de défilement ainsi créée, référence qui pourra être ensuite utilisée pour la configuration de la barre.

$defilement = $mw->Scrollbar([ options ...] )

On doit faire au moins deux autres opérations afin qu'une barre de défilement fonctionne avec un autre widget. Il faut d'abord créer le widget à parcourir, qui utilisera la barre à l'aide de ses options -xscrollcommand ou -yscrollcommand. Puis, on doit configurer la barre de défilement pour qu'elle sache communiquer avec ce widget. Voici un exemple créant une boîte de liste (ne vous inquiétez pas si vous ne comprenez pas encore tout : je veux juste montrer un exemple complet avant de présenter toutes les options) :

# Création d'un barre de défilement verticale
$defil_v = $mw->Scrollbar();
$liste = $mw->Listbox(-yscrollcommand => ['set' => $defil_v]);
# Configuration de la barre pour qu'elle communique avec la liste
$defil_v->configure(-command => ['yview' => $liste]);

# On place d'abord la barre pour qu'elle ne disparaisse pas lorsque l'on
# modifie la taille de la liste
$defil_v->pack(-side => 'right', -fill => 'y');
$liste->pack(-side => 'left', -fill => 'both');

La création de la barre de défilement est très simple : nous utilisons toutes les valeurs par défaut de ses options. Comme nous créons une liste, nous devons utiliser une fonction de rappel pour que cette liste puisse communiquer avec la barre de défilement lorsque son contenu est parcouru. Notre barre est verticale, par conséquent c'est l'option -yscrollcommand qui utilise la commande set et notre barre qui lui est assignée (si elle avait été horizontale, on aurait utilisé -xscrollcommand). Lorsque le contenu de la liste est parcouru par l'utilisateur sans utiliser la barre de défilement, la liste alertera la barre en invoquant $defil_v->set(...).

La ligne $defil_v->configure(-command => ['yview' => $liste]) fait quasiment le contraire ; elle configure la barre de défilement pour qu'elle communique avec la boîte de liste. Lorsque l'utilisateur clique sur la barre, celle-ci appellera $liste->yview(...) pour demander à la liste de modifier la vue de son contenu. On utilise la version « y » de la méthode, car il s'agit d'une barre de défilement verticale.

Vous trouverez plus de détails sur yview dans la section qui lui est consacrée, plus loin dans ce chapitre. Les deux dernières lignes de l'exemple placent la barre et la liste dans la fenêtre de façon à ce qu'elles aient la même hauteur, et que la barre soit à droite de la liste.

Placez toujours d'abord vos barres de défilement dans la fenêtre ou le cadre : elles resteront visibles lorsque l'utilisateur réduira la fenêtre. Cela changera alors la taille de la liste (ou de tout autre widget utilisé), mais laissera les barres visibles sur les bords de l'écran.

Maintenant que nous avons vu un exemple complet de création d'une barre de défilement et de configuration du widget à parcourir, on peut passer les options en revue, en ayant déjà une petite idée de leurs utilisations.

VI-C-2 - Options des barres de défilement

Cette liste énumère les options possibles d'une barre de défilement, et en donne de brèves définitions. Les options importantes sont détaillées plus loin dans ce chapitre.

-activebackground => couleur

1. Configure la couleur que prendra la barre de défilement lorsque le pointeur de la souris passe au-dessus.

-activerelief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken'

1. Détermine l'aspect des éléments actifs. Cette option concerne les flèches des extrémités et l'ascenseur.

-background => couleur

1. Configure la couleur de fond de la barre de défilement (la couleur de la zone de défilement n'est pas concernée).

-borderwidth => montant

1. Configure la largeur des bords de la barre, des flèches et de l'ascenseur.

-command => fct_rappel

1. Précise la fonction qui sera appelée lorsque l'on clique sur la barre de défilement.

-cursor => nom_curseur

1. Configure le curseur qui s'affichera lorsque le pointeur de la souris est au-dessus de la barre de défilement.

-elementborderwidth => montant

1. Configure la largeur des bords des flèches et de l'ascenseur.

-highlightbackground => couleur

1. Configure la couleur que prendra le rectangle de focus autour de la barre de défilement lorsque celle-ci n'a pas le focus clavier.

-highlightcolor => couleur

1. Configure la couleur que prendra le rectangle de focus autour de la barre de défilement lorsque celle-ci a le focus clavier.

-highlightthickness => montant

1. Configure l'épaisseur du rectangle de focus. La valeur par défaut est 2.

-jump => 0 | 1

1. Indique si la barre de défilement « sautera » ou non.

-orient => "horizontal" | "vertical"

1. Précise l'orientation de la barre de défilement.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie les contours du widget.

-repeatdelay => millisecondes

1. Précise le nombre de millisecondes pendant lequel il faut presser une flèche avant qu'elle ne passe en répétition automatique. La valeur par défaut est 300 ms.

-repeatinterval => millisecondes

1. Précise le nombre de millisecondes entre les répétitions automatiques. La valeur par défaut est 100 ms.

-takefocus => 0 | 1 | undef

1. Indique si la barre de défilement pourra obtenir le focus clavier.

-troughcolor => couleur

1. Modifie la couleur de la zone de défilement (des deux côtés de l'ascenseur).

-width => montant

1. Configure la largeur de la barre de défilement.

VI-C-3 - Couleurs d'une barre de défilement

Une barre de défilement contient une nouvelle partie de widget : la zone de défilement, qui se trouve derrière les flèches et l'ascenseur. Cette zone possède sa propre coloration grâce à l'option -troughcolor. La figure 6.6 en est un exemple.

Figure 6.6 : Barre de défilement avec -troughcolor valant 'green'

Le fond de la barre est formé des flèches, de l'ascenseur et de ce qui entoure la zone de défilement. L'option -background permet de modifier la couleur du fond et l'option -activebackground contrôle la couleur qui sera utilisée lorsque le curseur de la souris est au-dessus des flèches ou de l'ascenseur. La figure 6.7 montre deux exemples : le premier n'utilise que -background, le deuxième lui ajoute -troughcolor.

Figure 6.7 : Exemples d'utilisation de -background

VI-C-4 - Style des barres de défilement

Les options -relief et -borderwidth agissent sur les contours de la barre de défilement, des flèches et de l'ascenseur. Cela fonctionne de la même façon qu'avec les cases à cocher et les boutons radio. La figure 6.8 montre une copie d'écran des effets de différentes valeurs de ces deux options.

Figure 6.8 : La première ligne montre différents types de contours ; la deuxième leur ajoute -borderwidth => 4

L'option -activerelief affecte la décoration des flèches et de l'ascenseur lorsque le curseur de la souris est au-dessus d'eux. L'option -elementborderwidth affecte ces mêmes éléments en permettant de changer leur largeur. L'option -borderwidth fait de même, mais modifie également les contours du widget. Remarquez comment le contour de la barre de défilement est restée à 2 dans la figure 6.9.

Figure 6.9 : Barre de défilement avec -elementborderwidth à 4

La largeur d'une barre de défilement, définie par l'option -width, est la distance de la partie étroite de la barre, sans les bords. La figure 6.10 montre les modifications de la barre lorsque l'on change la valeur de -width.

Figure 6.10 : La barre du haut a une largeur de 15 (défaut), celle du bas a une largeur de 20

VI-C-5 - Orientation d'une barre de défilement

Comme nous l'avons écrit plus haut, une barre de défilement peut être verticale (ce qui est l'orientation par défaut) ou horizontale. Pour modifier cela, on utilise l'option -orient :

$defil_h = $mw->Scrollbar(-orient => 'horizontal');

On peut aussi utiliser -orient => 'vertical', mais comme il s'agit du comportement par défaut, cela n'est pas nécessaire.

VI-C-6 - Utilisation des flèches et de l'ascenseur

Lorsque l'on clique sur l'une des flèches d'une barre de défilement, cela déplace l'ascenseur d'une unité dans cette direction. Si l'on continue de presser le bouton de la souris pendant un certain temps, l'ascenseur répétera automatiquement son mouvement. Le délai d'attente avant le démarrage de la répétition automatique est déterminé par l'option -repeatdelay : par défaut, il est de 300 millisecondes.

Lorsque l'on a maintenu suffisamment longtemps le bouton de la souris enfoncé afin de déclencher la répétition automatique, un court délai intervient entre chaque répétition de l'action. Ce délai est contrôlé par l'option -repeatinterval, qui vaut 100 millisecondes par défaut.

Normalement, le fait de cliquer sur l'ascenseur et de le bouger déplace aussi les données affichées dans le widget. C'est parce que la barre de défilement met continuellement à jour le widget pendant que l'on déplace l'ascenseur. Pour faire en sorte que la barre ne mette le widget à jour que lorsque l'on abandonne l'ascenseur, on utilise l'option -jump et on la met à 1 (sa valeur par défaut est 0). On utilisera -jump => 1 lorsque le widget à parcourir contient beaucoup de données, et que le fait d'attendre que l'écran se mette à jour lorsque l'on se déplace dans ces données donnerait l'impression d'une application lente.

VI-C-7 - Assignation d'une fonction de rappel

Lorsque l'on crée une barre de défilement, on lui indique le widget avec lequel communiquer, ainsi que la méthode de celui-ci qu'il convient d'appeler grâce à l'option -command, à laquelle on affecte une liste anonyme. Cette liste contient le nom de la méthode à appeler et le widget à partir duquel cette méthode doit être appelée. Dans l'exemple qui suit, on peut voir que nous souhaitons utiliser la méthode yview pour parcourir le widget $liste (une boîte de liste) :

$defil_v->configure(-command => ['yview' => $liste]);

Lorsque l'utilisateur cliquera sur cette barre de défilement, cela appellera $liste->yview. Nous savons que la barre associée à $liste est verticale, car elle utilise la méthode yview ; pour une barre horizontale, on aurait utilisé xview. Ces deux méthodes demandent au widget de déplacer son contenu d'un montant déterminé par l'emplacement sur lequel a cliqué l'utilisateur dans la barre. Elles sont traitées dans la section suivante.

VI-C-8 - Communication entre une barre de défilement et les autres widgets

Comme nous l'avons déjà évoqué, nous utilisons l'option -command de la barre de défilement afin qu'elle sache quel widget et quelle méthode utiliser lorsque l'on clique dessus. La méthode sera xview pour les barres horizontales et yview pour les verticales. On peut appeler soi-même ces méthodes, mais, la plupart du temps, ce ne sera pas nécessaire.

xview et yview prennent le même type de paramètres. L'endroit de la barre sur lequel clique l'utilisateur déterminera la valeur utilisée, mais celle-ci sera toujours d'une des formes suivantes :

$widget->xviewMoveto(fraction) ; # ou
$widget->yviewMoveto(fraction) ;

Cette forme est utilisée lorsque l'utilisateur clique sur l'ascenseur, le déplace et le relâche. Le paramètre est une fraction, un nombre réel compris entre 0 et 1, représentant la première partie des données à afficher dans le widget. Si l'utilisateur déplace l'ascenseur jusqu'au sommet ou à l'extrême gauche de la barre, le tout début des données devrait apparaître à l'écran. Cela signifie que le paramètre devrait être 0 :

$widget->xviewMoveto(0);

Si l'ascenseur est déplacé au centre de la barre, le paramètre est 0.5 :

$widget->xviewMoveto(0.5);
$widget->xviewScroll(nombre, "units"); # ou
$widget->yviewScroll(nombre, "units");

Cette forme est utilisée lorsque l'utilisateur clique sur l'une des flèches de la barre. Le widget déplacera ses données vers le haut/bas ou vers la gauche/droite, unité par unité.

Le premier paramètre est le nombre d'unités à parcourir. Sa valeur peut être n'importe quel nombre, mais vaut généralement 1 ou -1. Une valeur de 1 signifie que la prochaine unité de donnée en bas ou à droite du widget deviendra visible (défilement d'une unité vers la gauche ou vers le haut). Une valeur de -1 signifie que l'unité de donnée précédente sera visible en haut ou à gauche du widget (une unité sera décalée du bas ou de la droite du widget). Par exemple, à chaque fois que l'utilisateur clique sur la flèche inférieure d'une barre verticale associée à une boîte de liste, une nouvelle ligne apparaît en bas de la liste.

Le deuxième paramètre est la chaîne « units ». Ce qu'elle signifie dépend du widget : pour une boîte de liste, l'unité est une seule ligne de texte, pour une zone de saisie, c'est un simple caractère.

Voici quelques exemples d'appels :

# L'utilisateur a cliqué sur la flèche inférieure
$liste->yviewScroll(1, "units");

# L'utilisateur a cliqué sur la flèche supérieure
$liste->yviewScroll(-1, "units");

# L'utilisateur a cliqué sur la flèche droite
$saisie->xviewScroll(1, "units");

$widget->xviewScroll(nombre, "page"); # ou
$widget->yviewScroll(nombre, "page");

Cette forme est exactement la même que la précédente, sauf le dernier paramètre, qui est « page » au lieu de « units ». Lorsque l'utilisateur clique sur la zone de défilement de la barre (entre l'ascenseur et les flèches), il s'attend à ce que les données soient déplacées d'une page entière. Ce qu'est une page dépend du widget parcouru. Pour une boîte de liste, par exemple, une page verticale est le nombre de lignes de la liste, une page horizontale est la largeur de cette liste.

VI-C-9 - Configuration d'une barre de défilement

On peut connaître et positionner n'importe quelle option d'une barre de défilement à l'aide de cget et configure. Voyez l'annexe A, Configuration des widgets avec configure et cget, pour les détails complets de ces méthodes.

VI-C-10 - Définir ce qui est visible

La méthode set, que l'on a évoquée lors de la création d'un widget avec barre de défilement, définit ce qui est visible. Dans notre premier exemple, nous avions créé une boîte de liste et lui avions demandé d'utiliser notre barre de défilement grâce à la méthode set :

$defil_v = $mw->Scrollbar(); # Barre de défilement verticale
$liste = $mw->Listbox(-yscrollcommand => ['set' => $defil_v]);

Lorsque le widget appelle la méthode set, il lui passe deux fractions (premier et dernier) en paramètres :

$defil_v->set(premier, dernier);

Cela change la position de que nous voyons des données. Les paramètres premier et dernier sont des nombres réels compris entre 0 et 1. Ils représentent, respectivement, la position du premier et du dernier élément des données visibles. Si nous pouvons voir toutes les données de notre widget, ils vaudront 0 et 1. La première valeur deviendra plus grande au fur et à mesure que des données supplémentaires disparaissent vers le haut, et la deuxième deviendra plus petite au fur et à mesure que des données supplémentaires disparaissent vers le bas. Vous ne rencontrerez probablement jamais de situations dans lesquelles vous devrez appeler set vous-même, mais essayez simplement d'avoir une idée de ce qui se passe en coulisses.

Figure 6.11 : Vue des données d'un widget avec la méthode set (avec une barre de défilement verticale)

La figure 6.11 montre un document imaginaire, visualisé dans un widget parcouru verticalement. Le rectangle en pointillés représente ce qui est vu actuellement dans le widget. Lorsque celui-ci appelle set, il détermine la position du premier élément visible et le passe comme premier paramètre. Ici, ce serait 10 %, soit 0.10. Le second paramètre passé à set est la position du dernier élément de données visible du document. Dans notre exemple, ce serait 80 %, soit 0.8.

VI-C-11 - Connaître la vue courante

La méthode get retourne une liste contenant les dernières valeurs des paramètres que nous venons de décrire pour set :

($premier, $dernier) = $defil_w->get();

Ces données varient lorsque le widget ou la barre de défilement demandent une modification de la position des données.

VI-C-12 - Activer les éléments d'une barre de défilement

Pour savoir quelle partie de la barre de défilement est active, on peut utiliser la méthode activate :

$element = $defil_v->activate();

La valeur renvoyée est une chaîne vide (ce qui signifie qu'aucun élément n'est actuellement actif) ou le nom de l'élément actif à ce moment-là. Les éléments possibles sont "arrow1", "arrow2", ou "slider", indiquant respectivement la flèche du haut (ou de gauche), la flèche du bas (ou de droite) et l'ascenseur.

Si vous passez à activate un nom d'élément comme paramètre, cet élément prendra les couleurs et l'aspect spécifiés par les options -activebackground et -activerelief. L'élément continuera de s'afficher dans cette couleur et avec ce style jusqu'à ce qu'un événement (comme le pointeur de la souris passant au-dessus de l'élément) provoque son changement. Contrairement à ce que l'on pourrait croire, l'utilisation d'activate n'invoque pas cet élément. Voici quelques exemples :

$defil_v->activate("arrow1");
$defil_v->activate("arrow2");
$defil_v->activate("slider");

activate ne peut agir sur la zone de défilement, car celle-ci ne change pas de couleur lorsque la souris est au-dessus d'elle.

VI-C-13 - Calcul des modifications en pixels

Le nombre réel (positif ou négatif), renvoyé par la méthode delta, indique de combien doit changer la barre de défilement pour déplacer l'ascenseur de deltax pixels à droite pour les barres horizontales et de deltay pixels vers le bas pour les barres verticales (deltax sera ignoré pour les barres verticales, et deltay sera ignoré pour les barres horizontales).

$montant = $defil_v->delta(deltax, deltay);

Le montant renvoyé est un pourcentage, positif ou négatif.

VI-C-14 - Localiser un point de la zone de défilement

Étant donné un point en (x, y), la méthode fraction renverra un nombre réel, compris entre 0 et 1 et indiquant où ce point se situera dans la zone de défilement de la barre :

$loc = $defil_v->fraction(x, y) ;

Le point (x, y) doit être exprimé relativement à la barre de défilement. La figure 6.12 montre l'emplacement de trois résultats possibles de fraction : 0 .0, 0.5, et 1.0.

Figure 6.12 : Exemple de valeurs retournées par la méthode fraction

VI-C-15 - Identification des éléments

La méthode identify renvoie une chaîne contenant le nom de l'élément situé aux coordonnées x, y :

$element = $defil_v->identify(x, y) ;

Si les coordonnées x et y ne correspondent à aucun élément, la chaîne renvoyée sera vide. x et y doivent être des coordonnées en pixels, exprimées relativement à la barre de défilement. Les noms d'éléments possibles sont "arrow1", "arrow2", "trough" (désignant la zone de défilement) et "slider" (ce dernier nom désignant l'ascenseur).

VI-D - Exemples

Nous présentons ici des exemples dans l'espoir de lever les confusions qui pourraient exister quant à la façon d'utiliser les barres de défilement dans le monde réel. Chaque exemple utilise, lorsque c'est possible, la méthode Scrolled ; puis on fait la même chose manuellement. Nous n'avons pas encore présenté tous les types de widgets utilisés ici, mais nous ne ferons rien de bien spécial avec eux. Si vous ne reconnaissez pas une méthode ou une option, consultez le chapitre traitant de ce widget pour en savoir plus.

VI-D-1 - Zone de saisie

Une zone de saisie ne peut être parcourue qu'horizontalement. Comme elle ne peut contenir qu'une seule ligne de texte au maximum, une barre de défilement verticale ne servirait à rien. La création d'une zone de saisie avec barre de défilement est facile avec Scrolled :

$mw->Scrolled("Entry", -scrollbars => "s", -width => 30)->pack();

Si l'on souhaite que la barre de défilement ne s'affiche que lorsque les données de la zone de saisie le nécessitent, on utilise -scrollbars => "os". L'utilisation de la méthode Scrollbar implique un peu plus de travail :

$defil_h = $mw->Scrollbar(-orient => 'horizontal');
$saisie = $mw->Entry(
-width => 30,
-xscrollcommand => ['set' , $defil_h]);
$defil_h->configure(-command => ['xview', $saisie]);
$defil_h->pack(-side => 'bottom', -fill => 'x');
$saisie->pack(-side => 'bottom', -fill => 'x');

Les deux méthodes créeront une zone de saisie ressemblant à celle de la figure 6.13.

Figure 6.13 : Zone de saisie avec barre de défilement

VI-D-2 - Widgets liste, texte et canevas

Un widget liste peut être parcouru horizontalement et verticalement, bien que l'on ne souhaite pas toujours utiliser ces deux possibilités. Si l'on connaît la largeur qu'auront les données et que l'on sait que la fenêtre pourra s'en accommoder, une barre horizontale n'est pas nécessaire. Notre premier exemple utilise la méthode Scrolled pour créer deux barres de défilement :

$mw->Scrolled(
"Listbox",
-scrollbars => "se",
-width      => 50,
-height     => 12
)->pack();

Pour faire la même chose manuellement, il faut utiliser Scrollbar pour créer les deux barres, et les configurer pour qu'elles fonctionnent avec le widget :

$cadre = $mw->Frame()->pack(
-side   => 'top',
-expand => 1,
-fill   => 'both'
);
$defil_h = $cadre->Scrollbar( -orient => 'horizontal' );
$defil_v = $cadre->Scrollbar();
$liste   = $cadre->Listbox(
-width          => 50,
-height         => 12,
-yscrollcommand => [ 'set', $defil_v ],
-xscrollcommand => [ 'set', $defil_h ]
);
$defil_h->configure( -command => [ 'xview', $liste ] );
$defil_v->configure( -command => [ 'yview', $liste ] );
$defil_h->pack( -side => 'bottom', -fill => 'x' );
$defil_v->pack( -side => 'right',    -fill => 'y' );
$liste->pack( -side => 'bottom', -fill => 'both', -expand => 1 );

Comme vous pouvez le constater, l'utilisation de Scrolled économise beaucoup de travail. La figure 6.14 montre une liste avec deux barres de défilement : l'une au sud et l'autre à l'est. Cette fenêtre a été créée en utilisant Scrolled. Il existe une subtile différence : le petit espace carré à l'endroit où les deux barres se rencontrent, dans le coin inférieur droit. Lorsque l'on crée soi-même les barres, cet espace n'existe pas (la première barre de défilement placée occupera cet espace).

Figure 6.14 : Liste avec deux barres de défilement

Les widgets texte et canevas avec barres de défilement sont créés exactement comme ci-dessus, nous n'allons donc pas perdre de temps à répéter le même code.

VI-D-3 - Une seule barre de défilement, plusieurs widgets

Parfois, on souhaite utiliser une seule barre de défilement avec plusieurs widgets. Lorsque l'utilisateur cliquera sur cette barre, cela fera défiler tous les widgets en même temps dans la même direction. Dans cet exemple, nous créerons trois listes, contenant chacune onze éléments. Une barre de défilement unique fera défiler les trois listes. Quelle que soit la liste sélectionnée par tabulation, son parcours via les flèches de la barre de défilement ou les touches 'PgUp' et 'PgDn' du clavier fera aussi défiler les autres listes. La figure 6.15 montre à quoi ressemblera la fenêtre.

Figure 6.15 : Fenêtre avec trois listes, toutes contrôlées par la même barre de défilement

Le code ayant permis de créer la fenêtre de la figure 6.15 est le suivant :

use Tk;
$mw = MainWindow->new();
$mw->title("Une seule barre/trois listes");
$mw->Button(
-text    => "Fin",
-command => sub { exit }
)->pack( -side => 'bottom' );
$defil_v = $mw->Scrollbar();

# Tableau anonyme de trois widgets listes
$listes = [ $mw->Listbox(), $mw->Listbox(), $mw->Listbox() ];

# Cette méthode est appelée lorsqu'une liste est parcourue avec le clavier
# La barre reflètera les changements et fera défiler les autres listes.
sub defiler_listes {
my ( $barre, $parcouru, $lsts, @args ) = @_;
$barre->set(@args);    # indique à la barre ce qu'il faut afficher
my ( $haut, $bas ) = $parcouru->yview();
foreach $liste (@$lsts) {
$liste->yview( moveto => $haut );    # ajuste chaque liste
}
}

# Configure chaque liste pour appeler &defiler_listes
foreach $liste (@$listes) {
$liste->configure( -yscrollcommand => [ \&defiler_listes, $defil_v, $liste, $listes ] );
}

# Configure la barre pour faire défiler chaque liste
$defil_v->configure(
-command => sub {
foreach $liste (@$listes) {
$liste->yview(@_);
}
}
);

# Place la barre et les listes
$defil_v->pack( -side => 'left', -fill => 'y' );
foreach $liste (@$listes) {
$liste->pack( -side => 'left' );
$liste->insert( 'end', "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze" );
}

MainLoop;

Pour connecter plusieurs widgets à une seule barre de défilement, on utilise d'abord la méthode Scrollbar pour créer la barre. Puis, on la configure afin qu'elle appelle yview pour chacune des listes que nous voulons faire défiler (ces dernières sont stockées dans un tableau anonyme afin que les méthodes puissent facilement y faire référence). Pour vraiment connecter les listes, on configure chacune d'entre elles pour qu'elle appelle une fonction particulière, faisant défiler les trois listes en plus d'ajuster la barre. Normalement, on ne devrait assigner que ['set',\$liste] à -yscrollcommand : au lieu de cela, nous utilisons la fonction de rappel \&defiler_listes et nous appelons set depuis celle-ci.

VI-E - Essayez et amusez-vous !

- Créez deux widgets, de chacun des types de widgets vus ci-dessus, l'un avec Scrolled et l'autre manuellement. Cela vous permettra de savoir quelle est la méthode que vous préférez ;

- créez deux barres de défilement et attachez-les au même widget (l'opposé de notre exemple « Une seule barre/plusieurs widgets ». Créez, par exemple, une liste avec une barre à gauche et une à droite, les deux faisant défiler la liste verticalement.

VII - Le widget boîte de liste

Un widget boîte de liste (nous emploierons aussi l'expression « widget liste ») sert à énumérer des chaînes de texte, à raison d'une par ligne. On peut alors en sélectionner une ou plusieurs afin de les traiter. Voici quelques exemples de ce que l'on peut trouver dans un tel widget :

- une liste de villes, classées par ordre alphabétique ;

- une liste de serveurs sur lesquels il est possible de se connecter. On choisit un nom de serveur, puis l'on entre un nom et un mot de passe dans des zones de saisie, et l'on clique sur un bouton « Se connecter » ;

- une liste de systèmes d'exploitation ;

- une liste de moyens de paiement : MasterCard, American Express, Carte Visa, chèque, liquide.

Une boîte de liste est la solution idéale pour remplacer de nombreux boutons radio ou cases à cocher qui ne pourraient être simultanément affichés. Habituellement, trois ou quatre boutons radio ou cases à cocher ne posent pas de problème, mais si l'on doit en afficher dix en même temps, la fenêtre risque d'être un peu surpeuplée. Un groupe de boutons radio peut être remplacé par une boîte de liste ne permettant qu'une seule sélection et dont l'un de ses éléments est sélectionné par défaut. Un ensemble de cases à cocher peut être remplacé par une boîte de liste autorisant des sélections multiples et simultanées.

VII-A - Création et remplissage d'une boîte de liste

On utilise la méthode Listbox sur le widget parent pour créer une boîte de liste :

$liste = $parent->Listbox( [ options ... ] )->pack;

Cette méthode renvoie une référence au widget liste ainsi créé. Cette référence pourra ensuite servir à configurer la boîte de liste, à y insérer des éléments, etc. Le plus souvent, on utilise la méthode insert après la création du widget pour réaliser cette dernière opération :

$liste->insert('end', @elts_liste);
# ou ...
$liste->insert('end', $elt1, $elt2, $elt3);

Le premier paramètre de la méthode insert est une valeur d'indice ; les autres seront considérés comme les éléments à placer dans la liste. Les indices d'une boîte de liste ressemblent à ceux d'une zone de saisie, sauf qu'ils désignent des lignes et non des caractères.

Nous pourrions utiliser une boîte de liste pour remplacer les boutons radio permettant de choisir la couleur de fond de notre fenêtre (voir le chapitre 4, Cases à cocher et boutons radio, pour l'exemple avec les boutons radio de la page 102).

Voici le code de la boîte de liste :

%fr_2_us = (
rouge => 'red',
jaune => 'yellow',
vert  => 'green',
bleu  => 'blue',
gris  => 'grey'
);
$liste = $mw->Listbox( -selectmode => "single" )->pack();
$liste->insert( 'end', qw/rouge jaune vert bleu gris/ );
$liste->bind(
'<Button-1>',
sub {
$liste->configure( -background => $fr_2_us{ $liste->get( $liste->curselection() ) } );
}
);

La fenêtre obtenue est celle de la figure 7.1.

L'option -selectmode précise ici que l'on ne pourra faire qu'un seul choix. On insère des couleurs parmi lesquelles on choisira celle du fond. Il n'existe pas d'option -command pour les widgets listes, aussi utilisons-nous bind (voir le chapitre 14, Liaisons d'événements) pour que quelque chose se passe lorsque l'utilisateur clique avec le bouton gauche de la souris sur un élément de la liste. À l'aide des méthodes get et curselection de la liste, on détermine l'élément sur lequel l'utilisateur a cliqué et on colorie alors le fond de la liste avec cette couleur(22). Cet exemple n'offre que cinq couleurs ; on pourrait en mettre d'autres et ajouter une barre de défilement. L'ajout d'une barre consiste à remplacer la ligne de code faisant appel à Listbox par la ligne suivante :

Figure 7.1 : Boîte de liste pour choisir la couleur de fond de la fenêtre

$liste = $mw->Scrolled(
"Listbox",
-scrollbars => "e",
-selectmode => "single"
)->pack();

Les autres lignes du programme resteront inchangées. Le chapitre 6, Barres de défilement, donne toutes les informations sur l'ajout et l'utilisation des barres de défilement.

Passons à présent en revue les options et les méthodes qui nous permettront d'utiliser au mieux les boîtes de liste.

VII-B - Options des boîtes de liste

Comme avec tous les widgets, une boîte de liste dispose de plusieurs options. Les options standard du widget sont -cursor, -font, -height, -highlightbackground, -highlightcolor, -highlightthickness, -takefocus, -width, -xscrollcommand et -yscrollcommand. La liste suivante n'énumère que les options se comportant comme avec les autres widgets. Celles spécifiques au widget liste seront traitées plus loin dans ce chapitre.

VII-C - Modes de sélection

-background => couleur

1. Configure la couleur de la zone située derrière le texte.

-borderwidth => montant

1. Configure la largeur des bords du widget. La valeur par défaut est 2.

-cursor => nom_curseur

1. Configure le curseur à afficher lorsque le pointeur de la souris se trouvera au-dessus de la liste.

-exportselection => 0 | 1

1. Précise si la sélection courante sera aussi disponible comme sélection X. Si cette option vaut 1, elle empêche deux listes d'avoir des sélections au même moment.

-font => nom_fonte

1. Configure la fonte du texte affiché dans la boîte de liste.

-foreground => couleur

1. Configure la couleur du texte non sélectionné dans la liste.

-height => montant

1. Fixe la hauteur de la boîte de liste.

-highlightbackground => couleur

1. Configure la couleur du rectangle de focus lorsque la boîte de liste n'a pas le focus clavier.

-highlightcolor => couleur

1. Configure la couleur du rectangle de focus lorsque la boîte de liste a le focus clavier.

-highlightthickness => montant

1. Fixe la largeur du rectangle de focus. Le montant par défaut est 2.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Configure l'aspect du contour de la boîte de liste.

-selectbackground => couleur

1. Configure la couleur qui apparaîtra derrière le texte sélectionné.

-selectborderwidth => montant

1. Configure la largeur du bord apparaissant autour d'un texte sélectionné.

-selectforeground => couleur

1. Configure la couleur du texte des éléments sélectionnés.

-selectmode => "single" | "browse" | "multiple" | "extended"

1. Détermine le nombre d'éléments qui pourront être sélectionnés en même temps ; agit aussi sur les liaisons clavier/souris ('Maj-clic', par exemple) de la boîte de liste. La valeur par défaut est "browse".

-setgrid => 0 | 1

1. Commute le maillage de la boîte de liste. Par défaut, il est désactivé.

-takefocus => 0 | 1 | undef

1. Fixe la capacité du widget à prendre le focus clavier ou non. 0 signifie jamais, 1 toujours, et undef laisse l'application décider.

-width => montant

1. Configure la largeur en caractères de la boîte de liste. Si montant est inférieur ou égal à 0, la liste sera aussi large que l'élément le plus long.

-xscrollcommand => fct_rappel

1. Assigne une barre de défilement horizontale au widget. Voir le chapitre 6.

-yscrollcommand => fct_rappel

1. Assigne une barre de défilement verticale au widget. Voir le chapitre 6.

VII-C-1 - Modes de sélection

Avec une boîte de liste, il existe différentes façons de sélectionner les éléments. On peut faire en sorte de limiter la sélection à un seul élément à un instant donné (la liste fonctionne alors comme un groupe de boutons radio), ou permettre de sélectionner plusieurs éléments, contigus ou non (la liste fonctionne alors comme des cases à cocher). On contrôle ce comportement avec l'option -selectmode.

Les modes de sélection possibles sont "browse", "single", "multiple" ou "extended". Le mode par défaut est "browse".

browse & single

1. " et "single" se ressemblent car, dans ces deux modes, seul un élément pourra être sélectionné à un moment donné ; cliquer sur l'un des éléments de la liste désactive toute autre sélection déjà effectuée. Le mode browse (naviguer, en français), a une légère particularité : lorsque la souris est déplacée avec son bouton maintenu pressé, la sélection se déplace en même temps que le pointeur. Pour fonctionner avec bind, une liaison <Button-1> sera appelée lors du premier clic. Si l'on veut capturer l'événement correspondant au relâchement du bouton de la souris, il faut définir une liaison ButtonRelease (le chapitre 14 explique comment lier des événements à des widgets).

extended

1. Ce mode permet de sélectionner plusieurs éléments simultanément. On peut cliquer sur un seul élément avec le bouton gauche de la souris, mais cela désactivera tout ce qui est déjà sélectionné. Pour sélectionner plusieurs éléments, il faut cliquer dessus tout en pressant la touche 'Maj' ou 'Ctrl'. La première combinaison étendra la sélection existante de l'élément déjà sélectionné jusqu'au nouvel élément cliqué. La seconde ajoutera l'élément cliqué à la sélection, mais ne modifiera pas les autres sélections. On peut aussi cliquer sur un élément avec le bouton de la souris, maintenir ce bouton pressé et déplacer le pointeur sur d'autres éléments pour les sélectionner. C'est ce que l'on appelle un déplacement « cliquer-glisser ». L'utilisation de extended permet des sélections très rapides de plusieurs éléments de la boîte de liste.

multiple

1. Ce mode vous permet de sélectionner plusieurs éléments. Au lieu d'utiliser les combinaisons 'Maj-clic' et 'Ctrl-clic', on doit les sélectionner un à un. Choisir un élément non sélectionné le sélectionnera, et vice-versa.

VII-C-2 - Différences entre systèmes d'exploitation

Alors que je testais l'option -selectmode, j'ai découvert que le mode de sélection "multiple" ne fonctionnait pas correctement sous Windows 95. Sous cet environnement seulement, il se comporte comme le mode "single" mais il fonctionne correctement sous Unix et Windows NT.

Lorsque l'on sélectionne un élément dans une boîte de liste, celui-ci est, par défaut, disponible en tant que sélection X (ce qui signifie que vous pouvez le copier et le coller comme n'importe quelle sélection dans une fenêtre X). Bien que cela n'ait aucun effet avec le bloc-notes des systèmes Win32, cela affecte quand même la sélection dans plusieurs listes simultanément. Les éléments ne peuvent être sélectionnés que dans une seule liste à un moment donné, même s'il y a plusieurs boîtes de liste. L'option -exportselection contrôle ce comportement : si elle vaut 0, vous pourrez sélectionner des éléments dans plusieurs listes à la fois.

VII-D - Couleurs

La plupart des widgets disposent des options de couleur -background et -foreground. Les boîtes de liste leur ajoutent les options -selectbackground et -selectforeground. Lorsqu'un élément d'une liste est sélectionné, il apparaît dans une couleur différente.

Bien que l'on puisse modifier la couleur du texte sélectionné, on ne peut utiliser qu'une couleur : on ne peut colorer différemment plusieurs lignes d'une boîte de liste.

Figure 7.2 : Exemples de valeurs de -foreground, -background, -selectforeground, et -selectbackground

La liste de gauche de la figure 7.2 utilise -foreground => 'red', -background => 'green', celle de droite -selectforeground => 'red', -selectbackground => 'green'. Lorsque vous modifiez ces options, assurez-vous que les valeurs du fond et du premier plan soient suffisamment contrastées.

VII-E - Style d'une boîte de liste

Par défaut, l'option -relief d'une liste vaut 'sunken' et l'option -borderwidth vaut 2. La figure 7.3 montre les six types de reliefs différents (flat, raised, ridge, groove, sunken et solid). La fenêtre du haut utilise la valeur par défaut de -borderwidth, tandis que celle du bas utilise une valeur de 4 pour cette même option. Pour économiser l'espace, je n'ai pas utilisé de barres de défilement.

Figure 7.3 : Exemples des effets de -relief et -borderwidth sur les boîtes de liste

VII-E-1 - Style des éléments sélectionnés

Il existe aussi une largeur de contour associée à un texte sélectionné et contrôlée par l'option -selectborderwidth. La figure 7.4 montre les effets d'une valeur de 4 pour cette option.

Figure 7.4 : Boîte de liste avec -selectborderwidth => 4

VII-E-2 - Changement de taille spécifique aux boîtes de liste

L'option -setgrid modifie la façon dont la fenêtre se redessinera lorsqu'elle changera de taille. Avec -setgrid => 1, la fenêtre modifiera sa taille en tenant compte du maillage créé par le widget liste. Cela signifie essentiellement que la boîte de liste n'affichera que des lignes complètes (non tronquées) et uniquement des caractères entiers. Un effet de bord intéressant est que la liste affichera toujours au moins une ligne et qu'elle ne pourra déborder de la fenêtre. Cette option n'a rien à voir avec le gestionnaire d'espace utilisé pour placer la boîte de liste dans la fenêtre.

VII-F - Configuration d'une boîte de liste

On peut utiliser la méthode cget pour récupérer les valeurs courantes de toutes les options de la liste. La méthode configure permet d'examiner ou de modifier n'importe quelle option du widget. Ces deux méthodes sont décrites en détail dans l'annexe A, Configuration des widgets avec configure et cget.

VII-G - Indices de liste

Les éléments d'une boîte de liste sont ordonnés : le premier est à l'indice 0 et les indices augmentent de 1 à chaque élément. Les valeurs suivantes conviennent à toutes les méthodes utilisant une valeur d'indice.

n

1. Indice numérique entier. Le premier élément d'une boîte de liste est à l'indice 0.

"active"

1. Indice de la liste représentant la position du curseur. Si la boîte de liste a le focus clavier, l'élément à cet indice sera souligné.

"anchor"

1. Cet indice est positionné grâce à la méthode selectionAnchor(...).

"end"

1. La fin de la liste. Selon la méthode utilisant cet indice, il peut signifier « juste après le dernier élément » (c'est le cas avec insert) ou « le dernier élément » (comme avec delete).

"@x,y"

1. L'élément de la liste recouvrant le point de coordonnées (x, y) (exprimées en pixels). Si ces coordonnées ne sont pas sur un élément précis, l'élément le plus proche sera utilisé.

VII-H - Insertion d'éléments

Pour ajouter des éléments à une boîte de liste, on utilise la méthode insert :

$liste->insert(indice, élément, élément, ...) ;

Chaque élément inséré sera une nouvelle ligne de la boîte de liste. L'indice est un indice décrit dans la section précédente et les éléments insérés le seront avant cette position. Pour ajouter, par exemple, des éléments à la fin de la liste, on écrit :

$liste->insert('end', @liste_elements);
# ou ...
$liste->insert('end', "Élément 1", "Élément 2", "Élément 3");

Pour insérer des éléments au début de la boîte de liste, on peut faire :

$liste->insert(0, @liste_elements);

VII-I - Suppression d'éléments

La méthode delete permet de supprimer des éléments d'une boîte de liste :

$liste->delete(indice_début [, indice_fin]);

Le premier paramètre est l'indice à partir duquel commencer la suppression. Pour supprimer plus d'un élément, on peut ajouter un second paramètre (indice_début doit être inférieur ou égal à indice_fin). Pour supprimer tous les éléments d'une boîte de liste, on écrit :

$liste->delete(0, 'end');

Pour supprimer le dernier élément, on fait :

$liste->delete('end');

VII-J - Récupération d'éléments

La méthode get renvoie une liste d'éléments compris entre deux indices de la boîte de liste :

$liste->get(indice_début [, indice_fin]);

Si seul indice_début est fourni, un seul élément sera renvoyé. Si indice_fin est aussi précisé, il doit être supérieur ou égal à indice_début. Pour obtenir la liste de tous les éléments de la boîte de liste, on utilise l'instruction :

@liste_elements = $liste->get(0, 'end');

Pour récupérer le dernier élément de la liste :

$dernier = $liste->get('end');

Pour connaître les éléments sélectionnés de la boîte de liste, on utilise la méthode curselection :

@selection = $liste->curselection();

Cette méthode renvoie une liste contenant les indices de tous les éléments sélectionnés à cet instant dans la boîte de liste. Si rien n'y est sélectionné, curselection renvoie la chaîne vide. Voici un exemple d'utilisation :

@selection = $liste->curselection();
foreach (@selection) {
# faire quelque chose avec l'indice contenu dans $_
}

Rappelez-vous bien que curselection renvoie une liste d'indices, pas une liste d'éléments.

VII-K - Méthodes de sélection

La méthode curselection, étudiée à la section précédente, ne renseigne que sur ce qu'a sélectionné l'utilisateur. En utilisant l'une des versions suivantes de la méthode selection, on peut aussi modifier la sélection.

VII-K-1 - Sélection des éléments

Pour sélectionner une suite d'éléments dans une boîte de liste, on peut utiliser la version set de la méthode selection (selectionSet). Celle-ci prend comme paramètre un indice ou un intervalle d'indices. Tous les éléments non compris dans cet intervalle ne seront pas concernés. Si l'on utilise un intervalle, le premier indice doit être inférieur ou égal au deuxième. Voici deux exemples :

# Tout sélectionner
$liste->selectionSet(0, 'end' );

# Sélectionner le premier élément
$liste->selectionSet(0);

Même si l'on a utilisé l'option -selectmode pour n'autoriser que les sélections uniques, on peut forcer la sélection de plusieurs éléments grâce à selectionSet.

VII-K-2 - Annulation de sélections

Pour annuler des sélections de la boîte de liste, on utilise la version clear de la méthode selection (selectionClear). On lui passe un indice ou un intervalle décrivant la sélection à annuler. Pour annuler toutes les sélections d'une boîte de liste, par exemple, on écrit :

$liste->selectionClear(0, "end");

Toutes les sélections ne correspondant pas aux indices de l'intervalle spécifié ne seront pas annulées – cela permet de désélectionner élément par élément. On peut aussi annuler la sélection d'un seul élément :

$liste->selectionClear("end");

VII-K-3 - Tester la sélection

Pour savoir si un indice est déjà sélectionné, on utilise la version includes de la méthode selection (selectionIncludes). Celle-ci renvoie 1 si l'élément à l'indice spécifié est sélectionné, 0 sinon. On peut, par exemple, savoir si le dernier élément est sélectionné en faisant :

if ($liste->selectionIncludes('end')) {
...
}

VII-K-4 - Ancrage de la sélection

La version anchor de la méthode selection (selectionAnchor) permet de positionner l'indice "anchor" à la position spécifiée. Cet indice est utilisé pour sélectionner plusieurs éléments de la boîte de liste à l'aide de la souris. Le premier élément cliqué (sans relâcher le bouton de la souris) devient l'"anchor" (« point d'ancrage », en français). On peut, par exemple, utiliser l'instruction suivante pour placer le point d'ancrage sur le premier élément de la liste :

$liste->selectionAnchor(0);

VII-L - Aller à un indice précis

La méthode see permet de montrer un élément spécifique de la boîte de liste :

$liste->see(indice);

Étant donné un indice, see fera défiler la boîte de liste d'une page vers le haut ou vers le bas pour que l'élément situé à cet indice soit visible. Pour un exemple d'utilisation de cette méthode, voir l'exemple « Listbox », plus loin dans ce chapitre.

VII-M - Conversion d'indices

La méthode index traduit une spécification d'indice (comme "active") en son équivalent numérique. Si la boîte de liste contient douze éléments, par exemple, l'instruction suivante affectera 11 à la variable $indice (rappelez-vous que l'indice du premier élément est 0) :

$indice = $liste->index("end");

VII-N - Compter les éléments

La méthode size renvoie le nombre total d'éléments présents dans la boîte de liste :

$nombre = $liste->size();

VII-O - Élément actif vs élément sélectionné

La méthode activate active l'élément à l'indice spécifié. Cela permet d'accéder ensuite à cet élément à l'aide de l'indice "active". La figure 7.5 montre deux fenêtres dans lesquelles les éléments actifs sont soulignés. Chaque boîte de liste est également entourée d'un rectangle noir, ce qui signifie qu'elles ont le focus clavier (l'élément actif n'est pas marqué tant que la liste n'a pas le focus).

# La première fenêtre active l'élément 'quatre'
$liste_1->activate(3);
$liste_1->focus();

# La seconde fenêtre active l'élément 'trois'
$liste_2->activate(2);
$liste_2->focus();

VII-P - Boîte de dimensionnement

La méthode bbox renvoie une liste de quatre éléments décrivant la boîte de dimensionnement du texte placé à l'indice spécifié :

Figure 7.5 : Fenêtre montrant des boîtes de liste avec un élément actif

($x, $y, $l, $h) = $liste->bbox(indice);

Ces quatre éléments sont, dans l'ordre : les coordonnées x et y du coin supérieur gauche de la boîte de dimensionnement, la largeur l et la hauteur h du texte (en pixels).

VII-Q - Recherche d'un indice par sa coordonnée y

Si vous connaissez une coordonnée y dans la boîte de liste, vous pouvez déterminer quel est l'indice de l'élément le plus proche à l'aide de la méthode nearest :

$indice = $liste->nearest(y);

Cette méthode renvoie un nombre correspondant à l'indice de l'élément visible le plus proche dans la boîte de liste.

VII-R - Méthodes de défilement

Les boîtes de liste peuvent être parcourues horizontalement et verticalement, elles disposent donc des méthodes xview et yview ainsi que de toutes leurs formes associées. Ces formes, ainsi que la façon de les utiliser, sont décrites en détail au chapitre 6.

La méthode scan permet d'obtenir un défilement très rapide. Elle est automatiquement liée par la boîte de liste au bouton 2 de la souris. Voici comment obtenir le même effet

dans votre fenêtre :

#!/usr/bin/perl -w
use Tk;

$mw = MainWindow->new;
$mw->title("Boîte de liste");

@contenu = qw/un deux trois quatre cinq six sept huit neuf dix onze/;
$liste = $mw->Scrolled("Listbox")->pack;
$liste->insert( 'end', @contenu );

$mw->bind( "Listbox", "<2>", [ 'scan', 'mark', Ev('x'), Ev('y') ] );
$mw->bind( "Listbox", "<B2-Motion>", [ 'scan', 'dragto', Ev('x'), Ev('y') ] );

MainLoop;

Lorsque l'on clique dans la fenêtre avec le bouton 2 de la souris et que l'on déplace le pointeur, le contenu de la boîte de liste défile très vite. On peut modifier le deuxième paramètre de chaque instruction bind si l'on veut lier ce comportement à une autre combinaison d'actions touches/souris. La méthode bind est décrite au chapitre 14.

VII-S - Exemple Listbox

Lorsqu'une boîte de liste contient de nombreux éléments, l'utilisateur devra parfois y chercher longtemps ce qui l'intéresse. Si l'on insère les éléments triés dans la liste, on peut implanter une fonction de recherche plus rapide. Voici un petit script expliquant comment utiliser une zone de saisie pour entrer le texte recherché et comment parcourir la boîte de liste à chaque fois qu'un nouveau caractère est entré dans cette zone de saisie :

#!/usr/bin/perl -w
use Tk;
use strict;

my $mw = MainWindow->new;
$mw->title("Listbox");

# Pour simplifier, on utilise un seul mot commençant par une même lettre
my @choix = qw/alpha beta charlie delta echo foxtrot golf hotel india
juliet kilo lima motel nancy oscar papa quebec radio sierra
tango uniform victor whiskey xray yankee zulu/;
my $cherche;    # contient le texte recherché
my $ancien;     # contient le texte précédemment recherché

# On crée la zone de saisie et on lie la fonction 'rechercher' à
# toute frappe au clavier
my $saisie = $mw->Entry( -textvariable => \$cherche )->pack(
-side => "top",
-fill => "x"
);

$saisie->bind( "<KeyPress>", [ \&rechercher, Ev("K") ] );

# On crée une boîte de liste et on y insère la liste des choix
my $liste = $mw->Scrolled( "Listbox", -scrollbars => "osoe", )->pack( -side => "left" );

$liste->insert( "end", sort @choix );

$mw->Button(
-text    => "Fin",
-command => sub { exit; }
)->pack( -side => "bottom" );

MainLoop;

# Cette fonction est appelée à chaque fois que l'on presse une touche
# Le deuxième paramètre, $touche, est le caractère entré par
# l'utilisateur, pas la chaîne $cherche complète.
sub rechercher {
my ( $saisie, $touche ) = @_;

# On ignore la touche 'backspace' et tout ce qui ne modifie pas le mot
# i.e. les touches Control ou Alt
return if ( $touche =~ /backspace/i );
return if ( $ancien eq $cherche );

# On utilise ce qui est actuellement affiché dans la boîte de liste
# pour rechercher
# Il s'agit d'une recherche ordonnée assez simple
my @elements = $liste->get( 0, "end" );
foreach ( 0 .. $#elements ) {
if ( $elements[$_] =~ /^$cherche/ ) {
$liste->see($_);
$liste->selectionClear( 0, "end" );
$liste->selectionSet($_);
last;
}
}
$ancien = $cherche;
}

VII-T - Essayez et amusez-vous !

Utilisez une boîte de liste pour créer un mini visualiseur de fichier : on lit un nom de fichier dans une zone de saisie et son contenu est chargé dans la boîte de liste lorsque l'on clique sur un bouton. Chacune de ses lignes devient alors un élément de cette dernière.

VIII - Le widget texte

Si l'on vous demandait à quoi peut bien servir un widget texte, vous répondriez automatiquement « à afficher du texte ». Bien que ce soit vrai, c'est un peu restrictif. Le widget texte est l'un des widgets standards les plus puissants de Perl/Tk. Il est souple, configurable, et facile à utiliser pour les tâches simples. Voici quelques exemples de cas où l'on peut l'utiliser :

- afficher et éditer un fichier en texte pur ;

- afficher un texte formaté à partir d'un document HTML ;

- créer une palette de couleurs avec barre de défilement et des boutons vous permettant de modifier les couleurs ;

- gérer du texte formaté (éventuellement avec des couleurs), sur plusieurs lignes, saisi par l'utilisateur (un mini traitement de texte) ;

- afficher du texte, avec une coloration syntaxique ;

- rendre « cliquables » certaines parties du texte et réaliser une action donnée lorsqu'elles sont cliquées. Cela pourrait être du HTML, ou ressembler à la démo widget (23) .

Dans un widget texte, on peut placer du texte simple ou formaté, ainsi que d'autres widgets. Un widget texte peut être utilisé avec des barres de défilement afin de permettre la visualisation de nombreuses pages d'information dans un espace réduit.

VIII-A - Création et utilisation d'un widget texte

Pour créer un widget texte, on utilise la méthode Text à partir du widget parent concerné :

$texte = $parent->Text( [ options ... ] )->pack;

Après avoir créé le widget texte, il y a plusieurs possibilités différentes pour le remplir. L'utilisateur peut directement y saisir son texte, ou vous pouvez utiliser la méthode insert :

$texte->insert('end', "Être ou ne pas être...\nLà est la question.");

La forme de base de la méthode insert prend deux paramètres. Le premier est un indice indiquant où commencer l'ajout du texte, le second est la chaîne à insérer. À la différence de la méthode insert des boîtes de liste, on ne peut utiliser un tableau comme deuxième paramètre : si vous le faites, seul le premier élément du tableau sera inséré dans le texte.

L'utilisation typique d'un widget texte consiste à lire un fichier et à le placer dans la boîte de texte au fur et à mesure qu'il est lu :

$texte = $mw->Scrolled("Text")->pack();
open (DESC_FIC, "chapitre1") ||
die "Impossible d'ouvrir chapitre1\n";
while (<DESC_FIC>) {
$texte->insert('end', $_);
}
close(DESC_FIC);

Vous pouvez modifier ce programme pour qu'il affiche le fichier « à l'envers » (ligne par ligne) : il suffit de remplacer la ligne d'insertion par$texte->insert(0, $_), cela placera la dernière ligne lue en haut du widget texte au lieu de la mettre en bas.

Le widget texte peut faire beaucoup plus que simplement afficher un fichier ou deux lignes d'une réplique shakespearienne. En plus des options, on dispose également de marqueurs, d'indices, et de marques contrôlant la façon dont le contenu du widget sera affiché.

VIII-B - Options du widget texte

Les options utilisables avec la méthode Text modifient la façon dont le texte est affiché dans les widgets texte. Celles qui suivent sont communes à tous les widgets : -background, -borderwidth, -cursor, -exportselection, -foreground, -highlightbackground, -highlightcolor, -highlightthickness, -insertbackground, -insertborderwidth, -insertofftime, -insertontime, -insertwidth, -padx, -pady, -selectbackground, -selectborderwidth, -selectforeground, -setgrid, -state, -takefocus, -wrap, -xscrollcommand et -yscrollcommand.

Le chapitre 3, Le bouton de base, traite de ces options.

-background => couleur

1. Modifie la couleur de l'écran affiché derrière le texte.

-borderwidth => montant

1. Configure la largeur des bords du widget.

-cursor => nom_curseur

1. Précise le curseur qui sera affiché lorsque le pointeur de la souris est au-dessus du widget texte.

-exportselection => 0 | 1

1. Détermine si le texte sélectionné dans le widget peut aussi être utilisé par le système de fenêtrage (X Window, par exemple).

-font => nom_fonte

1. Fixe la fonte dans laquelle sera affiché le texte.

-foreground => couleur

1. Configure la couleur du texte.

-height => montant

1. Configure la hauteur du widget. Le montant par défaut est 24.

-highlightbackground => couleur

1. Configure la couleur que devra prendre le rectangle de focus placé autour du widget lorsque ce dernier n'a pas le focus clavier.

-highlightcolor => couleur

1. Configure la couleur que devra prendre le rectangle de focus placé autour du widget lorsque ce dernier a le focus clavier.

-highlightthickness => montant

1. Configure l'épaisseur du rectangle de focus placé autour du widget. Le montant par défaut est 2.

-insertbackground => couleur

1. Modifie la couleur du point d'insertion.

-insertborderwidth => montant

1. Modifie la largeur du contour du point d'insertion.

-insertofftime => millisecondes

1. Configure la durée pendant laquelle le point d'insertion reste éteint. La durée par défaut est 300.

-insertontime => millisecondes

1. Configure la durée pendant laquelle le point d'insertion reste allumé. La durée par défaut est 600.

-insertwidth => montant

1. Configure la largeur du point d'insertion.

-padx => montant

1. Ajoute des espaces supplémentaires à gauche et à droite du texte à l'intérieur du widget texte.

-pady => montant

1. Ajoute des espaces supplémentaires au-dessus et en dessous du texte à l'intérieur du widget texte.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Configure le relief du contour du widget. La valeur par défaut est 'sunken'.

-selectbackground => couleur

1. Précise la couleur de la zone placée derrière le texte sélectionné.

-selectborderwidth => montant

1. Configure la largeur du contour de la zone sélectionnée.

-selectforeground => couleur

1. Fixe la couleur du texte sélectionné.

-setgrid => 0 | 1

1. Active le maillage du widget texte. Par défaut, il est inactif.

-spacing1 => montant

1. Précise l'espace supplémentaire qui sera laissé au-dessus d'une ligne de texte commençant sur sa propre ligne. Le montant par défaut est 0.

-spacing2 => montant

1. Précise l'espace supplémentaire qui sera laissé au-dessus d'une ligne de texte après qu'elle ait été automatiquement coupée par le widget texte. Le montant par défaut est 0.

-spacing3 => montant

1. Précise l'espace supplémentaire qui sera laissé après une ligne de texte terminée par un \n. Le montant par défaut est 0.

-state => 'normal' | 'disabled'

1. Indique l'état du widget texte. La valeur par défaut est 'normal'. Si cet état est 'disabled', aucun texte ne pourra être inséré, que ce soit par l'utilisateur ou par l'application (via la méthode insert).

-tabs => liste

1. Spécifie une liste de taquets de tabulation à utiliser dans le widget texte. La liste par défaut est indéfinie (utilisation des taquets par défaut, situés tous les 8 caractères).

-takefocus => 0 | 1 | undef

1. Indique si le widget pourra obtenir le focus clavier.

-width => montant

1. Fixe la largeur en caractères du widget texte. Le montant par défaut est 80.

-wrap => 'none' | 'char' | 'word'

1. Configure le mode utilisé pour déterminer la coupure de ligne automatique. La valeur par défaut est "char".

-xscrollcommand => fct_rappel

1. Précise la fonction de rappel à utiliser lorsque le widget texte est parcouru horizontalement.

-yscrollcommand => fct_rappel

1. Précise la fonction de rappel à utiliser lorsque le widget texte est parcouru verticalement.

VIII-B-1 - Fontes

L'option -font permet de modifier la fonte, et notamment la taille, du texte (voir la figure 8.1). Elle définit la fonte par défaut pour le widget entier. Celle-ci sera utilisée pour tout texte inséré sans marqueur (un marqueur permet de spécifier un formatage spécial qui s'appliquera uniquement à certaines parties du texte).

Figure 8.1 : Widget texte, utilisant -font => « r16 »

L'utilisation des fontes a été traitée au chapitre 3, où l'option -font a été rencontrée pour la première fois. L'annexe C présente les nouvelles méthodes de création et de manipulation des fontes de Tk8.0.

VIII-B-2 - Taille du widget

Lorsque l'on crée un widget texte, il occupe habituellement 24 lignes de haut et 80 caractères de large. Selon la façon dont vous le placez dans la fenêtre (avec pack et les options -expand et -fill, ou avec grid et -sticky => "nsew"), il pourra changer de taille en même temps que la fenêtre. Pour forcer une certaine taille, vous pouvez utiliser les options -width et -height :

# Widget texte de 20 caractères de large sur 10 lignes de haut
$mw->Text(-width => 20, -height => 10)->pack;

Les valeurs associées à -width sont exprimées en nombre de caractères, tandis que celles associées à -height le sont en nombre de lignes. Le widget texte peut ne pas être de la taille exacte requise par ces options si vous forcez la fenêtre à être plus grande via la fonction minsize ($mw->minsize(400,400), par exemple), notamment si vous avez utilisé -expand => 1 et -fill => 'both' avec la méthode pack. Rappelez-vous de tout cela si ce que vous voyez à l'écran ne correspond pas à ce que vous attendiez.

VIII-B-3 - Style d'un widget texte

Comme pour les autres widgets, vous pouvez modifier la façon dont sera tracé le contour d'un widget texte à l'aide des options -relief et -borderwidth. Les exemples de la figure 8.2 ne ressemblent peut-être pas à des widgets texte mais, croyez-moi, en sont bien !

Figure 8.2 : Widgets texte ayant des -relief différents (montre aussi l'utilisation de -width et -height pour forcer une taille plus petite

VIII-B-4 - Hauteur de l'interligne

Lorsque du texte s'affiche dans un widget texte, il peut être coupé automatiquement (word wrap) et continuer sur la ligne suivante s'il est plus long que la largeur du widget. L'espace laissé entre les lignes est défini par les options -spacingN. La figure 8.3 montre les différentes zones affectées par -spacing1, -spacing2 et -spacing3.

Figure 8.3 : Effets des options -spacingN

L'option -spacing1 intervient sur l'espace laissé au-dessus d'une nouvelle ligne de texte (la première d'un paragraphe). L'option -spacing2 affecte l'espace laissé entre les lignes lors des coupures automatiques. L'option -spacing3 détermine l'espace laissé après la dernière ligne d'un paragraphe (juste après un saut de ligne explicite).

VIII-B-5 - Taquets de tabulation

Les taquets de tabulation d'un widget texte sont, par défaut, placés tous les huit caractères. Chaque tabulation équivaut à huit espaces (mais n'utilise pas réellement de caractère espace). On peut remplacer ce réglage par défaut à l'aide de l'option -tabs :

-tabs => [qw/2 center/] # Place un taquet tous les 2 pixels
-tabs => [2, "center"] # Idem, avec une syntaxe différente

Le paramètre passé à -tabs est une liste anonyme spécifiant les positions où placer chacun des taquets de tabulation. On peut aussi donner une valeur facultative d'alignement de chacun des taquets après sa valeur numérique (comme dans l'exemple précédent). Cela semble plus compliqué, mais ne l'est pas. Voici quelques exemples qui rendent les choses plus claires :

# Tous les pouces, texte centré sur le taquet
-tabs => [qw/1i center/]
# Taquet à 1 pouce ; à 1,5 pouce, puis tous les demi-pouces
-tabs => [qw/1i 1.5i/]

L'alignement par défaut est "left", les autres valeurs possibles sont "right", "center" ou "numeric".

Lorsque l'on spécifie des valeurs (que ce soit en centimètres, pouces, ou pixels), elles ne se cumulent pas. La liste ["1i", "1.5i"] se traduit par « un taquet de tabulation à 1 pouce du bord gauche du widget texte, et le prochain taquet à 1,5 pouce de ce même bord gauche ». Si la liste n'est pas assez longue pour couvrir la fenêtre entière, la distance entre les deux derniers taquets spécifiés sera répétée tout au long de la ligne.

Bien sûr, le positionnement de nouveaux taquets est plutôt inutile, sauf si vous souhaitez soigner la présentation du texte ; dans la plupart des cas, vous ne vous occuperez pas de cette option.

On peut remettre les taquets de tabulation à leurs positions par défaut en affectant undef à -tabs :

$texte->configure(-tabs => undef);

VIII-C - Une courte pause pour un bref exemple

Avant d'aborder certaines possibilités plus complexes (et plus plaisantes) des widgets texte, étudions une utilisation complète.

Voici un court programme qui affichera le contenu d'un fichier, vous permettra de le modifier puis de le sauvegarder :

use Tk;
$mw = MainWindow->new;

# Crée les widgets nécessaires
$cadre = $mw->Frame->pack(
-side => 'top',
-fill => 'x'
);
$cadre->Label( -text => "Nom du fichier : " )->pack(
-side   => 'left',
-anchor => 'w'
);
$cadre->Entry( -textvariable => \$nom_fic )->pack(
-side   => 'left',
-anchor => 'w',
-fill   => 'x',
-expand => 1
);
$cadre->Button(
-text    => "Fin",
-command => sub { exit; }
)->pack( -side => 'right' );
$cadre->Button(
-text    => "Sauvegarder",
-command => \&sauve_fic
)->pack(
-side   => 'right',
-anchor => 'e'
);
$cadre->Button(
-text    => "Charger",
-command => \&lire_fic
)->pack(
-side   => 'right',
-anchor => 'e'
);
$mw->Label(
-textvariable => \$info,
-relief       => 'ridge'
)->pack(
-side => 'bottom',
-fill => 'x'
);
$texte = $mw->Scrolled("Text")->pack(
-side   => 'bottom',
-fill   => 'both',
-expand => 1
);

MainLoop;

# lire_fic vérifie quel est le nom du fichier et le charge
sub lire_fic {
$info = "Chargement du fichier '$nom_fic'...";
$texte->delete( "1.0", "end" );
if ( !open( FIC, "$nom_fic" ) ) {
$t->insert( "end", "ERREUR : Impossible d'ouvrir $nom_fic\n" );
return;
}
while (<FIC>) { $texte->insert( "end", $_ ); }
close(FIC);
$info = "Fichier '$nom_fic' chargé.";
}

# sauve_fic sauvegarde le fichier en utilisant le nom placé
# dans la zone de saisie
sub sauve_fic {
$info = "Sauvegarde de '$nom_fic";
open( FIC, ">$nom_fic" );
print FIC $texte->get( "1.0", "end" );
$info = "Fichier '$nom_fic' sauvegardé.";
}

La figure 8.4(24) montre la fenêtre obtenue lorsqu'un fichier a été chargé et sauvegardé.

Figure 8.4 : Un simple éditeur de texte, contenant 'monfichier'

VIII-D - Indices de texte

Lorsque nous avons parlé des valeurs d'indices pour les boîtes de liste, chaque indice faisait référence à une ligne de la liste. La première ligne était à l'indice 0, etc. Dans le cas d'un widget texte, l'indice peut désigner une ligne précise ou l'un de ses caractères. Un indice de widget texte est construit en utilisant un indice de base, suivi éventuellement d'un modificateur. L'ensemble indice, base et modificateur, doit être placé entre apostrophes doubles.

VIII-D-1 - Valeurs de l'indice de base

"n.m"

1. Ce format vous permet de spécifier explicitement un numéro de ligne et un numéro de caractère dans cette ligne. Les lignes commencent à l'indice 1 (ce qui est différent du widget liste), et les caractères débutent à la position 0.

"@x,y"

1. Désigne le caractère le plus près des coordonnées x, y dans le widget.

"end"

1. La fin du widget texte, après le dernier caractère \n.

"mark"

1. Désigne le caractère situé après l'emplacement appelé mark. Les deux marques fournies par Tk sont "current" et "insert". Ce qu'elles désignent sera expliqué plus loin dans ce chapitre.

"marqueur.first"

1. Un marqueur (tag , en anglais) remplace simplement certaines instructions de formatage spéciales (étudiées dans la section suivante). Après avoir créé des marqueurs, vous pouvez utiliser cette forme d'indice. marqueur.first est le premier caractère de type marqueur du widget texte. Vous pourriez, par exemple, créer un marqueur "titre" et utiliser l'indice "titre.first".

"marqueur.last"

1. Désigne le caractère placé immédiatement après le texte marqué par marqueur.

$widget

1. Si vous avez un widget contenu dans le widget texte, vous pouvez désigner son emplacement à l'aide de la variable qui le décrit.

$image

1. À partir de Tk8.0, vous pouvez intégrer des images dans un widget texte. Désigner l'emplacement d'une telle image est alors possible via la variable qui la décrit.

VIII-D-2 - Modificateurs d'indices

On peut utiliser des modificateurs d'indices après une valeur d'indice de base.

[ + | - ] nombre [ chars | lines ]

1. On peut utiliser + et - pour additionner/soustraire des lignes et des caractères à un indice de base. L'indice "end - 1 lines" désigne le texte placé sur la ligne avant "end". Faites bien attention lorsque vous utilisez ce modificateur, car toutes les lignes comptent aussi comme une ligne complète.

linestart

1. Modifie l'indice afin de désigner le premier caractère de cette ligne. Ainsi, $texte->insert("end linestart", $chaine) insérera la ligne devant la dernière ligne du widget texte. insert placera le nouveau texte avant l'indice fourni.

lineend

1. Désigne le dernier caractère de la ligne (habituellement, le caractère de retour à la ligne). Ce modificateur est utile lorsque l'on ne connaît pas le nombre exact de caractères d'une ligne, mais que l'on veut ajouter du texte à la fin de celle-ci.

wordstart

1. Ajuste l'indice pour qu'il désigne le premier caractère du mot pointé par l'indice de base.

wordend

1. Ajuste l'indice pour qu'il désigne le caractère placé après la fin du mot pointé par l'indice de base.

VIII-D-3 - Exemples d'indices de texte

"end"

1. La position juste après la dernière ligne de texte du widget, quel que soit le volume de texte dans celui-ci.

"1.0"

1. Le premier caractère de la première ligne du widget texte. Le 1 représente la ligne et le 0, le caractère.

"2.0 - 1 chars"

1. Le dernier caractère de la première ligne. On le désigne à l'aide du premier caractère de la deuxième ligne (2.0) et en ôtant un caractère à cette position. Si l'on utilise la méthode insert avec cet indice, cela insérerait le texte juste avant le \n à la fin de la première ligne.

"1.end"

1. Désigne aussi le dernier caractère de la première ligne, mais de façon plus simple.

"2.0 lineend"

1. La fin de la deuxième ligne. Il est nécessaire de préciser 2.0, et pas simplement 2 car cette dernière valeur n'est pas un indice de base correct.

Les indices de base sont faciles à comprendre, mais tout se complique lorsque l'on commence à faire de l'arithmétique avec les indices. Il suffit de se rappeler que l'on désigne une position dans le widget texte, et que cette position peut changer si du texte est inséré ou supprimé (soit par l'utilisateur, soit par l'application).

Bien que certaines combinaisons puissent sembler stupides ("1.0 linestart", par exemple), pensez qu'il est possible que vous appeliez des méthodes renvoyant des informations peu précises sur un événement. Si, par exemple, un utilisateur clique dans le widget texte et presse un bouton qui augmentera la taille de la fonte de la ligne entière, l'arithmétique des indices vous permettra de désigner toute cette ligne sans même connaître avec certitude son emplacement.

VIII-E - Marqueurs de texte

Les marqueurs de texte vous offrent un autre moyen d'accéder à des parties du texte du widget. Un marqueur sert trois objectifs et le même marqueur peut être utilisé pour tous, ou pour un seul :

- associer des informations de formatage à une partie (ou à des parties) du texte ;

- associer une liaison à du texte dans le widget ;

- gérer le texte sélectionné.

Les marqueurs servent aussi à modifier l'apparence du texte à l'écran : la fonte, la taille, la couleur et l'espacement sont quelques-unes des propriétés du texte affectées par les marqueurs. On peut modifier ces propriétés en créant ses propres marqueurs (avec leur propre nom) et en utilisant des paires option/valeur pour introduire des informations de formatage. En plus de la modification du format, les marqueurs servent à appliquer une liaison spécifique (telle que l'exécution d'une tâche lorsque l'utilisateur clique sur ce texte). Un marqueur spécial, "sel", gère le texte sélectionné : à chaque fois que l'utilisateur sélectionne du texte, l'emplacement de celui-ci est marqué avec le marqueur "sel".

Tout texte contenu dans un widget texte peut avoir un ou plusieurs marqueurs qui lui sont associés. Si l'on applique deux marqueurs au même morceau de texte et qu'ils modifient tous deux la fonte, le dernier marqueur appliqué prédomine.

VIII-E-1 - Options des marqueurs

Les options permettant de configurer du texte marqué sont quasiment un sous-ensemble des options de configuration du widget texte lui-même. Certaines options ne concernent que du texte marqué.

-background => couleur

1. Configure la couleur de la zone située derrière le texte.

-bgstipple => motif

1. Précise le motif utilisé pour tracer la zone derrière le texte. Cette option permet de donner un aspect ombré.

-borderwidth => montant

1. Configure la largeur du relief tracé autour du texte ligne par ligne.

-fgstipple => motif

1. Précise le motif utilisé pour tracer le texte.

-font => nom_fonte

1. Indique la fonte qui sera utilisée par le texte.

-foreground => couleur

1. Configure la couleur du texte.

-justify => 'left' | 'right' | 'center'

1. Établit la position du texte dans le widget.

-lmargin1 => montant

1. Précise l'indentation des premières lignes de paragraphes à partir du bord gauche du widget.

-lmargin2 => montant

1. Précise l'indentation des lignes de paragraphes (sauf la première) à partir du bord gauche du widget. Parfois appelé « paragraphes en retrait ».

-offset => montant

1. Précise le nombre de pixels duquel le texte sera surélevé ou abaissé par rapport à la ligne de référence. Cette option peut servir à créer des indices et des exposants.

-overstrike => 0 | 1

1. Si cette option est vraie, le texte sera barré par une ligne.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Détermine la façon dont sera tracé le contour du texte, ligne par ligne.

-rmargin => montant

1. Précise l'espace qui sera laissé entre le texte et le bord droit du widget.

-spacing1 => montant

1. Précise le supplément d'espace laissé au-dessus d'une ligne de texte commençant sa propre ligne. Par défaut, montant vaut 0.

-spacing2 => montant

1. Précise le supplément d'espace laissé au-dessus d'une ligne de texte après qu'elle a été coupée automatiquement par le widget. Par défaut, montant vaut 0.

-spacing3 => montant

1. Précise le supplément d'espace laissé après une ligne terminée par un \n. Par défaut, montant vaut 0.

-tabs => liste

1. Indique l'ensemble des taquets de tabulation pour ce texte. Consultez la section consacrée aux taquets de tabulation, plus haut dans ce chapitre, pour plus d'informations.

-underline => booléen

1. Indique si le texte doit être souligné.

-wrap => 'none' | 'char' | 'word'

1. Détermine le mode dans lequel le texte sera coupé. 'none' signifie que les lignes plus longues que le widget n'est large ne seront pas coupées. 'char' coupera sur tout caractère, tandis que 'word' ne coupera qu'entre les mots.

VIII-E-2 - Un exemple de marqueur simple

Voici un exemple montrant comment créer un marqueur simple et comment l'utiliser

pour insérer du texte dans un widget texte (l'écran obtenu est montré en figure 8.5 :

$texte = $mw->Text()->pack();
$texte->tagConfigure( 'gras', -font => "-*-Courier-Bold-B-Normal--*-120-*-*-*-*-*-*" );

# Utilisez -font => "{Courier New} 24 {bold}" pour les systèmes Win32
$texte->insert( 'end', "Voici du texte normal\n" );
$texte->insert( 'end', "Voici du texte en gras\n", 'gras' );

La première ligne crée le widget texte et le place à l'écran.

La ligne 2 crée le marqueur 'gras'. Ne soyez pas troublé par l'utilisation du mot « configure » au lieu de « create ». Lorsque l'on configure un marqueur, on le crée aussi. Ici, 'gras' est associé à une fonte différente (la même que celle par défaut sous Unix, mais en gras).

Jusqu'à maintenant, nous n'avons encore rien modifié dans le widget texte. Nous avons simplement fait en sorte de pouvoir utiliser le marqueur plus loin dans le code. N'importe quel nom de marqueur convient, tant qu'il s'agit d'une chaîne correcte. On aurait pu choisir « texte_en_gras » ou « fonte_en_gras », ou « marqueur1 ». Si vous utilisez un style de programmation correct (et que vous souhaitez pouvoir maintenir votre code), utilisez un nom indiquant à quoi sert le marqueur.

La troisième ligne insère du texte dans le widget.

La ligne 4 insère du texte supplémentaire, en utilisant le marqueur 'gras'. La méthode insert permet de spécifier un marqueur en tant que troisième paramètre ; la chaîne sera insérée dans le widget et associée au marqueur spécifié. Le marqueur 'gras' a été configuré pour modifier la fonte ; par conséquent, tout texte avec le marqueur 'gras' s'affichera dans la fonte associée à celui-ci.

Figure 8.5 : Widget texte avec du texte normal et en gras

Il s'agit ici d'un exemple très simplifié. Que faire, si l'on désire modifier le texte saisi par l'utilisateur ? Dans ce cas, on ne peut utiliser la méthode insert, mais on se servira de la méthode tagAdd :

$texte->tagAdd('gras', '1.0', 'end');

Cette ligne applique le marqueur 'gras' à tout le texte du widget.

VIII-E-3 - Utilisation du marqueur "sel" pour manipuler la sélection

Le marqueur "sel" est un marqueur spécial, géré par le widget texte. Tout texte sélectionné par l'utilisateur se verra affecter ce marqueur. On peut aussi forcer la sélection en utilisant certaines méthodes propres aux marqueurs (méthodes dont nous n'avons pas encore traité) afin de placer "sel" sur un texte donné. Pour sélectionner, par exemple, la troisième ligne du texte :

$texte->tagAdd("sel", "3.0", "3.0 lineend");

Voici un exemple montrant comment ajouter un autre marqueur au texte actuellement sélectionné :

$texte->tagAdd('gras', 'sel.first', 'sel.last')
if ($texte->tagRanges('sel'));

Lorsque l'on utilise le marqueur "sel" comme partie d'un indice, on doit d'abord s'assurer que le marqueur existe dans le widget texte (à l'aide de tagRanges), ou l'on obtiendra une énorme erreur.

VIII-E-4 - Création et configuration des marqueurs

La première action sur un marqueur consiste à le créer à l'aide de tagConfigure (sauf si vous utilisez le marqueur "sel", créé automatiquement). Le premier paramètre de tagConfigure est le nom du marqueur et les autres (qui sont facultatifs) sont les paires option/valeur décrites à la section précédente. Voici quelques exemples :

# Création d'un marqueur sans option
$texte->tagConfigure("special");
# Création d'un marqueur qui modifiera la couleur
$texte->tagConfigure("bleu", -foreground => "blue");
# Création d'un marqueur qui soulignera le texte
$texte->tagConfigure("souligne", -underline => 1);
# Création d'un marqueur changeant la couleur et l'espacement
$texte->tagConfigure("grand_bleu",
-foreground => "blue",
-spacing2   => 6
);

On peut modifier la configuration d'un marqueur déjà créé en utilisant tagConfigure une seconde fois. Tous les changements effectués sur le marqueur affectent immédiatement tout texte ayant ce marqueur dans le widget :

# Ajoute une couleur de fond à "bleu"
$texte->tagConfigure("bleu", -background => "red");
# Modifie l'espacement de "grand_bleu"
$texte->tagConfigure("grand_bleu", -spacing2 => 12);

Comme avec les méthodes configure des widgets, on peut utiliser tagConfigure pour récupérer la configuration courante d'un marqueur donné. Pour obtenir toutes les options d'un marqueur, ainsi que leur valeur sous la forme d'une liste de listes :

@liste_de_listes = $texte->tagConfigure("bleu");
foreach $opt (@liste_de_listes) { print "@$opt\n"; } # l'affiche

Chaque sous-liste contient cinq éléments, dont le nom de l'option et sa valeur. On peut aussi limiter à une seule option l'information à récupérer :

@liste = $texte->tagConfigure("bleu", -font);

On utilisera tagCget si l'on souhaite connaître la valeur d'une option particulière :

$valeur = $texte->tagCget("grand_bleu", -spacing2);

VIII-E-5 - Ajout d'un marqueur à du texte existant

Nous avons déjà vu un exemple d'utilisation de la méthode tagAdd : elle permet d'ajouter un marqueur à des parties du texte dans le widget. Son utilisation est la suivante :

$texte->tagAdd('nom_marqueur', début1 [, fin1, début2, fin2, ... ] )

On peut ajouter un marqueur à un simple indice ou à un intervalle d'indices ; cela signifie que l'on peut ajouter un marqueur au widget texte à plusieurs endroits simultanément. Supposons que vous vouliez ajouter le marqueur entete aux lignes 1, 12 et 30, car il s'y trouve des titres de sections que vous voulez mettre en évidence par rapport au reste du texte. La ligne tagAdd ressemblerait alors à ceci :

$texte->tagAdd('entete', '1.0',  '1.0 lineend',
'12.0', '12.0 lineend',
'30.0', '30.0 lineend');

Supposons maintenant que le formatage de 'entete' agrandisse la fonte : les lignes concernées s'affichent maintenant différemment du reste du texte dans le widget.

Vous pouvez ajouter plus d'un marqueur à une section de texte. Il est possible, par exemple, d'avoir à la fois les marqueurs 'entete' et 'couleur'. Si les deux marqueurs modifient la même option (-font, par exemple), la dernière configuration de cette option l'emporte, sauf si la priorité du marqueur a été modifiée à l'aide de tagRaise ou tagLower.

Lorsque l'on affecte un marqueur à un ensemble de texte, tout ce qui sera inséré entre les indices de début et de fin de ce texte se verra automatiquement affecté par ce marqueur ; que l'on utilise insert sans marqueur spécifique, ou que le texte soit saisi dans le widget par l'utilisateur. Si l'on spécifie un marqueur avec insert, ce marqueur surcharge celui affecté au texte environnant.

VIII-E-6 - Utilisation de bind avec les marqueurs

L'une des principales raisons d'utiliser des marqueurs est qu'ils permettent d'assigner une liaison à certaines parties du texte. Après avoir créé un marqueur à l'aide de tagConfigure, vous pouvez utiliser bind pour qu'une fonction de rappel s'exécute lorsqu'une certaine séquence d'événements (un clic souris, par exemple) intervient sur ce texte marqué. Avec les widgets bouton, nous avions une liaison par défaut entre <Button-1> (le bouton gauche de la souris) et la fonction de rappel associée à l'option -command. On peut faire la même chose avec du texte marqué.

Le meilleur exemple est l'utilisation du texte comme un lien hypertexte, comme c'est le cas sur le Web. Lorsque l'on clique sur le lien, quelque chose se passe : un nouveau document se charge ou une autre fenêtre est créée et présentée à l'utilisateur. La forme classique d'un appel à tagBind est la suivante :

$texte->tagBind(nom_marqueur [, séquence, fct_rappel ] )

La fonction de rappel est identique à celle que l'on indique à l'option -command d'un bouton. La séquence est une description de l'événement qui déclenche le script. Les seules séquences que l'on peut indiquer sont celles qui ont un rapport avec la souris ou le clavier (voir le chapitre 14, Liaisons d'événements, pour plus de détails sur les événements disponibles).

Le code suivant montre un exemple de « pseudo-lien ». Tout ce que fait le lien, lorsque l'on clique sur lui, est de montrer la fin du widget texte :

$texte = $mw->Scrolled( "Text", -width => 40 )->pack(
-expand => 1,
-fill   => 'both'
);
$texte->tagConfigure( 'fin', -underline => 1, -foreground => 'red' );
$texte->tagBind( 'fin', "<Button-1>", sub { shift->see('end'); } );

# Mise en place des liaisons pour modifier le curseur
# lorsqu'il est sur cette ligne
$texte->tagBind( 'fin', "<Any-Enter>", sub { shift->configure( -cursor => 'hand2' ) } );
$texte->tagBind( 'fin', "<Any-Leave>", sub { shift->configure( -cursor => 'xterm' ) } );
$texte->insert( 'end', "FIN\n", "fin" );

# Insertion de quelques lignes
for ( $i = 1 ; $i <= 100 ; $i++ ) {
$texte->insert( 'end', "$i\n" );
}

Dans les sub des appels à tagBind, on utilise l'instruction shift afin d'invoquer une méthode. On peut procéder ainsi, car le premier paramètre envoyé implicitement à la fonction de rappel de bind est le widget texte. Quel que soit le widget sur lequel tagBind est invoquée, c'est lui qui sera envoyé comme premier paramètre à la fonction de rappel. Pour utiliser plus d'une fois le widget dans la fonction de rappel, il suffit de l'assigner à une variable locale en faisant, par exemple, my $widget = shift.

Si l'on a créé le widget texte dans la portée globale du programme et que l'on a placé une référence à ce widget dans la variable $texte, on peut aussi y accéder dans la fonction de rappel en utilisant cette variable. Cela n'est possible que parce que $texte est de portée globale et qu'elle est donc visible par la fonction de rappel. Si l'on souhaite associer la même fonction à deux widgets texte différents, il faut utiliser shift pour obtenir le widget correct :

$texte1->tagBind('fin', "<Button-1>", \&goto_fin );
$texte2->tagBind('fin', "<Button-1>", \&goto_fin );
sub goto_fin {
my $texte = shift;
$texte->see('end');
}

L'utilisation d'une même fonction de rappel pour les deux widgets vous permet d'économiser des lignes de code dans votre programme.

Pour déterminer quelles sont les liaisons d'un marqueur, il suffit d'utiliser tagBind en lui passant uniquement son nom en paramètre :

@liaisons = $texte->tagBind("marqueur");

La liste obtenue sera vide si le marqueur n'a aucune liaison au moment de cet appel.

VIII-E-7 - Suppression de toutes les instances d'un marqueur

La méthode tagDelete permet de supprimer un marqueur déjà créé :

$texte->tagDelete(marqueur1 [ ,marqueur2 . . .] )

Cette méthode supprime complètement les marqueurs ; cela signifie que le texte reprend sa configuration par défaut et que toutes les liaisons ou autres informations associées à ces marqueurs sont aussi détruites.

tagDelete peut aussi être utilisée lorsque vous créez dynamiquement des marqueurs temporaires dans votre programme et que vous devez les détruire ensuite, lorsque les informations ne sont plus correctes.

VIII-E-8 - Annulation de l'effet d'un marqueur

Pour ôter un marqueur d'un bloc de texte précis, on utilise la méthode tagRemove :

$texte->tagRemove(marqueur, début1 [ , fin1, début2, fin2, ...] );

On indique le nom du marqueur et un indice ou intervalle d'indices duquel supprimer le marqueur qui reste intact : il est simplement ôté du texte indiqué par les indices.

VIII-E-9 - Priorités des marqueurs

Lorsque plusieurs marqueurs agissent sur le même texte, le dernier appliqué surcharge les précédents ; ses options de configuration sont prioritaires. On peut modifier la priorité des marqueurs en utilisant les méthodes tagLower et tagRaise :

$texte->tagRaise(marqueur [, marqueur_sup]);
$texte->tagLower(marqueur [, marqueur_inf ] );

Ces méthodes prennent un nom de marqueur en premier paramètre. S'il n'a pas d'autre paramètre, le premier marqueur reçoit la priorité la plus haute (tagRaise) ou la plus basse (tagLower). Ceci concerne tout le texte du widget texte, quel que soit l'endroit où les marqueurs s'appliquent. Si un deuxième marqueur est indiqué, le premier est placé avant (tagRaise) ou après le deuxième marqueur.

Cela fait penser à une réorganisation de la pile des marqueurs (s'appliquant tous au même texte). Le marqueur au sommet est celui qui a le plus à dire, et s'il a une option -foreground valant 'red', alors tout le texte ayant ce marqueur sera rouge, quelle que soit la valeur de -foreground pour les autres marqueurs. Si l'on utilise tagRaise pour mettre au sommet un marqueur ayant la valeur 'blue' pour cette option, le texte marqué changera de couleur pour s'afficher en bleu.

VIII-E-10 - Obtention des noms de marqueurs

On peut retrouver tous les différents marqueurs s'appliquant à un indice donné, ou à tout le texte, en utilisant la méthode tagNames :

$texte->tagNames([ indice ] )

Si l'on donne un indice, la liste renvoyée ne contiendra que les marqueurs qui s'y appliquent. Sinon, la liste contiendra tous les marqueurs ayant un rapport avec le widget texte, qu'ils s'appliquent ou non au texte du widget.

VIII-E-11 - Déterminer où s'applique un marqueur

Si l'on connaît le nom d'un marqueur, on peut trouver où il s'applique dans le widget texte à l'aide des méthodes range. La première, tagRanges, renvoie une liste contenant des paires d'indices pour l'ensemble du widget :

@liste = $texte->tagRanges("marqueur");
# renvoie ( début1, fin1, début2, fin2 ...)

S'il n'y a pas de texte ayant ce marqueur dans le widget, la liste sera vide.

On peut aussi obtenir une par une les paires d'indices à l'aide de la méthode tagNextrange :

($debut, $fin) = $texte->tagNextrange("marqueur", début [ , fin ] );

La recherche de "marqueur" débutera à début et ne dépassera jamais fin. Si ce dernier n'est pas précisé, la recherche continuera jusqu'au marqueur ou stoppera à la fin du widget texte.

VIII-F - Insertion de texte

Maintenant que nous avons étudié les indices et les marqueurs de texte, nous pouvons détailler des méthodes de manipulation du contenu du widget.

Comme nous l'avons vu dans les nombreux exemples de ce chapitre, nous plaçons du texte dans le widget à l'aide de insert. Le premier paramètre est un indice précisant où le texte sera inséré. Le deuxième paramètre est la chaîne à placer. Le suivant, facultatif, est un nom de marqueur unique ou une liste de marqueurs à affecter au texte inséré. Voici le mode d'utilisation de insert :

$texte->insert(indice, chaîne
[ , liste_marqueurs, chaîne, liste_marqueurs ...] ) ;

Jusqu'à maintenant, nous n'avons utilisé que des marqueurs uniques. Si l'on souhaite indiquer plus d'un marqueur, il suffit de placer leurs noms entre crochets, afin de créer une liste :

$texte->insert('end', "Voici une ligne très marquée",
['marqueur1', 'marqueur2', 'marqueur3']);

Pour utiliser plusieurs ensembles de marqueurs, on peut fournir d'autres lignes de texte et des listes de marqueurs supplémentaires :

$texte->insert( 'end', "Ceci est l'entête", [ 'entete', 'souligne' ], "Deuxième ligne", [ 'gras', 'bleu' ] );

Lorsque l'on utilise la méthode insert pour insérer plus d'une ligne de texte avec plusieurs marqueurs, on doit fournir des couples : texte, marqueurs, texte, marqueurs, etc. Si le marqueur utilisé n'est pas défini (avec tagConfigure), le texte ne sera pas affecté, mais le marqueur lui sera quand même assigné. Si on le désire, on peut créer le marqueur plus tard.

VIII-G - Suppression de texte

Pour supprimer du texte du widget, on utilise la méthode delete :

$texte->delete(début [ , fin ] );

Le premier indice est nécessaire, le deuxième facultatif. Si les deux sont fournis, le premier indice doit être inférieur ou égal au second. Tous les caractères entre début et fin (fin non compris) sont supprimés du widget. Pour tout ôter du widget, il suffit de faire $texte->delete("1.0", 'end').

VIII-H - Capture de texte

On utilisera beaucoup la méthode get : elle renvoie le texte situé entre début et fin. Si fin n'est pas précisé, seul le caractère placé à l'indice début est renvoyé. L'utilisation de get est la suivante :

$t = $text->get(début [ , fin ] );

Comme dans tout intervalle d'indice, début doit être inférieur ou égal à fin, ou une chaîne vide sera renvoyée.

VIII-I - Conversion des valeurs d'indices

Lorsque l'on travaille avec des indices, il est utile de pouvoir convertir une forme d'indice compliquée en une forme plus simple. La méthode index renvoie un indice sous la forme ligne.car :

$nouvelle_valeur = $texte->index(indice);

La valeur indice peut être n'importe quelle expression correcte d'un indice.

VIII-J - Comparaison d'indices

La méthode compare permet de comparer deux indices :

$texte->compare(indice1, op, indice2 );

Les paramètres sont : le premier indice, l'opération de test à réaliser et le deuxième indice. Les valeurs possibles de op sont « < », « <= », « == », « > », « >= » et « != ». Cette méthode renvoie 1 si le test est vérifié, 0 sinon. L'appel

$etat = $texte->compare("1.0", "<=", "end");

renvoie 1, car l'indice « 1.0 » est inférieur ou égal à "end".

VIII-K - Affichage du texte situé à un indice

La méthode see force le widget à afficher la partie du texte contenant l'indice spécifié :

$texte->see(indice);

À la suite de cet appel, le texte du widget défilera vers le haut ou vers le bas. Si l'indice était déjà visible, rien ne se passera.

VIII-L - Obtention de la taille d'un caractère

La méthode bbox renvoie une liste de quatre éléments décrivant la boîte autour du caractère situé à l'indice indiqué :

($x, $y, $l, $h) = $texte->bbox(indice);

Les deux premiers éléments renvoyés sont les coordonnées x et y du coin supérieur gauche. Les deux derniers sont la largeur et la hauteur de la boîte. La boîte de dimensionnement décrit seulement la partie visible du caractère : s'il est à moitié caché ou invisible, les valeurs renvoyées refléteront cet état de fait.

VIII-M - Obtention d'informations sur une ligne

La méthode dlineinfo renvoie une liste de cinq éléments. Ceux-ci décrivent la partie de la ligne contenant l'indice indiqué :

- abscisse du coin supérieur gauche ;

- ordonnée du coin supérieur gauche ;

- largeur de la zone ;

- hauteur de la zone ;

- position de la ligne de référence de la ligne, mesurée à partir de l'abscisse contenue dans le premier élément.

Voici un exemple d'utilisation :

($x, $y, $l, $h, $base) = $texte->dlineinfo("indice");

À la différence de la méthode bbox, même les zones non visibles (dues aux mots non coupés) sont utilisées dans les calculs tant qu'une partie de la ligne est visible. Cependant, si la ligne n'est pas visible du tout, la liste sera vide. Si la ligne est coupée en plusieurs lignes, la zone complète sera utilisée.

VIII-N - Recherche dans un widget texte

On utilise la méthode search pour chercher un motif ou expression rationnelle dans le widget texte. Cette méthode prend comme paramètres des options éventuelles, le motif à rechercher et un indice indiquant où commencer la recherche :

$indice = $texte->search([options], motif, début [ , fin ] );

Si une correspondance est trouvée, l'indice renvoyé pointera sur le premier caractère de celle-ci. Si rien n'est trouvé, la méthode renverra une chaîne vide.

Les options reconnues sont :

-forwards

1. Demande à search de rechercher « vers l'avant » du widget texte en commençant à l'indice indiqué. C'est l'option par défaut.

-backwards

1. Demande à search de rechercher « vers l'arrière » du widget texte en commençant au caractère précédant l'indice indiqué.

-exact

1. Le motif doit correspondre exactement au texte. C'est le comportement par défaut.

-regexp

1. Le motif sera considéré comme une expression rationnelle.

-nocase

1. Ignore les différences de casse entre motif et le texte du widget.

-count => variable

1. variable est un pointeur vers une variable (i.e. \$variable). Le nombre de caractères reconnus y sera stocké.

--

1. Cette option ne fait rien, si ce n'est forcer le prochain paramètre à être considéré comme un motif, même si la chaîne suivante débute par « - ».

Voici un exemple simple d'utilisation de search :

$resultat = $texte->search(-backwards, "trouve-moi", 'end');
$emplacement = $texte->search(-nocase, "OPTIONS", "1.0");

VIII-O - Barres de défilement

Un widget texte peut être parcouru horizontalement et verticalement, il dispose donc des méthodes xview et yview. Celles-ci sont décrites au chapitre 6, Barres de défilement.

VIII-P - Marques

Il existe plusieurs moyens de désigner différentes positions tout au long d'un widget texte. Les indices font référence à un caractère, les marqueurs sont des références nommées à un seul ou à plusieurs caractères. Le terme « marque » permet de faire référence aux espaces entre les caractères. Comme les marqueurs, les marques ont un nom. La marque "insert", par exemple, désigne la position du point d'insertion. Cependant, les marqueurs font référence à des caractères réels et, si ceux-ci sont détruits, le marqueur ne leur est plus associé. Une marque reste en place, que les caractères qui l'entourent soient supprimés ou que d'autres soient ajoutés. Les marques ne peuvent désigner qu'un seul emplacement du widget à la fois.

Une fois créée, une marque est utilisable comme un indice. Le pôle d'attraction de la marque décidera du côté où le texte sera inséré. Si cette attraction vaut 'right' (c'est sa valeur par défaut), le texte sera inséré à gauche de la marque, car celle-ci est soudée au caractère à sa droite. Si l'attraction vaut 'left', le texte sera inséré à droite de la marque et celle-ci désignera la gauche du dernier caractère inséré. Avec cette valeur, le texte semble être inséré à l'envers.

Le widget texte place automatiquement deux marques spéciales : "insert" et "current". La première est la position du point d'insertion, où qu'il se trouve. La seconde est la position la plus proche de la souris et se modifie lorsque cette dernière est déplacée (tant que son bouton n'est pas pressé). Les deux marques sont gérées de façon interne et ne peuvent être détruites.

Vous verrez aussi une marque nommée "anchor" apparaissant dans la liste renvoyée par la méthode markNames lorsque l'on clique dans le widget texte. Cette marque a toujours la même valeur d'indice que la marque "insert", mais elle peut ne pas exister.

VIII-P-1 - Configuration et obtention du pôle d'attraction d'une marque

La méthode markGravity permet de configurer le pôle d'attraction d'une marque :

$texte->markGravity(marque [ , direction ] );

Les valeurs possibles de direction sont "right" et "left". L'attraction par défaut d'une nouvelle marque est "right". Si l'on ne précise pas d'attraction, cette méthode renvoie l'attraction actuelle de cette marque.

VIII-P-2 - Noms des marques

La méthode markNames permet de récupérer une liste de toutes les marques du widget texte :

@noms = $texte->markNames();

Cette méthode ne prend aucun paramètre et renvoie une liste. Voici un exemple montrant comment énumérer les marques se trouvant dans un widget texte :

$mw->Button(
-text    => "Rapport",
-command => sub {
my @marques = $texte->markNames();
foreach (@marques) {
print "MARQUE : $_ à ", $texte->index($_), "\n";
}
}
)->pack( -side => 'left' );

Lorsque l'on clique dans la fenêtre pour positionner le point d'insertion, on obtient les messages suivants :

MARQUE : insert à 2.15
MARQUE : anchor à 2.15
MARQUE : current à 3.0

VIII-P-3 - Création et suppression de marques

La méthode markSet permet de créer une marque et de la fixer à un certain indice :

$texte->markSet(marque, indice);

En plus de la marque que vous souhaitez créer, précisez l'indice où placer celle-ci. Si, par exemple, vous voulez pouvoir toujours insérer à la fin de la ligne 3 :

$texte->markSet("fin de la ligne 3", "3.0 lineend");
...
$texte->insert("fin de la ligne 3", "texte à insérer");

La méthode markUnset ôte la marque du widget texte et la supprime définitivement. Elle n'apparaîtra plus dans la liste renvoyée par markNames et elle ne pourra plus, non plus, servir de valeur d'indice. On peut donner plus d'une marque en paramètre de markUnset :

$texte->markUnset(marque1 [ , marque2, marque3 ... ] );

VIII-Q - Imbrication de widgets

L'une des possibilités les plus intéressantes des widgets texte est que l'on peut y placer d'autres widgets (tels que des boutons ou des zones de saisie). L'avantage des widgets imbriqués est que l'on peut créer un ensemble de widgets muni d'une barre de défilement, chacun d'eux étant sur une ligne distincte.

Avant de passer en revue les différentes fonctions disponibles pour gérer l'incorporation de widgets, étudions un court exemple : dans un programme, on souhaite souvent faire beaucoup de saisies de données, ce qui signifie que l'on a besoin de beaucoup de widgets étiquettes et de zones de saisie. Il y en a parfois trop pour les placer tous à l'écran sans désorganiser la fenêtre. En utilisant un widget texte avec barre de défilement et en y plaçant les étiquettes et les zones de saisie, on peut créer bien plus de widgets dans bien moins d'espace. Voici le code :

use Tk;
my %info;    # stockage des informations recueillies
$mw = MainWindow->new;
$mw->title("Zones de saisie");
$cadre = $mw->Frame->pack( -side => 'bottom' );
$cadre->Button(
-text    => "Fin",
-command => sub { exit; }
)->pack( -side => 'left' );
$cadre->Button(
-text    => "Sauver",
-command => sub {       # faire quelque chose de %info;
}
)->pack( -side => 'bottom' );
$texte = $mw->Scrolled(
"Text",
-width => 40,
-wrap  => 'none'
)->pack(
-expand => 1,
-fill   => 'both'
);

foreach (
qw/Nom Adresse Ville Code-postal Téléphone Emploi
Société Activité_Adresse Téléphone-bureau/
)
{
$champ = $texte->Label(
-text   => "$_ :",
-relief => 'groove',
-width  => 20
);
$texte->windowCreate( 'end', -window => $champ );
$champ = $texte->Entry(
-width        => 20,
-textvariable => \$info{$_}
);
$texte->windowCreate( 'end', -window => $champ );
$texte->insert( 'end', "\n" );
}
$texte->configure( -state => 'disabled' );    # Empêche la saisie

MainLoop;

La figure 8.6 montre la version Win32 de cette fenêtre.

Figure 8.6 : Widget texte contenant d'autres widgets

On désactive le widget texte avant de lancer MainLoop, car on ne veut pas que l'utilisateur entre du texte directement dans le widget texte. Cela ne désactive que la possibilité d'entrer ou de supprimer du texte - les widgets internes fonctionnent quand même normalement. On désactive aussi l'option -wrap pour que les étiquettes et les zones de saisie ne passent pas accidentellement à la ligne lorsque la fenêtre change de taille.

On pourrait également placer un widget texte dans un autre widget texte, mais vous ne le ferez probablement pas.

VIII-Q-1 - Les méthodes window

Comme nous l'avons vu avec l'exemple précédent, la méthode windowCreate permet d'insérer un widget imbriqué. Celui-ci doit d'abord avoir été créé et être un fils du widget texte. La syntaxe générale est :

$widget = $texte->Widget( - );
$texte->windowCreate(indice,
-window => $widget [ , option => valeur ] );

Dans l'exemple précédent, nous avons utilisé l'indice 'end', mais n'importe quel indice de widget texte convient pour insérer des widgets imbriqués. La seule option utilisée est -window avec une référence au nouveau widget.

Voici les options des méthodes window :

-align => base

1. Les valeurs possibles de base sont 'baseline', 'bottom', 'center', ou 'top'. Cette option détermine où le widget sera placé dans la ligne s'il est plus petit qu'elle. La valeur par défaut est 'center'.

-padx => montant et -pady => montant

1. Ajoute de l'espace autour du widget dans les directions x et y, respectivement.

-stretch => 0 | 1

1. Une valeur vraie étire le widget pour qu'il remplisse la ligne de haut en bas.

-window => $widget

1. Prend une référence à un autre widget.

Il existe plusieurs formes de méthodes window : la forme Create crée un widget dans le widget texte, et la forme Names nous permet de savoir quels types de widgets sont incorporés dans le widget texte.

@types = $texte->windowNames();

Le résultat sera quelque chose comme :

.text.radiobutton.text.label.text.button.text.entry.text.checkbutton

La méthode windowCget permet d'obtenir des informations sur les options utilisées lors de la création du widget dans le widget texte :

$valeur = $texte->windowCget(indice, option);

Pour utiliser windowCget, il faut connaître l'indice actuellement occupé par le widget (chaque widget occupe la place d'un caractère dans le widget texte, même s'il semble prendre plus de place).

La forme Configure permet de modifier les options associées au widget situé à un indice donné, ou d'obtenir la valeur d'une option de configuration :

$texte->windowConfigure(indice [, option => valeur ] );

Rappelez-vous que les seules options utilisables avec cette méthode sont -align, -padx, -pady, -stretch et -window. À part ça, windowConfigure se comporte comme n'importe quelle méthode configure classique. Pour modifier directement $widget, on utilise $widget->configure(...).

VIII-R - Test du code

La méthode debug prend un booléen comme paramètre éventuel :

$texte->debug( [ booléen ] );

Si la valeur fournie est vraie, les tests internes de consistance des données seront activés sur le code de l'arbre binaire associé aux widgets texte. Sinon, les tests ne seront pas assurés. Sans aucun paramètre, la méthode debug renverra la valeur "on" si elle est active, et "off" sinon. Tous les widgets texte d'une même application partagent le même commutateur de test.

VIII-R-1 - Recherche

Les méthodes scanMark et scanDragto sont utilisées en interne avec le widget texte. Un appel à scanMark enregistre simplement les valeurs x et y qui lui sont passées pour les utiliser plus tard avec scanDragto. scanMark renvoie une chaîne vide.

$texte->scanMark(x, y);

scanDragto prend aussi les coordonnées x et y, et les compare à celles fournies à scanMark. La partie visible du widget texte est alors ajustée de dix fois la différence entre les deux paires de coordonnées.

$texte->scanDragto(x, y);

VIII-S - Essayez et amusez-vous !

- Créez un widget texte muni d'une barre de défilement. Insérez un bouton dont le texte décrit la couleur de premier plan du widget texte. Lorsque l'on clique sur ce bouton, faites en sorte qu'il passe en revue plusieurs couleurs et qu'il mette à jour l'option -foreground du bouton et du texte. Comme application pratique, on peut envisager d'afficher plusieurs boutons, chacun étant associé à une couleur de l'application. Lorsqu'un bouton est cliqué, on modifie la couleur (éventuellement à l'aide du widget composite ColorEdit).

- Créez un widget texte affichant un fichier uniquement accessible en lecture. Créez deux boutons, l'un pour diminuer la taille de la fonte du widget texte, l'autre pour l'augmenter.

IX - Le widget canevas

Le widget canevas sert principalement à dessiner des éléments comme des arcs, des lignes, des rectangles, des cercles, etc. Il peut aussi contenir du texte et d'autres widgets. On peut l'assimiler à une sorte de toile : elle est blanche jusqu'à ce que vous décidiez d'y dessiner quelque chose, mais, à la différence de celle-ci, qui a une taille fixe, un canevas peut être parcouru dans les deux directions. Voici quelques exemples d'utilisations possibles de ce widget :

- création d'un programme de dessin ;

- affichage d'un graphique en fonction de valeurs entrées par l'utilisateur ;

- création d'un ascenseur personnalisé.

Tout élément créé dans un canevas peut posséder des liaisons lui permettant d'interagir facilement avec l'utilisateur.

IX-A - Création d'un canevas

Je vous conseille de toujours créer un canevas grâce à la méthode Scrolled, sauf si vous êtes absolument certain qu'il sera d'une taille fixe convenant à la fenêtre :

$canevas = $mw->Canvas( [ option => valeur, ... ] )->pack();
# ou...
$canevas = $mw->Scrolled('Canvas', [ option => valeur, ... ] )->pack();

La première ligne crée un canevas simple, tandis que la deuxième munit le sien de barres de défilement (voir le chapitre 6, Barres de défilement, pour plus d'informations sur tout ce que l'on peut faire avec la méthode Scrolled). Pour créer un widget canevas, on utilise le widget parent pour invoquer la méthode Canvas et lui passer toutes les valeurs des options nécessaires. Cette méthode renvoie une référence au canevas ainsi créé.

Avant de passer en revue les options et les méthodes disponibles pour un canevas, voici quelques informations qu'il est bon de connaître lorsque l'on utilise ce type de widget.

IX-B - Système de coordonnées

Un widget canevas utilise un système de coordonnées particulier afin de décrire les emplacements des éléments qu'il contient. Ce système ressemble assez à un système de coordonnées « renversé ».

La figure 9.1 illustre cela.

Figure 9.1 : Système de coordonnées d'un canevas

Les coordonnées x ne présentent rien de particulier : elles augmentent de gauche à droite. Les coordonnées y semblent avoir bu de la Vodka : les valeurs les plus hautes sont vers le bas plutôt qu'au sommet, car le point 0; 0 est dans le coin supérieur gauche. Bien que cela soit rare, on peut utiliser des coordonnées négatives dans un canevas.

Ce système de coordonnées n'est pas difficile à gérer dès que l'on a compris ce qui se passe, mais si l'on tente de dessiner un bâtiment en ayant à l'esprit un système de coordonnées standard (avec y augmentant du bas vers le haut), il reposera sur le toit.

Il y a plusieurs façons de gérer tout cela. La première consiste à ajuster sa façon de penser afin de toujours considérer que les coordonnées y sont plus grandes vers le bas (et oublier toutes ces années durant lesquelles on a lutté pendant les cours de géométrie). Si vous êtes aussi têtu que moi, vous pouvez continuer à penser en coordonnées normales et faire en sorte que votre programme effectue quelques calculs avant d'envoyer les coordonnées y aux fonctions du canevas (multiplier toutes les coordonnées y par - 1 est astucieux, non ?).

Quelle que soit la méthode retenue, soyez cohérent et commentez bien votre code.

Les coordonnées x et y peuvent être indiquées dans n'importe quelle unité d'écran ; par défaut, il s'agira de pixels. Si l'on place un m après une coordonnée, la distance sera mesurée en millimètres. Les autres lettres utilisables sont p pour les points d'imprimante, i pour les pouces (inches), et c pour les centimètres. Nous utiliserons l'unité par défaut, le pixel, pour tous les exemples de ce chapitre.

IX-C - La région de parcours

La région de parcours (scrollable region) est la portion du widget canevas que l'utilisateur peut voir. Si l'on ne crée pas une telle zone (en utilisant l'option -scrollregion), l'utilisateur pourra faire défiler le canevas indéfiniment dans les deux directions et les barres de défilement ne refléteront pas les emplacements des éléments contenus dans le widget.

La figure 9.2 montre un exemple de région de parcours comparée à la surface visible d'un canevas. Si ces deux surfaces avaient la même taille, nous n'aurions pas besoin de barres de défilement pour le canevas (si l'on en utilisait, leurs ascenseurs rempliraient complètement la zone de défilement).

Figure 9.2 : Région de parcours comparée à la zone visible

Les flèches sur les axes de la figure 9.2 indiquent que le canevas peut être encore plus grand que la région de parcours indiquée. Si vous décidez, par exemple, d'ajouter un cercle en dehors de celle-ci, vous devrez ajuster cette région pour que l'utilisateur puisse voir ce nouveau cercle.

La meilleure façon de faire cela est d'utiliser la méthode bbox qui renvoie une boîte de dimensionnement pour tous les éléments correspondant aux marqueurs que vous lui indiquez. Voici à quoi ressemblerait le code :

$canevas->configure(-scrollregion => [ $canevas->bbox("all") ]);

L'exécution de cette instruction, après avoir ajouté ou supprimé des éléments au canevas, réinitialise la région de parcours à la taille nécessaire. Bien sûr, si l'on ajoute d'un seul coup de nombreux éléments, il faudra attendre de les avoir tous ajoutés avant de mettre à jour la région de parcours.

IX-D - Utilisation de bind avec un canevas

Lorsque l'on tente d'utiliser la méthode bind avec un widget canevas, on se trouve confronté à des problèmes inattendus. Soit l'on obtient une erreur et notre script ne fonctionne pas, soit le script fonctionne, mais la liaison semble n'avoir aucun effet. Afin d'éviter cela, il faut écrire explicitement Tk::bind au lieu de bind (car un canevas dispose de sa propre méthode bind qu'il faut éviter d'utiliser) :

$canevas = $mw->Canvas();
$canevas->Tk::bind("<Button-1>", sub { print "liaison !\n"; });

Vous pouvez aussi utiliser la notation SUPER::bind au lieu de Tk::bind : les deux sont correctes(25).

Si vous avez utilisé la méthode Scrolled pour créer votre canevas, vous rencontrerez une difficulté supplémentaire : il faudra utiliser la méthode Subwidget pour obtenir le widget canevas :

$canevas = $mw->Scrolled("Canvas");
$canevas_reel = $canevas->Subwidget("canvas");
$canevas_reel->Tk::bind("<Button-1>", sub { print "liaison !\n" });

À part ces petits désagréments, bind fonctionne comme on est en droit de s'y attendre. Voici un petit exemple (assez utile), qui affiche les coordonnées de l'endroit sur lequel on a cliqué :

$c = $mw->Scrolled("Canvas")->pack();
$canevas = $c->Subwidget("canvas");
$canevas->Tk::bind("<Button-1>", [ \&affiche_xy, Ev('x'), Ev('y') ]);
sub affiche_xy {
my ($canev, $x, $y) = @_;
print "(x,y) = ", $canev->canvasx($x), ", ",
$canev->canvasy($y), "\n";
}

IX-E - Options d'un canevas

Les options énumérées dans cette section affectent le widget entier, ainsi que les éléments qu'il contient. Ceux-ci sont des cercles, des lignes, des rectangles, du texte, ou d'autres widgets. Ces options agissent comme prévu (voir le chapitre 3, Le bouton de base, pour l'essentiel des options, et le chapitre 6 pour celles spécifiques aux barres de défilement) : -background, -borderwidth, -cursor, -height, -highlightbackground, -highlightcolor, -highlightthickness, -relief, -takefocus, -width, -xscrollcommand, et -yscrollcommand.

IX-E-1 - Nouvelles options

Lorsque l'on sélectionne des éléments du canevas avec la souris, celui-ci détermine si le curseur est ou non à l'intérieur de l'élément. L'option -closeenough contrôle la proximité que doit avoir la souris avec l'élément avant qu'elle soit considérée comme étant à l'intérieur de celui-ci. La valeur par défaut de -closeenough est « 1.0 », qui indique une proximité de un pixel. Cette option accepte n'importe quel nombre réel (qui sera toujours considéré en pixels).

Nous avons rapidement traité de l'option -scrollregion dans la section sur « La région de parcours », plus haut dans ce chapitre. Cette option prend une référence à une liste de quatre coordonnées qui décrivent une boîte de dimensionnement de la région de parcours du canevas et sont exprimées dans l'ordre [ minx, miny, maxx, maxy ]. On peut aussi considérer que ces coordonnées définissent, respectivement, les bords gauche, haut, droit et bas de celle-ci.

Normalement, le widget canevas limite l'utilisateur à ne voir que la région définie par l'option -scrollregion. On peut abolir cette limite en utilisant -confine => 0. La valeur par défaut de -confine est 1.

IX-E-2 - Nouvelles options de défilement

Les options -xscrollcommand et -yscrollcommand fonctionnent toutes les deux comme on l'a expliqué au chapitre 6, mais deux options supplémentaires agissent sur la façon dont le canevas fait défiler son contenu : -xscrollincrement et -yscrollincrement. Chacune d'elles prend pour valeur une distance d'écran qui sera l'unité utilisée par le canevas pour défiler dans la direction concernée. Avec -xscrollincrement => 10, par exemple, le contenu du canevas sera décalé, à chaque clic sur une flèche de la barre de défilement horizontale, d'une valeur multiple de 10. Pour résumer, le canevas décalera son contenu de 10 pixels dans la direction de la flèche.

Si la valeur associée à -xscrollincrement ou -yscrollincrement est inférieure ou égale à 0, le défilement utilise les pas de progression normaux.

IX-E-3 - Options pour le texte

Les options suivantes s'appliquent à tout le widget canevas, bien qu'elles n'affectent réellement que les éléments de texte du canevas : -insertbackground,-insertborderwidth, -insertofftime,-insertontime, -insertwidth, -selectbackground,-selectborderwidth, et -selectforeground. Ces options fonctionnent comme pour les zones de saisie et les widgets texte ; elles sont décrites en détail aux chapitres 5, Les widgets étiquette et de saisie, et 6, Barres de défilement.

IX-E-4 - Liste des options du widget canevas

Toutes les options suivantes sont utilisables avec la méthode Canvas :

-background => couleur

1. Configure la couleur de fond du canevas.

-borderwidth => montant

1. Modifie la largeur des bords du canevas.

-closeenough => montant

1. Configure la distance à partir de laquelle on considère que le curseur est à l'intérieur d'un élément. montant est un nombre réel.

-confine => 1 | 0

1. Si elle vaut 1 (valeur par défaut), cette option précise que le canevas se limitera lui-même à la région définie par -scrollregion.

-cursor => nom_curseur

1. Indique que le curseur se modifiera en nom_curseur lorsqu'il sera au-dessus du canevas.

-height => montant

1. Configure la hauteur du canevas.

-highlightbackground => couleur

1. Configure la couleur qu'aura le rectangle de focus lorsque le canevas n'a pas le focus clavier.

-highlightcolor => couleur

1. Configure la couleur qu'aura le rectangle de focus lorsque le canevas a le focus clavier.

-highlightthickness => montant

1. Fixe l'épaisseur du rectangle de focus. Le montant par défaut est 2.

-insertbackground => couleur

1. Configure la couleur de la zone située derrière le point d'insertion.

-insertborderwidth => montant

1. Fixe la largeur des bords du point d'insertion.

-insertofftime => millisecondes

1. Configure le délai pendant lequel le point d'insertion disparaît de l'écran lorsqu'il clignote.

-insertontime => millisecondes

1. Configure le délai pendant lequel le point d'insertion apparaît à l'écran lorsqu'il clignote.

-insertwidth => montant

1. Fixe la largeur du point d'insertion.

- relief=> 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Précise l'aspect du contour du canevas. Le contour par défaut est 'flat'.

-scrollregion => [ gauche, haut, droit, bas ]

1. Définit la zone que l'utilisateur pourra faire défiler.

-selectbackground => couleur

1. Configure la couleur de la zone située derrière le texte sélectionné.

-selectborderwidth => montant

1. Fixe la largeur du contour de la zone sélectionnée.

-selectforeground => couleur

1. Configure la couleur du texte sélectionné.

-takefocus => 0 | 1 | undef

1. Détermine si le canevas pourra obtenir le focus clavier. La valeur par défaut laisse l'application décider.

-width => montant

1. Fixe la largeur du canevas.

-xscrollcommand => fct_rappel

1. Indique la fonction de rappel à utiliser lorsque le canevas est parcouru horizontalement (lorsque la méthode Scrolled est utilisée, cette option est automatiquement initialisée avec la fonction de rappel correcte).

-xscrollincrement => montant

1. Fixe la distance de déplacement du contenu du canevas lorsque l'utilisateur emploiera la flèche de la barre de défilement horizontale.

-yscrollcommand => fct_rappel

1. Indique la fonction de rappel à utiliser lorsque le canevas est parcouru verticalement.

-yscrollincrement => montant

1. Fixe la distance par laquelle le contenu du canevas se déplacera lorsque l'utilisateur emploiera la flèche de la barre de défilement verticale.

IX-F - Création d'éléments dans un canevas

Le but premier d'un canevas est de contenir des éléments : on peut y créer des arcs, des bitmaps, des images, des lignes, des rectangles, des ovales (cercles), des polygones, du texte et des widgets. Chacun de ces éléments dispose d'une méthode de création createXXX, où XXX est le type de l'élément concerné. Toutes ces méthodes de création renvoient un identificateur unique, pouvant servir à désigner l'élément. Lorsqu'une méthode prend un marqueur ou un identificateur comme paramètre, l'identificateur est celui renvoyé par la méthode de création.

IX-F-1 - Arcs

Pour créer un arc, il faut préciser un rectangle de dimensionnement à l'aide de deux paires de coordonnées x et y. L'arc est tracé dans les limites de ce rectangle. Des options supplémentaires permettent de modifier la façon dont celui-ci est tracé dans le canevas ; nous les décrirons brièvement. L'instruction createArc de base est la suivante :

$id = $canevas->createArc(x1,y1, x2,y2);

Les options supplémentaires sont précisées après les coordonnées :

$id = $canevas->createArc(x1,y1, x2,y2, option => valeur);

Chaque option d'un arc peut être utilisée plus tard par les méthodes itemcget et itemconfigure. Celles-ci sont :

-extent => degrés

1. La longueur de l'arc est précisée en degrés à l'aide de cette option. Cette longueur vaut 90 degrés par défaut. L'arc est tracé dans le rectangle défini par (x1, y1) et (x2, y2), de son point de départ (voir l'option -start) et en sens inverse des aiguilles d'une montre. degrés doit être un nombre compris entre -360 et 360 ; sinon, la valeur utilisée sera celle indiquée modulo 360.
Voici des exemples d'utilisation de l'option -extent :

# Trace la moitié d'un ovale
$canevas->createArc(0,0, 100,150, -extent => 180);
# Trace les 3/4 d'un ovale
$canevas->createArc(0,0, 100,150, -extent => 270);

-fill => couleur

1. Remplit l'arc avec couleur. Par défaut, un arc n'a pas de couleur de remplissage.

-outline => couleur

1. Normalement, un arc est tracé avec un contour noir. Pour modifier cette couleur par défaut, on utilisera cette option. La couleur du contour est distincte de celle du remplissage ; pour avoir un objet uniforme, il suffit de choisir la même teinte pour -fill et -outline.

-outlinestipple => bitmap

1. Pour utiliser cette option, il faut aussi définir -outline. Normalement, le contour d'un arc est tracé avec un trait plein ; on utilisera -outlinestipple pour utiliser un motif en bitmap afin de tracer le contour.

-start => degrés

1. La valeur associée à cette option détermine l'endroit où Perl/Tk débutera le tracé de l'arc. La position de départ par défaut est à trois heures(26) et les degrés indiqués ici sont ajoutés à cette position, mais dans l'ordre inverse des aiguilles d'une montre. On utilisera -start => 90 pour démarrer le tracé de l'arc à douze heures, -start => 180 pour le démarrer à neuf heures, etc.

-stipple => bitmap

1. Cette option permet de remplir l'arc avec un motif en bitmap, mais uniquement si l'option -fill a aussi été utilisée.

-style => 'pieslice' | 'chord' | 'arc'

1. Le -style d'un arc détermine la façon dont il sera tracé. La valeur par défaut, "pieslice" (NDT Littéralement, "part de gâteau" en français), trace l'arc et deux lignes partant du centre de celui-ci et se terminant aux extrémités du segment. La valeur "chord" trace l'arc et une ligne reliant les deux extrémités de son segment. La valeur "arc" ne trace que l'arc, sans ligne. Les options -fill et -stipple sont ignorées si cette dernière valeur est utilisée.

-tags => liste_marqueurs

1. Cette option permet d'assigner des marqueurs à un arc au moment de sa création. La valeur associée à -tags est une liste anonyme de marqueurs ; par exemple :

$canevas->createArc(0,0, 10,140,-tags => ["arc", "grand"]);

1. Si l'on n'indique qu'un seul marqueur, une liste n'est pas nécessaire :

$canevas->createArc(0,0, 10,140,-tags => "arc");

-width => montant

1. Précise la largeur du contour. La valeur par défaut est 1.

IX-F-2 - Bitmaps

Un widget canevas peut afficher un bitmap à la place du texte, tout comme les boutons et les étiquettes. On utilise createBitmap pour insérer un bitmap :

$id = $canevas->createBitmap(x,y);

Il faut, bien sûr, utiliser l'option -bitmap pour préciser le bitmap à afficher ou vous ne verrez rien. On crée donc vraiment un bitmap ainsi :

$id = $canevas->createBitmap(x,y, -bitmap => bitmap);

J'ignore la raison pour laquelle le bitmap n'est pas simplement le troisième paramètre : c'est comme ça. Les autres options de createBitmap sont :

-anchor => "center" | "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw"

1. L'option -anchor précise comment sera placé le bitmap dans le canevas, relativement aux coordonnées x et y indiquées. La valeur par défaut est "center", qui place le centre de l'image à ces coordonnées. L'utilisation d'un point cardinal unique ("e", par exemple) placerait le centre de ce bord aux coordonnées x et y.

-background => couleur

1. Cette option précise la couleur à utiliser pour tous les pixels à 0 (zéro) du bitmap. Si l'on ne précise pas cette option, ou si sa valeur est une chaîne vide, les pixels à 0 seront transparents. Ce sont ces pixels qui forment le fond d'un bitmap.

-bitmap => nom_bitmap

1. Il faut utiliser cette option pour indiquer au canevas quel bitmap afficher. On peut utiliser les bitmaps internes, comme 'info' ou 'warning', comme pour le widget bouton, ou indiquer un nom de fichier. N'oubliez pas qu'il faut mettre un signe @ devant le nom du fichier bitmap.

-foreground => couleur

1. La couleur du premier plan d'un bitmap est l'opposée de celle de son fond (par définition, les bitmaps n'ont que deux couleurs). Cette option colorie tous les pixels à 1 en couleur. La valeur par défaut est noir.

-tags => liste_marqueurs

1. Cette option assigne des marqueurs à un bitmap lors de sa création. La valeur associée à -tags est une liste anonyme de marqueurs ; par exemple :

$canevas->createBitmap(0,0, -bitmap => 'info', -tags => ["info", "bitmap"]);

1. Si vous n'indiquez qu'un seul marqueur, une liste n'est pas nécessaire :

$canevas->createBitmap(0,0, -bitmap => 'info', -tags => "bitmap");

IX-F-3 - Images

S'il est possible de créer un bitmap dans un canevas, il est normal que l'on puisse aussi créer une image. Pour ce faire, on utilise la méthode createImage :

$id = $canevas->createImage(x,y, -image => nom_image);

Ici encore, vous devez indiquer l'image à afficher ou vous ne verrez rien. Les autres options de createImage sont :

-anchor => "center" | "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw"

1. Cette option fonctionne avec une image exactement comme avec un bitmap. Elle précise la façon dont sera positionnée l'image par rapport aux coordonnées x et y. Sa valeur par défaut est "center".

-image => nom_image

1. L'option -image indique quelle image afficher. Sa valeur est, en fait, une référence à une image créée par la méthode Photo ou Bitmap (le chapitre 3 donne plus d'informations sur la façon d'indiquer un fichier image).

-tags => liste_marqueurs

1. On utilise cette option pour assigner des marqueurs à une image. La valeur associée à -tags est une liste anonyme ; par exemple :

$canevas->createImage(0,0, -image => $ptr_image,
-tags => ["image", "bleu"]);

1. Une liste est inutile si l'on ne donne qu'un seul marqueur :

$canevas->createImage(0,0, -image => $ptr_image,
-tags => "image");

IX-F-4 - Lignes

La méthode createLine permet, en réalité, de créer plusieurs lignes reliées entre elles, pas seulement une ligne unique. Les deux premières paires de coordonnées que vous indiquez créent la première ligne, et toute paire supplémentaire continuera la ligne (brisée) jusqu'à ce point.

$id = $canevas->createLine(0,0, 400,400); # Crée une seule ligne
$id = $canevas->createLine(0,0, 400,400, -50,240); # deux lignes

On peut utiliser des options à la suite des coordonnées afin de configurer la ou les lignes ; ces options et leurs valeurs possibles sont les suivantes :

-arrow => "none" | "first" | "last" | "both"

1. Cette option permet de placer des flèches aux extrémités de la ligne. Si la méthode createLine crée plusieurs lignes, seuls le premier et/ou le dernier point peuvent devenir des flèches. Pour que chaque ligne soit traitée, il faut utiliser plusieurs instructions createLine.

-arrowshape => [ dist1, dist2, dist3 ]

1. L'option -arrowshape ne s'applique que si -arrow est également utilisée. La figure 9.3 montre la signification des trois distances.

Figure 9.3 : Forme d'une flèche

On indique les trois distances à l'aide d'une liste anonyme comme dans l'exemple qui suit :

$canevas->createLine(
10, 10, 200, -40,
-arrow      => "both",
-arrowshape => [ 20, 20, 20 ]
);

-capstyle => "butt" | "projecting" | "round"

1. Cette option permet de spécifier autre chose que des flèches pour les extrémités d'une ligne.

-fill => couleur

1. L'option -fill porte mal son nom, car elle ne remplit rien ; la ligne est simplement tracée d'une autre couleur que le noir par défaut.

-joinstyle => "bevel" | "miter" | "round"

1. Cette option précise comment plusieurs lignes seront jointes. La valeur par défaut est "miter". Si l'on ne crée qu'une seule ligne, -joinstyle est sans effet.

-smooth => 1 | 0

1. Si cette option vaut 1, la ligne sera tracée comme une courbe de Bézier. Les deux premières lignes formeront la première spline, les deux suivantes, la seconde spline, etc. Pour créer une ligne droite, il suffit de répéter les points terminaux de la ligne droite désirée (ou de réutiliser createLine pour créer une autre ligne).

-splinesteps => nombre

1. Lorsque l'on utilise -smooth, plus -splinesteps est élevée, plus la courbe sera douce. Pour connaître le nombre d'étapes nécessaires à l'obtention de l'effet voulu, il faut essayer plusieurs valeurs.

-stipple => bitmap

1. Cette option permet de tracer la ligne en utilisant un motif bitmap (les 1 seront colorés, les 0 seront transparents). bitmap peut être un nom de bitmap intégré ou un nom de fichier. Plus la ligne est large (cf. l'option -width), plus le motif ressortira.

-tags => liste_marqueurs

1. Permet d'affecter des marqueurs à une ou plusieurs lignes au moment de leur création. La valeur associée à -tags est une liste anonyme ; par exemple :

$canevas->createLine(0,0, 100,100, -tags => ["ligne", "bleu"]);

1. Si l'on n'indique qu'un seul marqueur, une liste n'est pas nécessaire :

$canevas->createLine(0,0, 100,100, -tags => "ligne");

-width => montant

1. On peut élargir la ligne à l'aide de cette option. Normalement, une ligne ne fait qu'un pixel de large. montant est exprimable dans n'importe quelle distance d'écran (centimètres, pouces, etc.).

IX-F-5 - Ovales

S'il est tracé correctement, un ovale est un cercle. Pour créer un cercle/ovale, on utilise la méthode createOval et l'on indique deux paires de coordonnées définissant un rectangle (ou un carré) dans lequel sera tracé le cercle/ovale. Voici un bref exemple :

$id = $canevas->createOval(0,0, 50,50); # Crée un cercle
$id = $canevas->createOval(0,0, 50,100); # Crée un ovale

Les options d'un ovale sont classiques, je me contenterai donc de brèves descriptions :

-fill => couleur

1. L'ovale sera rempli avec la couleur indiquée. Cette couleur est différente de celle du contour. Par défaut, un ovale n'est pas coloré.

-outline => couleur

1. Le contour est la ligne délimitant le cercle. Par défaut, sa couleur est noire, mais on peut la modifier à l'aide de cette option. Si vous utilisez la même couleur pour le contour et pour le remplissage, l'ovale apparaîtra comme une surface pleine.

-stipple => bitmap

1. Cette option permet de remplir l'ovale avec un motif bitmap (les bits à 1 seront colorés, les bits à 0 seront transparents) : elle est sans effet si l'option -fill n'est pas aussi utilisée. bitmap peut être un bitmap intégré ou un nom de fichier.

-tags => liste_marqueurs

1. Permet d'affecter des marqueurs à un ovale au moment de sa création. La valeur associée à -tags est une liste anonyme ; par exemple :

$canevas->createOval(0,0, 100,100, -tags => ["ovale", "bleu"]);

1. Si l'on n'indique qu'un seul marqueur, une liste n'est pas nécessaire :

$canevas->createOval(0,0, 100,100, -tags => "ovale");

-width => montant

1. On peut élargir la ligne de contour d'un ovale à l'aide de cette option. Normalement, ce contour n'occupe qu'un pixel de large.

IX-F-6 - Polygones

Un polygone est ni plus ni moins un ensemble de lignes avec le premier point automatiquement relié au dernier pour former une figure fermée. La méthode createPolygon nécessite au moins trois paires de coordonnées x, y. Le code suivant, par exemple, crée un triangle :

$id = $canevas->createPolygon(1000,1000, 850,950, 30,40);

On peut ajouter des paires x, y supplémentaires :

$id = $canevas->createPolygon(1000,1000, 850,950, 30,40, 500,500);

Les options de createPolygon sont les mêmes que celles de createLine : -fill, -outline, -smooth, -splinesteps, -stipple, -tags, et -width. N'oubliez pas que createPolygon relie le premier point au dernier afin de fermer la figure.

IX-F-7 - Rectangles

Comme si createLine ou createPolygon ne suffisaient pas pour créer un rectangle, la méthode createRectangle existe aussi. Celle-ci ne prend que deux paires de coordonnées, représentant les coins opposés de la surface rectangulaire :

$id = $canevas->createRectangle(10,10, 50,150);

Ici encore, les options possibles, -fill, -outline, -stipple, -tags, et -width, ont déjà été vues avec les autres méthodes de création. Voici néanmoins quelques exemples :

# Un rectangle bleu avec un contour noir :
$canevas->createRectangle(10,10, 50,150, -fill => 'blue');
# Un rectangle bleu avec un contour plus épais :
$canevas->createRectangle(10,10, 50,150, -fill => 'blue', -width => 10);

IX-F-8 - Textes

Enfin ! Un type d'élément ne contenant pas de tracé ! On peut ajouter du texte dans un widget canevas à l'aide de la méthode createText. Celle-ci nécessite une paire de coordonnées x, y qui indique où placer le texte dans le canevas, ainsi que le texte à afficher :

$id = $canevas->createText(0,0, -text => "origine");

En fait, l'option -text est facultative, mais, si vous ne l'utilisez pas, vous ne verrez rien à l'écran. Comme cela n'a aucun intérêt, nous supposerons que vous préciserez toujours cette option avec le texte à afficher. Les autres options de createText sont :

-anchor => "center" | "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw"

1. Cette option détermine l'emplacement du texte par rapport aux coordonnées x et y. Le comportement par défaut consiste à centrer le texte sur ce point, quelle que soit la taille du texte.

-fill => couleur

1. Normalement, le texte est écrit en noir ; on peut modifier cela à l'aide de cette option qui porte mal son nom lorsqu'il s'agit de texte (habituellement, les widgets utilisent -foreground pour modifier la couleur du texte). Avec l'option -fill => 'blue', par exemple, le texte apparaîtra en bleu.

-font => nom_fonte

1. Modifie la fonte utilisée pour afficher le texte.

-justify => "left" | "right" | "center"

1. Si le texte affiché comporte plusieurs lignes, cette option permet de contrôler son alignement. Par défaut, le texte sera aligné à gauche.

-stipple => bitmap

1. Cette option est un peu bizarre, mais elle existe bien… Si l'on indique un nom de bitmap (ou de fichier), le texte sera écrit en utilisant le motif du bitmap. Cela rendra souvent le texte illisible donc ne l'utilisez pas, sauf avec de grandes fontes.

-tags => liste_marqueurs

1. liste_marqueurs est une liste anonyme de marqueurs ou un simple marqueur, qui seront affectés au texte.

-text => chaîne

1. Cette option est nécessaire ; la chaîne indiquée est affichée dans le canevas, aux coordonnées x et y.

-width => montant

1. Il s'agit encore d'une option mal nommée, car elle ne modifie pas la largeur des caractères du texte, mais détermine la longueur maximale d'une ligne de texte. Si le texte dépasse cette limite, la ligne sera automatiquement coupée pour former une seconde ligne. Par défaut, montant vaut 0, ce qui ne coupera les lignes qu'en présence d'un caractère de fin de ligne. Les lignes sont toujours coupées sur des espaces, donc aucun mot ne sera coupé en son milieu.

IX-F-9 - Indices des éléments texte

Les méthodes concernant les éléments texte demandent parfois une valeur d'indice. Les indices des widgets texte ont été traités au chapitre 8 et les indices d'un élément texte d'un canevas leur ressemblent. Chaque élément est toutefois considéré ici comme une ligne unique (même si elle contient des caractères \n). Les valeurs d'indices possibles sont les suivantes :

n

1. n est une valeur numérique : 0 ou 12, par exemple. Le premier caractère a l'indice 0, le second a l'indice 1, etc.

"end"

1. Désigne l'emplacement placé immédiatement après le dernier caractère. Cet indice est souvent utilisé avec la méthode insert pour ajouter du texte à la fin d'une chaîne.

"insert"

1. Désigne le caractère placé immédiatement avant le point d'insertion.

"sel.first"

1. Le premier caractère du texte sélectionné. Ne peut être utilisé que si une sélection est active.

"sel.last"

1. Le dernier caractère du texte sélectionné. Ne peut être utilisé que si une sélection est active.

"@x, y"

1. Désigne le caractère le plus près du point de coordonnées x, y du canevas (ce ne sont pas des coordonnées écran).

IX-F-10 - Suppression de caractères

La méthode dchars permet de supprimer des caractères d'un élément texte :

$canevas->dchars(marqueur/id, premier [, dernier ] )

On indique un marqueur ou un identificateur pour désigner le ou les éléments de texte, et l'indice où commencer la suppression. Si dernier n'est pas utilisé, seul le caractère situé à l'indice premier sera supprimé. Le reste de la chaîne ne sera pas affecté.

IX-F-11 - Positionnement du point d'insertion

On peut placer explicitement le point d'insertion à l'aide de la méthode icursor :

$canevas->icursor(marqueur/id, indice)

Le curseur n'apparaîtra que si l'élément concerné a le focus clavier, mais on peut quand même configurer la position du point s'il ne l'a pas.

IX-F-12 - Informations sur les indices

On peut retrouver un indice en fonction d'un autre, à l'aide de la méthode index. Voici un exemple qui clarifiera son utilisation :

$indice = $canevas->index("element", "sel.first");

Cette instruction renverra l'indice numérique associé au premier caractère sélectionné de l'élément texte. Si plus d'un élément correspond au marqueur ou à l'identificateur indiqué (ici, il s'agit d'un marqueur nommé "element"), le premier rencontré sera utilisé.

IX-F-13 - Ajout de texte

La méthode insert permet d'ajouter du texte à un élément texte :

$canevas->insert(marqueur/id, indice, chaîne)

IX-F-14 - Sélection de texte

La méthode selectClear annule la sélection (toute sélection, en fait, car cette méthode n'utilise ni marqueur ni identificateur) :

$canevas->selectClear();

Les méthodes selectFrom et selectTo, quant à elles, permettent de sélectionner une portion de texte. Les deux lignes suivantes sélectionnent le texte du début à la fin du premier élément correspondant au marqueur « marqueur_texte » :

$canevas->selectFrom("marqueur_texte", 0);
$canevas->selectTo("marqueur_texte", "end");

La méthode selectAdjust permet d'ajuster la sélection :

$canevas->selectAdjust(marqueur/id, indice)

Pour obtenir l'identificateur de l'élément sélectionné, on utilisera la méthode selectItem :

$id = $canevas->selectItem()

IX-F-15 - Widgets

Un canevas peut contenir n'importe quel type de widget : bouton, case à cocher, widget texte, ou même un autre canevas (au cas où vous seriez un peu fou).

Pour cela, on utilise la méthode createWindow, qui nécessite que le widget à placer dans le canevas soit d'abord créé. Voici un exemple :

$bouton = $canevas->Button(
-text    => "Bouton",
-command => sub {
print "Un bouton dans le canevas\n";
}
);
$id = $canevas->createWindow( 0, 0, -window => $bouton );

Certains points méritent d'être notés dans cet exemple (qui est assez classique, si ce n'est la fonction inutile associée au bouton) :

- le bouton est un fils du widget canevas. Il pourrait descendre d'un ancêtre de celui-ci (être un fils de la fenêtre principale si celle-ci est le parent du canevas). Cependant, il ne doit pas être le fils d'un autre widget de premier niveau n'ayant aucun rapport avec le canevas ;

- en réalité, la méthode createWindow ne crée pas le widget : elle ne fait que le placer dans le canevas. Le bouton y est positionné aux coordonnées indiquées et ne doit pas être placé à l'écran avec les méthodes pack, grid, ou place ;

- vous devez créer le widget avant d'appeler createWindow ;

- si l'on clique sur le bouton, cela invoquera la fonction de rappel qui lui est associée, comme avec tout bouton ;

- lorsque l'on crée le widget, on peut utiliser n'importe laquelle de ses options pour le configurer. On peut aussi utiliser sa référence ($bouton, par exemple), pour le configurer plus tard.
Les options suivantes de createWindow ressemblent plus à celles utilisées avec pack
qu'à des options de widget :

-anchor => "center" | "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw"

1. Le widget sera placé aux coordonnées x, y selon la valeur d'ancrage indiquée par cette option. Par défaut, celle-ci vaut "center", ce qui signifie que le centre du widget sera placé en x, y.

-height => montant

1. Fixe la taille du widget. Sans cette option, celui-ci gardera la taille qu'il avait au moment de sa création (habituellement, il s'agit de la taille naturelle du widget).

-tags => liste_marqueurs

1. liste_marqueurs associe un ou plusieurs marqueurs au widget. Il peut s'agir d'un nom unique, ou d'une liste anonyme de marqueurs.

-width => montant

1. Fixe la largeur du widget. Sans elle, celui-ci conservera la largeur obtenue au moment de sa création (la taille naturelle du widget).

-window => $widget

1. Cette option est obligatoire : sans elle, aucun widget ne sera placé dans le canevas. $widget est une référence à un élément widget que l'on peut avoir créé au préalable, ou au moment de l'appel à createWindow, comme dans l'exemple ci-dessous :

$canevas->createWindow(
0, 0,
-window => $canevas->Button(
-text => "Bouton",
)
);

La création du widget au moment de l'appel à createWindow n'a de sens que si l'on n'a pas l'intention d'y faire référence par la suite.

IX-G - Configuration du widget canevas

Comme d'habitude, les méthodes configure et cget, détaillées à l'annexe A, Configuration des widgets avec configure et cget, permettent de configurer ou d'obtenir des informations sur le widget canevas. N'oubliez pas que ces deux méthodes agissent sur le canevas entier (et qu'elles sont donc susceptibles d'affecter les éléments qu'il contient).

IX-H - Configuration des éléments d'un canevas

Pour modifier les options de configuration de n'importe quel élément d'un canevas, il suffit de connaître le nom du marqueur ou l'identificateur de cet élément ; on peut alors utiliser les méthodes itemcget et itemconfigure. Celles-ci se comportent comme cget et configure, mais attendent le marqueur ou l'identificateur du ou des éléments comme premier paramètre. J'ai écrit « du ou des éléments », car un même marqueur peut désigner plusieurs éléments. Voici deux exemples d'utilisation de ces méthodes :

$couleur = $canevas->itemcget("cercle", -fill);
$canevas->itemconfigure($ident, -fill => "yellow", -outline => 5);

On doit s'assurer que les options utilisées avec itemconfigure et itemcget sont correctes. Chaque type d'élément reconnaît un certain ensemble d'options ; celles-ci ont été décrites plus haut dans ce chapitre, avec les méthodes de création.

Lorsque l'on configure l'option -tags, la méthode itemconfigure remplacera tous les marqueurs affectés à l'élément. La liste de marqueurs associée à cette option peut aussi être vide, ce qui a pour effet de supprimer tous les marqueurs.

IX-I - Marqueurs

Un ou plusieurs marqueurs peuvent être associés à chaque élément. Nous avons déjà traité de leur utilisation dans l'exposé consacré au widget texte, mais rappelons que des parties de texte peuvent y être désignées par un marqueur. L'association se fait au moment de la création de l'élément, ou après celle-ci, à l'aide de la méthode addtag.

Les deux marqueurs spéciaux, "current" et "all", sont automatiquement assignés et gérés. "all" désigne tous les éléments du canevas, tandis que "current" fait référence à l'élément situé au-dessus des autres et sur lequel pointe la souris. Si ce pointeur se trouve à l'extérieur du canevas, ou n'est sur aucun élément, ce marqueur n'existe pas.

Les marqueurs permettent de modifier plusieurs éléments en même temps. Si, par exemple, on souhaite que tous les cercles aient la même couleur, mais que l'on veut pouvoir modifier celle-ci de temps en temps, il suffit d'affecter à tous les cercles le marqueur « cercle » au moment de leur création. La méthode itemconfigure permettra, ensuite, de modifier les options de configuration de tous les éléments ayant le marqueur "cercle".

Voici quelques exemples de la syntaxe employée pour créer des marqueurs :

$canevas->addtag("nouveau", "above", marqueur/id)

Le marqueur "nouveau" est ajouté à l'élément se trouvant au-dessus de celui référencé par marqueur ou id. S'il y en a plus d'un, le dernier élément trouvé sera utilisé afin que "nouveau" soit directement au-dessus de lui dans la liste d'affichage créée lorsque l'on ajoute des éléments au canevas. Cette dernière peut être manipulée avec les méthodes raise et lower.

$canevas->addtag("nouveau", "all");

Le mot-clé "all" est un marqueur spécial désignant tous les éléments du canevas. Les éléments ajoutés au canevas après l'appel à addtag ne seront pas concernés par le marqueur "nouveau".

$canevas->addtag("nouveau", "below", marqueur/id)

Le marqueur "nouveau" est ajouté à l'élément placé immédiatement en dessous de celui référencé par marqueur ou id. S'il y en a plus d'un, l'élément le plus à l'arrière-plan sera utilisé.

$canevas->addtag("nouveau", "closest", x,y)

Le marqueur "closest" sélectionne l'élément le plus proche des coordonnées x, y (du canevas). S'il y en a plusieurs, c'est le dernier trouvé qui sera choisi.

Si plusieurs éléments recouvrent le point indiqué, la méthode renverra le dernier ajouté. Pour indiquer la zone qui sera considérée comme « recouvrante », on ajoute un paramètre qui agrandira essentiellement la zone de recouvrement. Pour ajouter 10 pixels à cette zone, on écrira :

$canevas->addtag("nouveau", "closest", 50, 100, 10);

On peut également préciser un marqueur/identificateur de début de recherche. L'appel ressemble alors à :

$canevas->addtag("nouveau", "closest", x, y, 10, $mrq_ou_id);

Avec cette dernière forme, on peut passer en revue tous les éléments les plus proches.

$canevas->addtag("nouveau", "enclosed", x1,y1,x2,y2);

La forme "enclosed" de addtag permet d'assigner le même marqueur à plusieurs éléments se trouvant dans la zone délimitée par (x1,y1) et (x2,y2). Ceux-ci ne recevront le marqueur "nouveau" que s'ils sont entièrement dans la zone. Les coordonnées doivent être telles que x1 ≤ x2 et y1 ≤ y2.

$canevas->addtag("nouveau", "overlapping", x1,y1,x2,y2);

On utilisera "overlapping" au lieu de "enclosed" pour assigner des marqueurs à des éléments dont une partie se trouve à l'intérieur de la zone délimitée. Même si un seul pixel de l'élément se trouve dans celle-ci, il sera pris en compte. Les contraintes de coordonnées sont les mêmes que pour "enclosed".

$canevas->addtag("nouveau", "withtag", marqueur/id);

Assigne "nouveau" à tous les éléments ayant le marqueur ou l'identificateur indiqué.

IX-I-1 - Liaison d'éléments à l'aide des marqueurs

Chaque élément d'un canevas peut être lié à une séquence d'événements afin qu'une fonction de rappel soit invoquée lorsque cette séquence arrive. Cette association ressemble à l'ajout d'une liaison à une séquence d'événements pour les widgets, mais on y utilise des marqueurs ou des identificateurs d'éléments. N'oubliez pas que, pour ajouter une liaison normale au canevas lui-même, il faut utiliser Tk::bind (ou canvasBind, si l'on utilise Tk8.0) à la place de bind.

La forme générale de cet appel est :

$canevas->bind(marqueur/id [ , séquence, commande ] );

séquence sera quelque chose comme "<Button-1>" ou "<Double-1>". Le chapitre 14, Liaisons d'événements, donne une définition et une explication complètes des séquences d'événements.

Lorsque l'on crée des liaisons d'éléments, on doit se rappeler que seules les liaisons souris et clavier sont reconnues. On ne peut pas se permettre toutes les liaisons ésotériques possibles pour les widgets.

Voici un exemple modifiant la couleur de n'importe quel élément ayant le marqueur "bleu" lorsque la souris est au-dessus de lui :

# Lorsque la souris est au-dessus de l'élément, on le colorie en bleu
$c->bind( "bleu", "<Enter>", sub { $c->itemconfigure( "bleu", -fill => "blue" ); } );

# Lorsque la souris est ailleurs, on le colorie en noir
$c->bind( "bleu", "<Leave>", sub { $c->itemconfigure( "bleu", -fill => "black" ); } );

IX-I-2 - Recherche de marqueurs

La méthode find permet de connaître les éléments associés à un marqueur donné. Les différentes façons d'appeler find sont les mêmes que pour addtag (exception faite du paramètre "nouveau"). Voici quelques formes d'appels classiques (la section consacrée aux marqueurs, plus haut dans ce chapitre, détaille leur fonctionnement) :

$canevas->find("above", marqueur/id);
$canevas->find("all");
$canevas->find("below", marqueur/id);
$canevas->find("closest", x, y [ , zone_supplémentaire ]
[ , marqueur/id ] ) ;
$canevas->find("enclosed", x1, y1, x2, y2);
$canevas->find("overlapping", x1, y1, x2, y2);
$canevas->find("withtag", marqueur/id);

IX-I-3 - Obtention des marqueurs d'un élément donné

Pour obtenir la liste de tous les marqueurs associés à un élément, on utilise l'instruction suivante :

@liste = $canevas->gettags(marqueur/id);

Si marqueur/id correspond à plusieurs éléments, le premier trouvé sera utilisé. Si la recherche échoue, la méthode renverra une chaîne vide.

IX-J - Coordonnées de la boîte de dimensionnement

Lorsque nous avons évoqué la zone de parcours d'un canevas, nous avons vu un exemple d'utilisation de la méthode bbox. Celle-ci renvoie une liste de quatre éléments qui définissent la zone dans laquelle se trouvent tous les marqueurs indiqués. L'exemple utilisait le marqueur spécial "all", qui désigne tous les éléments du canevas, pour définir la région de parcours, mais on peut préciser plusieurs marqueurs/id :

($gauche, $droite, $haut, $bas) = $canevas->bbox("bleu", "rouge");

En supposant que vous ayez assigné les marqueurs "bleu" et "rouge" aux éléments ayant ces couleurs, ce code renverra la région du canevas contenant tous les éléments bleus et rouges.

IX-K - Conversion de coordonnées

Lorsque l'on met en place une fonction de rappel et que l'on utilise les paramètres Ev('x') et/ou Ev('y') pour savoir où a cliqué l'utilisateur, on doit convertir cette information en coordonnées du canevas (Ev est expliqué au chapitre 14). Les méthodes canvasx et canvasy s'acquittent de cette tâche :

$x = $canevas->canvasx(x_écran [, maillage ] );
$y = $canevas->canvasy(y_écran [, maillage ] );

Les deux méthodes acceptent un paramètre optionnel permettant de préciser le maillage utilisé ; les coordonnées canevas seront alors arrondies à la valeur la plus proche convenant à ce maillage.

IX-L - Déplacement des éléments

Lorsqu'un élément a été créé dans le canevas, on peut le déplacer à l'aide des méthodes move ou coords. La première attend un marqueur ou un identificateur indiquant quels sont les éléments à déplacer et les montants à ajouter aux coordonnées x et y :

$canevas->move(marqueur/id, distance_x, distance_y);

Le code suivant, par exemple, déplacera les éléments portant le marqueur « bleu » de 100 pixels dans la direction x et de 100 pixels dans la direction y :

$canevas->move("bleu", 100, 100);

Pour déplacer un élément vers l'origine des axes, il suffit d'indiquer une valeur négative pour distance_x et/ou distance_y. L'autre méthode, coords, permet de préciser explicitement de nouvelles valeurs x et y pour le premier élément trouvé correspondant au marqueur ou à l'identificateur :

$canevas->coords(marqueur/id, nouveau_x, nouveau_y);

Si l'élément nécessite plusieurs paires de coordonnées, il suffit de les préciser :

$canevas->coords(marqueur/id,
nouveau_x1, nouveau_y1,
nouveau_x2, nouveau_y2, ...);

En utilisant coords sans indiquer de coordonnées, on peut aussi rechercher l'emplacement courant d'un élément dans le canevas :

@liste_coords = $canevas->coords(marqueur/id);

Rappelez-vous que la méthode coords ne s'applique qu'au premier élément qu'elle trouve et qui correspond au marqueur ou à l'identificateur indiqué.

IX-M - Modification de la liste d'affichage

À chaque fois qu'une méthode parcourt tous les éléments d'un canevas à la recherche d'un marqueur ou d'un identificateur donné, elle cherche dans la liste d'affichage modifiée à mesure que des éléments sont ajoutés au canevas. Le premier élément placé dans le canevas est aussi le premier de la liste et les suivants sont ajoutés dans leur ordre de création. Les derniers éléments ajoutés recouvrent les autres et sont donc tracés au-dessus d'eux. Les méthodes raise et lower permettent de modifier l'ordre d'affichage :

$canevas->raise(marqueur/id, dessus/id);
$canevas->lower(marqueur/id, dessous/id);

Le premier paramètre de chaque méthode est le marqueur ou l'identificateur du ou des éléments que vous souhaitez déplacer dans la liste d'affichage. Le second est le marqueur ou l'identificateur de l'élément à côté duquel le premier doit être placé (soit au-dessus, soit en dessous). Si le premier marqueur ou identificateur correspond à plusieurs éléments, ceux-ci seront tous déplacés.

Si vous utilisez la méthode Scrolled pour créer le canevas, vous ne pourrez utiliser l'élément renvoyé par celle-ci pour invoquer raise ou lower :vous obtiendrez une déplaisante erreur à propos de types de paramètres incorrects, car Scrolled n'appelle pas cette version de raise ou lower, mais une autre. Utilisez Subwidget, comme dans l'exemple de la section « Utilisation de bind avec un canevas » , pour obtenir la référence au canevas afin de pouvoir appeler les versions voulues de ces méthodes.

IX-N - Suppression d'éléments

La méthode delete permet de supprimer définitivement un ou plusieurs éléments du canevas. Celle-ci attend une liste de marqueurs ou d'identificateurs comme paramètre : soyez prudent, car elle supprimera toutes les correspondances trouvées pour les marqueurs. Voici un exemple utilisant trois marqueurs/identificateurs distincts :

$canevas->delete("bleu", "cercle", $num_id);

On peut indiquer un seul marqueur/identificateur, ou autant que nécessaire.

IX-O - Suppression de marqueurs

La méthode dtag permet d'ôter des marqueurs des éléments. Elle est utilisable sous deux formes :

$canevas->dtag(marqueur);
$canevas->dtag(marqueur/id, marqueur_à_ôter);

La première recherchera les éléments possédant le marqueur indiqué et leur ôtera alors celui-ci. La seconde recherchera les éléments possédant le marqueur ou l'identificateur indiqué et leur ôtera le marqueur marqueur_à_ôter (s'il existe). Cela vous permet de supprimer un sous-ensemble de marqueurs, plutôt que de répéter cette suppression sur chacun d'eux.

IX-P - Déterminer le type d'un élément

Pour déterminer le type d'un élément, on fait appel à la méthode type :

$canevas->type(marqueur/id);

Si le marqueur ou l'identificateur correspond à plus d'un élément, seul le type du premier est renvoyé. La valeur obtenue sera une chaîne rendant compte de son type : "oval", "text", "rectangle", etc.

IX-Q - Focus clavier

Pour donner le focus clavier à un élément, on utilise la méthode focus :

$canevas->focus(marqueur/id);

Si l'élément ne sait que faire du focus clavier, rien ne se passera.

IX-R - Représentation PostScript d'un canevas

La méthode postscript permet d'obtenir une copie PostScript d'un canevas. Elle renverra le PostScript produit ou, si l'option -file est utilisée, le placera dans le fichier indiqué et renverra une chaîne vide :

$postscript = $canevas->postscript();
$canevas->postscript(-file => "canevas.ps");

Vous pouvez contrôler la génération PostScript à l'aide des options suivantes :

-colormap => \@tableau

1. Chaque élément de @tableau doit être une commande PostScript permettant de configurer les couleurs, "1.0 1.0 0.0 setrgbcolor", par exemple.

-colormode => "color" | "gray" | "mono"

1. Crée le PostScript en couleurs, en nuances de gris ou en monochrome.

-file => nom_fichier

1. Précise le fichier dans lequel sera placé le code PostScript obtenu.

-fontmap => \@tableau

1. Chaque élément de @tableau doit être un tableau à deux éléments contenant un nom de fonte et une taille en points. Le nom de la fonte doit être un nom complet pour que Tk l'analyse correctement, "-*-Helvetica-Bold-O-Normal--*-140-*", par exemple.

-height => hauteur

1. Fixe la hauteur de la zone à imprimer. Par défaut, il s'agit de la hauteur du canevas.

-pageanchor => "n" | "e" | "s" | "w" | "center"

1. Indique où doit être placée la page sur le point de positionnement spécifié par les options -pagex et -pagey. Par défaut, elle sera centrée sur celui-ci.

-pageheight => hauteur

1. Fixe la hauteur de la page imprimée. La taille de l'image du canevas sera ajustée pour convenir à celle-ci. hauteur est exprimée en distances d'écran.

-pagewidth => largeur

1. Fixe la largeur de la page imprimée. La taille de l'image du canevas sera ajustée pour convenir à celle-ci.

-pagex => x

1. Configure l'abscisse du point de positionnement. x est exprimée en distances d'écran.

-pagey => y

1. Configure l'ordonnée du point de positionnement. y est exprimée en distances d'écran.

-rotate => 0 | 1

1. Si cette option vaut 1, la page est orientée en mode paysage. L'orientation par défaut est le mode portrait.

-width => largeur

1. Fixe la largeur de la zone du canevas qui sera imprimée. Par défaut, il s'agit de la largeur de celui-ci.

-x => x

1. Fixe le bord gauche de la zone à imprimer (en coordonnées du canevas). Par défaut, il s'agit du bord gauche de la fenêtre.

-y => y

1. Fixe le sommet de la zone à imprimer (en coordonnées du canevas). Par défaut, il s'agit du bord supérieur de la fenêtre.

IX-S - Modification de l'échelle d'un canevas

Lorsque l'on place un grand nombre d'éléments dans un canevas, il devient parfois difficile de tous les voir sans parcourir tout le widget. La méthode scale permet de modifier l'échelle d'un canevas pour que, par exemple, il réduise tout ce qu'il contient de moitié ou qu'il en double la taille. La syntaxe de cette méthode est la suivante :

$canevas->scale(marqueur/id, origine_x, origine_y, échelle_x, échelle_y);

L'échelle est centrée autour de origine_x et origine_y. Je vous conseille d'utiliser l'origine réelle (0, 0), sauf si vous trouvez une bonne raison de ne pas le faire. échelle_x et échelle_y sont les facteurs d'échelle utilisés sur les deux coordonnées de chaque élément. Voici quelques exemples :

$canevas->scale("all", 0, 0, 1, 1); # pas de changement !
$canevas->scale("all", 0, 0, .5, .5); # réduit tout de moitié
$canevas->scale("all", 0, 0, 2, 2); # double tout
$canevas->scale("all", 0, 0, 3, 3); # triple tout !

C'est une très bonne idée d'ajouter un bouton de grossissement et de réduction s'occupant pour vous de la mise à l'échelle. Conservez le facteur d'échelle dans une variable ($echelle, par exemple) et initialisez-la à 1. Multipliez-la par 0.5 pour réduire, et par 2 pour agrandir. Le dernier point à vérifier est que, si vous insérez de nouveaux éléments au canevas, les coordonnées de ceux-ci devront aussi être multipliées par le facteur d'échelle (sinon, ils apparaîtront trop grands ou trop petits, par rapport aux autres éléments du canevas).

IX-T - Recherche

La méthode scan implante la recherche dans un canevas :

$canevas->scanMark(x,y);
$canevas->scanDragto(x,y);

Le premier appel mémorise les coordonnées x et y et la vue courante du canevas. Le second ajuste cette vue de 10 fois la différence entre les coordonnées indiquées et celles fournies à scanMark. Le canevas semblera alors être parcouru à très grande vitesse.

IX-T-1 - Méthodes de parcours

On peut parcourir un widget canevas dans les deux directions : les méthodes xview et yview servent à communiquer avec les barres de défilement. Le chapitre 6 donne plus d'informations sur le fonctionnement de ces deux méthodes.

IX-U - Exemple de programme de dessin

Le widget canevas est bon à tout faire et peut servir à afficher différents types d'éléments. Lorsque l'on pense à un canevas, la première idée qui vient à l'esprit est celle d'un programme de dessin. Pour vous éviter ce travail, j'ai écrit un tel programme, rudimentaire : Quick Draw vous permet de tracer des rectangles, des ovales et des lignes. Vous pouvez également modifier l'épaisseur des objets avant de les dessiner. Voici son code :

#!/usr/bin/perl
use strict;
use warnings;
use Tk;

my $mw = MainWindow->new;
$mw->title("Quick Draw");

my $f = $mw->Frame(
-relief => 'groove',
-bd     => 2,
-label  => "Tracé :"
)->pack(
-side => 'left',
-fill => 'y'
);
my $element_trace = "rectangle";
$f->Radiobutton(
-variable => \$element_trace,
-text     => "Rectangle",
-value    => "rectangle",
-command  => \&liaison
)->pack( -anchor => 'w' );
$f->Radiobutton(
-variable => \$element_trace,
-text     => "Ovale",
-value    => "ovale",
-command  => \&liaison
)->pack( -anchor => 'w' );
$f->Radiobutton(
-variable => \$element_trace,
-text     => "Ligne",
-value    => "ligne",
-command  => \&liaison
)->pack( -anchor => 'w' );
$f->Label( -text => "Épaisseur de la ligne :" )->pack( -anchor => 'w' );
my $epaisseur = 1;
$f->Entry( -textvariable => \$epaisseur )->pack( -anchor => 'w' );
my $c = $mw->Scrolled( "Canvas", -cursor => "crosshair" )->pack(
-side   => "left",
-fill   => 'both',
-expand => 1
);
my $canvas = $c->Subwidget("canvas");
my ( $debut_x, $debut_y );

liaison();

MainLoop;

sub liaison {

# S'il y une liaison "Motion", il faut autoriser l'utilisateur
# à terminer de tracer l'élément avant de lier à nouveau Button-1
# Cette fonction sera appelée de nouveau lorsque le tracé sera
# terminé.
my @liaisons = $canvas->Tk::bind("<Motion>");
return if ( $#liaisons >= 0 );
if (   $element_trace eq "rectangle"
|| $element_trace eq "ovale"
|| $element_trace eq "ligne" )
{
$canvas->Tk::bind( "<Button-1>", [ \&debut_dessin, Ev('x'), Ev('y') ] );
}
}

sub debut_dessin {
my ( $canv, $x, $y ) = @_;
$x = $canv->canvasx($x);
$y = $canv->canvasy($y);

# Petite vérification d'erreurs
$epaisseur = 1 if ( $epaisseur !~ /[0-9]+/ );
if ( $element_trace eq "rectangle" ) {
$canvas->createRectangle(
$x, $y, $x, $y,
-width => $epaisseur,
-tags  => "trace_moi"
);
}
elsif ( $element_trace eq "ovale" ) {
$canvas->createOval(
$x, $y, $x, $y,
-width => $epaisseur,
-tags  => "trace_moi"
);
}
elsif ( $element_trace eq "ligne" ) {
$canvas->createLine(
$x, $y, $x, $y,
-width => $epaisseur,
-tags  => "trace_moi"
);
}
$debut_x = $x;
$debut_y = $y;

# Fait correspondre la liaison Button-1 à &fin_dessin
# plutôt que de commencer à tracer
$canvas->Tk::bind( "<Motion>",   [ \&taille_elt, Ev('x'), Ev('y') ] );
$canvas->Tk::bind( "<Button-1>", [ \&fin_dessin, Ev('x'), Ev('y') ] );
}

sub taille_elt {
my ( $canv, $x, $y ) = @_;
$x = $canv->canvasx($x);
$y = $canv->canvasy($y);
$canvas->coords( "trace_moi", $debut_x, $debut_y, $x, $y );
}

sub fin_dessin {
my ( $canv, $x, $y ) = @_;
$x = $canv->canvasx($x);
$y = $canv->canvasy($y);

# finalise la taille de l'élément et ôte le marqueur
$canvas->coords( "trace_moi", $debut_x, $debut_y, $x, $y );
$canvas->dtag("trace_moi");

# Supprime la liaison <Motion>
$canvas->Tk::bind( "<Motion>", "" );
liaison();
}

MainLoop;

On remarquera que je n'ai pas utilisé -scrollregion, car je souhaite créer un espace de dessin non limité (ne rien faire était la meilleure façon d'offrir cette possibilité !). Il s'agit d'un petit programme destiné à présenter l'utilisation de bind et de plusieurs méthodes du widget canevas. La figure 9.4 montre une copie d'écran de l'application après que plusieurs éléments ont été tracés.

IX-V - Essayez et amusez-vous !

L'application Quick Draw n'est pas très utile, mais elle donne des idées d'évolution :

- ajouter la possibilité d'imprimer dans un fichier PostScript ;

- créer une possibilité de sauvegarder le tracé en stockant le type et les coordonnées de chaque élément dans un fichier texte. Il faudra, bien sûr, ajouter aussi la possibilité de relire un tel fichier ;

- ajouter la création d'éléments textuels ;

- ajouter une zone de saisie permettant de modifier la couleur de tracé des éléments (en donnant un nom de couleur).

X - Le widget barre de progression

Une barre de progression (scale) est un étrange petit widget. Elle ressemble à une barre de défilement, car est allongée et étroite et qu'un ascenseur la traverse en son milieu, mais elle ne parcourt rien sauf elle-même. Elle mémorise quelque chose de terrifiant : un nombre. La valeur associée à ce dernier varie lorsque l'on modifie la position de l'ascenseur. Voici quelques possibilités des barres de progression :

- créer un widget à partir duquel on pourra choisir un nombre entre 1 et 100 ;

- créer trois barres de progressions, représentant chacune une valeur RGB (rouge, vert et bleu) ;

- créer quatre ascenseurs, chacun représentant une partie d'une adresse IP. Chaque échelle pourrait varier de 0 à 255 et il serait pratique qu'elles commencent à 255. On utiliserait un widget étiquette pour afficher l'adresse IP complète, en notation pointée ;

- créer une échelle de température variant de -50 à 130 degrés ;

- montrer la somme des précipitations de l'année. L'échelle progresserait de cinq en cinq centimètres.

Une barre de progression peut être horizontale ou verticale, selon l'espace disponible dans votre application.

X-A - Création d'une barre de progression

Comme pour les autres widgets, on crée une barre de progression en utilisant un widget parent et en passant des options à la barre pour modifier sa configuration :

$parent->Scale( [ option => valeur ] )->pack;

On utilise l'un des gestionnaires d'espace étudiés au chapitre 2, Gestion de l'espace, pour la placer à l'écran.

La plupart des options associées à une barre de progression sont celles qui sont utilisées avec tous les autres widgets ; elles sont toutes énumérées dans la liste qui suit. Nous étudierons ensuite les options particulières, aux significations inhabituelles, ainsi que les options spécifiques à ce type de widget.

-activebackground => couleur

1. Fixe la couleur qu'aura l'ascenseur de la barre lorsque le curseur sera au-dessus de lui (l'option -state est 'active').

-background => couleur

1. Configure la couleur du fond de la barre, et celle de l'ascenseur lorsque le curseur n'est pas au-dessus d'elle (l'option -state est 'normal').

-bigincrement => montant

1. Fixe le montant par lequel la barre modifiera la valeur lorsqu'on lui fait faire des pas importants. La valeur par défaut est 0, ce qui modifiera la valeur de un dixième de l'intervalle total de la barre.

-borderwidth => montant

1. Configure la largeur du contour du widget. Le montant par défaut est 2.

-command => fct_rappel

1. Précise la fonction de rappel qui sera invoquée lorsque l'ascenseur sera déplacé.

-cursor => nom_curseur

1. Précise le curseur à afficher lorsque la souris est au-dessus de la barre.

-digits => montant

1. Indique le nombre de chiffres significatifs à garder lors d'une conversion d'un nombre vers une chaîne.

-font => nom_fonte

1. Fixe la fonte à utiliser pour afficher le texte de la barre de progression.

-foreground => couleur

1. Indique la couleur du texte de la barre.

-from => valeur

1. Fixe la valeur inférieure de la barre, par défaut 0.

-highlightbackground => couleur

1. Précise la couleur du rectangle affiché autour de la barre lorsque celle-ci n'a pas le focus clavier.

-highlightcolor => couleur

1. Précise la couleur du rectangle affiché autour de la barre lorsque celle-ci a le focus clavier.

-highlightthickness => montant

1. Configure l'épaisseur du rectangle affiché autour de la barre.

-label => chaîne

1. Affecte une étiquette à la barre de progression. Elle n'en a pas par défaut.

-length => montant

1. Fixe la longueur de l'ascenseur (sa dimension longue, quelle que soit la valeur de -orient) en distances d'écran.

-orient => 'vertical' | 'horizontal'

1. Configure la direction dans laquelle sera tracée la barre. Par défaut, elle est verticale.

-relief => 'raised' | 'sunken' | 'flat' | 'ridge' | 'groove' | 'solid'

1. Détermine l'aspect qu'aura le contour du widget. La valeur par défaut est 'flat'.

-repeatdelay => millisecondes

1. Fixe le nombre de millisecondes pendant lequel le widget attendra avant de commencer la répétition automatique du mouvement de l'ascenseur.

-repeatinterval => millisecondes

1. Fixe le nombre de millisecondes pendant lequel le widget attendra entre deux répétitions automatiques.

-resolution => valeur

1. Configure le pas de progression de la valeur associée à la barre. Le pas par défaut est 1.

-showvalue => 0 | 1

1. Si cette option vaut 0, la valeur associée à l'ascenseur n'est pas affichée. Elle est affichée par défaut.

-sliderlength => valeur

1. Configure la taille de l'ascenseur (dans le widget). Par défaut, cette longueur est de 25.

-state => 'normal' | 'active' | 'disabled'

1. Détermine l'état du widget et si l'utilisateur pourra, ou non, le manipuler. L'état par défaut est 'normal'.

-takefocus => 1 | 0 | undef

1. Indique si le widget pourra, ou non, recevoir le focus clavier. Le comportement par défaut consiste à laisser le programme décider.

-tickinterval => valeur

1. Décrit les étiquettes inscrites à la droite (ou en bas) de la barre de progression. Celles-ci sont placées toutes les valeurs. Si cette option vaut 0, aucune étiquette ne sera mise, ce qui est le comportement par défaut.

-to => valeur

1. Fixe la valeur supérieure de la barre. Le maximum par défaut est 100.

-troughcolor => couleur

1. Précise la couleur de la zone située derrière l'ascenseur (comme pour une barre de défilement).

-variable => \$variable

1. Indique la variable qui conservera la valeur de l'ascenseur.

-width => montant

1. Fixe la largeur de la partie étroite de l'ascenseur (quelle que soit la valeur associée à -orient).

X-B - Assignation d'une fonction de rappel

Comme d'habitude, l'option -command permet d'assigner une fonction de rappel au widget. Celle-ci est invoquée à chaque fois que la valeur de la barre est modifiée. Ainsi, si l'on change cette valeur de 50 à 100 et que le pas de progression est de 1, la fonction de rappel sera appelée 50 fois. Elle sera également invoquée au moment de la création du widget. Je vous conseille de ne pas utiliser cette option, sauf si le nombre de valeurs possibles est faible.

X-C - Orientation

L'option -orient permet de modifier l'orientation d'une barre de progression. Elle prend comme valeur une chaîne contenant "horizontal" ou "vertical", cette dernière valeur étant celle par défaut. La figure 10.1 montre une barre horizontale et une barre verticale.

Figure 10.1 : Barre de progression verticale (orientation par défaut) et horizontale

X-D - Valeurs minimum et maximum

Les options -from et -to permettent de modifier l'intervalle des valeurs possibles de la barre de progression. Habituellement, la valeur associée à -from est inférieure à celle de -to. Si vous les inversez, la barre affichera simplement la valeur -from à gauche (ou en haut) et la valeur supérieure à droite (ou en bas) et la valeur -to à droite (ou en haut). L'une des valeurs (ou les deux) peut être négative. Voici quelques exemples :

$mw->Scale(-from => -10, -to => 10)->pack;
$mw->Scale(-from => 10, -to => -100)->pack;
$mw->Scale(-from => -100, -to => -50)->pack;
$mw->Scale(-from => -0.5, -to => 0.5, -resolution => 0.1)->pack;

Comme vous pouvez le constater, les valeurs assignées à -from et à -to ne sont pas nécessairement des valeurs entières.

X-E - Valeur affichée vs valeur stockée

Parfois, la valeur que l'on recherche est comprise entre deux nombres très éloignés, 0 et 1 000 000, par exemple. Laisser l'utilisateur passer ces valeurs en revue une à une serait peu charitable. L'option -resolution permet de modifier le pas de progression du nombre affiché ; par défaut il vaut 1, mais n'importe quelle valeur est tolérée.

Si la résolution est supérieure à 1, l'ascenseur peut avoir une valeur (affectée par le programme, par exemple) plus petite ou plus grande que la valeur affichée.

X-F - Ajout d'une étiquette

L'option -label permet de placer une étiquette associée à la barre de progression. L'emplacement dépend de la valeur de -orient (voir la figure 10.2).

Figure 10.2 : Deux barres de progression, avec étiquette

X-G - Affichage des pas de progression

La barre de progression affiche sa valeur courante au-dessus, ou à gauche, de l'ascenseur (selon l'orientation définie par -orient). Supposons que l'on souhaite afficher des étiquettes (comme 0, 10, 20, -100) montrant approximativement à l'utilisateur où il doit placer l'ascenseur pour choisir ces valeurs. Avec l'option -tickinterval, on affichera ces étiquettes en dessous ou à gauche de la barre. Par défaut, cette option vaut 0 et aucun nombre ne sera donc affiché ; pour faire apparaître des nombres de 10 en 10, on utilisera -tickinterval => 10. Ce pas de progression dépend de l'intervalle des valeurs de la barre : plus ce dernier est large, plus le pas doit être important ou l'on aura un paquet de nombres si près les uns des autres que le tout sera illisible. La figure 10.3 présente deux utilisations correctes de cette option.

X-H - Modification de la taille de la barre

Les options -length et -width permettent de modifier la taille d'une barre de progression. La taille de l'ascenseur, quant à elle, dépend de la valeur de l'option -sliderlength : celle-ci prend une valeur exprimée en unités d'écran. La figure 10.3 en montre les effets.

$mw->Scale(-sliderlength => 100); # fixe l'ascenseur à 100 pixels

Figure 10.4 : Différentes valeurs de -sliderlength

X-I - Options rarement utilisées

Les deux dernières options de la méthode de création du widget sont -bigincrement et -digits. La première précise la taille des sauts de progression lorsque de très grands nombres sont utilisés. Sa valeur par défaut est 0, ce qui signifie que les sauts s'effectueront par pas de 1/10 de l'intervalle total.

L'option -digits précise le nombre de chiffres utilisés lors d'une conversion d'une valeur numérique en chaîne. La valeur par défaut (0) force la barre à utiliser une précision qui fera que toute valeur possible de la barre sera convertie en une chaîne différente.

X-J - Configuration d'une barre de progression

Comme d'habitude, la barre de progression dispose des méthodes configure et cget, qui vous permettent d'interroger et de positionner les options du widget. L'annexe A donne plus de détails sur l'utilisation de ces méthodes.

X-K - Obtenir la valeur d'une barre de progression

La méthode get renverra la valeur courante de la barre :

$valeur = $progress->get();

Cette méthode accepte également des coordonnées x et y et renverra alors la valeur de la barre lorsque l'ascenseur est sur ce point :

$valeur = $progress->get(x, y);

X-L - Configuration de la valeur d'une barre

La valeur associée à une barre de progression peut être initialisée avec la méthode set :

$progress->set(valeur);

Cette méthode est très pratique pour donner une valeur initiale lorsque l'on n'utilise pas l'option -variable. Si vous utilisez cette dernière, il suffit de l'initialiser à la valeur de départ souhaitée.

X-M - Obtention des coordonnées

La méthode coords renvoie une liste contenant les coordonnées x et y :

($x, $y) = $progress->coords();

Ces coordonnées indiquent la position à laquelle se trouve la valeur courante dans la barre de progression. On peut aussi passer une valeur dont on recherche les coordonnées :

($x, $y) = $progress->coords(valeur);

X-N - Identifier les parties d'une barre de progression

La méthode identify permet de retrouver la partie de la barre correspondant aux coordonnées indiquées :

$valeur = $progress->identify(x,y);

Cette méthode renvoie une chaîne contenant l'une des valeurs suivantes : "slider" (ascenseur), "trough1" (zone avant l'ascenseur), "trough2" (zone après l'ascenseur), ou la chaîne vide (si les coordonnées ne désignent aucune de ces parties).

X-O - Essayez et amusez-vous !

- créez un formulaire contenant des barres de progression qui représentent des informations sur l'utilisateur. L'âge (0−150 est un intervalle correct) et le nombre d'enfants à charge peuvent être représentés par une barre. Utilisez judicieusement les options -resolution, -to, et -from pour faciliter la tâche de l'utilisateur ;

- créez une application ping utilisant des widgets barre de progression (une barre par partie de l'adresse IP) afin de demander une adresse IP à l'utilisateur.

XI - Menus

XI-A - Différents types de menus

Il existe plusieurs façons de créer et d'utiliser un menu dans une application Perl/Tk. En voici quelques suggestions d'utilisation(27) :

- création de menus « Fichier », « Édition » et « Aide » en haut de la fenêtre de l'application ;

- affichage d'une liste de fontes dans laquelle l'utilisateur pourra choisir (la fonte sélectionnée sera cochée par une marque) ;

- affichage d'une liste de commandes d'édition apparaissant lorsque l'on clique avec le bouton droit de la souris sur un objet (une boîte de liste ou une zone de saisie, par exemple) de la fenêtre.

Chacun de ces différents types de menus se construit avec le widget menu de base.

Celui-ci est une liste des éléments qui s'afficheront, ligne par ligne, dans une boîte. Chaque élément peut posséder une fonction de rappel qui sera appelée lorsqu'il est invoqué ou sélectionné. À la différence des autres widgets que nous venons d'étudier, on ne peut pas utiliser de gestionnaire d'espace avec un menu ; on doit utiliser la méthode post pour l'afficher (post sera étudiée plus loin dans ce chapitre).

La figure 11.1 montre le contenu d'un widget menu typique : il contient plusieurs éléments, un séparateur et encore d'autres éléments. Les séparateurs servent à regrouper des commandes ayant des points communs et offrent une coupure visuelle lorsqu'un menu contient beaucoup de commandes.

Figure 11.1 : Widget menu simple, contenant cinq éléments ; Choix1, Choix2, un séparateur, Choix3 et Choix4

Une utilisation possible s'avère particulièrement utile : on peut placer dans un menu des cases à cocher et des boutons radio afin d'économiser beaucoup d'espace pour des widgets plus importants.

Le widget bouton de menu s'inspire du widget menu et possède un bouton contrôlant l'apparition de ce dernier à l'écran. Le bouton contient une chaîne décrivant les éléments du menu et, lorsqu'il est pressé, le menu s'affiche directement au-dessous de lui. Dans 90 % des cas, vous utiliserez ce type de widget pour réaliser vos menus. La figure 11.2 représente un bouton de menu après qu'il a été pressé ; le bouton du widget est l'endroit où apparaît le mot « Fichier ».

Figure 11.2 : Widget bouton de menu utilisant un widget menu

Un widget bouton de menu prend en charge les fonctions d'affichage du menu, et cela constitue son avantage principal. Il s'agit du widget le plus fréquemment utilisé pour les menus, nous le présenterons donc avant les autres.

Le dernier widget ayant un rapport avec les menus est le menu d'options, qui se comporte différemment des deux autres. Il permet à l'utilisateur de choisir un élément dans une liste de possibilités. On peut, par exemple, utiliser un menu d'options pour ajouter les fonctionnalités suivantes à un programme :

- permettre aux utilisateurs de choisir leur couleur favorite dans une liste ;

- permettre aux utilisateurs de choisir leur pays de résidence ;

- permettre de déterminer le sort réservé aux messages de l'application (modes « silencieux », « normal », ou « bavard »).

La figure 11.3 montre un menu d'options avec son troisième choix sélectionné.

Les menus offrent la possibilité de regrouper des tâches en rapport les unes avec les autres, tandis que les menus d'options rassemblent plusieurs choix. Chaque élément d'un menu a une fonction de rappel associée, exactement comme un widget bouton. Au lieu d'utiliser dix boutons différents, on peut créer deux menus contenant chacun cinq éléments. Cela économise l'espace d'affichage et aide les utilisateurs à comprendre que ces éléments servent un but commun et qu'ils ont été regroupés pour leur confort.

Figure 11.3 : Exemple de widget menu d'options

XI-B - Le widget Menubutton

Comme nous l'avons décrit plus haut, un bouton de menu (menubutton, en anglais) est un menu déroulant, apparaissant sous un bouton lorsque celui-ci est pressé. Ce menu disparaît de la fenêtre lorsque l'un de ses éléments est sélectionné ou que l'utilisateur clique sur un autre endroit de l'application.

De nombreux programmes utilisent de tels widgets. Une barre regroupe d'ordinaire les boutons de menus en haut de l'application. Ils portent des noms comme « Fichier », « Édition », « Options » et « Aide ». La figure 11.4 présente plusieurs boutons de menus, regroupés dans un cadre(28) .

Figure 11.4 : Exemple de fenêtre ayant plusieurs boutons de menu

XI-B-1 - Création d'un bouton de menu

Lorsque l'on crée un widget bouton de menu, on utilise le widget parent pour invoquer la méthode Menubutton qui produit alors une référence au bouton. Les options de cette méthode permettent à la fois de configurer le bouton affiché à l'écran et les éléments du menu :

$b_menu = $parent->Menubutton( [ options...] )->pack;

Seul le bouton du widget apparaîtra lors de son premier affichage par l'un des gestionnaires d'espace. Il s'agit d'un bouton « plat » (correspondant à une valeur de relief 'flat'). La partie menu ne surgira pas tant qu'il n'a pas été pressé. La figure 11.5 montre un widget bouton de menu avant et après que son bouton a été pressé (et dont le relief est modifié après cette action).

Figure 11.5 : Bouton de menu avant et après avoir cliqué sur le bouton

XI-B-2 - Options d'un bouton de menu

Les options de la méthode Menubutton (ou passées à la méthode configure) affectent uniquement la partie bouton, uniquement la partie menu, ou l'ensemble du widget. Les options agissant sur la partie menu fonctionnent avec un widget menu comme avec un bouton de menu. Nous traiterons rapidement des options disponibles (certaines seront un peu plus détaillées) pour étudier leurs effets.

Lorsque la description d'une option précise qu'elle n'agit que sur le bouton, son comportement est le même que pour un widget bouton classique.

-activebackground => couleur

1. Modifie la couleur de fond du bouton et de l'élément de menu sélectionné.

-activeforeground => couleur

1. Modifie la couleur du texte du bouton et de l'élément de menu sélectionné.

-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'

1. N'affecte que le bouton. Modifie la position du texte dans celui-ci.

-background => couleur

1. Affecte le bouton et le menu. Précise la couleur de fond utilisée lorsque le bouton et les éléments du menu sont dans l'état 'normal'.

-bitmap => nom_bitmap

1. N'affecte que le bouton. Affiche le bitmap spécifié à la place du texte.

-borderwidth => montant

1. N'affecte que le bouton. Modifie la largeur de son contour.

-cursor => nom_curseur

1. N'affecte que le bouton. Modifie l'aspect du curseur lorsqu'il sera au-dessus de la partie bouton du widget.

-disabledforeground => couleur

1. Affecte la couleur du bouton et du texte des éléments du menu lorsque leur état est 'disabled'.

-font => nom_fonte

1. N'affecte que le bouton. Modifie la fonte du texte.

-foreground => couleur

1. N'affecte que le bouton. Fixe la couleur du texte ou du bitmap qu'il affiche.

-height => montant

1. N'affecte que le bouton. Précise sa hauteur.

-highlightbackground => couleur

1. N'affecte que le bouton. Modifie la couleur du rectangle tracé autour de celui-ci lorsqu'il n'a pas le focus clavier.

-highlightcolor => couleur

1. N'affecte que le bouton. Modifie la couleur du rectangle tracé autour de celui-ci lorsqu'il a le focus clavier.

-highlightthickness => montant

1. N'affecte que le bouton. Fixe l'épaisseur du rectangle de focus tracé autour du bouton.

-image => ptr_image

1. N'affecte que le bouton. Affiche une image à la place du texte.

-indicatoron => 0 | 1

1. Affecte le bouton et, indirectement, le mécanisme d'affichage du menu. Lorsque cette option vaut 1, une petite barre apparaît sur la droite du bouton, à côté du texte, du bitmap ou de l'image.

-justify => 'left' | 'right' | 'center'

1. N'affecte que le bouton. Indique l'alignement du texte.

-menu => $menu

1. Indique au bouton de menu qu'il doit afficher le menu associé à $menu, et donc négliger l'option -menuitems.

-menuitems => liste

1. liste est une liste décrivant chaque élément du menu.

-padx => montant

1. N'affecte que le bouton. Ajoute de l'espace supplémentaire à gauche et à droite de celui-ci, à l'intérieur de ses limites.

-pady => montant

1. N'affecte que le bouton. Ajoute de l'espace supplémentaire au-dessus et en dessous de celui-ci, à l'intérieur de ses limites.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. N'affecte que le bouton. Configure son aspect. Lorsqu'il est pressé, son aspect sera 'raised', quelle que soit la valeur de ce paramètre.

-state => 'normal' | 'active' | 'disabled'

1. Affecte le bouton et, indirectement, le menu (qui ne s'affichera pas si -state vaut 'disabled').

-takefocus => 0 | 1 | undef

1. N'affecte que le bouton. Indique si celui-ci pourra, ou non, avoir le focus clavier. Par défaut, il ne peut pas.

-tearoff => 0 | 1

1. N'affecte que le menu. Si cette option vaut 0 la ligne en pointillés permettant de détacher le menu du bouton ne sera pas affichée.

-text => chaîne

1. N'affecte que le bouton. Y affiche la chaîne indiquée (cette option est ignorée si l'on utilise l'option -bitmap ou -image).

-textvariable => \$variable

1. N'affecte que le bouton. Celui-ci affichera l'information contenue dans $variable.

-underline => position

1. N'affecte que le bouton. Le caractère à la position indiquée sera souligné. Si le bouton a le focus clavier, et que l'on presse la touche correspondant à ce caractère, le menu apparaîtra.

-width => montant

1. N'affecte que le bouton. Fixe sa largeur.

-wraplength => position

1. N'affecte que le bouton. La position par défaut vaut 0. Cette option détermine la longueur maximale, en distances d'écran, d'une ligne de texte.

XI-B-3 - Options propres au bouton

Les options suivantes n'affectent que la partie bouton du widget et ont la même signification que celles décrites au chapitre 3 : -cursor, -anchor, -bitmap, -borderwidth, -font, -foreground, -height, -highlightbackground, -highlightcolor, -highlightthickness,-image, -justify, -padx, -pady, -relief, -state, -takefocus, -text, -textvariable, -underline, -width et -wraplength.

XI-B-4 - Éléments détachables

Tout widget menu créé peut être « détaché ». Son premier élément est une ligne en pointillés (voir la figure 11.6) dont la sélection le détache et le place dans sa propre fenêtre où il demeurera jusqu'à ce qu'on le ferme à l'aide du gestionnaire de fenêtres.

On peut déplacer le menu sur l'écran, mais pas modifier sa taille. Les autres éléments se comporteront normalement lorsqu'ils sont sélectionnés. Attention : on peut détacher le même menu plusieurs fois.

L'option -tearoff => 0 supprime la possibilité de détachement. On l'indique au moment de la création du menu, et la ligne en pointillés n'y apparaîtra pas.

Cette ligne du menu compte comme un élément : si elle existe, elle utilisera donc l'indice 0 et les éléments à proprement parler commenceront donc à l'indice 1. Si l'on utilise -tearoff => 0, ils commenceront à 0.

Figure 11.6 : Menu avec une ligne de découpage et menu sans cette ligne

XI-B-5 - Options de couleurs

Plusieurs options concernent les couleurs du bouton et du menu : -activebackground, -activeforeground, -background, et -disabledforeground.

-activebackground et -activeforeground affectent toutes les deux le texte/bitmap affiché dans le bouton et l'élément actif du menu. Ce dernier est celui qui se trouve sous le curseur de la souris : il ressort légèrement et sa couleur dépend de ces options. Les effets sur le bouton sont les mêmes que pour un bouton normal.

L'option -background concerne le fond du bouton et du menu entier. -disabledforeground modifie la couleur du texte de tous les éléments du menu désactivés par -state => 'disabled'; elle altère aussi la couleur du texte/bitmap du bouton si celui-ci est désactivé.

XI-B-6 - Indicateur du bouton

Au chapitre 4, nous avons vu que les cases à cocher et les boutons radio disposaient, chacun, de leur propre type d'indicateur. La partie bouton d'un bouton de menu en possède également un, qui peut y être affiché. Cet indicateur est une petite barre en 3D, affichée à droite du texte, du bitmap ou de l'image du bouton (voir la figure 11.7). Habituellement, l'indicateur sert à indiquer que quelque chose de différent se passera lorsque l'on cliquera sur ce bouton. Sa présence est contrôlée par l'option -indicatoron, la même que celle utilisée par les cases à cocher et les boutons radio.

Figure 11.7 : Bouton de menu avec indicateur

Le fait de mettre -indicatoron à 1 ne modifie absolument pas l'aspect du menu. Cette option n'est utile que si vous l'employez comme un menu d'options ou bien de façon totalement inhabituelle.

XI-B-7 - Ajout d'éléments dans un menu

Le contenu de cette section s'applique aux widgets bouton de menu et menu, qui utilisent tous deux l'option -menuitems.

La solution la plus simple pour ajouter des éléments au menu d'un bouton de menu consiste à utiliser l'option -menuitems. La valeur associée à celle-ci est une liste de listes(29) indiquant l'ordre des éléments dans le menu, mais aussi toutes les options de configuration possibles de cet élément. Un exemple illustre au mieux tout cela :

$b_menu = $mw->Menubutton(
-text      => "Bouton de menu",
-menuitems => [
[ 'command' => "Élément 1" ],
[ 'command' => "Élément 2" ],
"-",
[ 'command' => "Élément 3" ],
[ 'command' => "Élément 4" ]
]
);

Nous avons créé le menu de la figure 11.5 à l'aide de ce code.

L'option -menuitems attend une liste de listes. Les sous-listes sont ordonnées et contiennent des informations sur chaque élément du menu. Le premier élément d'une sous-liste est une chaîne précisant le type de l'entrée de menu créée : il peut valoir "command", "radiobutton", "checkbutton", ou "cascade". Le deuxième élément est la chaîne qui s'affichera dans le menu. On peut, à leur suite, ajouter des options qui affecteront ce type d'entrée. On crée un séparateur en utilisant une chaîne à la place d'une sous-liste.

Comme vous pouvez le constater, nous n'avons pas assigné de fonctions de rappel aux éléments du menu : si l'utilisateur en choisit une, rien ne se passera. Pour associer des fonctions de rappel, on devrait modifier l'instruction :

$b_menu = $mw->Menubutton(
-text      => "Bouton de menu",
-menuitems => [
[
'command' => "Élément 1",
-command  => \&choix_1
],
[
'command' => "Élément 2",
-command  => \&choix_2
],
"-",
[
'command' => "Élément 3",
-command  => \&choix_3
],
[
'command' => "Élément 4",
-command  => \&choix_4
]
]
);

On utilise l'option -command pour ajouter les fonctions de rappel et chaque élément est associé à une fonction différente. Associer une fonction de rappel au séparateur n'aurait pas de sens.

Les deux premiers éléments de chaque sous-liste doivent être deux chaînes : l'une indiquant le type de l'entrée du menu, l'autre précisant le texte qui sera affiché pour cette entrée. Même si l'on a l'intention d'afficher un autre texte à l'aide de l'option -label ou d'afficher une image, le deuxième élément de la sous-liste doit être une chaîne.

L'utilisation de "command" et -command peut sembler difficile, mais ce n'est pas le cas. La première est la chaîne indiquant le type de l'élément tandis que la seconde est une option (à laquelle on doit associer une fonction de rappel).

La méthode AddItems permet également d'ajouter des entrées dans un menu :

$b_menu->AddItems(
"command",
-label   => "Élément 1",
-command => \&choix_1
);

La liste des paramètres est légèrement différente (on n'indique que le type, et on doit utiliser l'option -label pour indiquer le texte apparaissant dans le menu), mais toutes les options de chaque entrée sont exactement les mêmes que pour les sous-listes vues plus haut.

Certaines options ne s'appliquent qu'à certains types d'entrées de menu. Elles seront étudiées dans les sections suivantes.

XI-B-8 - Entrées de type commande

Pour l'instant, tous les exemples de menu que nous avons donnés n'utilisent que les types "command" ou des séparateurs pour leurs entrées. Habituellement, on utilisera également l'option -command pour que quelque chose se passe lorsque cette entrée est sélectionnée.

XI-B-9 - Entrées de type bouton radio

On peut placer des boutons radio dans un menu plutôt que dans la fenêtre où ils occuperont de la place. Ils se présentent et agissent exactement comme s'ils se trouvaient dans une fenêtre, mais apparaissent dans le menu. Les règles propres aux boutons radio s'appliquent : il doit toujours y en avoir au moins deux et on les regroupe logiquement à l'aide de la même option -variable => \$variable pour chacun des groupes. La figure 11.8 montre un exemple de leur utilisation dans un menu.

Figure 11.8 : Boutons radio comme éléments d'un menu

Dans l'exemple de la page 102 du chapitre 4, Cases à cocher et boutons radio, nous utilisions des boutons radio pour choisir la couleur de fond de la fenêtre. Nous aurions pu également utiliser ceux-ci dans un menu et économiser ainsi de la place dans notre application :

#!/usr/bin/perl -w
use Tk;
my %us_2_fr = (
red    => 'rouge',
yellow => 'jaune',
green  => 'vert',
blue   => 'bleu',
grey   => 'gris'
);
my $mw = MainWindow->new;
$mw->title("Bouton de menu");
$b_menu = $mw->Menubutton( -text => "Couleur" )->pack(
-side   => 'left',
-anchor => 'n'
);
foreach ( sort keys(%us_2_fr) ) {
$b_menu->radiobutton(
-label    => $us_2_fr{$_},
-command  => \&colorier_fond,
-variable => \$couleur_du_fond,
-value    => $_
);
}

MainLoop;

sub colorier_fond {
print "La couleur du fond est maintenant : $us_2_fr{$couleur_du_fond}\n";
$mw->configure( -background => $couleur_du_fond );
}

La figure 11.9 montre l'aspect de la fenêtre après qu'elle a été agrandie et que le menu a été placé et ouvert.

Figure 11.9 : Utilisation de boutons radio dans un menu pour configurer la couleur de fond

XI-B-10 - Entrées de type case à cocher

Un menu peut également contenir des cases à cocher qui n'apparaîtront donc pas dans la fenêtre. On configure chaque case à l'aide de l'option -command pour qu'elle réalise une certaine action lorsqu'elle est cochée. La figure 11.10 présente un exemple de leur utilisation.

Rappelez-vous de ce que nous avons déjà dit des cases à cocher : chacune d'elles doit posséder sa propre variable (associée par l'option -variable), car elle peut être sélectionnée indépendamment des autres.

XI-B-11 - Entrées de type cascade

Une entrée de type cascade pointe vers un autre menu. Lorsqu'on la sélectionne, un sous-menu surgira à droite du menu actuel. Il s'agit du type d'entrée le plus difficile à mettre en place, car il faut créer un autre menu complet à afficher (le widget menu sera traité dans la prochaine grande section de ce chapitre). La figure 11.11 montre un exemple de ce type d'entrée.

Le sous-menu doit être un fils de la partie menu du bouton de menu, car cela permet à Perl/Tk de conserver une hiérarchie correcte du tout. La meilleure façon de créer un sous-menu consiste à créer avant lui le bouton de menu.

Figure 11.10 : Utilisation de cases à cocher dans un menu (les cases 1 et 5 ont été cochées par l'utilisateur)

Figure 11.11 : Entrée de type cascade dans un widget bouton de menu

$b_menu = $mw->Menubutton(-text => "Mon menu",
-menuitems => [["cascade" => "Sous-menu"]]);
$sous_menu = $b_menu->menu->Menu(-menuitems => [ ... ]);

On utilise la méthode menu du widget bouton de menu pour renvoyer l'entrée puis l'on crée le nouveau menu comme fils de ce widget menu. On peut alors ajouter l'entrée de type cascade au bouton de menu et on la configure pour qu'elle pointe vers ce nouveau sous-menu :

$b_menu->entryconfigure("Sous-menu", -menu => $sous_menu);

Du fait de certains problèmes avec les menus en cascade, il est nécessaire de créer d'abord l'entrée de type cascade puis de la configurer avec le menu qu'elle devra faire apparaître. Voici un programme Perl complet à étudier afin de bien comprendre les mécanismes de ces types de menus. On crée deux sous-menus, l'un contenant des nombres, l'autre des lettres :

#!/usr/bin/perl -w
use Tk;

my $mw = MainWindow->new;
$mw->title("Bouton de menu");

# On crée un bouton de menu et on l'affiche
$b_menu = $mw->Menubutton( -text => "Bouton de menu" )->pack;

# Le sous-menu en cascade sera un fils du menu principal
$sous_menu1 = $b_menu->menu->Menu;
foreach (qw/un deux trois quatre/) {
$sous_menu1->add( 'command', -label => $_ );
}

# Le deuxième sous-menu sera aussi un fils du menu principal
$sous_menu2 = $b_menu->menu->Menu;
foreach (qw/A B C D/) {
$sous_menu2->radiobutton( -label => $_ );
}

# On ajoute maintenant les entrées de type cascade au menu principal
$b_menu->cascade( -label => "Nombres" );
$b_menu->cascade( -label => "Lettres" );

# On configure maintenant ces entrées pour qu'elles pointent sur leur
# sous-menus respectif
$b_menu->entryconfigure( "Nombres", -menu => $sous_menu1 );
$b_menu->entryconfigure( "Lettres", -menu => $sous_menu2 );

MainLoop;

Vous pouvez aussi créer des entrées de type cascade dans un menu qui est lui-même un fils d'un autre menu, mais n'oubliez pas de le créer comme fils de ce dernier.

XI-B-12 - Entrées de type séparateur

Les séparateurs sont des parties non interactives d'un menu. Ils ne servent qu'à offrir une séparation visuelle entre des entrées. Pour en créer, on appelle la méthode separator sur le widget bouton de menu ou l'on utilise une chaîne à la place d'une sous-liste dans la liste associée à -menuitems.

La figure 11.12 montre un séparateur : il est représenté par une ligne continue, à la différence de la ligne de détachement du menu qui est en pointillés (elle n'apparaît pas dans la figure).

Ce menu a été créé à l'aide du code suivant :

$mw->Menubutton(
-tearoff   => 0,
-menuitems => [
[ 'command' => "Élément 1" ],

[ 'command' => "Élément 2" ],
"-",
[ 'command' => "Élément 3" ],
[ 'command' => "Élément 4" ]
]
)->pack;

Figure 11.12 : Séparateur dans un bouton de menu

Nous aurions pu utiliser n'importe quelle chaîne à la place de « - », mais il est préférable de toujours utiliser la même afin qu'elle soit facilement identifiable.

XI-B-13 - Accélérateurs

L'option -accelerator vous permet de placer une chaîne à droite du texte ou de l'image affichée dans le menu. Elle contient habituellement la description d'une combinaison de touches rapide qui lancera la commande associée à l'entrée du menu. Dans la figure 11.13, Élément 1 a la chaîne accélératrice Alt+1 placée à côté de lui. Cet élément de menu a été créé à l'aide de la liste de l'option -menuitems :

[ 'command' => 'Élément 1', -accelerator => "Alt+1" ]

Pour que cette combinaison de touches réalise vraiment l'action voulue, vous devrez utiliser la méthode bind (décrite au chapitre 14, Liaisons d'événements).

Affichage d'une image comme entrée de menu

Chaque entrée de menu est un type de bouton et on peut donc afficher une image à la place du texte. La figure 11.14 montre ce qui se passe lorsque l'on utilise aussi l'option -image. Le code ayant créé ce menu est le suivant(30) :

Figure 11.13 : Menu avec un accélérateur à côté de Choix 1

$image1 = $mw->Bitmap( -file => "/usr/local/lib/xemacs/xemacs-packages/etc/w3/telnet.xbm" );
$mw->Menubutton(
-text      => "Bouton de menu",
-menuitems => [
[ 'command' => "Élément 1" ],
[
'command' => "Élément 2",
-image    => $image1
],
'-',
[ 'command' => "Élément 3" ],
[ 'command' => "Élément 4" ]
]
)->pack( -side => 'left' );

Figure 11.14 : Image affichée à la place du texte

Nous avons déjà traité (chapitre 4 44) de l'utilisation d'icônes et de l'intérêt qu'elles présentent lorsqu'il s'agit de rendre les options plus accessibles à l'utilisateur. Essayez de garder la tête froide et ne mettez pas d'images dans tous les éléments des menus. Trop d'icônes peu parlantes (comme celui de la figure 11.14) rendent l'interface difficile à maîtriser.

XI-B-14 - Assignation d'un menu différent

L'option -menuitems créera par défaut un menu, mais on peut concevoir son propre widget de ce type et indiquer au bouton de menu qu'il doit l'utiliser à la place. Cette façon de procéder pose le problème de la poule et de l'œuf : on doit créer le bouton de menu avant le widget menu qui sera un fils de celui-ci. On utilisera la méthode configure pour assigner ce nouveau menu au bouton de menu. Voici un exemple :

# Création d'un bouton de menu aux entrées inutiles
$menu_princ = $mw->Menubutton(
-text      => "Menu 1",
-menuitems => [
[ 'command' => "Élément 1" ],
[ 'command' => "Élément 2" ],
"-",
[ 'command' => "Élément 3" ],
[ 'command' => "Élément 4" ]
]
)->pack(
-side   => "left",
-expand => 'y',
-fill   => 'both'
);

# Création d'un widget menu, fils de $menu_princ
$menu = $menu_princ->Menu(
-menuitems => [ [ 'command' => "Élément 1" ], [ 'command' => "Élément 2" ], [ 'command' => "Élément 3" ] ] );

# Utilisation de ce menu avec le bouton de menu $menu_princ
$menu_princ->configure( -menu => $menu );

MainLoop;

Comme on l'a déjà précisé, il faut d'abord créer le bouton de menu comme un fils de $mw (la fenêtre principale). On crée des entrées différentes dans le nouveau menu afin de pouvoir constater lequel est utilisé par $menu_princ.

XI-B-15 - Configuration d'un bouton de menu

La méthode cget fournit des informations sur la configuration de toutes les options associées à un bouton de menu. On peut également utiliser configure pour interroger ou modifier ces options. Ces deux méthodes sont expliquées en détail à l'annexe A, Configuration des widgets avec configure et cget.

XI-B-16 - Configuration des entrées d'un bouton de menu

Le widget bouton de menu possède une méthode entrycget identique à celle du widget menu.

$valeur = $b_menu->entrycget( indice, option );

Les paramètres sont un indice et l'option à interroger. Les valeurs d'indices permises sont décrites dans la section consacrée au widget menu, plus loin dans ce chapitre.

La méthode entryconfigure existe aussi pour les boutons de menu. Elle a la même fonction que celle du widget menu :

$b_menu->entryconfigure( indice, [ option ] );

XI-B-17 - Ajout d'entrées à un bouton de menu

La méthode AddItems vous offre un autre moyen d'ajouter des entrées à un menu. Elle placera toujours la ou les nouvelles entrées à la fin du menu et dans l'ordre dans lequel elles sont placées dans la liste. Les paramètres de cette méthode sont les mêmes que ceux utilisés pour l'option -menuitems : ils sont contenus dans plusieurs listes. On n'a pas besoin, ici, d'inclure celles-ci dans une autre liste, car les seuls paramètres passés à cette méthode sont des listes d'entrées. Voici un exemple :

$b_menu = $mw->Menubutton( -text => "Fichier" )->pack;
$b_menu->AddItems(
[ "command" => "Ouvrir", -command => \&ouvrir_fic ],
[ "command" => "Fermer", -command => \&fermer_fic ],
"-", [ "command" => "Quitter", -command => sub { exit } ]
);

Cette utilisation de AddItems n'est qu'une autre façon d'exprimer ce qui suit :

$b_menu = $mw->Menubutton(
-text      => "Fichier",
-menuitems => [
[ "command" => "Ouvrir", -command => \&ouvrir_fic ],
[ "command" => "Fermer", -command => \&fermer_fic ],
"-",
[ "command" => "Quitter", -command => sub { exit } ]
]
)->pack;

Notez les [ ] supplémentaires autour des listes contenant les informations des entrées de menu. Toutes les informations situées entre ces crochets sont exactement les mêmes que les paramètres passés à AddItems.

La méthode command ajoute une entrée de type commande à la fin du menu. Lorsqu'on l'utilise, il faut employer l'option -label pour indiquer le texte à afficher dans le menu. Le code suivant crée le même menu que précédemment :

$b_menu = $mw->Menubutton(-text => "Fichier")->pack;
$b_menu->command(-label => "Ouvrir", -command => \&ouvrir_fic);
$b_menu->command(-label => "Fermer", -command => \&fermer_fic);
$b_menu->separator;
$b_menu->command(-label => "Quitter", -command => sub { exit });

XI-B-18 - Création d'une entrée de type case à cocher

La méthode checkbutton ajoute une case à cocher à la fin du menu. Comme pour command, vous devez utiliser l'option -label pour indiquer le texte qui s'affichera avec cette case dans le menu. Toutes les autres options des entrées de type case à cocher sont les mêmes que celles qui ont été vues plus haut dans ce chapitre, à la section Ajout d'éléments dans un menu. Voici un exemple :

$b_menu = $mw->Menubutton(-text => "Options");
$b_menu->checkbutton(-label => "Confirmer la sortie ?"
-variable => \$confirmer_sortie);

Bien que checkbutton soit une méthode du widget menu, elle fonctionne aussi avec un bouton de menu. Il en va de même pour les méthodes radiobutton, separator, et cascade.

XI-B-19 - Création d'une entrée de type bouton radio

La méthode radiobutton ajoute une entrée de type bouton radio à la fin du menu. On indique le texte à afficher dans le menu à l'aide de l'option -label.

$b_menu->radiobutton(-label=>"Bouton radio");

XI-B-20 - Création d'une entrée de type séparateur

La méthode separator ajoute une ligne de séparation à la fin du menu. Elle ne prend pas de paramètre :

$b_menu->separator();

XI-B-21 - Création d'un menu en cascade

La méthode cascade ajoute une entrée de type cascade à la fin du menu. On indique le texte à afficher avec l'option -label et l'on assigne le menu qui sera ouvert par cette option avec $b_menu->entryconfigure(-menu => $sous_menu).

# On suppose que $menu_supp a déjà été créé
$b_menu->cascade(label => "Menu supplémentaire...");
$b_menu->entryconfigure("Menu supplémentaire...", -menu => $menu_deplacer);

XI-B-22 - Référence à un menu

La méthode menu renvoie une référence au menu utilisé par le bouton de menu. Cela permet de créer des entrées de type cascade avec le menu courant comme parent du menu en cascade. Cela permet aussi d'accéder à toutes les méthodes du widget menu. On pourrait, ainsi, supprimer une entrée d'un menu en faisant $b_menu->menu->delete(1), qui ôterait la deuxième entrée. La section consacrée au widget menu, plus loin dans ce chapitre, détaille les méthodes de ce type de widget.

XI-C - Exemples complets d'utilisation des boutons de menu

Les menus sont des widgets plus compliqués que ceux que nous avions étudiés auparavant, car l'on n'utilise pas toujours le même procédé pour leur ajouter des éléments. Parfois, on emploie simplement l'option -menuitems, d'autres fois on ajoutera les entrées pendant l'exécution du programme, dynamiquement. Cette section présente quelques scripts complets de création de menus.

XI-C-1 - Création d'une barre de menu

Voici le code qui a servi à créer la fenêtre et la barre de menu de la figure 11.4 :

#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new;
$mw->title("Bouton de menu");
$mw->Button(
-text    => "Quitter",
-command => sub { exit; }
)->pack( -side => "bottom" );
my $cadre = $mw->Frame( -relief => 'ridge', -borderwidth => 2 );
$cadre->pack(
-side   => 'top',
-anchor => 'n',
-expand => 1,
-fill   => 'x'
);
foreach (qw/Fichier Édition Options Aide/) {
push( @menus, $cadre->Menubutton( -text => $_ ) );
}

$menus[3]->pack( -side => 'right' );
$menus[0]->pack( -side => 'left' );
$menus[1]->pack( -side => 'left' );
$menus[2]->pack( -side => 'left' );

MainLoop;

On crée d'abord un cadre en travers du haut de la fenêtre et on le place pour qu'il change de taille automatiquement en même temps que celle-ci. Puis, on crée les boutons de menu et on les place dans ce cadre. Ceux-ci ne contiennent pas d'entrées : nous laissons leur ajout en exercice au lecteur.

XI-C-2 - Liste dynamique de documents

Dans certaines situations, on souhaite ajouter ou supprimer dynamiquement des entrées d'un menu. De nombreuses applications mémorisent les derniers documents ouverts et placent les noms de ceux-ci dans le menu « Fichier » pour faciliter leur accès ultérieur. Cet exemple est ainsi conçu, mais le problème est simplifié – un bouton crée simplement un nouveau nom de document que nous affichons dans une zone de saisie afin de visualiser celui qui est en cours d'édition. Avec un bouton de menu et quelques méthodes de sélection du widget menu, on peut créer une solution comme celle-ci :

#!/usr/bin/perl -w
use Tk;
$mw = MainWindow->new;
$mw->title("Documents");

# Création d'un cadre pour placer la barre de menu
# en haut de la fenêtre
$cadre = $mw->Frame( -relief => 'ridge', -borderwidth => 2 )->pack(
-side   => 'top',
-anchor => 'n',
-expand => 1,
-fill   => 'x'
);

# Création du bouton de menu, avec deux entrées :
# 'Nouveau' et un séparateur
$menu_fic = $cadre->Menubutton(
-text      => "Fichier",
-tearoff   => 0,
-menuitems => [
[
"command" => "Nouveau",
-command  => \&nouveau_document
],
"-"
]
)->pack( -side => 'left' );

# Nous commençons par ouvrir le document 1, et on limite
# les numéros de documents à 0-9
# (ce qui permet 10 docs au maximum dans le menu)
$num_doc     = 1;
$max_num_doc = 9;

# Création du bouton qui fera la même chose que
# l'entrée 'Nouveau' du menu
$mw->Button(
-text    => "Nouveau",
-command => \&nouveau_document
)->pack(
-side   => 'bottom',
-anchor => 'e'
);

# La zone de saisie affichera le document courant
# que nous 'éditons'
$saisie = $mw->Entry( -width => 80 )->pack(
-expand => 1,
-fill   => 'both'
);

MainLoop;

# Création du nouveau document en ligne, incrémente le compteur de
# documents. Ajoute ce document au menu et ôte du menu tous les
# documents dépassant le maximum (on supprime d'abord le plus vieux)
sub nouveau_document {
my $nom = "Document $num_doc";
$num_doc++;
push( @courant, $nom );
$menu_fic->command(
-label   => "$nom",
-command => [ \&select_document, $nom ]
);
&select_document($nom);
if ( $#courant > $max_num_doc ) {
$menu_fic->menu->delete(2);
shift(@courant);
}
}

sub select_document {
my ($selection) = @_;
$saisie->delete( 0, 'end' );
$saisie->insert( 'end', "DOCUMENT SÉLECTIONNÉ : $selection" );
}

La figure 11.15 montre la fenêtre produite après la création de trois documents.

Figure 11.15 : Exemple de fenêtre d'historique des documents

XI-D - Le widget Menu

Parfois, on ne souhaite pas utiliser de bouton de menu ; on peut aussi avoir besoin de créer des menus qui s'ouvrent en cascade. On devra quand même créer des menus. On peut aussi souhaiter utiliser un menu ne nécessitant pas de bouton ; l'application peut être, par exemple, configurée pour qu'un menu contextuel surgisse lorsque l'utilisateur presse le bouton droit de la souris au-dessus d'un widget(31).

Il est aussi conseillé de bien connaître les méthodes de manipulation d'un menu, qu'il en soit un par lui-même, ou qu'il soit attaché à un bouton de menu.

XI-D-1 - Création d'un menu de base

La méthode Menu, invoquée sur le parent concerné, crée un widget menu :

$menu = $parent->Menu(options);

Le widget menu est le seul sur lequel on n'utilise pas directement les gestionnaires d'espace. Il s'affiche avec la méthode post :

$menu->post( ... );

Les différents paramètres de post déterminent la façon dont le menu s'affichera. Cette méthode est décrite plus loin dans ce chapitre.

XI-D-2 - Options de création d'un menu

Comme pour tout widget, certaines options affectent l'aspect et le comportement du menu. La plupart des options du widget menu ont été traitées dans la partie de ce chapitre consacrée au bouton de menu, je n'évoquerai donc que celles qui n'existent pas dans ce dernier, ou qui agissent différemment.

-activebackground => couleur

1. Fixe la couleur du fond, derrière l'entrée active du menu.

-activeborderwidth => montant

1. Définit la largeur du contour de l'entrée active.

-activeforeground => couleur

1. Fixe la couleur du texte de l'entrée active du menu.

-background => couleur

1. Définit la couleur de fond du menu entier.

-borderwidth => montant

1. Définit la largeur du contour du menu.

-cursor => nom_curseur

1. Configure le curseur qui s'affichera lorsque le pointeur de la souris sera au-dessus du menu.

-disabledforeground => couleur

1. Définit la couleur du texte d'une entrée de menu désactivée.

-font => nom_fonte

1. Fixe la fonte attribuée au texte du menu.

-foreground => couleur

1. Définit la couleur du texte d'une entrée de menu.

-menuitems => liste

1. Définit une liste d'entrées qui seront créées dans le menu.

-postcommand => fct_rappel

1. Précise la fonction de rappel qui sera invoquée avant que le menu ne s'ouvre.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Fixe l'aspect des contours du menu.

-selectcolor => couleur

1. Définit la couleur de la boîte de sélection des entrées de type case à cocher ou bouton radio.

-takefocus => 0 | 1 | undef

1. Précise si le menu pourra être parcouru à l'aide du clavier. Par défaut, on ne le peut pas.

-tearoff => 0 | 1

1. Détermine si le menu contiendra une ligne de détachement comme première entrée. Par défaut, cette option vaut 1.

XI-D-3 - Styles de menu

Le contour d'un menu est, par défaut, 'raised' et sa largeur fait 2 pixels. Cela fait ressembler le menu à un grand bouton contenant une liste de plusieurs éléments. L'aspect des bords peut être modifié à l'aide de l'option -relief :

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken'| 'solid'

Les menus de la figure 11.16 ont été créés, puis détachés afin d'être conservés à l'écran. Le contour réel du menu est à l'intérieur des décorations du gestionnaire de fenêtres.

Figure 11.16 : Différents aspects du widget menu

La largeur du contour (quelle que soit la valeur de -relief) est modifiable via l'option -borderwidth :

1. -borderwidth => montant

L'augmentation de -borderwidth fait toujours ressortir les différents types de relief, comme on peut le constater à la figure 11.17.

L'option -activeborderwidth affecte l'entrée active du menu (celle ayant le pointeur de la souris au-dessus d'elle) :

1. -activeborderwidth => montant

XI-D-4 - Fontes et curseurs

L'option -font définit la fonte du texte d'un menu :

1. -font => nom_fonte

La figure 11.18 montre un menu utilisant la fonte « lucidasans-14 ». Le chapitre 3 présente les fontes utilisables avec -font.

Figure 11.17 : Différents aspects du widget menu, avec -borderwidth => 4

Figure 11.18 : Menu n'utilisant pas la fonte par défaut

L'option -cursor permet de modifier le curseur qui s'affichera lorsque le pointeur de la souris sera au-dessus du widget menu :

1. -cursor => nom_curseur

Le curseur par défaut d'un menu est différent de celui d'une fenêtre : il s'agit d'une flèche pointant vers la droite (curseur 'arrow'), tandis que celui d'une fenêtre pointe vers la gauche.

XI-D-5 - Appel d'une fonction avant l'affichage du menu

L'option -postcommand permet d'indiquer une fonction qui sera appelée avant l'affichage du menu (via la méthode post, ou en utilisant un bouton de menu) :

-postcommand => fct_rappel

Le format de cette option est le même que celui utilisé pour un widget bouton (décrit au chapitre 2). Le meilleur emploi de cette option consiste à mettre à jour l'état de chaque entrée du menu, si nécessaire. Voici un exemple d'un bouton de menu utilisant cette option pour réactualiser son menu avant de l'afficher :

# Création du bouton de menu
$b_menu = $mw->Menubutton(
-text      => "Fichier",
-tearoff   => 0,
-menuitems => [
[ 'command' => "Ouvrir", -command => \&faire_qqchose ],
[ 'command' => "Sauver", -command => \&faire_qqchose ],
[ 'command' => "Fermer", -command => \&faire_qqchose ],
"-",
[ 'command' => "Quitter", -command => sub { exit } ]
]
)->pack();

# Indicateur de sauvegarde du document
$non_sauvegarde = 0;

# On doit attendre d'avoir créé le bouton de menu pour accéder
# à sa partie menu :
$b_menu->menu()->configure( -postcommand => \&maj_menu );

# Vérifie certains indicateurs du programme et détermine si les
# entrées doivent être, ou non, mises à jour.
sub maj_menu {
if ($non_sauvegarde) {
$b_menu->menu->entryconfigure( 1, -state => "normal" );
}
else {
$b_menu->menu->entryconfigure( 1, -state => "disabled" );
}
}

XI-D-6 - Ajout d'entrées

L'option -menuitems permet de créer le menu et ses entrées en une seule étape. Le format est identique à celui de l'option correspondante du bouton de menu.

Il n'existe pas de méthode AddItems pour le widget menu ; celle-ci n'est disponible que pour les boutons de menu et il faudra alors utiliser l'option -menuitems ou la méthode add (traitée dans la section suivante).

XI-D-7 - Indices de menu

Tout comme les zones de saisie et les widgets texte, les menus disposent d'un mécanisme d'indexation spécifique qui utilise les indices suivants :

n

1. Les entrées d'un menu sont numérotées de 0 à n ; 0 étant la première entrée et n la dernière (la ligne de détachement, si elle est présente, utilise l'indice 0 ; on utilise l'option -tearoff => 0 pour l'empêcher d'apparaître).

"active"

1. Désigne l'entrée de menu active (celle sur laquelle se trouve le pointeur de la souris et qui est mise en évidence). S'il n'y a pas d'entrée active, cet indice signifie la même chose que "none".

"end"

1. Désigne la dernière entrée du menu. Si ce dernier ne contient pas d'entrée, cet indice signifie la même chose que "none".

"last"

1. Synonyme de "end".

"none"

1. Ne fait référence à aucune entrée.

"@y"

1. y est une ordonnée de la fenêtre. Cette forme d'indice fera référence à l'entrée la plus proche de y. "@0" désigne la même entrée que 0.

"motif"

1. Le motif indiqué est le texte à rechercher dans les entrées du menu à partir de l'indice 0. La première entrée satisfaisant cette recherche sera utilisée.

Il n'y a pas vraiment beaucoup de méthodes applicables à un widget menu. Les plus importantes sont probablement entryconfigure et delete, car ce sont celles que vous utiliserez le plus souvent. Rappelez-vous que, si vous utilisez un bouton de menu, vous pouvez directement invoquer une méthode de widget menu par $b_menu->menu->methode() (où methode est une méthode de menu).

XI-D-8 - Configuration d'un widget menu

La méthode cget renvoie la valeur d'une option. Elle ne concerne que celles agissant sur le menu entier ; il existe aussi une méthode entrycget qui renverra les valeurs d'options spécifiques aux entrées. L'annexe A étudie en détail les méthodes configure et cget.

XI-D-9 - Configuration des entrées de menu

La méthode entrycget concerne une option d'une entrée donnée d'un menu et renvoie la valeur de cette option :

$menu->entrycget(indice, -option);

L'indice indique quelle est l'entrée qui sera interrogée par entrycget. -option peut être n'importe quelle option ajoutée par la méthode add (traitée à la section suivante).

La méthode entryconfigure renvoie ou modifie les options de configuration de l'entrée du menu située à l'indice spécifié, exactement comme le fait configure pour le menu entier :

$menu->entryconfigure(indice [, -option, valeur, ...] );

Si l'on ne précise pas d'option, la méthode renvoie les valeurs de toutes les options de configuration de l'entrée située à l'indice indiqué. On peut n'indiquer qu'une option afin d'obtenir sa valeur ou préciser plusieurs couples option/valeur pour l'entrée située à cet indice.

XI-D-10 - Ajout d'entrées dans un menu

En plus de l'option -menuitems, la méthode add permet d'ajouter des entrées à la fin d'un menu. Son premier paramètre est le type de l'entrée à ajouter ; celui-ci peut être "command", "radiobutton", "checkbutton", "separator", ou "cascade". Voici un exemple d'appel de cette méthode :

$menu->add(type [, options ... ] );

Les options affectant chaque entrée du menu sont les mêmes que celles qui sont utilisées par -menuitems : -activebackground, -activeforeground, -accelerator, -background, -bitmap, -command, -font, -foreground, -image, -indicatoron, -label, -menu, -offvalue, -onvalue, -selectcolor, -selectimage, -state, -underline, -value et -variable.

Les résultats des deux portions de code suivantes sont identiques :

# Code 1
# Utilisation de add pour ajouter des entrées de menu
$menu = $mw->Menu;
$menu->add(
"command",
-label   => "Ouvrir",
-command => \&ouvrir_fic
);
$menu->add(
"command",
-label   => "Fermer",
-command => \&fermer_fic
);

# Code 2
# Utilisation d'une liste avec l'option -menuitems
$menu = $mw->Menu(
-menuitems => [
[
"command" => "Ouvrir",
-command  => \&ouvrir_fic
],
[
"command" => "Close",
-command  => \&fermer_fic
]
]
);

Tout nouvel appel à add ajoutera une nouvelle entrée à la fin du menu. Pour en ajouter une ailleurs, on utilisera la méthode insert, traitée à la section suivante.

On peut utiliser l'option -label à la place de -text et -textvariable pour indiquer le texte que l'on souhaite utiliser pour une entrée de menu. On remarquera qu'il n'existe pas d'option -labelvariable. La méthode entryconfigure, traitée plus haut, permet de modifier le texte en question.

XI-D-11 - Insertion d'entrées dans un menu

La méthode insert fonctionne exactement comme add, sauf que la nouvelle entrée sera insérée juste avant celle de l'indice spécifié. On ne peut pas insérer d'entrée avant la ligne de détachement, car cette dernière doit toujours être la première du menu :

$menu->insert(indice, type [, options ...] );

Voici un exemple d'utilisation de cette méthode :

$menu->insert("end", "radiobutton", -label=>"rouge");

XI-D-12 - Suppression d'entrées d'un menu

La méthode delete permet de supprimer des entrées d'un menu :

$menu->delete(indice);
# ou ...
$menu->delete(début, fin);

Pour détruire une seule entrée, on ne précise qu'un indice et, pour en supprimer plusieurs, on indique un intervalle d'indices. Voici quelques exemples :

$menu->delete('last');   # supprime la dernière entrée
$menu->delete(0, 'end'); # supprime toutes les entrées
# (sauf la ligne de détachement)
$menu->delete("Ouvrir"); # supprime l'entrée correspondant à "Ouvrir"

XI-D-13 - Appel d'entrées du menu

La méthode invoke tentera d'invoquer l'entrée de menu située à l'indice spécifié (comme si l'on cliquait dessus avec la souris) :

$menu->invoke(indice);

Le résultat de cet appel dépend du type de l'entrée. Dans le cas d'une commande (type -command), le résultat sera ce que retourne la fonction de rappel associée.

$menu->invoke("rouge");

XI-D-14 - Obtention du type d'une entrée

La méthode type renvoie une chaîne indiquant le type de l'entrée située à l'indice spécifié :

$type = $menu->type(indice);

La chaîne renvoyée sera l'une des suivantes :

- "command"
- "radiobutton"
- "checkbutton"
- "cascade"
- "separator"
- "tearoff"

$type = $menu->type(0); # interroge la première entrée

XI-D-15 - Conversion d'indices

La méthode index renvoie l'indice numérique de l'entrée située à l'indice spécifié :

$menu->index(indice);

L'instruction $menu->index('end') renverra 9 s'il y a 10 entrées dans le menu, et $menu->index("Ouvrir") renverra l'indice numérique de l'entrée « Ouvrir ».

XI-D-16 - Affichage d'un menu

Si vous n'utilisez pas un bouton de menu pour afficher votre menu, vous devez disposer d'un moyen de le faire. Les méthodes post et Popup sont conçues pour cela.

post affichera le menu, qui ne se refermera qu'après avoir sélectionné une entrée ou appelé explicitement la méthode unpost. Popup n'affichera le menu que pendant que le bouton de la souris est pressé (il disparaîtra dès que ce bouton est relâché).

La méthode post nécessite les coordonnées x et y qui lui indiqueront où placer le menu à l'écran. Généralement, on l'affichera à l'endroit où l'utilisateur a cliqué (sauf si l'on souhaite le faire apparaître toujours au même endroit). Voici un exemple affichant le menu lorsque l'on clique avec le bouton droit de la souris dans une boîte de liste :

# Création d'un menu avec -menuitems
$menu = $mw->Menu(
-tearoff   => 0,
-menuitems => [ [ 'command' => "A" ], [ 'command' => "B" ] ]
);

# Création et placement d'une boîte de liste
$bl = $mw->Listbox()->pack();

# Création d'une liaison pour la boîte de liste qui affichera
# le menu lorsque l'on clique avec le bouton droit de la souris
$bl->bind( "<Button-3>", [ \&affiche_menu(), Ev('X'), Ev('Y') ] );

sub affiche_menu {
my ( $liste, $x, $y ) = @_;
$menu->post( $x, $y );
}

On crée un menu simple afin de passer rapidement à ce qui nous intéresse dans cet exemple. J'ai supprimé la ligne de détachement du menu, car je n'aime pas avoir des menus détachés partout (certains utilisateurs, au contraire, apprécient cette possibilité, ne l'oubliez pas). La liaison consiste à faire correspondre l'affichage du menu à l'appui sur le bouton droit de la souris. Ev("X") et Ev("Y") permettent de transmettre les coordonnées écran de l'endroit où le clic a eu lieu (vous trouverez plus d'informations sur ces événements au chapitre 14). La fonction ne fait qu'appeler post avec les paramètres corrects.

Le menu restera affiché même lorsque l'utilisateur relâchera le bouton de la souris. Il se refermera de lui-même lorsqu'une entrée aura été choisie.

L'autre façon d'afficher un menu consiste à appeler Popup. Cette méthode n'affichera le menu que pendant que l'on presse le bouton de la souris, glisse son pointeur vers l'entrée désirée et qu'on le relâche. On peut l'appeler sans paramètre, ou avec une ou deux options : -popover et -popanchor. L'appel suivant :

$menu->Popup();

affiche le menu au centre de l'écran : ce n'est pas très pratique, aussi je vous conseille d'utiliser au moins l'option -popover. Celle-ci a pour valeur la chaîne "cursor" ou une référence à un widget. Le menu sera alors centré sous le pointeur de la souris ou sur le widget. Voici quelques exemples :

$menu->Popup(-popover => "cursor"); # centre le menu sous le pointeur
$menu->Popup(-popover => $bouton); # centre le menu sur $bouton
$menu->Popup(-popover => $liste); # centre le menu sur $liste

Vous remarquerez que nous n'utilisons pas la syntaxe \$liste, car les scalaires spécifiés contiennent déjà une référence à un widget.

La deuxième option, -popanchor, affecte la façon dont le menu sera positionné par rapport au paramètre -popover (ou à l'écran entier, si cette dernière option n'est pas utilisée). -popanchor a pour valeur l'une des chaînes de caractères suivantes : "nw", "ne", "sw", ou "se" (32) . Ainsi, si l'on souhaite afficher le menu de façon à ce que son coin supérieur gauche soit placé à l'endroit où le clic a eu lieu, on utilisera l'instruction suivante :

$menu->Popup(-popover => "cursor", -popanchor => "nw");

C'est comme cela que je crée des menus contextuels associés aux widgets. Un exemple complet est donné plus bas.

XI-D-17 - Affichage d'un menu en cascade

Si votre menu possède un menu en cascade qui lui est associé, on doit utiliser la méthode postcascade pour afficher ce dernier :

$menu->postcascade(indice);

Cette méthode fermera les éventuels sous-menus déjà ouverts et affichera le menu en cascade associé à l'entrée située à l'indice spécifié. Si celle-ci n'est pas une entrée de type cascade, le seul effet sera de fermer les autres sous-menus.

$menu->postcascade("sous-menu");

XI-D-18 - Fermer un menu

Si vous avez affiché le menu à l'aide de post, vous le fermerez avec unpost pour le faire disparaître de l'écran :

$menu->unpost();

Cela ôtera $menu de la fenêtre. Si un menu en cascade était affiché à partir de ce menu, il sera lui aussi fermé. Cette méthode ne fonctionne pas sur les systèmes Win32, car ceux-ci ont leur propre mécanisme de fermeture des menus.

XI-D-19 - Obtention de la position d'une entrée

La méthode yposition renvoie une chaîne représentant un nombre décimal donnant l'ordonnée du pixel le plus haut de l'entrée située à l'indice spécifié :

$emplacement = $menu->yposition(indice);

XI-D-20 - Exemple de menu contextuel

On souhaite parfois disposer d'un menu contextuel(33). Un canevas est parfait pour utiliser un tel menu ; il y a souvent tant d'actions possibles qu'associer des menus différents aux différents types d'objets d'un canevas permet de simplifier sa manipulation.

Pour créer un menu contextuel, il suffit de créer un widget menu, d'y ajouter les entrées souhaitées, et d'utiliser l'option -command pour que celles-ci réalisent des tâches utiles. Pour afficher le menu lorsque l'on presse le bouton droit de la souris sur l'objet désiré, on utilise la méthode bind :

$objet->bind("<Button-3>",
sub { $menu->Popup(-popover => 'cursor'); });

Un tel menu peut servir à supprimer ou modifier l'élément sélectionné d'une boîte de liste.

XI-E - Le widget Optionmenu

Un menu d'options est une implantation particulière du bouton de menu. La différence est que le premier positionne automatiquement l'option -indicatoron à 1, supprime la ligne de détachement et gère l'affichage du menu d'une façon légèrement différente.

Un menu d'options est utile lorsque l'on veut offrir le choix à l'utilisateur sans gaspiller l'espace nécessaire à une boîte de liste et une barre de défilement ou plusieurs boutons radio. Pour ajouter des éléments, on utilise l'option -options à la place de -menuitems ou des autres méthodes permettant d'ajouter des entrées à un menu ou à un bouton de menu.

XI-E-1 - Création et configuration d'un menu d'options

Un menu d'options est créé avec la méthode Optionmenu :

$menu_options = $mw->Optionmenu( ... );

Toutes les options possibles pour un bouton de menu sont également disponibles pour ce type de menu. Les options suivantes lui sont spécifiques : -textvariable, -options, -variable, -command.

L'option -options a pour valeur une liste anonyme pouvant contenir des chaînes ou des sous-listes anonymes. Cela permet de sélectionner une entrée unique dans une liste. Le texte affiché est celui de l'entrée choisie. L'option -textvariable permet d'indiquer où sera stocké ce texte, et l'option -variable sera utilisée pour stocker une valeur différente de celle affichée par le menu. Les valeurs affichées et stockées sont indiquées par l'option -options. Si la valeur affichée est la même que celle qui est stockée, on utilise une liste simple :

-options => [1, 2, 3, 4, 5, 6], -textvariable => $nombre

Si la valeur stockée est différente de celle affichée, on utilise le code suivant :

-options => [ ["un", 1],     ["deux", 2], ["trois", 3],
["quatre", 4], ["cinq", 5], ["six",6]
],
-textvariable => \$affiche,
-variable     => \$nombre

Dans cet exemple, les mots sont affichés dans le menu et l'un d'eux, sitôt sélectionné, est placé dans $affiche). Les valeurs stockées (dans $nombre) sont des entiers. La valeur non affichée peut être un scalaire quelconque.

L'option -command assigne une fonction de rappel qui sera exécutée lorsqu'un choix aura été fait. Le paramètre par défaut de celle-ci est la variable stockée pour le menu, quelles que soient les valeurs de -textvariable et -variable. Les fonctions de rappel servent à réaliser une action selon l'entrée choisie dans le menu.

Voici un script complet vous permettant d'appréhender la plupart des fonctionnalités utiles d'un menu d'options :

#!/usr/bin/perl -w
use Tk;

$mw = MainWindow->new;
$mw->title("Menu d'options");
$affichage = "dix";
$mw->Optionmenu(
-command      => sub { print "Params : @_\n"; print "dans le menu d'options\n"; },
-textvariable => \$affichage,
-variable     => \$stockage,
-options      => [ [ "dix", 10 ], [ "vingt", 20 ], [ "trente", 30 ] ]
)->pack();

MainLoop;

Il est préférable de créer également un widget étiquette pour que l'utilisateur connaisse le but du menu d'options (c'est ce que l'on a fait pour la figure 11.19).

Figure 11.19 : Menu d'options avec une étiquette placée à gauche

Les seules méthodes disponibles pour les menus d'options sont cget et configure. La première renvoie la valeur d'une option, tandis que la deuxième permet d'obtenir ou de modifier la valeur des options du widget. Ces deux méthodes sont détaillées à l'annexe A.

XI-F - Essayez et amusez-vous !

- Créez un unique menu contenant deux entrées : Désactivé et Normal. Lorsque l'on presse avec le bouton droit de la souris sur un widget, ce menu surgira ; si l'on sélectionne l'entrée Désactivé, cela désactivera le widget qui sera réactivé en choisissant l'entrée Normal.

- Créez une application comportant deux boutons de menu. Faites en sorte que les entrées du premier ajoutent et suppriment différents types d'entrées du second.

- Reprenez toutes les suggestions des chapitres précédents et ajoutez-leur des menus. Faites en sorte d'avoir au moins un menu Fichier avec une entrée Quitter. Faites preuve d'imagination !

XII - Cadres

Un widget cadre est, au premier abord, peu excitant. Il ne fait qu'offrir un emplacement où d'autres widgets pourront se trouver ; cela semble anodin, mais ne l'est pas. Les gestionnaires d'espace de Perl/Tk présentent quelques limites (voir le chapitre 2, Gestion de l'espace), et les cadres peuvent les aider à mieux fonctionner. Dans ce chapitre, nous utiliserons pack en tant qu'exemple de gestionnaire d'espace, car c'est le plus employé, mais ne négligez pas que les règles générales d'utilisation d'un cadre s'appliquent aussi aux autres gestionnaires.

La raison d'être d'un widget cadre est de contenir d'autres widgets, et de s'adapter à la taille de ceux-ci. Un cadre ne contenant pas de widget est invisible. Si la taille des widgets qu'il contient est modifiée pour une raison quelconque, le cadre tentera d'adapter ses dimensions en conséquence (en devenant plus grand ou plus petit)(34).

XII-A - Création d'un cadre

On utilise la méthode Frame sur le widget parent du cadre à créer :

$cadre = $parent->Frame( [ option => valeur, ... ] )->pack();

$parent est soit une fenêtre, soit un widget de premier niveau, soit un autre cadre(35). Après sa création, le cadre peut devenir parent d'autres widgets ; pour cela, il n'a pas besoin d'avoir été placé à l'écran. On peut très bien placer des widgets dans un cadre : ceux-ci ne s'afficheront que lorsqu'on aura demandé au cadre lui-même de se placer à l'écran.

Comme avec tous les autres widgets de Perl/Tk, les options utilisées avec la méthode Frame modifient l'aspect du cadre dans la fenêtre. Le widget cadre n'accepte que quelques options, toutes très simples, présentées ci-dessous :

-background => couleur

1. Fixe la couleur du fond du cadre (il n'y a pas de premier plan).

-borderwidth => montant

1. Configure la largeur du contour du cadre. Par défaut, celle-ci est de 0 pixel.

-class => nom_classe

1. Indique quelle sera la classe associée au cadre dans la base de données des options (voir la section concernant cette base de données du chapitre 16, Méthodes communes à tous les widgets). Cette option n'est, en fait, pas spécifique aux cadres et tous les widgets peuvent l'utiliser.

-colormap => "new" | $fenetre

1. Indique si le cadre doit utiliser une nouvelle palette de couleurs ou en partager une avec un autre widget de l'application. Par défaut, cette option n'est pas définie, ce qui implique que le cadre utilisera la même palette que son parent.

-container => 0 | 1

1. Cette option n'existe qu'avec Tk8.0. Si elle est vraie, le cadre servira à contenir une application imbriquée.

-cursor => nom_curseur

1. Modifie le curseur utilisé lorsque le pointeur de la souris se trouvera au-dessus du cadre.

-height => montant

1. Fixe la hauteur du cadre. montant est exprimé en distances d'écran.

-highlightbackground => couleur

1. Configure la couleur qu'aura le rectangle de focus lorsque le cadre n'aura pas le focus clavier.

-highlightcolor => couleur

1. Configure la couleur qu'aura le rectangle de focus lorsque le cadre aura le focus clavier. Par défaut : noir.

-highlightthickness => montant

1. Fixe l'épaisseur du rectangle de focus ; par défaut, celle-ci est nulle.

-label => chaîne

1. Ajoute une étiquette au cadre. Le texte affiché sera chaîne.

-labelPack => [ options de placement ]

1. Précise les options de placements que l'étiquette devra utiliser.

-labelVariable => \$variable

1. Indique la variable qui contiendra le texte de l'étiquette.

-relief => 'flat' |'groove' | 'raised' |'ridge' | 'sunken' |'solid'

1. Modifie l'apparence du contour du widget. La valeur par défaut est 'flat'.

-takefocus => 0 | 1 | undef

1. Indique si le cadre pourra recevoir le focus. Par défaut, il ne le peut pas.

-visual => "type #"

1. Utilisée avec X Window, cette option modifie le nombre de couleurs disponibles pour votre application. Elle n'a pas d'effet avec les systèmes Win32.

-width => montant

1. Fixe la largeur initiale du cadre, exprimée en distances d'écran.

XII-B - Styles des cadres

Comme pour tous les widgets, les options -relief et -borderwidth modifient l'aspect du contour d'un cadre. La valeur par défaut de -relief est 'flat' et celle de -borderwidth est 0. Si l'on souhaite que le cadre ait un contour visible, on doit s'assurer que cette dernière option soit supérieure à 0 mais, dans tous les cas, vous ne verrez rien tant que le cadre ne contiendra pas de widget. Ainsi, pour les exemples de la figure 12.1, on a ajouté une étiquette et une zone de saisie afin de mettre en évidence le contour de chaque cadre. On notera que l'étiquette a été créée à l'aide de Label et que nous n'avons pas utilisé l'option -label, étudiée dans la section suivante.

Figure 12.1 : Les différents reliefs d'un cadre, avec une largeur de 2 et de 5

L'utilisation de -relief et -borderwidth est un bon moyen de mettre en évidence un cadre dans une fenêtre car, lorsque celle-ci contient beaucoup de widgets, il n'est pas facile de mémoriser où se trouvent les cadres. Pour contourner ce problème, j'utilise souvent ces options avec les valeurs 'groove' et 5, respectivement.

XII-B-1 - Ajout d'une étiquette à un cadre

On ajoute une étiquette à un cadre avec l'option -label, qui prend une chaîne comme valeur :

$mw->Frame(-label => "Mon cadre :")->pack;
...
# On peut aussi configurer l'étiquette plus tard :
$cadre->configure(-label => "Mon cadre :")->pack;

Par défaut, l'étiquette est placée en haut du cadre, centrée dans le sens de la largeur, comme le montre la figure 12.2. On a placé un bouton classique dans le cadre afin que ce dernier soit visible. Le cadre lui-même utilise les options -relief => 'groove' et -borderwidth => 2 afin de bien délimiter son contour.

Figure 12.2 : Cadre avec une étiquette placée à son emplacement par défaut

L'emplacement de l'étiquette dans le cadre dépend de l'option -labelPack. Celle-ci a pour valeur un tableau anonyme contenant les options de placement de l'étiquette :

-labelPack => [ -side => 'left', -anchor => 'w']

On notera qu'il n'existe pas d'option -labelGrid et qu'il faut utiliser pack pour placer les widgets dans un cadre utilisant l'option -label sinon l'application ne fonctionnera pas correctement (voire pas du tout).

Au lieu d'utiliser une chaîne littérale pour l'étiquette, on peut utiliser une variable à l'aide de l'option -labelVariable :

-labelVariable => \$var_texte

Lorsque le contenu de \$var_texte sera modifié, l'étiquette du cadre le sera aussi.

Dès que l'on utilise -label ou -labelVariable, une étiquette est créée et placée dans le cadre. Ces options peuvent être employées dans l'appel initial à Frame ou plus tard, à l'aide de $cadre->configure( ... ). Dans ce dernier cas, l'étiquette sera placée au-dessus de tous les autres widgets contenus par le cadre.

XII-C - Les cadres ne sont pas interactifs

Un cadre n'est pas un widget interactif ; par défaut, il n'accepte aucune entrée de la part de l'utilisateur. Les widgets qu'ils contiennent le peuvent, mais pas le cadre lui- même. Comme toujours, la gestion du focus est contrôlée par l'option -takefocus :

-takefocus => 0

Pour les cadres, cette option vaut 0. Si, pour une raison quelconque, vous voulez que l'utilisateur puisse interagir avec le cadre, il suffit d'utiliser -takefocus => 1.

XII-D - Problèmes avec les palettes de couleurs

Lorsque plusieurs applications s'exécutent simultanément, et que l'on lance le navigateur de Netscape, on note parfois que les couleurs sont perturbées. Lorsque l'on passe d'une application au navigateur, les couleurs des autres applications changent soudainement. Si l'on repasse du navigateur à une application, ce sont celles du navigateur qui sont affectées. Ces problèmes sont dus au fait que le navigateur est un gouffre à couleurs ; il a demandé plus de couleurs que le système d'exploitation ne peut en fournir à l'ensemble des applications employées sur le moment. Celui-ci doit alors modifier la palette de couleurs des applications afin de permettre à celle qui est active de disposer des couleurs voulues, aux dépens des autres. Cette palette sert au système d'exploitation pour mémoriser qui utilise quelles couleurs.

Les applications Perl/Tk peuvent aussi être multicolores - on peut donner dans le clinquant et attribuer une couleur à chaque bouton. Cela posera des problèmes si d'autres applications en cours d'exécution utilisent aussi un grand nombre de couleurs ; en ce cas, Perl/Tk basculera en mode monochrome. Si l'on n'apprécie pas ce comportement, l'option -colormap permet de s'en affranchir. Elle prend comme paramètre le mot "new" ou une référence à une autre fenêtre. Dans le premier cas, l'application créera sa propre palette. Si vous utilisez l'option avec une autre fenêtre, les deux fenêtres partageront la palette. Mais il y a une entourloupette, sous la forme de l'option -visual. L'option -visual prend pour valeur une chaîne contenant un mot-clé et un nombre ; par exemple :

-visual => "staticgrey 2"

Le mot-clé peut être : staticgrey, greyscale, staticcolor, pseudocolor, truecolor, ou directcolor. Le nombre sert à préciser le nombre de couleurs utilisées (2 = noir et blanc).

Lorsque que l'on utilise -colormap pour partager la palette de couleurs entre deux fenêtres, celles-ci doivent avoir la même option -visual. Ceci signifie que -visual doit être indéfini pour les deux fenêtres, ou contenir la même valeur. La méthode configure ne permet pas de modifier -colormap, ni -visual.

Vous rencontrerez aussi les options -colormap et -visual au chapitre 13, Widgets de premier niveau. Nous les avons d'abord évoquées ici, car c'est la première fois que nous en avons l'utilité. Pour être honnête, vous n'utiliserez probablement jamais ces deux options avec quelque widget que ce soit.

XII-D-1 - L'option magique -class

Cette option vous permet de créer un cadre dont le type ne sera pas la classe Frame.

Il suffit de lui fournir une chaîne représentant un identificateur unique de classe :

-class => "Mon_cadre"

Le chapitre 13 traite en détail des classes (et de leur utilité).

XII-E - Méthodes du widget Frame

Seules cget et configure sont disponibles pour un widget cadre. Celles-ci sont décrites à l'annexe A, Configuration des widgets avec configure et cget.

XII-F - Essayez et amusez-vous !

Lorsque l'on utilise la méthode Scrolled, on emploie des cadres sans même le savoir. Le widget ainsi créé est placé avec ses barres de défilement dans un cadre afin de pouvoir se comporter comme un widget unique. Voici quelques autres suggestions d'utilisation des cadres :

- créez plusieurs lignes contenant, chacune, une étiquette et une zone de saisie. Chaque « ligne » doit être dans son propre cadre pour apparaître proprement ;

- placez une image contre le bord de la fenêtre de votre application. Placez des widgets dans un cadre (à gauche ou à droite) et l'image de l'autre côté ;

- placez une boîte de liste avec une barre de défilement dans votre application, un cadre contenant trois boutons (OK, Annuler, Appliquer) en bas de la fenêtre ; ajoutez à droite un cadre contenant deux boutons (Supprimer, Ajouter) permettant de manipuler la liste. Avec les cadres, vous pouvez regrouper des boutons dans une zone unique au lieu de les placer avec d'autres boutons n'ayant pas de points communs avec eux.

XIII - Widgets de premier niveau

Toute application Perl/Tk contient au moins un widget de premier niveau (toplevel widget). Lorsque l'on appelle la méthode new sur un widget MainWindow, on crée un widget de premier niveau sans même le savoir. Une application doit reposer sur un widget de premier niveau de type MainWindow, mais peut aussi, si nécessaire, contenir d'autres widgets de ce type.

Un widget MainWindow est particulier, car il s'affiche automatiquement lorsque l'on appelle la fonction MainLoop(). Les autres widgets de premier niveau doivent être explicitement affichés par une instruction dans votre code.

Voici deux exemples d'utilisation de ce type de widget :

- afficher une fenêtre d'informations, munie d'un bouton « Fermer »(36) (voir le premier exemple de la section suivante) ;

- déclencher la récupération de données par une action de l'utilisateur (appui sur un bouton, par exemple).

Tous les widgets de premier niveau ont le même comportement et leur aspect correspond aux conventions du système sous lequel l'application s'exécute. Chacun d'eux peut contenir d'autres widgets et/ou plusieurs groupes de widgets (ils peuvent, par exemple, être regroupés dans un cadre).

La suite de ce chapitre traitera de l'utilisation des widgets de ce type et des options permettant de modifier leur comportement.

XIII-A - Création d'un widget de premier niveau

La méthode Toplevel, appelée sur le widget parent, permet de créer un widget de premier niveau, habituellement un MainWindow (vous savez déjà que, pour créer une fenêtre principale, il faut écrire MainWindow->new()). L'élément renvoyé est une référence au widget ainsi créé ; elle vous permet de le configurer, de lui appliquer des méthodes et d'y placer des éléments. Voici un exemple simple :

use Tk;
$mw = MainWindow->new;
$mw->title("Fenêtre principale");
$mw->Button(
-text    => "Premier niveau",
-command => \&premier_niveau
)->pack();

MainLoop;

sub premier_niveau {
if ( !Exists($pn1) ) {
$pn1 = $mw->Toplevel();
$pn1->title("Premier niveau");
$pn1->Button(
-text    => "Fermer",
-command => sub { $pn1->withdraw }
)->pack;
}
else {
$pn1->deiconify();
$pn1->raise();
}
}

Lorsqu'on lance ce programme, et que l'on presse le bouton « Premier niveau » de la fenêtre principale, le widget de premier niveau est créé (si nécessaire) et affiché. Presser le bouton « Fermer » rend le widget invisible. On doit tester l'existence de celui-ci avant de l'afficher car, s'il existe, on ne doit pas le recréer et, s'il n'existe pas, on ne doit pas tenter de l'afficher.

Lorsque le bouton « Fermer » est pressé, le widget de premier niveau est effacé. Il existe encore, mais n'est plus visible ; cela permettra de gagner du temps lorsqu'il faudra le réafficher. La méthode withdraw permet également d'éviter que le widget ne s'affiche tandis qu'on y place d'autres widgets. Il suffit d'utiliser la méthode withdraw, de placer les widgets internes, puis de réafficher le widget de premier niveau à l'aide de deiconify et raise.

Les options suivantes sont utilisables lors de l'appel à Toplevel ou via la méthode configure.

-background => couleur

1. Configure la couleur du fond du widget. Ce fond peut être masqué par les widgets placés dans le widget de premier niveau s'il est entièrement rempli.

-borderwidth => montant

1. Fixe la largeur du contour du widget. Par défaut, elle vaut 0.

-class => nom_classe

1. Précise le nom de la classe utilisée avec la base de données des options de ce widget.

-colormap => "new" | $fenetre

1. Indique si le widget doit utiliser une nouvelle palette de couleurs, ou partager celle d'un autre widget de l'application. Par défaut, cette option est indéfinie.

-container => 0 | 1

1. Ne fonctionne qu'avec TK8.0. Si cette option est vraie, la fenêtre contiendra une application intégrée (voir l'option -use).

-cursor => nom_curseur

1. Fixe le type de curseur qui sera utilisé lorsque le pointeur de la souris sera au-dessus du widget de premier niveau.

-height => montant

1. Précise la hauteur du widget.

-highlightbackground => couleur

1. Indique la couleur du rectangle entourant le widget lorsqu'il ne détient pas le focus clavier.

-highlightcolor => couleur

1. Indique la couleur du rectangle entourant le widget lorsqu'il détient le focus clavier.

-highlightthickness => montant

1. Fixe l'épaisseur du rectangle de focus. Par défaut, cette option vaut 0.

-menu => $menu

1. Cette option n'existe qu'avec Tk8.0. Elle indique que le widget de premier niveau utilisera le menu $menu et que celui-ci sera placé en haut de la fenêtre.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

1. Modifie l'apparence du contour du widget. La valeur par défaut est 'flat'.

-screen => nom_écran

1. Fixe l'écran sur lequel placer le widget. La valeur de cette option ne peut être modifiée par la méthode configure.

-takefocus => 0 | 1 | undef

1. Précise si le widget de premier niveau pourra recevoir le focus clavier. Sa valeur par défaut, 0, indique qu'il ne le peut pas.

-use => $id_fenetre

1. Cette option n'existe que pour Tk8.0. $id_fenetre doit être une chaîne représentant l'identificateur, exprimé en hexadécimal, de la fenêtre à intégrer dans le widget de premier niveau. Pour utiliser cette option, -container doit valoir 1.

-visual => "type #"

1. Avec le système X Window, cette option permet de modifier le nombre de couleurs disponibles pour l'application. Elle est sans effet avec les systèmes Win32.

-width => montant

1. Fixe la largeur initiale du widget.

XIII-B - Méthodes des widgets de premier niveau

Les sections suivantes énumèrent et détaillent les méthodes des widgets de premier niveau (il est important de noter que toutes ces méthodes s'appliquent aussi au widget MainWindow, qui n'est qu'un widget de premier niveau particulier). Nous n'avons guère rencontré ces méthodes jusqu'à présent, car les widgets de premier niveau sont différents de ceux dont nous avons déjà traité. Rappelez-vous également que la plupart de ces méthodes ont été conçues pour être utilisées dans un environnement de fenêtrage Unix, et que certaines d'entre elles seront considérées comme étant « sans effet sur un système Win32 ». Un grand nombre de ces méthodes n'ont pas grand intérêt pour les applications Perl/Tk ordinaires, mais nous les décrirons par souci d'exhaustivité.

Plusieurs d'entre elles modifient les propriétés du gestionnaire de fenêtres, souvent décrites par des constantes comme WM_PROPRIETE_PROP. Ces propriétés sont traditionnellement associées au système X Window, mais certaines s'appliquent également aux systèmes Win32. Une documentation de méthode n'indiquera rien sur le système lorsqu'elle fonctionnera partout : si elle ne s'applique qu'à l'un ou l'autre (ou ne fonctionne qu'imparfaitement sur un système), cela sera explicitement indiqué.

XIII-B-1 - Configuration d'un widget de premier niveau

Les méthodes cget et configure permettent de configurer et d'obtenir les valeurs des options d'un widget de premier niveau. L'annexe A détaille leur utilisation.

XIII-B-2 - Donner une taille à un widget de premier niveau

La méthode geometry permet de définir ou d'obtenir une chaîne de placement. Celle-ci précise la taille et l'emplacement d'une fenêtre à l'écran. Ce concept a vu le jour sur les systèmes Unix et, à première vue, une telle chaîne est énigmatique. Voici une expression rationnelle décrivant une chaîne complète :

^=?(\d+x\d+)?([+-]\d+[+-]\d+)?$

Le signe égal peut être omis (et l'est généralement). La première partie de l'expression, (\d+x\d+) correspond à la largeur et la hauteur (dans cet ordre) séparées par le caractère x. Par défaut, ces deux mesures sont exprimées en pixels et en nombre de mailles si la fenêtre est maillée avec la méthode grid décrite plus loin. La dernière partie de l'expression correspond aux coordonnées x et y de l'emplacement de l'écran où sera placé le widget de premier niveau. Ces coordonnées sont toujours exprimées en pixels. Voici quelques exemples de chaînes de placement :

300x300       # largeur et hauteur de 300 pixels
300x450       # largeur de 300 pixels, hauteur de 450 pixels
300x450+0+0   # largeur de 300, hauteur de 450,
# placée dans le coin supérieur gauche
300x450-0-0   # largeur de 300, hauteur de 450,
# placée dans le coin inférieur droit
300x450+10+10 # largeur de 300, hauteur de 450,
# placée à 10 pixels du coin supérieur gauche
+0+0          # fenêtre de taille 'normale',
# placée dans le coin supérieur gauche

Lorsque geometry est appelée sans paramètre, elle renvoie la chaîne de placement courante. On peut également utiliser un paramètre représentant un nouvel emplacement pour modifier celui du widget. Pour configurer immédiatement la taille et la position de la fenêtre, on écrirait :

$mw = MainWindow->new();
$mw->geometry("300x450+0+0");

Si l'on ne précise que la largeur et la hauteur, le gestionnaire de fenêtres décidera de l'emplacement du widget. Si l'on ne précise que l'emplacement, la taille de la fenêtre dépendra des widgets qu'elle contient, mais elle sera placée aux coordonnées x et y indiquées.

Si l'on donne à geometry une chaîne vide en paramètre, la fenêtre reprendra sa taille normale :

$premier_niveau->geometry("");

XIII-B-3 - Taille maximale

La méthode maxsize permet de limiter la taille maximale d'une fenêtre ; elle attend deux paramètres. L'instruction suivante limite la taille du widget $premier_niveau à une largeur de 300 et à une hauteur de 450 pixels :

$premier_niveau->maxsize(300,450);

Si cette méthode est appelée sans paramètre, elle renvoie une chaîne vide ou une liste de deux éléments représentant la largeur et la hauteur courantes. Si les deux paramètres sont des chaînes vides, la limitation de la taille est annulée.

XIII-B-4 - Taille minimale

La taille minimale de la fenêtre peut également être précisée à l'aide de la méthode minsize. La fenêtre sera alors toujours d'une taille au moins égale à celle spécifiée :

$premier_niveau->minsize(100,150);

garantit que la largeur du widget sera toujours au moins égale à 100 pixels, et que sa hauteur minimale est de 150 pixels.

Si cette méthode est appelée sans paramètre, elle renvoie une chaîne vide ou une liste de deux éléments représentant la largeur et la hauteur courantes. Si les deux paramètres sont des chaînes vides, la taille minimale est annulée.

XIII-B-5 - Limitation des changements de taille

La méthode resizable permet de contrôler la modification de la largeur et/ou de la hauteur d'une fenêtre :

$premier_niveau->resizable(1, 0);
($modif_largeur, $modif_hauteur) = $premier_niveau->resizable();

Une valeur de 1 signifie que la dimension correspondante est modifiable, 0 indiquera une taille fixe. Sans paramètre, resizable renvoie une liste de deux éléments valant 1 ou 0. Le premier indique si la largeur pourra être modifiée, le second concerne la hauteur. Par défaut, la taille d'une fenêtre est modifiable dans les deux directions.

XIII-B-6 - Utilisation de contraintes sur la taille

La méthode aspect permet de forcer la fenêtre à conserver une largeur et une hauteur dans une plage de proportions donnée :

$premier_niveau->aspect( [ numMin, denMin, numMax, denMax ] ) ;

Cette méthode réalise des opérations subtiles et vous ne l'utiliserez probablement jamais. Si vous le faites, essayez différentes valeurs (en commençant par celles de l'exemple ci-dessous) pour obtenir l'effet recherché. Les quatre paramètres, s'ils sont fournis, servent à définir un intervalle de proportions largeur/hauteur pour le widget.

Cet intervalle sera compris entre numMin/denMin et numMax/denMax :

($nMin/$dMin) < largeur/hauteur < ($nMax/$dMax)

Si aucun paramètre n'est fourni, aspect renvoie une chaîne vide (indiquant qu'il n'y a pas de contrainte sur les proportions de la fenêtre) ou un tableau de quatre éléments précisant les contraintes existantes :

($nMin, $dMin, $nMax, $dMax) = $premier_niveau->aspect;

Vous pouvez également utiliser quatre chaînes vides comme paramètres afin d'annuler les restrictions sur les proportions de la fenêtre. Vous constaterez notamment que les effets de l'instruction $premier_niveau->aspect(1,2,3,1) sont subtils.

XIII-B-7 - Configuration du titre

Le texte situé dans la barre de titre est modifiable par la méthode title :

$premier_niveau->title("Voici le titre");

Si la fenêtre est visible, le nouveau titre apparaîtra immédiatement. Sans paramètre, cette méthode renvoie le titre courant. Dans le cas d'un système X Window, le titre par défaut d'une fenêtre est le nom du programme dont la première lettre est mise en majuscule. Pour Microsoft Windows, le titre par défaut est toujours Toplevel.

XIII-B-8 - Affichage d'un widget de premier niveau

La méthode deiconify affiche le widget ou restaure la fenêtre et supprime l'icône immédiatement à condition que la fenêtre ait déjà été affichée une première fois. Si elle a été effacée, l'instruction $premier_niveau->raise() doit également être utilisée afin d'afficher correctement la fenêtre.

Appelée sans paramètre, la méthode raise place le widget de premier niveau au-dessus de toutes les autres fenêtres de l'application :

$premier_niveau->raise();

Elle permet également de placer le widget au-dessus d'un autre widget de premier niveau :

$premier_niveau->raise($autre_premier_niveau);

Il est parfois nécessaire d'utiliser à la fois deiconify et raise pour que la fenêtre apparaisse à l'écran.

XIII-B-9 - Effacement d'un widget de premier niveau

Lorsque l'on crée une fenêtre, il est préférable qu'elle reste invisible tant que l'on n'y a

pas placé les widgets. Pour cela, on utilise la méthode withdraw :

$premier_niveau->withdraw();

Si la fenêtre est visible, le gestionnaire de fenêtre l'oubliera jusqu'à ce qu'elle soit désiconifiée.

XIII-B-10 - Mettre en icône un widget de premier niveau

La méthode iconify met le widget sous forme d'icône :

$premier_niveau->iconify();

Il ne s'agit pas de la même opération que celle réalisée par withdraw ; cette dernière n'affiche pas d'icône sur le bureau.

XIII-B-11 - Spécification du bitmap de l'icône

Avec le système X Window, lorsqu'une application est mise sous forme d'icône, cette dernière est représentée à l'écran par un bitmap. Pour spécifier celui-ci, on utilise la méthode iconbitmap :

$premier_niveau->iconbitmap();
$premier_niveau->iconbitmap("bitmap");

Cette méthode prend un bitmap en paramètre, tout comme le fait l'option -bitmap du widget bouton (voir le chapitre 3, Le bouton de base). Si on l'appelle sans paramètre, elle renvoie le bitmap courant ou une chaîne vide. Appelée avec une chaîne vide, elle ôte le bitmap courant.

Sur les systèmes Win32, l'application est représentée dans la barre de tâches par une icône Tk non modifiable et le nom de l'application. L'utilisation de cette méthode avec un tel système est sans effet.

XIII-B-12 - Utilisation d'un masque d'icône

La méthode iconmask permet d'indiquer un masque pour le bitmap de l'icône (rappelez-vous que cela ne fonctionne qu'avec le système X Window). Elle prend comme paramètre un fichier bitmap ou un bitmap prédéfini (voir l'option -bitmap, décrite au chapitre 3). Tout pixel du bitmap de l'icône correspondant à un 0 dans le masque ne sera pas affiché.

L'appel de iconmask sans paramètre renvoie le masque courant ou une chaîne vide si aucun bitmap n'est utilisé. Si le paramètre est une chaîne vide, cette méthode annule le masque :

$masque_courant = $premier_niveau->iconmask(); # obtient le masque
$premier_niveau->iconmask("nom_bitmap"); # utilise le masque
$premier_niveau->iconmask(""); # annule le masque

XIII-B-13 - Configuration du nom de l'icône

La méthode iconname configure ou renvoie le texte associé à l'icône affichée lorsque l'application est sous cette forme. On peut lui passer une nouvelle chaîne ou une chaîne vide :

$premier_niveau->iconname("nouveau_nom");
$nom_courant = $premier_niveau->iconname();

Si l'on ne passe pas de paramètre, iconname renvoie le nom courant de l'icône ou une chaîne vide. On peut interroger et configurer le nom de l'icône sur un système Win32, mais c'est sans effet ; cette méthode n'agit qu'avec X Window.

XIII-B-14 - Configuration de la position de l'icône

La méthode iconposition propose au système X Window un emplacement pour l'icône sur le bureau lorsque l'application sera mise sous cette forme :

($x, $y) = $premier_niveau->iconposition();
$premier_niveau->iconposition($x, $y);

Si x et y ne sont pas utilisées, la méthode renvoie une liste ne contenant que deux éléments : les coordonnées x et y courantes. Pour annuler la proposition faite au gestionnaire de fenêtre, on appelle iconposition avec deux chaînes vides pour x et y.

XIII-B-15 - Utilisation d'une fenêtre à la place d'une icône

Certains systèmes (pas Win32) permettent d'utiliser un widget (ou une fenêtre) à la place d'un bitmap pour représenter l'icône. On indique le widget à l'aide de la méthode iconwindow. Pour connaître le widget utilisé, il suffit d'appeler la méthode sans paramètre (si aucun widget n'est associé, elle renverra une chaîne vide). Si le paramètre est une chaîne vide, cela annulera l'utilisation d'un widget pour l'icône.

$fenetre_courante = $premier_niveau->iconwindow(); # interroge
$premier_niveau->iconwindow($fenetre);             # configure
$premier_niveau->iconwindow("");                   # annule

XIII-B-16 - État d'un widget de premier niveau

La méthode state renvoie l'une des trois chaînes suivantes, qui indiquent l'état courant du widget : "normal", "iconic", ou "withdrawn".

$etat = $premier_niveau->state();

XIII-B-17 - Configuration de la machine cliente

La méthode client permet d'initialiser la propriété WM_CLIENT_MACHINE avec la valeur du paramètre qui lui est passé :

$premier_niveau->client("nom");

Pour obtenir le nom courant, il suffit d'écrire :

$nom = $premier_niveau->client();

Si le paramètre de client est une chaîne vide, la valeur courante de WM_CLIENT_MACHINE est effacée.

XIII-B-18 - Propriétés d'une fenêtre

La méthode protocol contrôle les propriétés de fenêtre WM_DELETE_WINDOW, WM_SAVE_YOURSELF, et WM_TAKE_FOCUS. La fonction de rappel associée à chaque propriété sera appelée lorsque le gestionnaire de fenêtre reconnaîtra l'événement correspondant :

$premier_niveau->protocol ( [ nom_propriété ] [, fct_rappel ] );

La fonction de rappel de la propriété WM_DELETE_WINDOW est invoquée lorsque la fenêtre est détruite par le gestionnaire de fenêtres. Par défaut, Perl/Tk utilise une classique fonction de destruction de la fenêtre. Si vous assignez une autre fonction, celle-ci sera appelée à la place de la fonction par défaut. Pour sauvegarder les données associées à cette fenêtre, faites-le dans la fonction de rappel, qui appellera ensuite $premier_niveau->destroy() afin de copier le comportement par défaut.

Les deux autres propriétés, WM_SAVE_YOURSELF et WM_TAKE_FOCUS, sont beaucoup moins souvent utilisées. WM_TAKE_FOCUS, par exemple, existe sur les systèmes Unix, mais pas sous Win32. La présence de ces propriétés dépend du système de fenêtrage utilisé et, si votre application doit être multiplateforme, ne vous attendez pas à ce qu'elles soient toujours disponibles. Pour savoir si elles le sont, il suffit d'assigner à chacune d'elles une fonction de rappel qui affiche un message, puis de lancer l'application pour vérifier si ces messages sont affichés.

Si vous n'assignez pas de fonction de rappel lors de l'appel à protocol, cette méthode renverra celle qui est affectée à cette propriété (ou une chaîne vide, s'il n'y en a aucune). Pour supprimer la fonction de rappel, il suffit de passer une chaîne vide au lieu d'un nom de fonction. Sans paramètre, cette méthode renvoie une liste de toutes les propriétés ayant des fonctions de rappel qui leur sont assignées.

XIII-B-19 - Définition de la palette de couleurs

La méthode colormapwindows affecte la propriété WM_COLORMAP_WINDOWS. Celle-ci sert à indiquer au gestionnaire de fenêtres celles qui utilisent une palette de couleurs privée. Sans paramètre, cette méthode renvoie une liste des fenêtres (par ordre de priorité) qui ont une palette différente de celle de leur parent :

@list = $premier_niveau->colormapwindows();

On peut également lui passer une liste de fenêtres :

$premier_niveau->colormapwindows(@liste);

Si vous n'utilisez pas cette méthode, Perl/Tk s'occupera de tout à votre place, bien que l'ordre des fenêtres puisse être différent.

XIII-B-20 - La propriété commande

La méthode command (que l'on ne doit pas confondre avec l'option -command, utilisée avec la plupart des widgets) contrôle la propriété WM_COMMAND. Sans paramètre, cette méthode renvoie une référence à une liste :

$ref_liste = $premier_niveau->command();

Cette liste contient les mots formant la commande utilisée pour lancer l'application. On utilisera ces lignes pour déterminer la commande qui a lancé notre application :

$ref_liste = $mw->command();
foreach (@$ref_liste) {
print "$_\n";
}

La propriété WM_COMMAND est désactivée en utilisant une chaîne vide :

$premier_niveau->command("");

XIII-B-21 - Mode de gestion du focus

La méthode focusmodel contrôle si le widget de premier niveau abandonnera le focus clavier lorsqu'une autre application ou une autre fenêtre le réclamera :

$premier_niveau->focusmodel( [ "active" | "passive" ] ) ;

La valeur par défaut est « passive », ce qui signifie que le widget abandonnera le focus clavier. Le mode de passage du focus dans votre application dépend entièrement du gestionnaire de fenêtres sous lequel celle-ci s'exécute. Je n'ai constaté aucune différence entre Win32 et X Window.

XIII-B-22 - Parent d'un widget de premier niveau

La méthode frame renvoie une chaîne hexadécimale représentant l'identificateur du parent du widget de premier niveau :

$id = $premier_niveau->frame();

L'instruction $widget->id() permet d'obtenir l'identificateur de n'importe quel widget de votre application.

XIII-B-23 - Maillage de l'application

La méthode grid pose quelques problèmes : rappelez-vous du chapitre 2 où, là aussi, il y avait une méthode grid contrôlant la gestion de l'espace. Pour contourner ce petit problème, on doit appeler cette méthode d'une façon curieuse :

$mw->wm('grid', ... );

Il faut utiliser la méthode wm (qui signifie window manager, gestionnaire de fenêtres) afin d'invoquer grid indirectement.

Cet appel clarifié, nous pouvons détailler ce que fait wm('grid', ...). Lorsque l'on demande à la fenêtre de se mailler, on limite sa taille : celle-ci doit toujours se raccrocher au maillage défini. Rappelons-nous du widget boîte de liste et de l'option -setgrid, étudiés au chapitre 7, Le widget boîte de liste. Lorsque l'on a utilisé -setgrid => 1 sur une liste, on peut écrire @liste = $premier_niveau->wm('grid') afin de connaître les valeurs du maillage. Celles que j'obtiens sur mon système sont 10, 10, 7, 17. Cela signifie que la largeur et la hauteur de base sont de 10 pixels et qu'une maille couvre une zone de 7 pixels de large et de 17 pixels de haut. La taille de la grille et des pas de progression sont modifiables en indiquant les nouvelles valeurs lors de l'appel à wm('grid', ...). On annule ces valeurs de maillage en utilisant des chaînes vides pour chacune des valeurs.

XIII-B-24 - Devenir le chef d'un groupe

Vous n'utiliserez jamais la méthode group, mais il est préférable de savoir pourquoi. Cette méthode promeut un widget en chef d'un groupe de fenêtres. Utilisez l'instruction $premier_niveau->group($widget) pour chaque widget de premier niveau que vous souhaitez placer dans le groupe de $widget. Si vous ne précisez pas ce dernier, cet appel renverra le chef actuel du groupe de $premier_niveau, ou une chaîne vide si $premier_niveau n'appartient à aucun groupe.

Si le paramètre de group est une chaîne vide, cela annule l'appartenance du widget de premier niveau à ce groupe.

XIII-B-25 - Suppression des décors des fenêtres

Pour qu'une fenêtre apparaisse sans aucun décor (barre de titre, contour, etc.), on utilise la méthode overrideredirect en lui passant une valeur vraie :

$premier_niveau->overrideredirect(1); # Ôte tous les décors

Soyez tout de même prudent : vous ne pourrez plus déplacer la fenêtre affichée à l'écran et, si vous avez oublié d'y placer un bouton de type « Fin », vous ne pourrez quitter correctement l'application (il faudra donc composer 'Ctrl-C' dans la fenêtre ayant lancé le script afin de tuer son processus).

Cela permet d'afficher une fenêtre temporaire d'accueil – apparaissant pendant le démarrage de votre application. N'oubliez pas d'appeler MainLoop, sinon elle n'apparaîtra pas.

L'appel de overrideredirect sans paramètre renvoie la valeur courante (1 ou 0) :

$val_courante = $premier_niveau->overrideredirect();

Une fois que la fenêtre est affichée, un nouvel appel avec un paramètre valant 0 ne remettra pas le décor en place.

XIII-B-26 - Qui a placé la fenêtre ?

Lorsque le widget de premier niveau est affiché sur la fenêtre, le gestionnaire de fenêtres indique au programme où se placer, ou bien le programme indique au gestionnaire où il veut être. Dans certains cas, c'est l'utilisateur qui place lui-même la fenêtre lorsqu'elle apparaît.

$qui = $premier_niveau->positionfrom(); # "program" ou "user" ?
$premier_niveau->positionfrom("program"); # Tente de forcer

Sans paramètre, la méthode positionfrom rend compte de ce qui s'est passé. S'il s'agit de "program", d'une chaîne vide ou d'un widget, cela signifie que c'est le gestionnaire de fenêtres ou le programme qui a demandé la position. S'il s'agit de "user", c'est que c'est l'utilisateur qui a fait cette demande.

On force le comportement souhaité en appelant positionfrom avec le paramètre "program" ou "user", mais cela ne fonctionnera que si votre gestionnaire de fenêtres l'accepte.

XIII-B-27 - Qui a modifié sa taille ?

La méthode sizefrom fait la même chose que positionfrom sauf qu'elle concerne le changement de taille de la fenêtre.

$qui = $premier_niveau->sizefrom(); # "program" ou "user" ?
$premier_niveau->sizefrom("user"); # Tente de forcer

XIII-B-28 - Une fenêtre irréelle

Une fenêtre transitoire (comme un menu déroulant) n'est pas vraiment une vraie fenêtre. La méthode transcient permet d'indiquer au gestionnaire de fenêtres qu'un widget de premier niveau (un menu déroulant ou une boîte de dialogue, par exemple) est lié à son parent (la fenêtre dans laquelle il s'affiche) :

$parent = $premier_niveau->transient();
$premier_niveau->transient($parent);

Sans paramètre, cette méthode renvoie le parent courant ou une chaîne vide.

XIII-C - Résumé

Il est préférable d'utiliser un autre widget de premier niveau plutôt que MainWindow si vous devez mettre plus d'informations que ne peut en contenir une seule fenêtre. Les widgets de premier niveau permettent de regrouper les informations, ce qui est une bonne pratique. Le choix de leur utilisation est une décision de conception qui vous appartient : si votre application comporte trop de fenêtres, l'utilisateur risquerait de s'y perdre et une application bien conçue se satisfera d'une ou deux fenêtres. Le module Tk, par exemple, dispose d'un module Tk::Dialog vous permettant de produire facilement des messages à l'attention de l'utilisateur. Consultez la documentation accompagnant le fichier Dialog.pm pour les détails concernant son utilisation.

XIII-D - Essayez et amusez-vous !

Reprenez l'exemple du chapitre 11 portant sur la gestion d'une liste dynamique des documents et créez un nouveau widget de premier niveau à chaque fois que l'utilisateur clique sur le bouton « Nouveau ». Si vous souhaitez aller encore plus loin, faites en sorte que l'application crée ou charge réellement un fichier.

XIV - Liaisons d'événements

Perl/Tk est un langage de programmation événementiel : on conçoit un programme de façon à ce qu'il réponde à des événements générés par celui-ci. Presser un bouton, déplacer la souris ou entrer des caractères au clavier sont des exemples d'événements. La relation entre ces actions et le widget qu'elles concernent s'appelle une liaison.

Tout widget Perl/Tk possède ses propres liaisons prédéfinies. Ainsi, le widget bouton modifie sa couleur lorsque le pointeur de la souris est au-dessus de lui et invoque la fonction de rappel spécifiée lorsqu'il est pressé. Ce sont des liaisons par défaut, qui sont créées en même temps que le widget lui-même.

Vous pouvez faire en sorte que votre programme réponde à d'autres événements en utilisant la méthode bind qui vous permettra d'assigner des fonctions de rappel à différentes séquences d'événements ; sa forme générale est :

$widget->bind( séquence, fct_rappel );

Vous pouvez aussi surcharger les liaisons prédéfinies en les supprimant ou en créant les vôtres.

XIV-A - La méthode bind

Pour utiliser la méthode bind, invoquez-la sur le widget pour lequel vous souhaitez ajouter la liaison. L'instruction $bouton->bind vous permet, par exemple, d'ajouter une liaison au bouton $bouton. Dans certains cas, vous agirez sur la fenêtre principale de votre application : $mw->bind(...). Il existe plusieurs paramètres possibles pour bind, décrits par la liste suivante :

$widget->bind();

1. Sans paramètre, bind renvoie la liste des liaisons (<Button-1>, <Key-D>, par exemple) qui ont été créées pour ce widget. Elle ne renvoie pas les liaisons prédéfinies. Voici un exemple :

$bouton = $mw->Button( ... )->pack;
$bouton->bind("<Button-3>", sub { ... } );
@liaisons = $bouton->bind();
print "Liaisons du bouton : @liaisons\n";
# Cela afficherait :
# Liaisons du bouton : <Button-3>

2. Cet appel renverra une chaîne vide si aucune liaison supplémentaire n'a été définie pour ce widget.

$widget->bind(séquence);

3. Pour connaître la fonction de rappel associée à une séquence d'événement, il suffit de passer celle-ci comme premier paramètre (« <Button-3> », par exemple) et bind renverra alors la fonction associée. En étendant l'exemple précédent, l'information contenue dans @liaisons servira à connaître les fonctions de rappel associées aux différentes liaisons :

foreach (@liaisons) {
print "$_ utilise la fonction de rappel ",
$bouton->bind($_), "\n";
}
# <Button-3> utilise la fonction de rappel :
# Tk::Callback=CODE(0x91fdcc)

4. Si vous passez une séquence n'existant pas pour le widget, vous obtiendrez une chaîne vide. De plus, si vous utilisez une séquence qui est considérée comme une liaison prédéfinie (« <Button-1> » sur un widget bouton, par exemple), vous obtiendrez aussi une chaîne vide (sauf si vous avez créé une autre liaison pour celui-ci).

$widget->bind(séquence, fct_rappel);

5. Pour qu'une fonction de rappel soit invoquée lorsqu'un événement survient, il suffit de l'indiquer à la suite de la séquence dans l'appel à bind. Cette fonction peut être exprimée sous l'une des formes étudiées au chapitre 3. Voici quelques exemples :

$bouton->bind( "<Button-3>", sub { print "Clic droit\n" } );
$saisie->bind(
"<Return>",
sub {
print "Pressez 'Entrée' dans la zone de saisie\n";
}
);
$bouton->bind( "<Button-1>", \&action_clic_gauche() );
$canevas->Tk::bind( "<Button-1>", [ \&trace_rectangle, Ev("X"), Ev("Y") ] );

6. Pour ôter une liaison pour une séquence donnée, il suffit de passer une chaîne vide comme fonction de rappel.

$widget->bind(marqueur [, séquence, fct_rappel ] );

7. Le marqueur permet de désigner un type de widget. On l'utilise lorsque l'on souhaite que tous les widgets d'un type donné partagent le même comportement. Si, par exemple, on veut afficher un menu de recherche lorsque l'on clique avec le bouton droit de la souris dans un widget texte, on écrira :

$t1 = $mw->Scrolled("Text")->pack( -expand => 1, -fill => 'both' );
$t2 = $mw->Scrolled("Text")->pack( -expand => 1, -fill => 'both' );
$menu = $mw->Menu(
-menuitems => [
[
"command" => "Recherche",
-command  => \&cherche_fic
],
[
"command" => "Nouvelle recherche",
-command  => \&cherche_encore
]
],
-tearoff => 0
);
$mw->bind( Tk::Text,
"<Button-3>",
sub {
$menu->Popup(
-popover   => 'cursor',
-popanchor => "nw"
);
}
);

8. Tous les widgets texte de l'application disposeront alors de ce menu. Il faudrait peaufiner un peu le travail des fonctions de recherche pour qu'elles sachent quel est le widget qui les a déclenchés, mais l'intérêt est qu'il n'y a pas besoin de réécrire la même liaison pour chacun des widgets textes.

9. Dans cet exemple, nous avons indiqué la séquence « <Button-3> » à notre liaison. Si nous n'avions pas spécifié de fonction de rappel, nous aurions obtenu la liste de celles qui sont associées à ce marqueur.

10. Le marqueur 'all' est spécial : il désigne tous les widgets ainsi que la fenêtre de l'application. Soyez prudent : votre fonction de rappel sera beaucoup plus active que vous ne le pensez !

XIV-B - Paramètres passés à la fonction de rappel

Le premier paramètre d'une fonction de rappel assignée par bind est toujours une référence au widget appelant, même si la liaison a lieu sur tout un type de widget. Cette référence permet de connaître le widget ayant invoqué la fonction.

Voici un exemple d'utilisation d'une simple zone de saisie :

$saisie = $mw->Entry()->pack;
$saisie->bind("<Return>", \&Entree_pressee);

sub Entree_pressee {
my ($s) = @_;
print "La zone de saisie contient : ", $s->get, "\n";
}

Lorsque bind appelle une fonction de rappel sur tout un type de widget, elle facilite beaucoup le travail consistant à déterminer le widget à l'origine de l'événement :

$mw->Scrolled("Text")->pack( -expand => 1, -fill => 'both' );
$mw->Scrolled("Text")->pack( -expand => 1, -fill => 'both' );
$menu = $mw->Menu(
-menuitems => [
[
"command" => "Sauver",
-command  => \&sauve_fic
],
[
"command" => "Ouvrir",
-command  => \&ouvrir_fic
]
],
-tearoff => 0
);
$mw->bind( Tk::Text, "<Button-3>", sub { $menu->Popup( -popover => 'cursor' ) } );

sub sauve_fic {
my ($texte) = @_;
open( FIC, ">monfic" ) || die "Impossible de créer monfic\n";
print FIC $texte->get( "1.0", "end" );
close FIC;
}

L'appel à bind utilise Tk::Text comme premier paramètre. La liaison s'appliquera à tous les widgets texte de l'application. Dans cet exemple, quel que soit le widget texte sur lequel on a cliqué, son contenu sera écrit dans le fichier monfic. L'application pourrait également demander à l'utilisateur d'entrer un nom de fichier afin que tout cela soit un peu plus pratique.

XIV-C - Définition de séquences d'événements

Pour l'instant, nous avons utilisé les séquences d'événements <Button-3>, <Button-1> et <Return>, mais nous n'avons pas encore expliqué comment en construire. Bien que les exemples déjà vus semblent simples et triviaux, les séquences d'événements peuvent devenir bien plus compliquées.

Une séquence est construite à partir d'un modificateur éventuel, d'un événement et d'un détail optionnel. Ces parties sont séparées par des tirets et placées entre chevrons.

1. <modificateur-événement-détail>

Lorsque nous étudierons les différentes liaisons possibles, souvenez-vous qu'il est possible que plusieurs séquences puissent correspondre à une même liaison. Si une liaison existe pour un bouton précis, et qu'une autre est créée pour tous les boutons, c'est la fonction de rappel de celle qui est spécifique qui sera d'abord appelée ; les fonctions des liaisons plus générales le seront ensuite.

XIV-C-1 - Modificateurs

Un modificateur est un événement survenant en même temps que l'événement principal, la pression de la touche 'Ctrl' en même temps qu'un clic avec le bouton de la souris, par exemple. L'événement modificateur doit survenir avant afin que la séquence complète corresponde (on presse d'abord la touche, puis le bouton de la souris).

Les modificateurs possibles sont décrits ci-dessous :

Control

1. La touche 'Ctrl' doit être pressée lorsque l'événement principal survient (<Control-Button-1>, par exemple).

Shift

1. La touche 'Maj' doit être pressée lorsque l'événement principal survient (<Shift-Button-3>, par exemple).

Lock

1. La touche de verrouillage des majuscules doit être pressée afin de passer en majuscules (<Lock-Key-a>, par exemple).

Alt

1. La touche 'Alt' doit être pressée lorsque l'événement principal survient (<Alt-Key-x>, par exemple). Les utilisateurs de Microsoft Windows doivent savoir que, parfois, ce système ne permet pas au gestionnaire d'événements d'avertir les applications que la touche 'Alt' gauche a été pressée. Cette touche est, normalement, celle que l'on utilise pour basculer entre les applications en faisant 'Alt-Tab'. Si elle ne fonctionne pas, essayez celle de droite avant d'abandonner. Cet avertissement s'adresse aussi à ceux qui utilisent un serveur X Window (Exceed, par exemple) sous MS Windows pour accéder à un système Unix.

ButtonX où X vaut 1, 2, 3, 4 ou 5 (le raccourci BX est également autorisé)

1. Ces modificateurs indiquent que le numéro de bouton spécifié doit être pressé avant que le reste de l'événement ne survienne. Si, par exemple, on souhaite déclencher un événement lorsque l'utilisateur clique sur le bouton 3 pendant qu'il appuie sur le bouton 1, on utilisera la séquence <Button1-Button-3> (ou <B1-Button-3>). L'événement ne sera pas déclenché si l'on clique sur le bouton 1 pendant que le 3 est pressé. L'ordre est important.

On ne peut utiliser simplement <ButtonX> car, s'il n'y a pas le tiret entre Button et le nombre, cela dénote un modificateur d'un autre événement.

Double

1. Double est un type de modificateur spécial indiquant que l'événement principal doit survenir deux fois. Il impose une contrainte sur le délai maximum entre les deux occurrences de l'événement principal. Le plus souvent, il sert à indiquer un double clic sur un bouton de la souris. Il est important de noter que <Double-Button-1> n'est pas équivalent à <Button-1><Button-1>. Bien qu'ils semblent signifier la même chose, il n'y a pas de contrainte de délai dans le dernier événement : il signifie « vous avez pressé le bouton 1, puis, un peu plus tard, vous avez recommencé ». <Double-Button-1>, lui, signifie « vous avez pressé le bouton 1, et dans un certain laps de temps, vous avez recommencé ».

Triple

1. Ce modificateur ressemble à Double, mais nécessite que l'événement survienne trois fois dans une succession rapide. Un aspect important de Double et Triple est que ces modificateurs peuvent se cumuler. Si l'on clique rapidement cinq fois sur le bouton 1 de la souris, le premier clic correspondra à l'événement <Button-1>, le second à <Double-Button-1>, le troisième à <Triple-Button-1>, le quatrième également, etc. Cela n'est vrai que si l'événement <Triple-Button-1> est défini, si seul <Double-Button-1> l'est, le troisième clic réactivera cette liaison au lieu de celle avec <Triple->. Le chronogramme de la figure 14.1 montre quand sont générés les événements.

Figure 14.1 : Exemple de doubles clics cumulés

Meta (ou M )

1. La touche 'Meta' doit être pressée pendant l'événement principal. Cette touche n'existe généralement que sur les systèmes X Window.

ModX (ou MX )

1. Ce modificateur n'existe aussi que sur les systèmes X Window. Il existe plusieurs modificateurs (1--5) ; utilisez Ev('K') pour trouver leur emplacement sur votre clavier.

XIV-C-2 - Types d'événements (avec détails optionnels)

La partie de la chaîne d'événement consacrée à l'événement principal est ce qui nous intéresse ici. La section précédente vous a expliqué comment ajouter un éventuel modificateur. Lorsque l'on dit qu'un événement est déclenché ou généré, cela signifie qu'il a survenu. Si aucune fonction de rappel ne lui est associée, tout se passera comme si rien n'était vraiment arrivé. La liste suivante présente les différents types d'événements et les détails éventuels qui leur sont applicables.

ButtonPress ou Button

1. Cet événement survient lorsqu'un bouton de la souris est pressé. Il désigne n'importe lequel des boutons disponibles, mais vous pouvez ajouter un détail pour faire référence à un bouton précis : <Button-1>, <Button-2>, etc.

ButtonRelease

1. Cet événement survient lorsqu'un bouton de la souris est relâché. Certaines opérations peuvent avoir lieu selon le bouton pressé (ButtonPress ou Button) relâché (ButtonRelease). On peut préciser le bouton concerné : <ButtonRelease-1>, <ButtonRelease-2>, etc. Dans le cas contraire, l'événement désigne n'importe quel bouton.

Circulate

1. Cet événement est généré lorsque l'application a plus d'une fenêtre et que l'ordre d'empilage de celles-ci est modifié.

Colormap

1. Cet événement survient lorsque la palette de couleurs du widget (habituellement de premier niveau) est modifiée.

Configure

1. Survient lorsqu'un widget est configuré. Si l'on fait correspondre une fonction de rappel à cet événement, il faut être prudent : elle sera probablement appelée très souvent. À chaque fois que la fenêtre de l'application change de taille, tous les widgets qu'elle contient sont reconfigurés, ce qui provoque la génération de l'événement Configure pour chacun d'eux. Celui-ci survient également lors de la création d'un widget.

Destroy

1. Généré lorsque le widget est détruit. On force la destruction d'un widget en faisant $widget->destroy().

Enter

1. Survient lorsque le pointeur de la souris pénètre dans la zone occupée par le widget. Il est important de se souvenir que cet événement ne correspond pas à l'appui de la touche 'Entrée'.

Expose

1. Cet événement survient lorsque la fenêtre est mise au premier plan.

FocusIn

1. Généré lorsque le widget reçoit le focus clavier consécutivement à une tabulation de la part de l'utilisateur (ou à une instruction $widget->focus() du programme).

FocusOut

1. Cet événement est l'opposé du précédent. Il est déclenché lorsque le widget perd le focus clavier.

Gravity

1. Survient lorsque le widget est déplacé consécutivement à une modification de la taille de son parent.

KeyPress (ou Key)

1. Lorsqu'une touche du clavier est pressée, cet événement est généré. Pour spécifier une touche précise – 'a', par exemple –, on détaille l'événement : <Key-a>. Si vous souhaitez savoir quelle est la touche qui a déclenché l'événement, utilisez Ev('K') comme paramètre de votre fonction de rappel :

$mw->bind("<Key>", [ \&verif_touche, Ev('K') ]);

1. Ceci a pour effet de passer le symbole de la touche pressée en paramètre à verif_touche. Pour connaître les symboles des touches, utilisez ce script :

use Tk;
$mw = MainWindow->new;
$mw->bind("<Key>", [ sub { print "Touche : $_[1]\n"; },
Ev('K')] );
MainLoop;

1. Lorsque vous pressez une touche, son symbole s'affiche à l'écran. Notez que les caractères placés sous les chiffres ('&', 'é', '"', etc.) sont désignés par leur nom ("ampersand", "eacute", "quotedbl", etc.).

KeyRelease

1. Cet événement est le compagnon du précédent. Il est déclenché lorsque la touche est relâchée. Dans certains cas, il est préférable d'attendre que la touche soit relâchée avant de faire quoi que ce soit.

Leave

1. Cet événement survient lorsque le pointeur de la souris quitte la zone occupée par le widget. Utilisez les événements Enter et Leave pour créer deux liaisons pour le même widget et vous pourrez alors faire des choses aussi amusantes que la modification du curseur de la souris lorsqu'il est au-dessus du widget (si le widget dispose de l'option -cursor, utilisez plutôt celle-ci).

Map

1. Survient lorsque la fenêtre a été placée ou ouverte (désiconifiée).

Motion

1. Cet événement est généré lorsque la souris se déplace, à l'écran, au-dessus de votre application. Il est préférable de ne pas lier cet événement, car la fonction de rappel serait appelée en permanence. Cela dit, si vous ne le liez qu'à un seul widget, vous ne capturerez cet événement que lorsque le pointeur de la souris passera sur celui-ci, mais cela représente encore beaucoup d'appels à la fonction de rappel. Je vous conseille donc de ne le lier que si vous avez de très bonnes raisons pour le faire.

Reparent

1. Cet événement survient lorsque le parent du widget lié est modifié.

Unmap

1. Généré lorsque la fenêtre liée est mise sous forme d'icône.

Visibility

1. Cet événement est généré lorsqu'un widget s'affiche pour la première fois. Il y a plusieurs façons de rendre un widget visible dans une application :

1. au démarrage de l'application, lorsque le widget est placé à l'écran, l'événement Visibility est généré. Ce ne sera pas le cas si vous créez un widget sans le placer sur l'écran ;

2. lorsque le widget est ôté par pack('forget'), puis replacé ;

3. lors d'une modification de la taille de la fenêtre et que le widget apparaît alors dans la partie visible de celle-ci (ce qui est habituellement le cas lorsqu'une fenêtre, qui avait été réduite, est agrandie) ;

4. lorsque le widget est à l'intérieur d'un autre (tel qu'un widget texte ou un canevas) et que le parcours de ce dernier fait apparaître le widget à l'écran.

XIV-D - Informations sur un événement

La méthode Ev permet d'obtenir des informations sur un événement donné. Celle-ci sait utiliser de nombreuses valeurs, documentées sur le site documentaire de Perl/Tk, http://w4.lns.cornell.edu/~pvhp/ptk/doc/bind.htm, maintenu par Peter Prymmer. Je ne présenterai ici que les valeurs utilisées dans 99.9 % des cas. Certaines de celles-ci ne fonctionnent qu'avec des événements précis et, si vous en utilisez une qui ne convient pas, vous obtiendrez une valeur non définie.

XIV-D-1 - Coordonnées

On utilisera les appels Ev('x') et Ev('y') pour connaître l'emplacement où est survenu un événement. Ceux-ci renvoient des coordonnées relatives à la fenêtre dans laquelle l'événement a été généré. Si l'on préfère obtenir des coordonnées relatives à la fenêtre principale du système de fenêtrage (celle du bureau sous Windows, la fenêtre racine sous X), il suffit d'utiliser les majuscules X et Y : Ev('X') et Ev('Y').

Attention : cette dernière forme ne fonctionne que pour les événements ButtonPress, ButtonRelease, KeyPress, KeyRelease, ou Motion.

XIV-D-2 - Numéro du bouton de la souris

L'appel Ev('b') renvoie le numéro du bouton pressé de la souris. Il ne fonctionne que pour les événements ButtonPress ou ButtonRelease.

XIV-D-3 - Hauteur et largeur

Les paramètres 'h' et 'w' permettent d'obtenir, respectivement, la hauteur et la largeur associées à un événement : celles du widget. Si, par exemple, on souhaite connaître la nouvelle taille d'un bouton après un agrandissement manuel de la fenêtre, on écrira :

$bouton->bind("<Configure>",
[ sub { print "Haut : $_[1], Larg : $_[2]\n"; },
Ev('h'), Ev('w') ]);

La fonction de rappel ne sera appelée que lorsque le widget est configuré, ce qui est le cas lors de sa création et à chaque fois qu'il change de taille.

Les appels Ev('h') et Ev('w') ne fonctionnent qu'avec les événements Configure, Expose et GraphicsExpose.

XIV-D-4 - Informations sur le clavier

Plusieurs moyens existent pour retrouver les touches pressées par l'utilisateur. Le paramètre 'K' permet de produire le symbole (keysym) associé à une touche et l'on obtiendra le code ASCII associé en utilisant le paramètre 'k'. Essayez l'instruction suivante pour constater la différence :

$b->bind("<Key>", [ sub { print "Params : @_\n"; },
Ev('k'), Ev('K') ]);

Les appels Ev('k') et Ev('K') ne fonctionnent que pour les événements KeyPress et KeyRelease.

Pour obtenir le symbole de la touche sous forme décimale, plutôt que sous forme de chaîne, utilisez l'appel Ev('N').

XIV-D-5 - Type de l'événement

Un appel à Ev('T') renverra le type de l'événement ayant déclenché la fonction de rappel. S'il s'agit de KeyPress, la chaîne renvoyée sera « KeyPress ». Bien que tout cela semble évident, cette possibilité est utile lorsque l'on utilise la même fonction de rappel pour répondre à plusieurs événements différents.

XIV-E - S'échapper d'une fonction de rappel créée avec bind

Pour stopper le traitement effectué par votre fonction de rappel, utilisez l'instruction return pour la quitter. Cela n'empêchera pas le traitement des fonctions de rappel en attente ; pour stopper le traitement de toutes les fonctions de rappel liées à une combinaison widget/événement, on doit utiliser Tk::break au lieu de return, moins catégorique.

XIV-F - La méthode bindtags

La méthode bindtags permet de connaître tous les marqueurs associés à un widget :

print join(' ', $bouton->bindtags());
# affiche : Tk::Button . bouton . all
print join(' ', $mw->bindtags());
# affiche : MainWindow . all

Cette méthode nous indique l'ordre dans lequel le widget répondra aux fonctions de rappel des liaisons. La première réponse est toujours faite à la classe du widget : Tk::Button dans le premier exemple, et MainWindow dans le deuxième.

L'information renvoyée par bindtags n'est pas aussi intéressante que quand on lui passe des paramètres. Pour supprimer toutes les liaisons d'un widget, sauf celles qui s'appliquent à 'all' :

$bouton->bindtags(['all']);

Désormais, le bouton ne répondra plus s'il est pressé, ni aux mouvements de la souris ni aux liaisons prédéfinies du widget. Comme l'explique la page web de Perl/Tk consacrée à bindtags, on peut inverser l'ordre dans lequel le widget répond aux événements à l'aide de l'instruction suivante :

$b->bindtags(['all', $b->toplevel, ref($b), $b]);

Nous savons déjà que 'all' désigne toutes les liaisons associées au marqueur de liaison 'all'. L'appel $b->toplevel renvoie la fenêtre dans laquelle $b se trouve : MainWindow=HASH(0x9798d8). L'appel ref(\$b) donne le paquetage auquel appartient $b : Tk::Button. Enfin, $b désigne l'instance spécifique : Tk::Button=HASH(0x99c0cc).

HASH(0x99c0cc) est ce que vous verrez si vous affichez la valeur. Le nombre hexadécimal entre parenthèses n'est que l'emplacement en mémoire physique occupé par ce widget. HASH signifie qu'il est mémorisé sous la forme d'un hachage.

XIV-G - Utilisations de bind

L'utilisation de bind constitue un moyen puissant pour simplifier vos applications. Vous pouvez, par exemple, ajouter une liaison à une boîte de liste pour qu'elle affiche un menu lorsque vous y pressez le bouton droit de la souris. L'utilisation de bind avec les marqueurs de texte vous permet de créer des documents en « pseudoHTML ». L'ajout d'une liaison pour un double clic dans une boîte de liste rend possible une action particulière, déclenchée lorsqu'un élément de cette liste est « double cliqué ». Il y a tant d'autres façons d'utiliser cette méthode que je ne peux toutes les traiter ici. Assurez-vous cependant de ne pas créer une liaison inutilisable (un triple clic avec appui simultané de la touche 'Ctrl', par exemple, est un peu compliqué).

XV - Widgets composites

Pour l'instant, nous avons présenté chaque widget de base séparément, mais la distribution de Perl/Tk contient également plusieurs widgets composites, qui sont des combinaisons de widgets dont le fonctionnement dépend de la façon dont ils sont combinés. En voici quelques exemples :

Optionmenu

1. Construit autour d'un bouton de menu, ce widget permet de choisir parmi une liste d'options située sur le menu.

LabEntry

1. Construit autour d'un cadre, ce widget est une zone de saisie comportant une étiquette configurable.

Dialog

1. Construit autour d'un widget de premier niveau, affiche un bitmap et un message permettant d'informer l'utilisateur.

Ces exemples ont été choisis parce qu'ils montrent bien ce que peuvent être les widgets composites. Ceux-ci peuvent être construits à partir d'un widget (un bouton de menu, dans notre exemple), d'un cadre contenant des widgets, ou d'un widget de premier niveau contenant d'autres widgets et formant une fenêtre complète.

Lorsque j'ai commencé à étudier les widgets composites, je pensais toujours passer à côté de quelque chose. Si je lisais le code du coin de l'œil, tout semblait être correct. Cependant, si je le consultais à tête reposée, j'étais alors complètement embrouillée et n'étais plus sûre de ce que ce code réalisait vraiment. Il est important de savoir qu'un certain nombre d'actions se passent en coulisses et que nous pourrons tirer parti de celles-ci lorsque nous créerons un widget composite.

Le but de ce chapitre n'est pas que vous sachiez écrire le widget composite le plus complexe qui soit, mais que vous compreniez comment ils fonctionnent, ce qui sera déjà suffisant : vous pourrez alors continuer à progresser lentement. La meilleure démarche consiste à lire entièrement ce chapitre, puis consulter les exemples livrés avec la distribution de Perl/Tk. Les widgets composites fournis avec le module Tk sont complets, ils ont été vérifiés par de nombreuses personnes différentes et fonctionneront lorsque vous les lancerez (habituellement, ils sont documentés avec le système documentaire de Perl, au format pod).

XV-A - Détour par un exemple

Je l'admets : j'aime les exemples. Ce sont des points de repère où l'on peut revenir lorsque l'on passe aux choses sérieuses. Comme il y a beaucoup de points épineux avec les widgets composites, nous commencerons par des exemples simples et progresserons à partir d'eux.

En consultant le code des widgets composites cités plus haut, on constate que celui du widget LabEntry est le plus court. Voici le contenu de LabEntry.pm(37) :

# Copyright (c) 1995-1997 Nick Ing-Simmons. Tous droits réservés.
# Ce programme est un logiciel libre ; vous pouvez le redistribuer
# et/ou le modifier selon les mêmes conditions que Perl lui-même.
# Classe LabeledEntry (zone de saisie étiquetée)
package Tk::LabEntry;
require Tk::Frame;
@ISA = qw(Tk::Frame);
Construct Tk::Widget 'LabEntry';

sub Populate {
require Tk::Entry;

# Constructeur d'un LabeledEntry (zone de saisie étiquetée)
#
my ( $cw, $args ) = @_;
$cw->SUPER::Populate($args);

# Advertised subwidgets: entry.
my $e = $cw->Entry();
$e->pack( -expand => 1, -fill => 'both' );
$cw->Advertise( 'entry' => $e );
$cw->ConfigSpecs( DEFAULT => [$e] );
$cw->Delegates( DEFAULT => $e );
$cw->AddScrollbars($e) if ( exists $args->{-scrollbars} );
}
1;

Il s'agit du code commenté et complet. La ligne @ISA = qw(Tk::Frame) indique qu'il s'agit d'un widget composite construit autour d'un cadre. Nous pouvons nous reporter à Programming Perl (O'Reilly, 1997)(38) pour savoir à quoi sert le tableau @ISA : « À l'intérieur de chaque package, un tableau spécial appelé @ISA indique à Perl où chercher une méthode s'il ne parvient pas à la trouver dans ce package. » On trouve, par la suite, de nombreux détails sur cette implantation de l'héritage, mais je ne voudrais pas abuser des mots qu'il emploie, uniquement pour expliquer un concept simple : pour que votre widget composite fonctionne, il faut insérer cette ligne dans votre code(39). Toutes les autres explications vont trop dans les détails(40).

L'étape suivante consiste à comprendre comment intervient le widget de saisie. On sait qu'il est créé, car lorsque l'on utilise une zone de saisie étiquetée, on constate qu'elle contient une zone de saisie. On remarquera que le fichier ne contient qu'une fonction, nommée Populate. Elle n'est jamais appelée directement, mais elle l'est. Les paramètres de cette fonction sont deux scalaires : une référence au cadre lui-même et une référence à un hachage contenant tous les couples de paramètres utilisés pour créer le widget. Voici un exemple de création d'un widget LabEntry :

$saisie_etiquettee = $mw->LabEntry(
-textvariable => \$texte,
-label        => "Entrez votre nom :",
-labelPack    => [ -side => 'left' ]
)->pack();

Si vous examinez le code de LabEntry.pm, vous saurez qu'il crée une zone de saisie grâce à la ligne my $e = $cw->Entry(). Viennent ensuite plusieurs opérations lancées par Advertising, ConfigSpecs et Delegates. Pour l'instant, il nous suffit de dire que ces fonctions permettent à la zone de saisie de se comporter comme on le souhaite.

L'étiquette de la zone de saisie étiquetée est automatiquement créée, car on utilise l'option -label lors de sa création. Si l'on se souvient du chapitre 12, Cadres, on sait que l'utilisation de cette option avec un cadre crée automatiquement une étiquette. Ce qui rend ce widget composite simple est qu'il utilise l'étiquette déjà comprise dans un widget cadre.

XV-B - Emplacement des fichiers

Lorsque l'on construit ses propres widgets composites, on crée un fichier portant le même nom (à la casse près) que le widget et terminé par .pm. Si, par exemple, on créait un nouveau widget composite nommé BoutonListe, son code serait placé dans un fichier BoutonListe.pm.

Si les fichiers des widgets composites sont placés dans le même répertoire que ceux

de l'application, il suffit d'ajouter la ligne use BoutonListe après la ligne use Tk commençant le code utilisant le nouveau widget. Si les fichiers des widgets sont placés ailleurs, il faut ajouter la ligne suivante, avant toute instruction use ou require :

use lib ("repertoire_1", "repertoire_2");

afin d'indiquer les répertoires utilisés pour BoutonListe.pm.

XV-C - Création d'un widget composite à partir d'un cadre

La création d'un widget composite utilisant un cadre est légèrement différente de celle d'un widget utilisant un widget de premier niveau. Je donnerai un bref exemple pour chacun d'eux afin que vous puissiez vous rendre compte de ce que vous pouvez faire.

Si vous comptez créer un widget composite nommé MonWidget, les lignes suivantes doivent absolument être placées au début du code de celui-ci :

package MonWidget;
require Tk::Frame;
@ISA = qw(Tk::Frame);

Construct Tk::Widget 'MonWidget';

sub Populate
{
...
}

Il faut déclarer le nouveau widget comme étant son propre paquetage, d'où la ligne package MonWidget (si vos widgets se trouvent dans un sous-répertoire, utilisez SousRep::MonWidget). Les deux lignes suivantes sont faciles à comprendre : require Tk::Frame assure que l'on a chargé les informations nécessaires à la manipulation d'un cadre, et @ISA = qw(Tk::Frame) ajoute le widget Tk::Frame au tableau @ISA. La ligne qui suit appelle la méthode Construct de Tk::Widget (nous aurions également pu écrire Tk::Widget-> Construct("MonWidget")) en lui passant le nom de notre widget. Dans cet appel, on n'ajoute pas le nom du répertoire où se trouve le widget.

L'appel à Construct crée un constructeur pour le nouveau widget ; cela vous permettra de créer un nouveau MonWidget en appelant la méthode MonWidget :

$nouveau_widget = $mw->MonWidget(…);

Lorsque l'on crée un widget composite à partir d'un cadre, il faut utiliser la fonction Populate pour créer les sous-widgets et réaliser les autres configurations nécessaires.

XV-C-1 - Populate détaillé

Il est préférable d'ajouter une instruction require pour chacun des autres widgets créés dans le widget composite. Ainsi, dans le code de LabEntry se trouve la ligne require Tk::Entry, car ce widget composite crée une zone de saisie.

Populate est appelée avec deux paramètres : une référence au widget composite et une référence à un hachage, que l'on affecte à deux variables afin de les utiliser plus tard :

my ($widget_comp, $params) = @_;

L'étape suivante consiste à gérer les options spécifiques qui s'appliquent au widget entier. Pour cela, il suffit de les extraire du hachage référencé par $params et de vérifier si une valeur a été définie :

$valeur_option = delete $params->"-option" ;
if (defined $valeur_option) {
...
}

Supposons que vous souhaitiez utiliser l'option -filename afin de placer la valeur associée à celle-ci dans la variable $nom_fic :

$nom_fic = delete $params->{"-filename"};
if (defined $nom_fic) {
# Ouvrir le fichier...
}

Après avoir traité tous les paramètres souhaités, il est conseillé d'appeler SUPER:: Populate :

$widget_comp->SUPER::Populate($params);

Puis, vous pouvez créer les widgets que vous souhaitez dans le widget composite. Ainsi, si vous désirez créer une boîte de liste disposant de plusieurs boutons, il suffit d'appeler les méthodes appropriées pour chacun d'eux. Si vous souhaitez que l'utilisateur puisse manipuler ceux-ci, appelez la méthode Advertise sur chacun d'eux.

XV-C-2 - Appel de Advertise

Advertise vous permet d'utiliser la méthode Subwidget pour désigner directement un des widgets inclus dans le composite, plus tard dans votre programme. Ainsi, lorsque le widget composite a été créé, on obtient une référence à la zone de saisie de la façon suivante :

$saisie_etiquette = $mw->LabEntry(
-textvariable => \$texte,
-label        => "Entrez votre nom :",
-labelPack    => [ -side => 'left' ]
)->pack();
$saisie      = $saisie_etiquette->Subwidget("entry");
$texte_saisi = $saisie->get();

Lorsque l'on crée un widget composite, il ne faut pas oublier d'ajouter un appel à Advertise pour chacun des widgets. Si, par exemple, on crée une zone de saisie et un bouton, on devra faire deux appels :

$cw->Advertise('entry' => $saisie);
$cw->Advertise('button' => $bouton);

XV-C-3 - Appel de Delegates

Un widget composite est, essentiellement, une combinaison de deux ou trois widgets afin de n'en former qu'un seul. Lorsque l'on invoque des méthodes sur ce widget composite, il faut définir celles qui seront vraiment appelées à l'aide de la méthode Delegates, à laquelle il suffit de fournir une référence au widget que l'on souhaite utiliser :

$cw->Delegates(DEFAULT => $saisie);

Toutes les autres méthodes des widgets formant le widget composite devront être accédées via la méthode Subwidget.

Delegates permet également d'appeler une méthode d'un sous-widget :

$cw->Delegates(
'insert' => $liste_defil,
'delete => $liste_defil,
DEFAULT => $saisie
);

Dans cet exemple, si l'appel $composite->insert(…) a lieu, il sera pris en charge par $liste_defil->insert. Les méthodes déjà définies par le widget composite ne seront pas déléguées ; si celui-ci utilise sa propre méthode insert, il faudra passer manuellement le contrôle au sous-widget.

XV-C-4 - Appel de ConfigSpecs

Lorsque l'on crée un widget composite, on veut pouvoir appeler la méthode configure sur celui-ci. Pour ce faire, on utilise ConfigSpecs, qui s'appelle de trois façons différentes : pour créer une option et pour la gérer, pour faire d'une option un synonyme d'une autre, ou pour préciser un widget par défaut qui prendra en charge tous les appels à configure.

Un widget composite simple, comme LabEntry, n'appellera ConfigSpecs que pour indiquer le widget par défaut qui sera chargé de toute la configuration :

$cw->ConfigSpecs(DEFAULT => [$saisie]);

On indique DEFAULT comme premier paramètre, puis on passe une liste anonyme contenant le widget à utiliser. Cet appel permettra de configurer la zone de saisie à chaque appel de configure avec la référence du widget composite.

XV-C-4-a - Création d'un synonyme

ConfigSpecs permet de créer un synonyme pour une option, pour disposer d'une version courte et longue de celle-ci, par exemple. Si l'on souhaite que l'option -file ait exactement la même signification que -filename, il suffit d'écrire :

$cw->ConfigSpecs('-file' => '-filename');

On indique d'abord le synonyme, puis l'option à laquelle il correspond.

XV-C-4-b - Définition d'options

Pour définir une nouvelle option et lui associer une action, on écrira :

$cw->ConfigSpecs(-nouvelle_option => [ <action>,
"nouvelle_Option",
"Nouvelle_Option",
<valeur_defaut> ]);

On précise d'abord la nouvelle option pour laquelle on veut créer une action, puis on utilise une liste anonyme de quatre éléments. Le premier est l'action à réaliser et doit être "DESCENDANTS", "ADVERTISED", "SELF", "CHILDREN", "PASSIVE", "METHOD", "CALLBACK", ou une référence à un sous-widget. Les deux éléments suivants concernent la base de données des options et sont, éventuellement, vides. Le dernier élément est la valeur par défaut que prendra cette option : undef, "" ou n'importe quelle valeur que vous souhaitez affecter à cette option lorsqu'elle n'est pas spécifiée explicitement.

Les valeurs possibles de l'action sont définies ainsi :

DESCENDANTS

1. La configuration de cette option sera appliquée récursivement à tous les descendants du widget composite.

ADVERTISED

1. La configuration sera appliquée à tous les sous-widgets avertis par Advertise.

SELF

1. La configuration sera appliquée au widget de base (ici un cadre, mais cela peut-être un autre widget composite).

CHILDREN

1. La configuration sera appliquée à tous les fils.

PASSIVE

1. La valeur sera stockée dans $params. C'est de cette façon que l'on utilisera ConfigSpecs pour toutes les options utilisables au moment de la création ou par les méthodes personnalisées du widget composite.

METHOD

1. Appellera la méthode portant le même nom que l'option. Si, par exemple, on utilise $cw->ConfigSpecs(-nouvelle_option => ["METHOD", "","", undef]) et que -nouvelle_option est utilisée, cela invoquera la méthode nouvelle_option (qui doit être définie quelque part dans le fichier). Cette action fonctionne lorsqu'aucune des autres ne peut être utilisée pour définir une option.

CALLBACK

1. Si la nouvelle option est configurée ou utilisée à la création du widget, cette action invoquera une méthode du widget composite.

$cw->ConfigSpecs(-mon_opt => ["CALLBACK", "ma_Methode",
"Ma_Methode", undef]) ;

2. par exemple, appellera la méthode ma_Methode lorsque l'option -mon_opt sera utilisée (à titre d'exemple, étudiez également les appels à ConfigSpecs dans le fichier BrowseEntry.pm ci-dessous).

$reference

1. Force un appel à $reference->configure(-option => valeur). Habituellement, $reference est un sous-widget du widget composite (une zone de saisie, par exemple).

XV-C-4-c - Exemple d'appel à ConfigSpecs

Voici l'appel à ConfigSpecs dans la version Tk8.0 de BrowseEntry.pm :

$w->ConfigSpecs(
-listwidth  => [qw/PASSIVE listWidth ListWidth/, undef],
-listcmd    => [qw/CALLBACK listCmd ListCmd/, undef],
-browsecmd  => [qw/CALLBACK browseCmd BrowseCmd/, undef],
-choices    => [qw/METHOD choices Choices/, undef],
-state      => [qw/METHOD state State normal/],
-arrowimage => [{-image => $b}, qw/arrowImage ArrowImage/, undef],
-variable   => "-textvariable",
DEFAULT     => [$e] );

Comme vous pouvez le constater, on utilise ici plusieurs paires d'informations. Il n'y a qu'une option PASSIVE, deux CALLBACK, et deux METHOD. Tous les autres appels à configure seront redirigés vers le sous-widget $e. Examinez le code complet afin de savoir ce que font les méthodes utilisées dans ConfigSpecs.

XV-C-5 - Résumé sur les widgets construits à partir d'un cadre

Voici un pseudo-code résumant la création d'un widget composite à partir d'un cadre :

$package NouveauWidget;
@ISA = qw(Tk::Frame);

Tk::Widget->Construct('NouveauWidget');

sub Populate()
{
my ($cw, $params) = @_;

# Ne traite que les options de création seulement
my $valeur = delete $params->{-option};
if (defined $valeur) {
…
}

# Création de tous les sous-widgets souhaités…
$widget = $cw->Widget(…);

$cw->Delegates();
$cw->ConfigSpecs( … );
}

sub mon_option {
…
}

1;

XV-D - Widgets composites construits à partir d'un widget de premier niveau

Il n'existe qu'une seule petite différence entre un widget composite construit à partir d'un cadre et un construit à partir d'un widget de premier niveau. Si l'on souhaite utiliser la méthode new pour créer la fenêtre, il faut définir InitObject au lieu de Populate. Cependant, la plupart des widgets composites fournis avec la distribution de Tk ne le font pas. Examinez les fichiers ColorEditor.pm et DialogBox.pm pour avoir des exemples de création de tels widgets composites. Toutes les règles d'utilisation de ConfigSpecs restent identiques.

XVI - Méthodes communes à tous les widgets

Tous les chapitres déjà vus ne traitaient que de widgets spécifiques. Celui-ci s'intéresse aux méthodes communes à tous les widgets : vous ne les utiliserez sûrement pas toutes, mais certaines sont fréquemment utilisées.

La plupart du temps, on utilise une référence vers un widget MainWindow (habituellement nommée $mw dans les exemples de ce livre) afin d'appeler ces méthodes, mais il est également possible de le faire à partir de références à d'autres widgets, comme $bouton, $bouton_radio, etc. Beaucoup de ces méthodes ne font que renvoyer des informations : on ne leur passe pas de paramètre et elles renvoient une valeur.

Nous utiliserons ici la référence générique $widget au lieu d'une référence vers un type particulier ; rappelons-nous que ces méthodes ne sont pas propres à un type spécifique de widget.

XVI-A - Construction d'une généalogie

Les méthodes children, name, parent, toplevel, manager et class s'occupent des ancêtres ou des fils des widgets, ainsi que de leur création.

XVI-A-1 - Fils d'un widget

On utilisera la méthode children pour connaître les fils d'un widget (habituellement de premier niveau ou un cadre) :

@fils = $widget->children();
# par exemple : Tk::Button=HASH(0x85e3a0) Tk::Button=HASH(0x85e4a8)

La liste renvoyée contient des scalaires représentant les fils du widget. Ces références serviront à réaliser certaines opérations par exemple la configuration de la couleur du fond ou la modification de la fonte.

XVI-A-2 - Nom d'un widget

Pour connaître le nom sous lequel le widget est connu de son parent, on utilise la méthode name :

$nom = $widget->name();

Les deux méthodes précédentes peuvent être combinées de la façon suivante :

@fils = $widget->children();
foreach (@fils) {
print "Nom :", $_->name(), "";
}

Voici un exemple de ce que cela pourrait produire :

bouton
bouton1

XVI-A-3 - Parent d'un widget

La méthode parent permet d'obtenir une référence au parent du widget :

$parent = $widget->parent();

XVI-A-4 - Premier niveau d'un widget

La méthode toplevel renvoie le widget de premier niveau contenant le widget :

$chemin = $widget->toplevel();

Le $chemin renvoyé est un nombre (8606484, par exemple) que l'on peut comparer à celui produit par un autre appel à toplevel pour déterminer, par exemple, si deux widgets appartiennent au même widget de premier niveau.

XVI-A-5 - Gestionnaire d'espace d'un widget

Afin de connaître le gestionnaire d'espace utilisé pour placer un widget, on utilise la méthode manager :

$gestionnaire = $widget->manager();

Cet appel renverra une chaîne contenant le nom du gestionnaire d'espace utilisé ; si le widget est de premier niveau, cette chaîne sera "grid", "pack", "place", ou "wm". Cette méthode semble ne pas fonctionner correctement sous Windows 95, mais ne pose pas de problème sous Unix et Windows NT.

XVI-A-6 - Classe d'un widget

La méthode class renvoie une liste indiquant la classe du widget. $liste->class(), par exemple, renverra "Listbox" et $menu->class(), "Menu".

XVI-A-7 - Identificateur d'un widget

On obtient la chaîne d'identification d'un widget avec la méthode id :

$id = $widget->id();
print "$id";
# Affiche 0x9c944c

La valeur renvoyée est exprimée en hexadécimal. Cette méthode ne fonctionne pas sous Windows 95.

XVI-A-8 - Chemin d'un widget

Le chemin d'accès d'une fenêtre est obtenu en appelant pathname avec l'identificateur renvoyé par la méthode id :

$chemin = $widget->pathname($id);

Il existe également une méthode PathName :

$chemin = $mw->PathName();

Celle-ci renvoie le chemin du widget qui l'a appelée. Dans le cas de $mw, par exemple, le résultat serait ".".

XVI-B - Méthodes concernant la couleur

Les méthodes colormapfull, rgb, cells et depth permettent de gérer les couleurs.

XVI-B-1 - La palette de couleurs est-elle saturée ?

La méthode colormapfull permet de déterminer si la palette de couleurs du widget est pleine :

$est_pleine = $widget->colormapfull();

colormapfull renvoie 1 si la palette est pleine, 0 sinon.

XVI-B-2 - Nombre de cellules

La méthode cells renvoie le nombre de cellules de la palette de couleurs :

$nbre = $widget->cells();

Ce nombre indique le nombre de couleurs : 64, par exemple.

XVI-B-3 - Profondeur de couleur

La méthode depth permet d'obtenir le nombre de bits par pixel(41) :

$profondeur = $widget->depth();
# $profondeur contiendra 16, par exemple.

XVI-B-4 - Conversion en valeur RGB

La méthode rgb permet de traduire un nom de couleur en valeurs de rouge, de vert et de bleu. On lui passe le nom (l'un de ceux qui ont été traités au chapitre 3), et elle renvoie une liste de trois éléments représentant, respectivement, les composantes rouge, vert et bleu.

($rouge, $vert, $bleu) = $widget->rgb("SlateGrey");

Les variables $rouge, $vert et $bleu contiennent maintenant un entier compris entre 0 et 255 (dans cet exemple : 112, 128 et 144).

XVI-B-5 - Configuration des couleurs

La méthode setPalette permet à votre application d'utiliser automatiquement des couleurs en fonction d'une couleur donnée.

$widget->setPalette(couleur);

La couleur de fond de $widget sera de la couleur spécifiée et les couleurs de tous les autres widgets seront calculées à partir de celle-ci. Ainsi, si le contour d'un bouton est plus clair que le fond, il apparaîtra dans une teinte plus claire que la couleur choisie. Cette méthode affecte l'application entière, même si on l'a appelée sur un widget plutôt que sur une fenêtre.

Les couleurs de certaines options sont configurables ; il suffit de fournir le nom de l'option, puis la couleur que l'on souhaite lui associer. Le fragment de code suivant, par exemple, met en rouge l'avant-plan de l'application et en bleu le fond :

$b->setPalette("background" => "blue", "foreground" => "red");

XVI-B-6 - Configuration de couleur prédéfinie

La méthode bisque utilise la configuration de couleur « bisque" pour toute l'application. L'appel $widget->bisque() est équivalent à $widget->setPalette("bisque").

XVI-C - Bases de données des options

Le système X Window utilise un fichier .Xdefaults, situé dans le répertoire personnel de l'utilisateur. Ce fichier contient des informations sur la configuration des applications X, comme les couleurs et les fontes utilisées et est entièrement modifiable par l'utilisateur. On peut créer le même type de fichier pour les systèmes Win32 et l'utiliser si nécessaire.

Habituellement, les lignes de ce fichier ressemblent à celles-ci :

screen*background: yellow
screen.button.foreground: green
screen*font: {Arial} 24 {normal}

Le premier élément d'une ligne est le nom de l'application, sauf si les options ne concernent que celle-ci. L'exemple ci-dessus concerne une application contenue dans un fichier nommé screen, c'est pourquoi ce nom est le premier mot-clé de chaque ligne. Le second élément (s'il est présent) est un type ou un nom de widget (on attribue un nom à n'importe quel widget en utilisant l'option -name lors de sa création). Le troisième mot-clé est la « classe » pour laquelle on veut fixer une valeur par défaut. Cette valeur peut être attribuée à toute option du widget. L'annexe A vous permettra de trouver la classe associée à chaque type de widget.

Pour lire ce fichier, on appelle la méthode optionReadfile, en lui passant son emplacement en paramètre ("options_couleur", "C:Xdefaults" ou ".Xdefaults", par exemple) :

$widget->optionReadfile("mon_fic");

Assurez-vous d'ajouter un caractère retour chariot à la fin de la dernière ligne de ce fichier, sous peine de provoquer une erreur affichant un message comme « missing newline on line 2 at C:\PERL\lib\site\Tk\Submethods.pm line 16 »(42) . Celui-ci n'a pas grand sens, excepté le premier numéro de ligne qui correspond au nombre de lignes que l'on a tenté de lire dans le fichier d'options. Si l'on utilise

$widget->option("readfile"…)

pour appeler cette méthode, le message sera plus explicite.

Le deuxième paramètre de optionReadfile est facultatif : il permet d'indiquer une priorité valant "widgetDefault", "startupFile", "userDefault", ou "interactive". La priorité la plus haute, qui est également celle par défaut, est "interactive".

$widget->optionReadfile("mon_fic", "widgetDefault");

On ajoute une option pendant l'exécution du programme à l'aide de la méthode optionAdd (que l'on ait, ou non, utilisé optionReadfile).

$widget->optionAdd(motif => valeur);

On peut, par exemple, modifier la fonte du programme entier à l'aide de l'instruction suivante :

$widget->optionAdd("screen*font", "Arial 24 normal");

La méthode optionClear réinitialise toutes les options courantes et relit le fichier (ou récupère celles-ci en interrogeant le gestionnaire de ressources) :

$widget->optionClear();

La méthode optionGet permet de connaître la valeur courante associée à un nom et à une classe :

$widget->optionGet(nom, classe);

XVI-D - Le nom de l'application

Le nom de l'application utilisé dans le fichier d'options étudié ci-dessus est, par défaut, le nom du fichier de script. La méthode appname permet de modifier le nom de l'application :

$mw->appname("nouveau_nom");

Sans paramètre, cette méthode renvoie le nom courant de l'application :

$name = $mw->appname();

XVI-E - Existence d'un widget

Pour savoir si un widget a été créé, il suffit d'utiliser l'appel Exists($widget) :

if (Exists($widget)) {
…
}

Notez le « E » majuscule de cette méthode. Celle-ci est différente de la fonction exists de Perl : attention à ne pas les confondre !

XVI-F - Le widget est-il affiché ?

La méthode ismapped permet de savoir si le widget a été affiché à l'écran :

if ($widget->ismapped()) {
# faire quelque chose
} else {
# affiche le widget
}

Cette méthode renvoie 1 si le widget est actuellement affiché à l'écran, 0 sinon.

XVI-G - Conversion des distances d'écran

Si vous préférez utiliser des pouces comme unité de distance d'écran, mais que vous souhaitez afficher en comptant en pixels, utilisez la méthode pixels pour convertir n'importe quelle distance d'écran en valeur exprimée en pixels :

$pixels = $widget->pixels("2i"); # 2 pouces en pixels ?
$pixels = $widget->pixels("2m"); # 2 millimètres en pixels ?

La méthode pixels arrondit la valeur au pixel entier le plus proche. Pour obtenir un résultat fractionnaire, utilisez fpixels :

$pixels = $widget->fpixels("2i"); # 2 pouces en pixels ?
$pixels = $widget->fpixels("2m"); # 2 millimètres en pixels ?

XVI-H - Taille d'un widget

Les méthodes suivantes permettent, par divers moyens, de connaître les différentes composantes de la taille d'un widget.

XVI-H-1 - Géométrie d'un widget

La méthode geometry renvoie la chaîne de géométrie du widget sous la forme largeur x hauteur+x+y.

$geom = $widget->geometry();

Cette chaîne a déjà été détaillée au chapitre 13. Les valeurs sont exprimées en pixels.

XVI-H-2 - Hauteur demandée

La méthode reqheight renvoie la hauteur du widget :

$hauteur = $widget->reqheight();

C'est le widget lui-même qui détermine sa hauteur correcte.

XVI-H-3 - Largeur demandée

La méthode reqwidth renvoie la largeur du widget :

$largeur = $widget->reqwidth();

XVI-H-4 - Largeur réelle

La méthode width renvoie la largeur courante du widget, tel qu'il est affiché :

$largeur_courante = $widget->width();

Lorsque le widget est créé, width retournera 1 jusqu'à ce que l'application ait fini de tout tracer. Elle renverra ensuite la largeur réelle du widget.

XVI-H-5 - Hauteur réelle

La méthode height renvoie la hauteur courante du widget :

$hauteur_courante = $widget->height();

Tout comme width, la méthode height renvoie 1 lorsque le widget est créé. On utilisera les méthodes update ou afterIdle pour forcer la suite des événements et on appellera ensuite height ou width pour obtenir les valeurs finales.

XVI-I - Position d'un widget

Toutes les méthodes de cette section gèrent la position d'un widget.

XVI-I-1 - Position relative à la fenêtre racine

La méthode containing permet de savoir quel est le widget se trouvant aux coordonnées $x et $y :

$qui = $widget->containing($x, $y);

$x et $y doivent être exprimées relativement à la fenêtre racine (ou au bureau, sur les systèmes Win32). La méthode renverra une chaîne vide s'il n'y a pas de widget à ces coordonnées. Si plusieurs s'y trouvent, c'est celui du dessus qui sera renvoyé.

XVI-I-2 - Coordonnées relatives au parent

Les méthodes x et y renvoient les coordonnées du coin supérieur gauche du widget, relativement à son parent :

$x = $widget->x();
$y = $widget->y();

XVI-I-3 - Coordonnées relatives à la fenêtre racine

Les méthodes rootx et rooty renvoient les coordonnées du widget, relativement à la fenêtre racine :

$x = $widget->rootx();
$y = $widget->rooty();

Ces coordonnées désignent le coin supérieur gauche du widget.

XVI-I-4 - Coordonnées du bureau virtuel

Certaines méthodes spéciales permettent d'obtenir des coordonnées relatives à un bureau virtuel, si vous en utilisez un. Les bureaux virtuels sont très courants sous X Window (notamment avec les gestionnaires de fenêtres fvwm et tvtwm), mais existent aussi sous Microsoft Windows.

Les méthodes vrootheight et vrootwidth renvoient, respectivement, la hauteur et la largeur du bureau virtuel :

$hauteur = $widget->vrootheight();
$largeur = $widget->vrootwidth();

Pour obtenir les coordonnées du coin supérieur gauche du widget, relativement au bureau virtuel, il suffit d'utiliser les méthodes vrootx et vrooty :

$x = $widget->vrootx();
$y = $widget->vrooty();

Ces quatre méthodes renvoient toutes une chaîne vide si elles ne détectent pas de bureau virtuel.

XVI-I-5 - Coordonnées du pointeur de la souris relativement au bureau

Les méthodes pointerx, pointery et pointerxy permettent de déterminer l'emplacement sur lequel on a cliqué dans le widget :

$x = $widget->pointerx();
$y = $widget->pointery();
($x, $y) = $widget->pointerxy();

Toutes les coordonnées obtenues sont relatives au bureau (même s'il est virtuel).

XVI-J - Informations sur l'écran

Les méthodes suivantes renvoient des informations concernant l'écran (qui est un bureau normal ou virtuel) et les couleurs utilisées par le bureau.

XVI-J-1 - Nom de l'écran

Chaque écran utilisé possède un nom ; pour le connaître, il suffit d'utiliser la méthode screen :

$nom = $widget->screen();

Le nom renvoyé sera au format "NomAffichage.IndexEcran" : il s'agit de ":0.0" sur ma machine Windows 95.

XVI-J-2 - Hauteur et largeur de l'écran

La hauteur et la largeur de l'écran représentent, en fait, sa résolution. On a parfois besoin de ces informations pour connaître la dimension maximale d'une fenêtre qui dépend du système employé par l'utilisateur. Les méthodes screenheight et screenwidth renvoient ces deux valeurs exprimées en pixels :

$hauteur = $widget->screenheight();
$largeur = $widget->screenwidth();

Si la résolution de l'écran est de 768 par 1024 pixels, la première méthode renverra 768 et la deuxième 1024. Si l'on préfère obtenir ces valeurs en millimètres, il suffit d'utiliser screenmmheight et screenmmwidth :

$hauteur_mm = $widget->screenmmheight();
$largeur_mm = $widget->screenmmwidth();

La même résolution, 768 par 1024, font que ces méthodes renvoient, respectivement, 203 et 270 millimètres pour mon écran.

XVI-J-3 - Nombre de cellules

La méthode screencells renvoie le nombre de cellules de la palette de couleurs par défaut :

$nombre = $widget->screencells();

La palette de couleurs par défaut de ma machine Windows 95 possède 64 cellules.

XVI-J-4 - Profondeur de l'écran

La méthode screendepth renseigne sur le nombre de bits qu'utilise l'écran pour représenter un pixel :

$profondeur = $widget->screendepth();

Ma machine Windows 95 utilise 16 bits par pixel.

XVI-J-5 - Type de couleur

Le type de couleur dépend de la classe de couleur employée. Les classes possibles sont "directcolor", "grayscale", "pseudocolor", "staticcolor", "staticgray", ou "truecolor". La méthode screenvisual renvoie celle de l'écran contenant le widget :

$type = $widget->screenvisual();

Pour connaître la classe de couleur du widget lui-même, on utilisera la méthode visual :

$type = $widget->visual();

La méthode visualsavailable permet de connaître la liste de toutes les classes disponibles pour la configuration courante :

@liste = $widget->visualsavailable();

Chaque élément de @liste décrit la classe et la profondeur de couleur de l'écran contenant le widget. Sur ma machine Windows 95, elle ne contient qu'un élément : "truecolor16".

XVI-J-6 - Type de serveur

La méthode server renvoie le type du serveur d'affichage :

$serveur = $widget->server();

Le type de serveur de Windows 95 est "Windows 4.0 67109975 Win32".

XVI-J-7 - Le widget est-il visible ?

Un widget est considéré comme visible si lui-même et tous ses ancêtres sont affichés. La méthode viewable interroge un widget sur sa visibilité courante :

$est_visible = $widget->viewable();

Cette méthode renvoie 1 si le widget est visible, 0 sinon.

XVI-K - Méthodes applicables aux atomes

À chaque widget est associé un atome constitué d'une chaîne contenant un nom (que l'on peut obtenir pour chaque widget à l'aide de la méthode name) et d'un identificateur sur 32 bits. Les méthodes que nous allons présenter ici sont utilisées de façon interne pour gérer des tâches telles que le mécanisme de sélection.

Si l'on passe le nom d'un widget à la méthode atom, celle-ci renvoie son identificateur 32 bits :

$id = $widget->atom($widget->name());

La démarche opposée consiste à passer l'identificateur du widget à la méthode atomname, afin d'obtenir son nom :

$nom = $widget->atomname($id);

XVI-L - Émettre un bip

Pour que l'ordinateur avertisse l'utilisateur par un signal sonore, on utilise la méthode bell :

$widget->bell();

XVI-M - Méthodes pour le presse-papiers

Les méthodes suivantes permettent de manipuler le presse-papiers interne de Tk, ainsi que celui du système de fenêtrage (X Window ou Win32).

La méthode clipboardAppend ajoute des données au presse-papiers :

$widget->clipboardAppend("données à ajouter");

Lorsque l'on appelle cette méthode, on peut indiquer un format à l'aide de l'option -format qui vaut, par défaut, "STRING". L'autre valeur possible est "ATOM". L'option -type peut également être utilisée, sa valeur est une chaîne telle que "STRING" ou "FILE_NAME".

Pour vider le presse-papiers (supprimer toutes les données qu'il contient), on utilisera la méthode clipboardClear :

$widget->clipboardClear();

Pour savoir ce qu'il y a dans le presse-papiers, étudiez la méthode selectionGet, traitée dans la section « Obtenir la sélection ».

XVI-N - Méthodes de sélection

Certains widgets permettent de sélectionner du texte ; c'est le cas, par exemple des widgets texte, des zones de saisie et des boîtes de liste. La sélection est manipulable par les méthodes suivantes.

XVI-N-1 - Annuler la sélection

La méthode SelectionClear permet d'annuler la sélection courante (et une éventuelle sélection X) :

$widget->SelectionClear();

On peut utiliser une option -selection, valant "PRIMARY" par défaut. Si elle vaut "CLIPBOARD", le presse-papiers sera également vidé.

XVI-N-2 - Obtenir le texte sélectionné

La méthode SelectionGet renvoie le texte sélectionné dans l'application :

$selection = $widget->SelectionGet();

L'option -selection est également reconnue par SelectionGet :

$presse_papier = $widget->SelectionGet(-selection => "CLIPBOARD");

Comme précédemment, -selection vaut "PRIMARY" ou "CLIPBOARD". La valeur par défaut est "PRIMARY" et, si l'on n'utilise pas cette option, la valeur renvoyée par SelectionGet sera donc le texte sélectionné dans l'application. Si cette option vaut "CLIPBOARD", la méthode renverra le contenu du presse-papiers.

XVI-N-3 - Assignation d'une fonction de rappel

La méthode SelectionHandle permet d'assigner une fonction de rappel qui sera automatiquement invoquée lorsque la sélection de $fenetre sera modifiée :

$widget->SelectionHandle($fenetre => \&fct_rappel);

La fonction de rappel fct_rappel sera invoquée lorsqu'une sélection sera faite dans $fenetre. On peut utiliser les options -format, -type, et -selection en leur donnant les mêmes valeurs que dans les exemples précédents. Si le nom de la fonction de rappel passée à SelectionHandle est une chaîne vide, cela supprimera la fonction de rappel déjà assignée.

XVI-N-4 - Connaître le propriétaire du texte sélectionné

La méthode SelectionOwner renvoie le widget propriétaire du texte sélectionné (le widget dans lequel la sélection a eu lieu).

$widget = $widget->SelectionOwner();

L'option -selection, et ses valeurs "PRIMARY" ou "CLIPBOARD", permet de préciser si elle appartiendra au propriétaire de la sélection ou au presse-papiers.

XVI-N-5 - Attribuer un propriétaire à une sélection

La méthode SelectionOwn force un widget à devenir le propriétaire de la sélection :

$widget->selectionOwn();

Le type de sélection considéré sera éventuellement précisé via l'option -selection. L'option -command, associée à une fonction de rappel, permettra d'invoquer celle-ci lorsque cette sélection de widget sera forcée.

XVI-O - Suppression d'un widget

La méthode destroy détruit le widget sur lequel elle est appelée (l'utilisation de if Tk::Exists est recommandée) :

$widget->destroy() if Tk::Exists($widget);

Si le widget est parent d'autres widgets, ceux-ci seront également détruits.

XVI-P - Méthodes de gestion du focus clavier

La méthode focus, appelée sur un widget, permet de le forcer à posséder le focus clavier :

$widget->focus();

Cela peut s'avérer nécessaire si l'on dispose d'une zone de saisie dans laquelle l'utilisateur doit d'abord entrer des informations. L'appel de focus juste avant celui de la fonction MainLoop fera que le widget obtiendra le focus. Si l'on presse la touche de tabulation, le focus sera automatiquement passé au widget suivant (rappelez-vous que l'on sait qu'un widget a le focus grâce au rectangle qui l'entoure). Les méthodes suivantes permettent de manipuler le focus.

La méthode focusFollowsMouse permet au focus de suivre le pointeur de la souris :

$widget->focusFollowsMouse();

Cette méthode fonctionne mal, que ce soit sous Windows 95 ou sous Unix. Un correctif a récemment été mis au point pour Tk8 : assurez-vous de l'avoir appliqué si vous comptez utiliser cette méthode.

Pour savoir quel est le widget qui possède le focus, il suffit d'appeler la méthode focusCurrent :

$qui = $widget->focusCurrent();

La méthode focusForce force un widget à prendre le focus, même si l'application n'est pas active à ce moment-là :

$widget->focusForce();

Ce n'est pas une bonne pratique, aussi je vous conseille de ne pas le faire.

Pour connaître le widget ayant eu le focus en dernier, on utilisera la méthode focusLast :

$qui = $widget->focusLast();

Si aucun des widgets de la fenêtre n'a le focus, cette méthode renvoie celui de premier niveau.

Pour connaître l'ordre dans lequel le focus est passé aux widgets, on utilisera les méthodes focusNext et focusPrev :

$widget_suiv = $widget->focusNext();
$widget_prec = $widget->focusPrev();

XVI-Q - Méthodes de capture

Lorsqu'une fenêtre effectue une « capture », elle s'attribue toutes les entrées au clavier et à la souris et interdit donc aux autres fenêtres de l'application de recevoir des entrées. Il existe également une capture globale, qui bloque les entrées pour toutes les applications du système à l'exception de la fenêtre qui l'a effectuée. Ces méthodes sont généralement appelées sur un widget de premier niveau.

La méthode grab effectue une capture locale :

$widget->grab();

Avec une telle capture, on peut interagir avec d'autres applications du système, mais pas avec d'autres fenêtres de la même application que celle de $widget. Pour effectuer une capture globale, on utilisera la méthode grabGlobal :

$widget->grabGlobal();

Pour « relâcher », il suffit d'appeler la méthode grabRelease :

$widget->grabRelease();

La méthode grabCurrent renvoie le widget qui a effectué une capture :

$qui = $widget->grabCurrent();

La méthode grabStatus renvoie l'état de capture courant d'un widget :

$etat = $widget->grabStatus();

La chaîne renvoyée sera "none", "local", ou "global".

La méthode grabs renvoie la liste de toutes les fenêtres concernées par la capture courante :

@fenetres = $widget->grabs();

XVI-R - Communication entre les applications

La méthode send permet aux applications Perl/Tk (et Tcl/Tk) de communiquer. Ses paramètres sont l'application à contacter et la commande à exécuter par celle-ci :

$widget->send("application" => fct_rappel);

On peut également utiliser l'option -async afin de redonner immédiatement le contrôle à l'appelant au lieu d'attendre la fin de l'exécution de la fonction de rappel.

Par défaut, votre application renverra une erreur à une autre application essayant de communiquer avec elle. Si vous souhaitez autoriser ces communications, exécutez Tk::Receive($widget, "command") et faites très attention au contenu de la chaîne de commande : permettre à une application d'envoyer à la vôtre des commandes inconnues est dangereux.

Lorsque vous autorisez les communications entre applications, il est préférable de lancer votre script Perl avec le commutateur -T, qui force une vérification des instructions dangereuses.

XVI-S - Attente d'événements

En certains points d'une application, il peut s'avérer utile d'attendre qu'un événement survienne. Si, par exemple, on crée une fenêtre ColorEditor pour qu'elle assigne la couleur choisie à une variable, et que l'on souhaite que l'application attende que cette variable ait été affectée, on utilisera la méthode waitVariable :

$widget->waitVariable(\$var);

Le traitement continuera dès que la valeur de $var a été modifiée.

Pour attendre qu'un widget devienne visible, on utilisera waitVisibility :

$widget->waitVisibility();

La méthode waitWindow attendra que le widget concerné soit détruit :

$widget->waitWindow();

Lorsque l'on appelle ces méthodes, rien ne se passera tant que l'événement attendu n'a pas lieu.

Au lieu d'appeler waitWindow, on peut invoquer OnDestroy en lui passant une fonction de rappel en paramètre. Les méthodes du widget sont encore disponibles lorsque l'on utilise OnDestroy :

$widget->OnDestroy(sub { … };

XVI-T - Événements concernant les fichiers

La méthode fileevent est une méthode spéciale : on l'utilise pour savoir si un fichier est accessible en lecture ou en écriture, et en être averti. Voici un exemple de code montrant son utilisation (il doit être exécuté sur un système Unix, car il utilise la commande tail(43)) :(44)

use Tk;
open (FIC, "tail -f -n 25 fichier_texte|") or
die "Impossible d'ouvrir le fichier !!";
my $mw = MainWindow->new();
my $texte = $mw->Scrolled("Text",
-width => 80,
-height => 25)->pack(-expand => 1);
$mw->fileevent(FIC, 'readable', [\&inserer_texte]);

MainLoop();

sub inserer_texte
{
my $ligne_courante;
if ($ligne_courante = <FIC>) # lit la première des 25 dernières lignes
{
$texte->insert('end', $ligne_courante);
$texte->yview('moveto', 100);
}
else {
$mw->fileevent(FIC, 'readable', "");
}
}

Une fois lancé, ce petit programme s'arrête pour attendre que le fichier soit lisible, puis insère l'information lue dans une zone de texte. On peut également utiliser 'writable' :

$mw->fileevent(FIC, 'writable', fct_rappel);

Si l'on ne précise pas le paramètre concernant la fonction de rappel, fileevent renverra celle qui est assignée. Si l'on passe une chaîne vide à la place de la fonction de rappel, celle qui est en place sera ôtée.

XVI-U - Traitement des options de la ligne de commande

Dans le monde Unix, il est courant de préciser des options sur la ligne de commande qui invoque une application : il n'est pas inhabituel de lancer un script par la commande monscript -geometry "80x40". Pour que Perl/Tk analyse et applique automatiquement ces options pour vous, il suffit d'appeler la méthode CmdLine immédiatement après la création de la fenêtre principale :

$mw->CmdLine();

Avec Tk4, si l'on souhaite que CmdLine arrête de traiter les paramètres de la ligne de commande et qu'elle en laisse certains que vous traiterez vous-même, il faut placer deux tirets (--) avant ceux que vous voulez qu'elle vous laisse :

monscript -geometry "80x40" -- -monoption

Avec Tk8.0, le traitement automatique des options cessera dès qu'une option inconnue est rencontrée.

Une autre façon de traiter les options de la ligne de commande consiste à utiliser les modules Getopts de Perl. Consultez l'ouvrage Programmation en Perl(45) pour savoir comment utiliser les fonctions disponibles de Getopts. Celles-ci ne gèrent pas les options pour vous, mais les placent dans une structure de données qu'il vous sera facile de traiter.

XVI-V - Temporisations

Par moment, un programme doit attendre un certain temps avant de poursuivre son exécution. Peut-être souhaitez-vous également qu'il exécute la même commande toutes les minutes ? La méthode after fera patienter votre programme pendant le nombre de millisecondes spécifié :

$widget->after(millisecondes);

Pour qu'une fonction de rappel soit invoquée après un certain délai, il suffit de la passer comme deuxième paramètre de after :

$id = $widget->after(millisecondes, fct_rappel);
# Exemple :
$id = $widget->after(1000, \&faire_qqchose);

Si vous souhaitez exécuter une fonction après une certaine période d'inactivité du programme, appelez la méthode afterIdle :

$id = $widget->afterIdle(fct_rappel);

La méthode afterCancel, utilisée avec l'identificateur renvoyé par l'appel à after ou afterIdle, annule cet appel :

$widget->afterCancel($id);
# On peut aussi utiliser :
$id->cancel();

La méthode repeat permet d'appeler la même fonction de rappel à intervalles réguliers :

$widget->repeat(millisecondes, fct_rappel) ;
# Exemple :
$widget->repeat(600, \&mettre_a_jour);

Si vous détruisez $widget, cela supprimera automatiquement tous les appels à after et repeat.

XVII - Configuration des widgets avec configure et cget

Tous les widgets de Perl/Tk (et certains fournis séparément) peuvent utiliser les méthodes configure et cget. Les paramètres passés à ces méthodes sont les mêmes quel que soit le widget, et leurs résultats ont le même format.

La méthode configure vous permet d'assigner ou de modifier la valeur d'une option d'un widget, et d'en obtenir la valeur. La méthode cget ne peut affecter des valeurs, mais elle permet d'interroger les options plus simplement que configure.

XVII-A - La méthode configure

La forme classique d'un appel à configure est la suivante :

$widget->configure( [ option => nouvelle_valeur… ] );

Selon les paramètres qui lui sont passés, cette méthode réalisera l'une de ces trois opérations :

- affectation ou modification des valeurs des options de $widget ;

- renvoi de la valeur courante de n'importe quelle option de $widget ;

- renvoi des valeurs courantes de toutes les options de $widget.

Pour affecter ou modifier la valeur d'une option, il suffit de passer en paramètre la paire option/valeur, comme on le fait lors de la création d'un widget :

$widget->configure(-option => nouvelle_valeur);

Quelle que soit l'option concernée, son effet se produira immédiatement. Pour connaître la valeur d'une seule option, il suffit de la passer en paramètre : la valeur renvoyée dépendra du fait que configure a été appelé dans un contexte de liste ou un contexte scalaire. La ligne suivante appelle la méthode dans un contexte de liste (car sa valeur de retour est assignée à un tableau) :

@infos = $widget->configure(-highlightthickness);

Dans un tel contexte, le résultat est un tableau de scalaires. Pour cet appel, il pourrait être :

-highlightthickness highlightThickness HighlightThickness 2 2

Le tableau renvoyé est composé des cinq valeurs suivantes (indiquées ici avec leur indice) :

0

Nom de l'option

1

Nom de l'option de la base de données des options (tel qu'il apparaîtrait dans le fichier .Xdefaults)

2

Classe dans la base de données des options

3

Valeur par défaut de l'option

4

Valeur courante de l'option

C'est souvent la valeur courante de l'option qui nous intéresse : en ce cas, il suffit d'appeler configure dans un contexte scalaire en affectant son résultat à une variable scalaire :

$valeur = $widget->configure(-highlightthickness);
print "$valeur\n";

Le résultat serait alors :

2

Si vous voulez obtenir la liste des valeurs de toutes les options reconnues par le widget, utilisez cette forme d'appel :

@config = $widget->configure();

@config est un tableau de tableaux. La façon la plus simple de l'afficher consiste à utiliser Tk::Pretty, qui se chargera de la tâche délicate de parcourir les tableaux et de mettre les informations sous une forme lisible :

use Tk;
use Tk::Pretty;

$mw = MainWindow->new();
$widget = $mw->Button;
@config = $widget->configure();
print Pretty @config;

Le résultat est le suivant(46) :

['-activebackground',activeBackground,Foreground,'#ececec','#ececec'],
['-activeforeground',activeForeground,Background,Black,Black],
['-activeimage',activeImage,ActiveImage,undef,undef],
['-anchor','anchor',Anchor,'center','center'],
['-background','background',Background,'#d9d9d9','#d9d9d9'],
['-bd',borderWidth],['-bg','background'],
['-bitmap','bitmap',Bitmap,undef,undef],
['-borderwidth',borderWidth,BorderWidth,2,2],
['-command','command',Command,undef,undef],
['-cursor','cursor',Cursor,undef,undef],
['-default','default',Default,'disabled','disabled'],
['-disabledforeground',disabledForeground,DisabledForeground,'#a3a3a3', '#a3a3a3'],
['-fg','foreground'],
['-font','font',Font,'Helvetica -12 bold', bless('Helvetica -12 bold',Tk::font)],
['-foreground','foreground',Foreground,Black,Black],
['-height','height',Height,0,0],
['-highlightbackground',highlightBackground,HighlightBackground,'#d9d9d9', '#d9d9d9'],
['-highlightcolor',highlightColor,HighlightColor,Black,Black],
['-highlightthickness',highlightThickness,HighlightThickness,1,1],
['-image','image',Image,undef,undef],
['-justify','justify',Justify,'center','center'],
['-padx',padX,Pad,3m,9],['-pady',padY,Pad,1m,3],
['-relief','relief',Relief,'raised','raised'],
['-state','state',State,'normal','normal'],
['-takefocus',takeFocus,TakeFocus,undef,undef],
['-text','text',Text,undef,''],
['-textvariable',textVariable,Variable,undef,undef],
['-underline','underline',Underline,-1,-1],
['-width','width',Width,0,0],
['-wraplength',wrapLength,WrapLength,0,0]

Bien que cette liste semble illisible, Tk::Pretty place des crochets et des virgules pour que les différentes sous-listes soient bien distinctes. Habituellement, cette liste ne sert que dans les phases de mises au point. La fin de cette annexe énumère les valeurs par défaut pour chaque type de widget.

XVII-A-1 - La méthode cget

Les valeurs des options peuvent également être obtenues avec la méthode cget (configuration get) :

$widget->cget(-option);

Cet appel renvoie uniquement la valeur courante de l'option (ou une adresse, si l'option sert à stocker une référence) plutôt que la liste entière, telle qu'elle est produite par configure. Voici quelques exemples d'utilisation :

print $b->cget(-highlightthickness), "\n";
# Affiche 2

# Renvoie la référence :
print $menu_option->cget(-textvariable), "\n";

# Renvoie la valeur réelle :
print ${$option_menu->cget(-textvariable)}, "\n";
# ou…
$ref = $option_menu->cget(-textvariable);
print $$ref, "\n";

XVII-B - Valeurs par défaut, pour chaque widget

Les tableaux suivants contiennent toutes les options de tous les widgets standards de Tk8. Les quatre colonnes représentent les quatre premières valeurs renvoyées pour chaque option par configure.

Les informations contenues dans ces tableaux ont été obtenues en utilisant le bout de code suivant (substituez le widget voulu à Widget) :

$w = $mw->Widget->pack;
@config = $w->configure();
print Pretty @config;

XVII-B-1 - Widget Button

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-activebackground

activeBackground

Foreground

SystemButtonFace

-activeforeground

activeForeground

Background

SystemButtonText

-activeimage

activeImage

ActiveImage

undef

-anchor

anchor

Anchor

center

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-bitmap

bitmap

Bitmap

undef

-borderwidth

borderWidth

BorderWidth

2

-command

command

Command

undef

-cursor

cursor

Cursor

undef

-default

default

Default

disabled

-disabledforeground

disabledForeground

DisabledForeground

SystemDisabledText

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemButtonText

-height

height

Height

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

1

-image

image

Image

undef

-justify

justify

Justify

center

-padx

padX

Pad

1

-pady

padY

Pad

1

-relief

relief

Relief

raised

-state

state

State

normal

-takefocus

takeFocus

TakeFocus

undef

-text

text

Text

undef

-textvariable

textVariable

Variable

undef

-underline

underline

Underline

-1

-width

width

Width

0

-wraplength

wrapLength

WrapLength

0

XVII-B-2 - Widget Canvas

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

0

-closeenough

closeEnough

CloseEnough

1

-confine

confine

Confine

1

-cursor

cursor

Cursor

undef

-height

height

Height

7c

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

2

-insertbackground

insertBackground

Foreground

SystemButtonText

-insertborderwidth

insertBorderWidth

BorderWidth

0

-insertofftime

insertOffTime

OffTime

300

-insertontime

insertOnTime

OnTime

600

-insertwidth

insertWidth

InsertWidth

2

-relief

relief

Relief

flat

-scrollregion

scrollRegion

ScrollRegion

undef

-selectbackground

selectBackground

Foreground

SystemHighlight

-selectborderwidth

selectBorderWidth

BorderWidth

1

-selectforeground

selectForeground

Background

SystemHighlightText

-takefocus

takeFocus

TakeFocus

undef

-width

width

Width

10c

-xscrollcommand

xScrollCommand

ScrollCommand

undef

-xscrollincrement

xScrollIncrement

ScrollIncrement

0

-yscrollcommand

yScrollCommand

ScrollCommand

undef

-yscrollincrement

yScrollIncrement

ScrollIncrement

0

XVII-B-3 - Widget Checkbutton

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-activebackground

activeBackground

Foreground

SystemButtonFace

-activeforeground

activeForeground

Background

SystemWindowText

-anchor

anchor

Anchor

center

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-bitmap

bitmap

Bitmap

undef

-borderwidth

borderWidth

BorderWidth

2

-command

command

Command

undef

-cursor

cursor

Cursor

undef

-disabledforeground

disabledForeground

DisabledForeground

SystemDisabledText

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemWindowText

-height

height

Height

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

1

-image

image

Image

undef

-indicatoron

indicatorOn

IndicatorOn

1

-justify

justify

Justify

center

-offvalue

offValue

Value

0

-onvalue

onValue

Value

1

-padx

padX

Pad

1

-pady

padY

Pad

1

-relief

relief

Relief

flat

-selectcolor

selectColor

Background

SystemWindow

-selectimage

selectImage

SelectImage

undef

-state

state

State

normal

-takefocus

takeFocus

TakeFocus

undef

-text

text

Text

undef

-textvariable

textVariable

Variable

undef

-underline

underline

Underline

-1

-variable

variable

Variable

undef

-width

width

Width

0

-wraplength

wrapLength

WrapLength

0

XVII-B-4 - Widget Entry

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-background

background

Background

SystemWindow

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

2

-cursor

cursor

Cursor

xterm

-exportselection

exportSelection

ExportSelection

1

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemWindowText

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

0

-insertbackground

insertBackground

Foreground

SystemWindowText

-insertborderwidth

insertBorderWidth

BorderWidth

0

-insertofftime

insertOffTime

OffTime

300

-insertontime

insertOnTime

OnTime

600

-insertwidth

insertWidth

InsertWidth

2

-justify

justify

Justify

left

-relief

relief

Relief

sunken

-selectbackground

selectBackground

Foreground

SystemHighlight

-selectborderwidth

selectBorderWidth

BorderWidth

0

-selectforeground

selectForeground

Background

SystemHighlightText

-show

show

Show

undef

-state

state

State

normal

-takefocus

takeFocus

TakeFocus

undef

-textvariable

textVariable

Variable

undef

-width

width

Width

20

-xscrollcommand

xScrollCommand

ScrollCommand

undef

XVII-B-5 - Widget Frame

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

0

-class

class

Class

Frame

-colormap

colormap

Colormap

undef

-container

container

Container

0

-cursor

cursor

Cursor

undef

-fg

foreground

-foreground

foreground

Foreground

Black

-height

height

Height

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

0

-label

undef

undef

undef

-labelPack

undef

undef

undef

-labelVariable

undef

undef

undef

-relief

relief

Relief

flat

-takefocus

takeFocus

TakeFocus

0

-visual

visual

Visual

undef

-width

width

Width

0

XVII-B-6 - Widget Label

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-anchor

anchor

Anchor

center

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-bitmap

bitmap

Bitmap

undef

-borderwidth

borderWidth

BorderWidth

2

-cursor

cursor

Cursor

undef

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemButtonText

-height

height

Height

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

0

-image

image

Image

undef

-justify

justify

Justify

center

-padx

padX

Pad

1

-pady

padY

Pad

1

-relief

relief

Relief

flat

-takefocus

takeFocus

TakeFocus

0

-text

text

Text

undef

-textvariable

textVariable

Variable

undef

-underline

underline

Underline

-1

-width

width

Width

0

-wraplength

wrapLength

WrapLength

0

XVII-B-7 - Widget Listbox

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

2

-cursor

cursor

Cursor

undef

-exportselection

exportSelection

ExportSelection

1

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemButtonText

-height

height

Height

10

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

1

-relief

relief

Relief

sunken

-selectbackground

selectBackground

Foreground

SystemHighlight

-selectborderwidth

selectBorderWidth

BorderWidth

1

-selectforeground

selectForeground

Background

SystemHighlightText

-selectmode

selectMode

SelectMode

browse

-setgrid

setGrid

SetGrid

0

-takefocus

takeFocus

TakeFocus

undef

-width

width

Width

20

-xscrollcommand

xScrollCommand

ScrollCommand

undef

-yscrollcommand

yScrollCommand

ScrollCommand

undef

XVII-B-8 - Widget Menu

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-activebackground

activeBackground

Foreground

SystemHighlight

-activeborderwidth

activeBorderWidth

BorderWidth

1

-activeforeground

activeForeground

Background

SystemHighlightText

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

1

-cursor

cursor

Cursor

arrow

-disabledforeground

disabledForeground

DisabledForeground

SystemDisabledText

-fg

foreground

-font

font

Font

Tim10

-foreground

foreground

Foreground

Black

-overanchor

undef

undef

undef

-popanchor

undef

undef

undef

-popover

undef

undef

undef

-postcommand

postCommand

Command

undef

-relief

relief

Relief

flat

-selectcolor

selectColor

Background

SystemMenuText

-takefocus

takeFocus

TakeFocus

0

-tearoff

tearOff

TearOff

1

-tearoffcommand

tearOffCommand

TearOffCommand

undef

-title

title

Title

undef

-type

type

Type

normal

XVII-B-9 - Widget Radiobutton

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-activebackground

activeBackground

Foreground

SystemButtonFace

-activeforeground

activeForeground

Background

SystemWindowText

-anchor

anchor

Anchor

center

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-bitmap

bitmap

Bitmap

undef

-borderwidth

borderWidth

BorderWidth

2

-command

command

Command

undef

-cursor

cursor

Cursor

undef

-disabledforeground

disabledForeground

DisabledForeground

SystemDisabledText

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemWindowText

-height

height

Height

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

1

-image

image

Image

undef

-indicatoron

indicatorOn

IndicatorOn

1

-justify

justify

Justify

center

-padx

padX

Pad

1

-pady

padY

Pad

1

-relief

relief

Relief

flat

-selectcolor

selectColor

Background

SystemWindow

-selectimage

selectImage

SelectImage

undef

-state

state

State

normal

-takefocus

takeFocus

TakeFocus

undef

-text

text

Text

undef

-textvariable

textVariable

Variable

undef

-underline

underline

Underline

-1

-value

value

Value

undef

-variable

variable

Variable

selectedButton

-width

width

Width

0

-wraplength

wrapLength

WrapLength

0

XVII-B-10 - Widget Scale

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-activebackground

activeBackground

Foreground

SystemButtonFace

-background

background

Background

SystemButtonFace

-bigincrement

bigIncrement

BigIncrement

0

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

2

-command

command

Command

undef

-cursor

cursor

Cursor

undef

-digits

digits

Digits

0

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemButtonText

-from

from

From

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

2

-label

label

Label

undef

-length

length

Length

100

-orient

orient

Orient

vertical

-relief

relief

Relief

flat

-repeatdelay

repeatDelay

RepeatDelay

300

-repeatinterval

repeatInterval

RepeatInterval

100

-resolution

resolution

Resolution

1

-showvalue

showValue

ShowValue

1

-sliderlength

sliderLength

SliderLength

10m

-sliderrelief

sliderRelief

SliderRelief

raised

-state

state

State

normal

-takefocus

takeFocus

TakeFocus

undef

-tickinterval

tickInterval

TickInterval

0

-to

to

To

100

-troughcolor

troughColor

Background

SystemScrollbar

-variable

variable

Variable

undef

-width

width

Width

5m

XVII-B-11 - Widget Scrollbar

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-activebackground

activeBackground

Foreground

SystemButtonFace

-activerelief

activeRelief

Relief

raised

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

0

-command

command

Command

undef

-cursor

cursor

Cursor

undef

-elementborderwidth

elementBorderWidth

BorderWidth

-1

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

0

-jump

jump

Jump

0

-orient

orient

Orient

vertical

-relief

relief

Relief

sunken

-repeatdelay

repeatDelay

RepeatDelay

300

-repeatinterval

repeatInterval

RepeatInterval

100

-takefocus

takeFocus

TakeFocus

undef

-troughcolor

troughColor

Background

SystemScrollbar

-width

width

Width

13

XVII-B-12 - Widget Text

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-background

background

Background

SystemWindow

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

2

-cursor

cursor

Cursor

xterm

-exportselection

exportSelection

ExportSelection

1

-fg

foreground

-font

font

Font

{MSSansSerif}8

-foreground

foreground

Foreground

SystemWindowText

-height

height

Height

24

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

0

-insertbackground

insertBackground

Foreground

SystemWindowText

-insertborderwidth

insertBorderWidth

BorderWidth

0

-insertofftime

insertOffTime

OffTime

300

-insertontime

insertOnTime

OnTime

600

-insertwidth

insertWidth

InsertWidth

2

-padx

padX

Pad

1

-pady

padY

Pad

1

-relief

relief

Relief

sunken

-selectbackground

selectBackground

Foreground

SystemHighlight

-selectborderwidth

selectBorderWidth

BorderWidth

0

-selectforeground

selectForeground

Background

SystemHighlightText

-setgrid

setGrid

SetGrid

0

-spacing1

spacing1

Spacing

0

-spacing2

spacing2

Spacing

0

-spacing3

spacing3

Spacing

0

-state

state

State

normal

-tabs

tabs

Tabs

undef

-takefocus

takeFocus

TakeFocus

undef

-width

width

Width

80

-wrap

wrap

Wrap

char

-xscrollcommand

xScrollCommand

ScrollCommand

undef

-yscrollcommand

yScrollCommand

ScrollCommand

undef

XVII-B-13 - Widget Toplevel

Nom de l'option

Nom dans .Xdefault

Nom de la classe

Valeur par défaut

-background

background

Background

SystemButtonFace

-bd

borderWidth

-bg

background

-borderwidth

borderWidth

BorderWidth

0

-class

class

Class

Toplevel

-colormap

colormap

Colormap

undef

-container

container

Container

0

-cursor

cursor

Cursor

undef

-fg

foreground

-foreground

foreground

Foreground

Black

-height

height

Height

0

-highlightbackground

highlightBackground

HighlightBackground

SystemButtonFace

-highlightcolor

highlightColor

HighlightColor

SystemWindowFrame

-highlightthickness

highlightThickness

HighlightThickness

0

-menu

menu

Menu

undef

-overanchor

undef

undef

undef

-popanchor

undef

undef

undef

-popover

undef

undef

undef

-relief

relief

Relief

flat

-screen

screen

Screen

undef

-takefocus

takeFocus

TakeFocus

0

-title

undef

undef

Toplevel

-use

use

Use

undef

-visual

visual

Visual

undef

-width

width

Width

0

XVIII - Différences entre systèmes d'exploitation

Au départ, Perl a été écrit pour des systèmes Unix et le module Tk a été conçu pour le système X Window, l'interface graphique d'Unix. Depuis, le langage a été porté sur de nombreuses autres plates-formes, dont Macintosh et Windows (95/98 et NT) et il en est de même pour le module Tk, bien que cela ait été plus lent. Désormais, Perl existe sur toutes les plates-formes et Perl/Tk est disponible pour le système X Window (qui peut être émulé ou exécuté sur de nombreux systèmes différents) et pour Windows.

Perl/Tk fonctionne quasiment de la même façon sur un système X Window Unix et sur Windows. La plupart des différences viennent du fait que Windows ne dispose pas de toutes les fonctions du système X Window. Tout au long de ce livre, vous avez pu noter que certaines méthodes ne fonctionnaient pas avec Windows, ou qu'elles avaient un comportement différent. Je ne reviendrai pas sur ces différences mineures. Une des différences les plus importantes entre Unix et Windows vient de la façon dont ces systèmes désignent les polices de caractères. L'annexe C, Fontes, détaille les spécifications de ces polices, consultez-la pour savoir comment les manipuler sous Unix et Windows.

XVIII-A - Unix

Toutes les méthodes décrites dans ce livre fonctionnent correctement sur les systèmes Unix. On peut rencontrer quelques différences subtiles entre les différentes déclinaisons de ce système (comme le type de valeur obtenue sur une machine Solaris et sur une machine Linux, par exemple), mais aucune n'avortera votre programme.

Toutes les captures d'écran de ce livre, sauf celles explicitement notées, ont été faites sur un système FreeBSD utilisant le système X Window avec des fenêtres de type NeXTStep. Le gestionnaire de fenêtres utilisé est Window Maker, qui produit un environnement graphique similaire aux machines NeXT. Je ne traiterai pas des différences entre les différents gestionnaires de fenêtres et de la façon dont ils modifient l'aspect de votre environnement graphique. De nombreux ouvrages traitent du système X Window et de ses gestionnaires de fenêtres.

XVIII-B - Windows NT et 95/98

Lorsque l'on crée une fenêtre Perl/Tk sous Windows NT ou Windows 95/98, elle ressemble à toutes les autres fenêtres de ces systèmes. Ainsi, elles auront une petite croix dans leur coin supérieur droit qui permettra de tuer l'application. À la gauche de celle-ci se trouvera un petit bouton permettant de maximiser la fenêtre et, à la gauche de celui-ci, un autre bouton permettant de la mettre sous forme d'icône. Le coin supérieur gauche de l'application comportera un petit « Tk » qui, s'il est cliqué, affichera un menu vous permettant de la minimiser, la maximiser ou la fermer. Toutes ces fonctionnalités sont caractéristiques d'une fenêtre Windows ; elles existent également dans les versions X Window de ces applications, mais apparaissent différemment, comme le montre la figure B.1.

Figure B.1 : Une fenêtre Win32 et une fenêtre X Window

XVIII-B-1 - Problèmes avec Windows 95

J'ai testé les codes de ce livre sur une machine Windows 95 et NT 4.0 (Service Pack 3) et ai constaté quelques problèmes mineurs avec les applications sous Windows 95. Windows NT ne semble pas souffrir de ces problèmes et, si je devais choisir entre les deux, je développerais et exécuterais mes applications Perl/Tk sous Windows NT. Voici une liste de quelques-uns des problèmes que j'ai pu rencontrer en testant mes applications Perl/Tk avec Windows 95 (elles ne sont pas nécessairement reproductibles à chaque fois ; je veux simplement vous mettre en garde) :

- création d'une fenêtre principale ne contenant qu'un bouton, modification de sa taille : impossibilité de cliquer sur le bouton ;

- même scénario que le précédent ; un clic en n'importe quel endroit de la fenêtre après la modification de sa taille a pour effet de presser le bouton ;

- parcours de plusieurs applications à l'aide de la touche de tabulation, clic dans une autre application et retour à l'application Tk : elle ne reconnaît plus la souris. Un clic sur l'icône de l'application dans la barre des tâches semble régler le problème (il ne semble pas y avoir de cause, ni d'effet reproductible efficace pour la perte de la reconnaissance de la souris) ;

- l'option -underline ne semble pas fonctionner correctement lorsque l'on essaie de souligner une lettre d'une entrée d'un menu, ce qui fait que la touche correspondante ne peut être utilisée comme raccourci clavier ;

- certaines méthodes (vous n'utiliserez pas la plupart d'entre elles, car elles sont obscures) ne renvoient pas une valeur correcte. Elles ont été mentionnées comme telles lors de leur étude dans ce livre ;

- on clique dans un widget texte pour lui donner le focus clavier, puis on clique ailleurs : le widget texte n'abandonne pas le focus. On peut utiliser la combinaison 'Maj-Tab' pour passer en revue les widgets de la fenêtre lorsqu'elle a le focus, mais elle ne semble pas pour autant abandonner celui-ci (le point reste sous la forme d'une barre verticale, et l'on ne peut interagir avec le bouton) ;

- affichage d'une photo dans un bouton (avec l'option -image), celle-ci ne ressort pas correctement ;

- à part ces problèmes mineurs, qui ne se poseront probablement pas pour une application classique, tout fonctionne correctement.

XVIII-B-2 - Sélections

Avec le système X Window, pour sélectionner du texte, il suffit de le mettre en surbrillance. Avec Windows, il faut le mettre en évidence, puis le placer dans le presse-papiers avec 'Ctrl-C' (pour Copie), et le coller avec 'Ctrl-V' (pour Vers), ou son équivalent pour l'application en cours. Plusieurs widgets disposent de l'option -exportselection (les boîtes de liste et de texte, par exemple) et fonctionnent comme nous venons de le décrire ; cependant, si cette option vaut 0, le texte sélectionné ne sera pas copié dans le presse-papiers.

XIX - Fontes

Cette annexe décrit les nouvelles méthodes de Tk8.0 permettant de créer et de gérer les fontes. Avec Tk4, on ne pouvait passer qu'une seule spécification de fonte à l'option -font ; cela reste possible avec Tk8.0, mais il existe également des méthodes pour créer ses propres fontes et les utiliser. Je traiterai d'abord de la façon la plus simple d'utiliser une fonte, qui fonctionne avec les deux versions de Tk, puis nous étudierons les méthodes plus complexes de Tk8.0.

XIX-A - Description d'une fonte

Lorsqu'un widget utilise l'option -font, il faut lui passer une chaîne de caractères représentant la fonte concernée. Il existe plusieurs façons de spécifier une telle chaîne :

- on indique le nom de la fonte à l'aide de la méthode fontCreate (décrite plus loin dans ce chapitre, dans la section Méthodes pour les fontes) ;

- on utilise une chaîne décrivant la fonte, suivant un format préétabli (voir l'annexe B, Différences entre systèmes d'exploitation) ; "Times 12 Normal", par exemple ;

- on utilise le nom d'une fonte pouvant être interprété par le dispositif d'affichage graphique (normalement, un système Unix utilisant X Window). Ces chaînes contiennent habituellement des astérisques et sont très difficiles à comprendre.

Pour spécifier une fonte à l'aide d'une chaîne de caractères, il faut d'abord connaître celles qui sont disponibles sur votre système.

XIX-A-1 - Déterminer les fontes disponibles

Avec Unix, les fontes sont désignées à l'aide d'une syntaxe longue et n'en finissant pas. Cette syntaxe utilise de nombreux astérisques représentant la famille, la taille, le type, etc. Sous le système X Window, on obtient la liste des fontes Unix en exécutant la commande suivante :

xlsfonts > fic_fontes

Le fichier fic_fontes contiendra alors une énorme liste des fontes dont vous disposez sur votre système. Soyez prudent lorsque vous utilisez des fontes décrites dans cette liste : si vous souhaitez que votre application puisse s'exécuter sur plusieurs systèmes différents, n'oubliez pas qu'une fonte disponible sur l'un ne l'est peut-être pas partout.

Avec Windows, les fontes disponibles seront obtenues par un autre moyen : cliquez sur le menu « Démarrer » et choisissez « Paramètres », puis « Panneau de configuration ». Lorsque ce panneau s'est affiché, cliquez deux fois sur l'icône « Fontes » et une fenêtre identique à celle de la figure C.1 apparaîtra.

Figure C.1 : Fontes disponibles sur un système Windows NT

Mon système dispose de la plupart des fontes standards, plus certaines que j'ai téléchargées sur l'Internet : augie et Bard, par exemple. Si l'on souhaite utiliser ces fontes, on doit savoir comment les spécifier : si l'on clique deux fois sur un nom, une autre fenêtre apparaît et affiche une description détaillée de la fonte (un exemple est donné avec la figure C.2).

Les informations affichées comprennent la place qu'occupe la fonte sur le disque dur (64 Ko, ici), sa version et son nom. Les tailles disponibles sont également indiquées : la fonte Arial va de 12 à 72 points. La chaîne utilisée avec l'option -font d'un widget (un bouton, par exemple) devra contenir le nom de la fonte (Arial), sa taille et son type (normal, bold ou italic). À l'aide de ces trois informations, on construit une chaîne de description de fonte : "Arial 24 normal"(47) , et c'est tout ce qu'il y a à faire. Pour créer un bouton utilisant cette fonte, il suffit d'utiliser l'option -font :

$mw->Button(-text => "Quitter", -command => sub { exit },
-font => "Arial 24 normal")->pack();

La figure C.3 montre un bouton utilisant la fonte par défaut, et un autre utilisant une fonte plus grande.

Figure C.2 : Détails de la fonte Arial

Si le nom de la fonte contient des espaces, comme avec « Times New Roman », la chaîne se construit de la même manière :

-font => "Times New Roman 12 normal"

Lorsque vous utiliserez ce type de spécification, l'erreur suivante apparaîtra éventuellement sur la console :

SplitString 'Times New Roman 12 normal' at script line 7

Vous pouvez négliger ces erreurs et, pour autant que je sache, il n'existe pas de moyen commode de s'en débarrasser. Heureusement, les dernières versions du module Tk géreront mieux ce problème (les premiers tests avec Tk8.0 montrent que cette erreur n'apparaît plus).

Je ne vous conseille pas de modifier la fonte du texte des widgets standards, car vous devrez alors vous soucier de la disponibilité de celle-ci. Le widget texte est le seul endroit dont vous pouvez avoir absolument besoin de modifier la fonte, et uniquement si vous souhaitez vraiment formater le texte.

Encore une chose : il existe un module Tk::Fonts, mais il ne fonctionne pas correctement avec Windows (95/98 ou NT). Si vous utilisez X Window, essayez ce module : il dispose de caractéristiques intéressantes.

XIX-A-2 - Méthodes pour les fontes

Les méthodes suivantes n'existent que dans la dernière version de Perl/Tk, qui contient Tk8.0.

XIX-A-2-a - Création

La méthode fontCreate crée une nouvelle fonte :

$nom = $widget->fontCreate();
$nom = $widget->fontCreate(nom_fonte);

On indique un nom de fonte en paramètre, sinon la méthode renverra un nom de la forme « fontX » où X est un nombre. Cette méthode reconnaît certaines options pour la fonte :

$nom = $widget->fontCreate(-size => 12);
$nom = $widget->fontCreate(nom_fonte, -size => 12);

Les options utilisables lors de la création d'une fonte sont :

-family => nom

1. Le nom de famille peut être "courier", "times", ou "helvetica". Si vous utilisez l'un de ces noms, la famille la plus proche disponible sur votre système sera utilisée. On peut également préciser le nom d'une fonte spécifique à sa machine ("Moon Runes", par exemple), mais elle risque de ne pas s'afficher sur d'autres systèmes.

-size => montant

1. Le montant spécifié précise la taille de la fonte : s'il est positif, cette taille sera exprimée en points ; s'il est négatif, la taille sera absolue et exprimée en pixels.

-weight => "normal" | "bold"

1. Cette option précise l'épaisseur (la « graisse ») de la fonte.

-slant => "roman" | "italic"

1. L'inclinaison de la fonte décrit la façon dont elle penche d'un côté. La valeur par défaut, "roman", signifie que la fonte est droite. "italic" bascule légèrement les caractères vers la droite.

-underline => 0 | 1

1. Si l'on souhaite souligner les caractères, il suffit de donner la valeur 1 à cette option.

-overstrike => 0 | 1

1. Si cette option vaut 1, une ligne barrera le texte.

XIX-A-2-b - Configuration

Les options associées à une fonte sont modifiables via la méthode fontConfigure, qui fonctionne exactement comme la méthode configure des widgets :

%valeurs_options = $widget->fontConfigure(nom_fonte);
$valeur = $widget->fontConfigure(nom_fonte, -size);
$widget->fontConfigure(nom_fonte, -size => 24); # Modifie la taille

Toutes les options de fontCreate sont utilisables avec fontConfigure.

XIX-A-2-c - Informations sur la fonte réelle

Si une taille de fonte demandée n'est pas disponible sur le système, celui-ci la remplace par une autre, voire par une autre fonte. Pour savoir quelle est la fonte réellement affichée, on utilise la méthode fontActual. Avec simplement le nom d'une fonte en paramètre, elle renvoie la liste de toutes les options et de leurs valeurs respectives :

%valeurs = $widget->fontActual(nom_fonte);

Pour obtenir la valeur réelle d'une seule option :

$valeur = $widget->fontActual(nom_fonte, -size);

Là encore, toutes les options de fontCreate sont utilisables avec cette méthode.

XIX-A-2-d - Suppression

La méthode fontDelete supprime une ou plusieurs fontes :

$widget->fontDelete(nom_fonte);
$widget->fontDelete(fonte1, fonte2);

Si la fonte est utilisée par un widget, elle ne sera pas réellement supprimée tant que le widget l'utilise. Si l'on recrée avec fontCreate une fonte portant le même nom, les widgets utilisant la fonte de départ utiliseront les informations de cette nouvelle fonte.

XIX-A-2-e - Taille du texte

La méthode fontMeasure vous permet de connaître la taille d'une chaîne de texte utilisant une fonte donnée :

$pixels = $widget->fontMeasure(nom_fonte, chaine_texte);

La valeur renvoyée n'est qu'une estimation, car des caractères comme "\t" ou "\n" ne sont pas traduits avant le calcul de cette taille.

XIX-A-2-f - Détails d'une fonte

Les détails d'une fonte sont sa partie haute, sa partie basse, l'interligne et si elle est proportionnelle. La méthode fontMetrics vous permet d'obtenir ces informations pour une fonte donnée. Si on ne lui passe en paramètre qu'un seul nom de fonte, cette méthode renverra tous les détails de celle-ci :

%valeurs = $widget->fontMetrics(nom_fonte);

En passant un détail comme option de cette méthode, on obtient sa valeur :

$valeur = $widget->fontMetrics(nom_fonte, -ascent);

On ne peut pas modifier les détails d'une fonte, car ils sont calculés au moment de sa création.

Les options permises pour indiquer les détails sont les suivantes :

-ascent

1. Mesure la partie de la fonte située au-dessus de la ligne de référence. La valeur renvoyée est exprimée en pixels.

-descent

1. Mesure la partie de la fonte située sous la ligne de référence. La valeur renvoyée est exprimée en pixels.

-linespace

1. Mesure la distance entre deux lignes de texte utilisant la même fonte. La valeur renvoyée est exprimée en pixels.

-fixed

1. Renvoie 1 si la fonte est d'une largeur fixe (tous les caractères ont la même largeur, comme dans Courier). Renvoie 0 si la fonte est proportionnelle (chaque caractère a une largeur dépendant de sa silhouette : la lettre "I" occupera moins de place que "M").

XIX-A-2-g - Familles et noms

La méthode fontFamilies permet de connaître toutes les familles de fontes existant sur le dispositif d'affichage utilisé par un widget donné :

@familles = $widget->fontFamilies();

La méthode fontNames renvoie les noms de toutes les fontes définies :

@noms = $widget->fontNames();

XX - Glossaire

Bêta : Une version bêta d'un logiciel est une version non finalisée, elle sert généralement à tester celui-ci afin de détecter les éventuels problèmes avant de produire la version finale, dite « stable ». Elle n'est, normalement, pas destinée au grand public, mais réservée aux développeurs et testeurs du produit. L'emploi d'une version bêta est aux risques et périls de son utilisateur (c'est le prix à payer pour profiter des derniers raffinements…).

Bitmap : Format d'image où chaque point est représenté par un bit (0 ou 1). Un bitmap est donc monochrome.

Bureau virtuel : Les interfaces utilisateur graphiques offrent un espace de travail. Par analogie avec la vie réelle, cet espace est appelé « bureau » (desktop, en anglais), car c'est là que l'utilisateur place ses applications. La taille physique du bureau est limitée par celle de l'écran et peut s'avérer insuffisante lorsque l'on y place simultanément plusieurs applications. Pour contourner ce problème, on fait appel à un « bureau virtuel »(48). Du point de vue de l'utilisateur, un bureau virtuel se manifeste de deux façons : un bureau unique plus grand que l'écran ou plusieurs bureaux de la taille de l'écran.

Core dump : Lorsqu'un programme se comporte de façon anormale Unix l'avorte et produit un fichier reflétant son contexte système (une image de la mémoire) au moment du problème. Le terme de core dump (« vidage mémoire ») traduit ce phénomène : généralement un fichier portant le nom du programme concerné et l'extension .core est alors créé. Ce fichier peut ensuite être analysé avec un programme de débogage (gdb par exemple). Cette génération peut, bien sûr, être désactivée.

Couper/copier et coller : (Cut/Copy and Paste, en anglais) : il s'agit du mécanisme consistant à sélectionner un élément et à le placer dans une zone tampon. Si ce placement s'accompagne d'une suppression de l'élément, on dit qu'on le « coupe », si l'élément initial est conservé, on le « copie ». Le contenu du tampon peut ensuite être recopié à un autre endroit (de la même application, ou non), on dit alors qu'on le « colle ».

CPAN : Acronyme de Comprehensive Perl Archive Network : il s'agit d'un site Internet (ou d'un ensemble de sites) contenant tout ce qui concerne ce langage. C'est là que les modules d'extension, dont Tk, sont placés et c'est par là qu'il faut commencer ses recherches lorsque l'on recherche une fonctionnalité particulière.

Expression rationnelle : Une expression rationnelle (regular expression, en anglais) est la traduction d'un automate fini à l'aide d'une syntaxe et d'une grammaire. Cette expression sert à représenter de façon symbolique les chaînes que l'on recherche ou que l'on souhaite modifier. Les expressions rationnelles sont la base de nombreux outils (sed, grep, awk, etc.) et de langages (Perl, notamment). Il serait vain de vouloir décrire complètement les expressions rationnelles en quelques phrases : le lecteur intéressé pourra consulter Mastering Regular Expressions, édité par O'Reilly.

FAQ : Acronyme de Frequently Asked Questions (« questions fréquemment posées »). Le plus souvent, pour conserver cet acronyme, on emploie l'expression française « Foire Aux Questions ». Cette définition est un peu trompeuse : une FAQ ne contient pas que les questions, mais aussi (et surtout) les réponses à celles-ci. Il existe quasiment autant de FAQ que de problèmes et la consultation de celles-ci évite souvent de poser une question sur un problème depuis longtemps résolu.

Focus : Ce terme fait référence à l'optique : c'est le point de netteté. Dire qu'un widget possède le focus signifie qu'il est celui qui recevra les entrées de l'utilisateur. Habituellement, un widget prend le focus lorsque l'on clique dessus à l'aide de la souris ou qu'il est sélectionné grâce à la touche de tabulation.

Gestionnaire d'espace : Partie d'une interface graphique, responsable du placement des widgets sur le bureau (voire bureau virtuel). Le terme anglais est geometry manager : le système X Window définit une géométrie comme étant la combinaison des coordonnées du point d'attache du widget, de sa largeur et de sa hauteur.

Gourou Perl : Un gourou, dans le monde de l'informatique, est une personne reconnue pour sa compétence dans un domaine particulier. À la différence du monde réel, un gourou ne s'autoproclame pas comme tel, ce n'est donc pas une qualité déclarée, mais reconnue. Il existe des gourous dans tous les domaines et un gourou Perl est simplement une personne dont les compétences dans ce langage sont reconnues et avérées.

GIF : Acronyme de Graphics Interchange Format. Conçu par Compuserve pour faciliter l'échange des images sur le réseau, ce format de représentation des images utilise un algorithme de compression appartenant à la société Unisys. Malgré le flou juridique actuel concernant les termes de son utilisation, les intérêts de ce format sont : faible encombrement (les images sont compressées de façon non destructive) et possibilité de créer des fonds transparents et des images animées. Le nombre de couleurs d'une image GIF est limité à 256.

GUI : Acronyme du terme anglais Graphic User Interface, que l'on peut traduire en français par « interface utilisateur graphique ». Toutefois, aucun acronyme consacré n'étant suffisamment répandu en français, nous avons choisi de conserver celui-là, unanimement reconnu.

Hacker Perl : Un hacker est un bricoleur aimant passer son temps à améliorer des programmes en modifiant directement les codes de ceux-ci. Le terme bricoleur ayant parfois une connotation péjorative, on lui préférera le terme de bidouilleur ou de bitouilleur. Un hacker ne doit pas être confondu avec un cracker, personne s'introduisant illégalement sur des systèmes dans un but de nuire ou de prouver les failles de sécurité. Le monde du logiciel libre doit beaucoup aux hackers qui, le plus souvent, font profiter tout le monde de leurs travaux.

HTML : Il s'agit de l'acronyme de Hyper Text Markup Language (« Langage hypertextuel à balises »). HTML est le langage utilisé pour écrire les pages web, il s'agit d'une version limitée et spécialisée de SGML (Standard Generalized Markup Language). HTML est un langage normalisé (la dernière norme est HTML 4.0), ce qui permet à tout navigateur web de l'interpréter. HTML étant trop limité pour gérer les documents complexes, un nouveau langage plus puissant, XML, est en cours de normalisation pour le remplacer.

JPEG : Acronyme de Joint Photographic Expert Group, format graphique libre d'utilisation. À la différence du format GIF (voir ce mot), ce format utilise une compression destructrice : plus l'image est compressée, plus on perd en qualité ; on doit donc faire un compromis entre la taille du fichier et la perte de qualité acceptable. Le nombre de couleurs n'est pas limité à 256 (chaque pixel est codé sur 24 bits au lieu de 8).

Look and feel : Expression difficilement traduisible. Le look and feel d'une interface regroupe son aspect visuel et son confort d'utilisation.

Méthode : En programmation orientée objet, les objets sont des structures de données disposant de leurs propres variables et méthodes (et héritent éventuellement de ceux de leur parent). Tandis qu'une variable d'un objet définit l'une de ses propriétés, une méthode définit l'un de ses comportements : il s'agit donc d'une action que l'objet entreprendra lorsqu'elle sera demandée. Tout objet possède au moins une méthode : celle permettant de le créer. Dans de nombreux langages orientés objet, l'appel à celle-ci sera :

Mon_Objet->new();

Pour simplifier, on peut considérer une méthode comme étant un sous-programme agissant toujours sur l'objet qui l'appelle.

Motif : Il s'agit de l'entité de base manipulée par les expressions rationnelles (voir ce mot). Un motif (pattern, en anglais) permet de représenter une configuration textuelle particulière à l'aide d'une syntaxe précise. Le plus souvent, cette configuration sera utilisée avec un texte afin, par exemple, de retrouver les occurrences de mots ayant une certaine propriété.

Navigateur web : Certains lui préféreront les termes de babillard, fureteur, etc. Un navigateur (browser , en anglais) est un logiciel permettant de se déplacer dans une arborescence : il permet d'explorer des emplacements censés contenir des fichiers. Les premiers navigateurs servaient principalement à se déplacer dans les arborescences de répertoires des disques d'un système. Avec l'apparition du Web (voir ce mot), et la dissémination des fichiers à l'échelle planétaire, ce concept s'applique maintenant à la recherche de ceux-ci sur l'Internet. En fonction du format d'un fichier, un navigateur peut décider du traitement à lui appliquer. La plupart des navigateurs actuels fonctionnent à l'aide d'une interface utilisateur graphique (voir GUI), certains ne fonctionnent qu'en mode texte (lynx, par exemple). Les deux navigateurs web les plus connus actuellement sont Netscape Navigator et Microsoft Internet Explorer.

PGM : Acronyme de Portable GrayMap. Il s'agit d'un format d'image en nuances de gris minimal. Chaque point de l'image est représenté par un entier représentant sa valeur de gris. L'intérêt principal de ce format est qu'il permet de stocker une image sous une forme textuelle. Les utilisateurs d'Unix pourront utiliser la commande man pgm, qui décrit la syntaxe complète. Voici un exemple d'image de 24 par 7 pixels, tiré de cette page de manuel :

P2
# feep.pgm
24 7
15
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  3  3  3  3  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15 15 15 15  0
0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0 15  0
0  3  3  3  0  0  0  7  7  7  0  0  0 11 11 11  0  0  0 15 15 15 15  0
0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0  0  0
0  3  0  0  0  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15  0  0  0  0
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

PostScript : Langage de description de page, créé par la société Adobe. Un document PostScript est généralement créé automatiquement à partir d'un logiciel de composition de texte, ou d'image : les instructions qui le composent sont alors interprétées par l'imprimante qui produit un document imprimé. On notera qu'il n'est pas nécessaire de disposer d'une imprimante PostScript pour imprimer de tels documents : le logiciel ghostscript, d'Aladdin Software (une version libre est disponible), permet de convertir un document PostScript dans le langage utilisé par quasiment toutes les imprimantes actuelles.

PPM : Acronyme de Portable PixMap. Il s'agit d'un format d'image couleur minimal. Chaque point de l'image est représenté par 3 valeurs décimales représentant ses composantes RGB. L'intérêt principal de ce format est qu'il permet de stocker une image sous une forme textuelle. Les utilisateurs d'Unix pourront utiliser la commande man ppm, qui décrit la syntaxe complète. Voici un exemple d'image de 4 par 4 pixels, tiré de cette page de manuel :

P3
# feep.ppm
4 4
15
0  0  0  0  0  0  0  0  0 15  0 15
0  0  0  0 15  7  0  0  0  0  0  0
0  0  0  0  0  0  0 15  7  0  0  0
15  0 15  0  0  0  0  0  0  0  0  0

README : La plupart des logiciels sont accompagnés d'un fichier s'appelant README (« LISEZ-MOI »). Est-il besoin d'expliquer ce qu'il convient d'en faire ?

RGB : Acronyme de Red Green Blue (RVB, en français). Comme chacun sait, toute couleur peut être exprimée à l'aide de ses composantes rouge, vert et bleu. Une valeur de 0 pour une composante indique son absence dans la teinte obtenue. Sous Unix, le programme showrgb permet de connaître les composantes des couleurs les plus utilisées :

% showrgb
(…)
255   0   0     red1
0 255   0     green1
0   0 255     blue1
0   0   0     black
119 136 153     LightSlateGrey
190 190 190     gray
255 255 255     white
(…)

Tcl/Tk : Tcl (Tool command language) est un langage de script créé par John K. Ousterhout, qui a également écrit l'ouvrage de référence, Tcl and the Tk Toolkit, chez Addison- Wesley. Son succès doit beaucoup à Tk (Tool kit), initialement conçu pour permettre de créer des interfaces graphiques pour les scripts écrits dans ce langage.

URL : Acronyme de Uniform Resource Locator. Pour simplifier, il s'agit de l'adresse d'une entité de l'Internet. Une URL indique le protocole utilisé pour obtenir cette entité, ainsi que le chemin pour accéder à celle-ci. http://www.ora.com, par exemple, désigne le site web des éditions O'Reilly : il est situé sur la machine www.ora.com et, pour le consulter, on utilisera le protocole http (Hyper Text Transfer Protocol).

Web : Ce mot anglais signifie « toile d'araignée » : l'expression complète est World Wide Web (« toile d'araignée à l'échelle planétaire » ou, plus simplement, « la toile »), d'où les trois fameuses lettres www. Le Web a été inventé par Tim Berners-Lee, chercheur au CERN de Genève, c'est un ensemble de protocoles et de documents disséminés sur l'Internet. Ils bénéficient de l'hypertexte permettant de parcourir les textes d'une façon contextuelle et non plus linéaire. Ces documents sont écrits en HTML (voir ce mot) et s'appellent alors des « documents web », repérés par leur URL (voir ce mot). Un navigateur web permet de lire le contenu d'un document web et de suivre les liens qu'il contient : on dit alors que le lecteur surfe sur le Web (par analogie avec un surfeur profitant des vagues pour progresser).

Widget : Le terme provient de la contraction de window gadget. Les widgets sont les briques de base manipulées par les interfaces graphiques. Programmer une telle interface en utilisant Perl/Tk revient à créer, placer et manipuler des widgets. Au niveau le plus haut se trouvent les widgets de type « fenêtre » qui, le plus souvent, contiendront des « widgets fils » tels que des boutons, des champs de texte, des étiquettes, ou tout autre objet que l'on est accoutumé à trouver dans les environnements utilisateur graphiques actuels comme X Window et MS-Windows. On peut diviser les widgets en « widgets conteneurs » pouvant contenir d'autres widgets et en « widgets de base », qui ne contiennent pas d'autres widgets. Les représentants les plus connus du premier type sont : les fenêtres, les cadres, les menus, les listes de choix, etc. Les widgets de base classiques sont : les boutons, les cases à cocher, les boutons radio, les barres de défilement, les options finales de menu, etc.

(1)NDT  Ces ouvrages ont été traduits en français : Introduction à PerI, Programmation en Perl, Programmation avancée en Perl et Perl en action, chez le même éditeur.

(2)Sauf si vous utilisez la distribution binaire pour Win32 de CPAN. Si vous avez installé la dernière distribution Perl d'ActiveState, vous devrez ensuite vous connecter à l'Internet pour installer le module Tk en utilisant PPM et en tapant INSTALL TK.

(3)NDT Tous les exemples de cette traduction ont été testés et fonctionnent parfaitement sous Unix avec Tk800.008.

(4)NDT Il est d'usage, lorsque l'on apprend un nouveau langage de programmation, de créer un premier programme affichant simplement « Bonjour tout le monde » (ou Hello, world!, en anglais).

(5)Si vous avez installé la dernière distribution Perl d'ActiveState, vous devrez ensuite vous connecter à l'Internet pour installer le module Tk en utilisant PPM et en tapant INSTALL TK.

(6)Sous Windows, utilisez la syntaxe Perl -e "use Tk".

(7)NDT Pour des raisons de lisibilité, ce message a été découpé en plusieurs lignes.

(8)NDT Si vous utilisez la version GNU de tar, vous pouvez regrouper les deux dernières commandes en une seule : tar -xvzf.

(9)Tout au long de ce livre, j'utiliserai $mw pour indiquer la variable désignant la fenêtre principale créée au début de l'application (main window en anglais, d'où l'acronyme que nous conserverons dans cette traduction).

(10)Certaines personnes m'ont précisé que pack pouvait aussi prendre une liste de widgets. Je n'en traiterai pas, car ce n'est pas de cette façon que pack est normalement utilisé.

(11)Lorsque j'utilisais le caractère spécial « ^ » avec Tk4.002, j'obtenais un core dump ennuyeux. Tk8.0 ne présente plus ce problème : si vous obtenez aussi cette erreur, vérifiez la version que vous utilisez.

(12)Lorsque j'ai essayé cet exemple sous Windows 95, je n'ai pas obtenu la palette de couleurs du fichier GIF. Le problème est peut-être dû à ma carte graphique ou au pilote de cette carte sous Windows 95, ce problème peut donc ne pas survenir sur votre machine.

(13)Le bouton 1 de la souris est celui se trouvant le plus à gauche, le bouton 2 est celui du milieu et le bouton 3, celui de droite.

(14)Connu sous le titre Programming Perl (traduit en français : Programmation en Perl) et édité par les éditions O'Reilly & Associates ; Inc.

(15)Le système de numérotation des versions de Perl/Tk suit celui de Tcl/Tk. Je ne sais pas pourquoi ils ont sauté les versions 5, 6 et 7 (NDT Cette nouvelle numérotation de Tk a été faite pour suivre celle de Tcl).

(16)Toutes les fontes n'existent pas sur tous les systèmes, bien que la fonte par défaut pour votre système devrait fonctionner. Utilisez l'instruction @config=$bouton->configure(-font);print"@config\n"; pour connaître cette fonte par défaut.

(17)Bien que nous traitions ici de couleurs, les figures de ce livre sont en noir et blanc. L'utilisation de couleurs dans les figures rendrait malheureusement le livre trop cher à produire. J'ai essayé de choisir des couleurs suffisamment contrastées afin d'obtenir des figures aussi précises que possible. Le meilleur moyen de vérifier ce qui se passe avec chaque couleur est d'essayer, et d'exécuter les exemples.

(18)Le curseur affiché dépend du gestionnaire de fenêtres utilisé, mais, la plupart du temps, c'est une flèche.

(19)Sur les systèmes Win32, le rectangle de focus est représenté par une ligne en pointillés dans le widget.

(20)Certains systèmes d'exploitation placent, en fait, une marque de validation () dans cette petite boîte. D'autres utilisent un minuscule x dans l'indicateur pour montrer qu'il a été coché.

(21)Si vous n'en créez qu'un, il se lancera comme étant non sélectionné (sauf si la variable que vous lui avez associée contient la valeur correspondant à un bouton sélectionné). Lorsque ce bouton aura été sélectionné, vous n'aurez plus la possibilité de le désélectionner.

(22)NDT Pour obtenir la liste des couleurs en français on utilise un hachage mettant en correspondance le nom de la couleur, tel qu'il s'affichera dans la liste, avec la valeur correspondante de l'option -background.

(23)Lorsque vous avez installé le module Tk avec Perl, la démo widget s'est aussi installée. Tapez widget sur la ligne de commande pour voir les possibilités des widgets en Perl/Tk.

(24)Les lecteurs les plus attentifs auront remarqué que cette capture d'écran a un aspect légèrement différent de celles déjà vues. C'est dû au fait qu'elle a été faite sous Windows 95 au lieu de X Window. Remarquez le « Tk » dans le coin supérieur gauche, et les contrôles de la fenêtre dans le coin supérieur droit.

(25)Si vous disposez de Tk8.0, vous pouvez utiliser canvasBind au lieu de Tk::bind. J'utiliserai cette dernière tout au long de ce chapitre, mais notez que vous devriez plutôt utiliser canvasBind avec Tk8.0.

(26)Cette expression permet de rendre compte d'un emplacement ou d'une direction en plaçant virtuellement l'interlocuteur debout sur l'axe d'un cadrant d'horloge à aiguilles et en lui indiquant l'heure où se trouverait alors l'objet à situer. L'expression « à trois heures » signifie donc « à droite », soit 0 degré, ici. Autres exemples : « À neuf heures » signifie « à gauche », et « À cinq heures » signifie « derrière, légèrement à droite ».

(27)En général, un menu contient des commandes que l'on utilise peu fréquemment, comme les options de configuration, Ouvrir un fichier, Fermer un fichier, Aide, etc. Il serait sage de mettre les commandes très utilisées dans la fenêtre pour permettre un accès plus facile à l'utilisateur.

(28)Il est possible d'obtenir la même apparence en utilisant un widget barre de menu. Cependant, la fonction supplémentaire fournie est minime, et nous ne la traiterons donc pas dans ce livre. Pour obtenir cet aspect, créez un widget cadre avec un contour « ridge » et une épaisseur de 2. Placez les boutons de menus avec -side => "left" pour tous les menus sauf celui d'aide, qui utilisera -side => "right".

(29)Si vous ne savez pas ce qu'est une « liste de listes », vous trouverez en Programmation en Perl une référence utile. La nouvelle édition contient beaucoup d'informations sur les listes, les tables de hachage et la création de listes anonymes.

(30)NDT : Modifiez le chemin d'accès au fichier bitmap selon la configuration de votre système.

(31)Utilisez <Button-3> avec bind.

(32)NDT : On aura compris que ces chaînes représentent des points cardinaux…

(33)qui s'ouvre lorsque l'on clique avec le bouton droit de la souris sur un widget ou un emplacement précis de l'application

(34)On peut modifier ce comportement grâce à packPropagate ou gridPropagate.

(35)Techniquement, n'importe quel widget peut être le parent d'un autre, mais j'aime me faciliter la vie au moment de placer le widget dans la fenêtre. Si le cadre était le fils d'un bouton, il me serait impossible de le placer dans celui-ci : je devrais utiliser l'option -in de pack, ce qui m'embrouillerait. Restez simple, cela vous rendra plus heureux.

(36)Étudiez Tk::Dialog, il a été conçu pour ce type d'application et utilise un widget de premier niveau.

(37)NDT Nous avons traduit les commentaires du code original.

(38)NDT Traduit en français sous le titre Programmation en Perl (O'Reilly, 1997).

(39)Il n'est pas nécessaire d'hériter d'un cadre, mais beaucoup le font. Cela simplifie la tâche, car l'on dispose alors d'un conteneur automatique pour le widget composite.

(40)Ce souci du détail peut aller jusqu'à suivre pas à pas le code Perl/Tk afin de savoir quelle méthode a été appelée, et d'où. Nous n'avons pas besoin d'une telle précision, ici.

(41)NDT On rappelle que ce nombre permet de déterminer le nombre maximum de couleurs affichables simultanément. 16 bits par pixel, par exemple, permettent d'afficher simultanément 2^16 , soit 65536, couleurs différentes.

(42)NDT « Caractère de fin de ligne absent de la ligne 2 du fichier C:\PERL\lib\site\Tk\Submethods.pm ligne 16 ».

(43)NDT La commande tail -f -n 25 fichier_texte utilisée affiche les 25 dernières lignes du fichier fichier_texte puis attend que de nouvelles lignes lui soient ajoutées et les affiche alors.

(44)Merci à mon ami Phivu Nguyen pour m'avoir permis d'emprunter ce code.

(45)http://www.editions-oreilly.fr/catalogue/pperl2.html

(46)NDT Pour améliorer la lisibilité, j'ai fait en sorte de placer chaque tableau sur une ligne distincte. Le script de l'exemple ne produit qu'une ligne unique.

(47)Le nom et le type d'une fonte peuvent également apparaître entre accolades : "{Arial} 24 {normal}", par exemple. Ces accolades sont facultatives, sauf si ces noms contiennent des espaces.

(48)Bureau dont la taille ne dépend plus de celle de l'écran.