Introduction à Perl/Tk

Interfaces graphiques avec Perl


précédentsommairesuivant

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.

Image non disponible
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 ».

Image non disponible
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.

Image non disponible
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) .

Image non disponible
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 :

 
Sélectionnez
$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).

Image non disponible
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

  • Modifie la couleur de fond du bouton et de l'élément de menu sélectionné.

-activeforeground => couleur

  • 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'

  • N'affecte que le bouton. Modifie la position du texte dans celui-ci.

-background => couleur

  • 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

  • N'affecte que le bouton. Affiche le bitmap spécifié à la place du texte.

-borderwidth => montant

  • N'affecte que le bouton. Modifie la largeur de son contour.

-cursor => nom_curseur

  • N'affecte que le bouton. Modifie l'aspect du curseur lorsqu'il sera au-dessus de la partie bouton du widget.

-disabledforeground => couleur

  • Affecte la couleur du bouton et du texte des éléments du menu lorsque leur état est 'disabled'.

-font => nom_fonte

  • N'affecte que le bouton. Modifie la fonte du texte.

-foreground => couleur

  • N'affecte que le bouton. Fixe la couleur du texte ou du bitmap qu'il affiche.

-height => montant

  • N'affecte que le bouton. Précise sa hauteur.

-highlightbackground => couleur

  • 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

  • N'affecte que le bouton. Modifie la couleur du rectangle tracé autour de celui-ci lorsqu'il a le focus clavier.

-highlightthickness => montant

  • N'affecte que le bouton. Fixe l'épaisseur du rectangle de focus tracé autour du bouton.

-image => ptr_image

  • N'affecte que le bouton. Affiche une image à la place du texte.

-indicatoron => 0 | 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'

  • N'affecte que le bouton. Indique l'alignement du texte.

-menu => $menu

  • Indique au bouton de menu qu'il doit afficher le menu associé à $menu, et donc négliger l'option -menuitems.

-menuitems => liste

  • liste est une liste décrivant chaque élément du menu.

-padx => montant

  • 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

  • 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'

  • 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'

  • Affecte le bouton et, indirectement, le menu (qui ne s'affichera pas si -state vaut 'disabled').

-takefocus => 0 | 1 | undef

  • 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

  • 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

  • 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

  • N'affecte que le bouton. Celui-ci affichera l'information contenue dans $variable.

-underline => position

  • 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

  • N'affecte que le bouton. Fixe sa largeur.

-wraplength => position

  • 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.

Image non disponible
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.

Image non disponible
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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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.

Image non disponible
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 :

 
Sélectionnez
#!/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.

Image non disponible
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.

Image non disponible
Figure 11.10 : Utilisation de cases à cocher dans un menu (les cases 1 et 5 ont été cochées par l'utilisateur)
Image non disponible
Figure 11.11 : Entrée de type cascade dans un widget bouton de menu
 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
#!/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 :

 
Sélectionnez
$mw->Menubutton(
    -tearoff   => 0,
    -menuitems => [
        [ 'command' => "Élément 1" ],

        [ 'command' => "Élément 2" ],
        "-",
        [ 'command' => "Élément 3" ],
        [ 'command' => "Élément 4" ]
    ]
)->pack;
Image non disponible
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 :

 
Sélectionnez
[ '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) :

Image non disponible
Figure 11.13 : Menu avec un accélérateur à côté de Choix 1
 
Sélectionnez
$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' );
Image non disponible
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 :

 
Sélectionnez
# 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.

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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.

 
Sélectionnez
$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 :

 
Sélectionnez
$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).

 
Sélectionnez
# 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 :

 
Sélectionnez
#!/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 :

 
Sélectionnez
#!/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.

Image non disponible
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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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

  • Fixe la couleur du fond, derrière l'entrée active du menu.

-activeborderwidth => montant

  • Définit la largeur du contour de l'entrée active.

-activeforeground => couleur

  • Fixe la couleur du texte de l'entrée active du menu.

-background => couleur

  • Définit la couleur de fond du menu entier.

-borderwidth => montant

  • Définit la largeur du contour du menu.

-cursor => nom_curseur

  • Configure le curseur qui s'affichera lorsque le pointeur de la souris sera au-dessus du menu.

-disabledforeground => couleur

  • Définit la couleur du texte d'une entrée de menu désactivée.

-font => nom_fonte

  • Fixe la fonte attribuée au texte du menu.

-foreground => couleur

  • Définit la couleur du texte d'une entrée de menu.

-menuitems => liste

  • Définit une liste d'entrées qui seront créées dans le menu.

-postcommand => fct_rappel

  • Précise la fonction de rappel qui sera invoquée avant que le menu ne s'ouvre.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'

  • Fixe l'aspect des contours du menu.

-selectcolor => couleur

  • 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

  • Précise si le menu pourra être parcouru à l'aide du clavier. Par défaut, on ne le peut pas.

-tearoff => 0 | 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.

Image non disponible
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 :

  • -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) :

  • -activeborderwidth => montant

XI-D-4. Fontes et curseurs

L'option -font définit la fonte du texte d'un menu :

  • -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.

Image non disponible
Figure 11.17 : Différents aspects du widget menu, avec -borderwidth => 4
Image non disponible
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 :

  • -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 :

 
Sélectionnez
# 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

  • 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"

  • 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"

  • 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"

  • Synonyme de "end".

"none"

  • Ne fait référence à aucune entrée.

"@y"

  • 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"

  • 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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
# 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 :

 
Sélectionnez
$menu->insert(indice, type [, options ...] );

Voici un exemple d'utilisation de cette méthode :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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) :

 
Sélectionnez
$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.

 
Sélectionnez
$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é :

 
Sélectionnez
$type = $menu->type(indice);

La chaîne renvoyée sera l'une des suivantes :

 
Sélectionnez
- "command" 
- "radiobutton" 
- "checkbutton" 
- "cascade" 
- "separator" 
- "tearoff"
 
Sélectionnez
$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é :

 
Sélectionnez
$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 :

 
Sélectionnez
# 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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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.

 
Sélectionnez
$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 :

 
Sélectionnez
$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é :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
$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 :

 
Sélectionnez
-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 :

 
Sélectionnez
-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 :

 
Sélectionnez
#!/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).

Image non disponible
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 !

précédentsommairesuivant
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.
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".
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.
NDT : Modifiez le chemin d'accès au fichier bitmap selon la configuration de votre système.
Utilisez <Button-3> avec bind.
NDT : On aura compris que ces chaînes représentent des points cardinaux…
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

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
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 © 2018 Developpez.com.