11. Chapitre 11. Les menus▲
Il y a deux méthodes de création des menus : la facile et la difficile. Toutes deux ont leur utilité, bien que dans la plupart des cas l'ItemFactory (la méthode facile) suffise. La méthode "difficile" consiste à créer tous les menus en les appelant directement, là où la méthode facile utilisera les appels de GtkItemFactory. Même si c'est effectivement beaucoup plus simple, des avantages et des inconvénients existent pour chaque approche.
Il est vrai que l'Itemfactory rend nettement plus simple la création et l'ajout ultérieur de menus ; mais faire la même chose manuellement, à l'aide de quelques fonctions, peut apporter beaucoup en termes d'ergonomie. Avec l'Itemfactory, vous ne pourrez pas ajouter d'images aux menus, ni y faire figurer le caractère "/".
11-1. Création manuelle de menus▲
Comme le veut la bonne vieille tradition pédagogique, nous commençons par la méthode difficile :).
Trois widgets entrent en jeu dans la création d'une barre de menu et de ses sous-menus :
- l'entrée de menu, qui représente ce que l'utilisateur veut faire/choisir (par exemple "Enregistrer") ;
- le menu, qui contient les entrées de menu ;
- la barre de menu, qui contient les menus.
Mais tout n'est pas si simple. En effet, les entrées de menu servent à deux choses : elles représentent à la fois les widgets placés dans le menu et celui placé dans la barre de menu (celui qui, une fois sélectionné, active le menu).
Observons les fonctions servant à créer menus et barres de menus :
barre_menus =
gtk.MenuBar
(
)
Cette première fonction, vous l'aurez deviné, crée une nouvelle barre de menu. On utilisera la méthode add() de GtkContainer pour la placer dans une fenêtre, ou bien les différentes méthodes pack() de GtkBox pour la placer dans une boîte (comme pour les boutons).
menu =
gtk.Menu
(
)
Cette fonction renvoie une référence à un nouveau menu ; il n'est jamais concrètement affiché (par la méthode show()), c'est juste un conteneur pour les entrées de menu. Tout ceci vous paraîtra, je l'espère, beaucoup plus clair après que vous aurez jeté un œil à l'exemple plus bas.
La fonction ci-dessous sert à créer les entrées qui seront placées dans le menu (et la barre de menu) :
entree =
gtk.MenuItem
(
label=
None
)
L'argument label (étiquette), si spécifié, sera parcouru à la recherche de caractères mnémoniques. Cet appel crée les entrées de menu qui doivent apparaître dans le menu. Attention à ne pas confondre le "menu", créé avec gtk.Menu(), et l'"entrée de menu", créée avec la fonction gtk.MenuItem(). L'entrée sera un véritable bouton, auquel sera associé une action, tandis que le menu sera un conteneur renfermant des entrées.
Une fois votre entrée créée, vous devez la placer dans un menu grâce à la méthode append(). Puis, pour pouvoir réagir à une sélection de cette entrée par l'utilisateur, il faudra capter son signal "activate" (activer) de la manière habituelle.
Imaginons que nous voulions créer un menu "Fichier" standard, avec les entrées Ouvrir, Enregistrer, et Quitter. Notre code devrait alors ressembler à ceci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
menu_fichier =
gtk.Menu
(
) #
Inutile
d'afficher
les
menus
avec
show()
#
Création
des
entrées
de
menu
entree_ouvrir =
gtk.MenuItem
(
"
Ouvrir
"
)
entree_enreg =
gtk.MenuItem
(
"
Enregistrer
"
)
entree_quitter =
gtk.MenuItem
(
"
Quitter
"
)
#
On
les
place
dans
le
menu
menu_fichier.append
(
entree_ouvrir)
menu_fichier.append
(
entree_enreg)
menu_fichier.append
(
entree_quitter)
#
On
connecte
les
fonctions
de
rappel
au
signal
"activate"
entree_ouvrir.connect_object
(
"
activate
"
, entrees_reponse, "
fichier
.
ouvrir
"
)
entree_enreg.connect_object
(
"
activate
"
, entrees_reponse, "
fichier
.
enregistrer
"
)
#
On
peut
connecter
la
entrée
Quitter
à
notre
fonction
de
sortie
entree_quitter.connect_object (
"
activate
"
, destroy, "
fichier
.
quitter
"
)
#
Il
nous
faut
afficher
les
entrées
entree_ouvrir.show
(
)
entree_enreg.show
(
)
entree_quitter.show
(
)
Nous avons maintenant notre menu. Il nous reste à créer une barre de menu, puis une entrée "Fichier" à laquelle nous relierons notre menu. Le code pour ce faire est le suivant :
2.
3.
4.
5.
6.
barre_menus =
gtk.MenuBar
(
)
fenetre.add
(
barre_menus)
barre_menus.show
(
)
entree_fichier =
gtk.MenuItem
(
"
Fichier
"
)
entree_fichier.show
(
)
Maintenant associons le menu avec entree_fichier. On fait appel pour cela à la méthode suivante :
entree.set_submenu
(
sous_menu)
Ce qui donnerait, pour la suite de notre exemple :
entree.set_submenu
(
menu_fichier)
Tout ce qui nous reste à faire est de placer le menu dans la barre de menu, en utilisant la méthode :
barre_menus.append
(
enfant)
qui, dans notre cas, donne :
barre_menus.append
(
entree_fichier)
Si nous voulions aligner notre menu sur la droite de la barre de menu, comme le sont souvent les menus d'aide, nous pourrions faire appel à la méthode ci-dessous (toujours sur entree_fichier dans notre exemple) avant de l'attacher à la barre de menu.
entree.set_right_justified
(
align_droite)
Voici un résumé des étapes nécessaires à la céation d'une barre de menu avec ses menus :
- créez un nouveau menu avec gtk.Menu() ;
- faites un appel à gtk.MenuItem() pour chaque entrée que vous voulez voir figurer dans votre menu, puis utilisez la méthode append() pour placer chacune de ces nouvelles entrées dans le menu ;
- créez une entrée avec gtk.MenuItem(). Ce sera la racine du menu, son étiquette apparaîtra directement dans la barre de menus.
- ctilisez la méthode set_submenu() pour attacher le menu à l'entrée racine (celle créée à l'étape précédente) ;
- créez une barre de menu avec gtk.MenuBar(). Si l'on crée une série de menus que l'on veut placer sur une seule barre de menu, cette étape n'a besoin d'être effectuée qu'une seule fois ;
- utilisez la méthode append() pour placer la racine dans la barre de menu.
Le procédé de création d'un menu popup est plus ou moins identique. La différence est que le menu n'est pas affiché "automatiquement" par une barre de menu, mais explicitement par un appel à la méthode popup(), lors d'un évènement "button-press", par exemple. Procédez comme suit :
-
créez une fonction de rappel gestionnaire d'évènement. Elle doit être de la forme :
Sélectionnezdef
fct_rappel
(
widget, evenement):et utilisera l'évènement pour savoir où afficher le menu ;
-
dans le gestionnaire d'évènement, si l'évènement est une pression d'un bouton de la souris, traitez-le comme un évènement bouton (ce qu'il est) et utilisez-le comme montré dans l'exemple de code pour transmettre l'information à la méthode popup() ;
- attachez ce gestionnaire d'évènement à un widget ainsi :
widget.connect_object
(
"
evenement
"
, fct_rappel, menu)
où widget est le widget auquel vous connectez l'évènement fct_rappel la fonction de rappel, et menu le menu crée par GtkMenu(). Ce dernier peut aussi être affiché par une barre d'état, comme dans notre code.
11-2. Démonstration de création manuelle de menus▲
Voilà pour la théorie. Maintenant, jetons un œil du côté de l'exemple menu.py pour tenter de clarifier un peu tout ça. La Figure 11.1 nous montre la fenêtre du programme :
Le code source de menu.py est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
#
!/usr/bin/env
python
#
exemple
menu.py
import
pygtk
pygtk.require
(
'
2
.
0
'
)
import
gtk
class
ExempleMenu:
def
__init__
(
self):
#
Création
d'une
fenêtre
fenetre =
gtk.Window
(
gtk.WINDOW_TOPLEVEL)
fenetre.set_size_request
(
200
, 100
)
fenetre.set_title
(
"
Test
menu
GTK
"
)
fenetre.connect
(
"
delete_event
"
, gtk.main_quit)
#
Initialisation
du
widget.
Souvenez-vous
:
on
n'affiche
jamais
#
(avec
show())
le
widget
menu
!
#
Il
s'agit
du
menu
qui
contiendra
les
commandes,
celui
qui
apparaîtra
#
après
un
clic
sur
le
"Menu
racine"
de
l'application.
menu =
gtk.Menu
(
)
#
Ensuite,
on
écrit
une
petite
boucle
qui
crée
trois
entrees
pour
le
menu.
#
Notez
l'appel
à
gtk_menu_append.
Ici,
nous
ajoutons
une
liste
de
commandes
#
à
notre
menu.
Nous
devrions
aussi
nous
débrouiller
pour
capter
le
signal
#
"clicked"
de
chacune
des
commandes
et
mettre
en
place
une
fonction
de
rappel
#
pour
le
traiter,
mais
pour
ne
pas
faire
trop
long
on
s'en
passera
ici.
for
i in
range(
3
):
#
On
copie
les
noms
dans
une
variable
"tampon".
tampon =
"
Sous
-
menu
test
-
%d
"
%
i
#
On
crée
une
nouvelle
commande,
avec
un
nom...
commandes =
gtk.MenuItem
(
tampon)
#
…
et
on
l'ajoute
au
menu
menu.append
(
commandes)
#
On
réagit
à
la
sélection
de
la
commande
commandes.connect
(
"
activate
"
, self.reponse_commande, tampon)
#
On
affiche
le
widget
commandes.show
(
)
#
Voici
le
menu
racine.
Ce
sera
aussi
l'étiquette
visible
dans
la
barre
de
#
menus.
On
ne
lui
connecte
pas
de
gestionnaire
de
signal,
son
rôle
étant
#
seulement
de
faire
apparaître
le
reste
du
menu
lorsqu'on
le
clique.
menu_racine =
gtk.MenuItem
(
"
Menu
racine
"
)
menu_racine.show
(
)
#
Ici
on
indique
que
notre
"menu"
créé
plus
haut
doit
être
le
menu
de
#
"Menu
racine"
menu_racine.set_submenu
(
menu)
#
Une
boîte
verticale
pour
y
placer
un
menu
et
un
bouton
:
boitev =
gtk.VBox
(
False
, 0
)
fenetre.add
(
boitev)
boitev.show
(
)
#
On
crée
une
barre
pour
contenir
les
menus,
et
on
la
place
dans
notre
fenêtre
barre_menus =
gtk.MenuBar
(
)
boitev.pack_start
(
barre_menus, False
, False
, 2
)
barre_menus.show
(
)
#
Création
d'un
bouton,
auquel
on
attache
le
menu
comme
popup
bouton =
gtk.Button
(
"
Cliquez
-
moi
"
)
bouton.connect_object
(
"
event
"
, self.evnmt_button_press, menu)
boitev.pack_end
(
bouton, True
, True
, 2
)
bouton.show
(
)
#
Enfin,
on
rattache
la
commande
à
la
barre
de
menu
--
il
s'agit
de
la
commande
#
"Menu
racine"
sur
laquelle
je
me
suis
emballé
=)
barre_menus.append (
menu_racine)
#
Toujours
afficher
la
fenêtre
en
dernier,
afin
qu'elle
soit
complète
lors
de
#
son
apparition
à
l'écran.
fenetre.show
(
)
#
On
répond
à
l'évènement
"button-press"
en
affichant
un
menu,
qui
est
#
transmis
comme
"widget".
#
Notez
que
l'argument
"widget"
N'EST
PAS
le
bouton
qui
a
été
enfoncé,
#
mais
bien
le
menu
à
afficher
def
evnmt_button_press
(
self, widget, evenement):
if
evenement.type =
=
gtk.gdk.BUTTON_PRESS:
widget.popup
(
None
, None
, None
, evenement.button, evenement.time)
#
On
fait
savoir
au
code
appelant
que
l'on
a
traité
l'évènement.
Son
#
histoire
s'arrête
ici.
return
True
#
On
fait
savoir
au
code
appelant
que
l'évênement
n'a
pas
été
traité.
Il
continue.
return
False
#
Affiche
une
chaîne
de
caractères
lorsqu'une
commande
est
sélectionnée.
def
reponse_commande
(
self, widget, chaine):
print
"
%s
"
%
chaine
def
main
(
):
gtk.main
(
)
return
0
if
__name__
=
=
"
__main__
"
:
ExempleMenu
(
)
main
(
)
Vous pouvez aussi n'affecter aucune réaction à la sélection d'une entrée de menu et, par l'intermédiaire d'une table de raccourcis clavier, lier certaines touches aux fonctions de rappel du menu.
11-3. Création de menus avec l'ItemFactory▲
À présent que nous avons vu la méthode difficile, voici comment faire la même chose avec les appels de gtk.ItemFactory.
11-4. Démonstration de l'ItemFactory▲
La Figure 11.2 montre la fenêtre créée par le programme d'exemple itemfactory.py, qui utilise la gtk.ItemFactory :
Voici le code source de itemfactory.py :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
#
!/usr/bin/env
python
#
exemple
itemfactory.py
import
pygtk
pygtk.require
(
'
2
.
0
'
)
import
gtk
class
ExempleItemFactory:
#
L'inévitable
petite
fonction
de
rappel
def
affiche_hello
(
self, w, donnees):
print
"
Salut
tout
le
monde
!
"
#
Voici
la
structure
d'ItemFactoryEntry
utilisée
pour
générer
de
nouveaux
menus
#
Element
1:
Le
chemin
du
menu.
la
lettre
qui
suit
le
caractère
"_"
indique
un
#
raccourci
clavier
actif
une
fois
le
menu
ouvert.
#
Element
2:
Le
raccourci
clavier
pour
cette
entrée.
#
Element
3:
La
fonction
de
rappel.
#
Element
4:
L'action
de
la
fonction
de
rappel.
Modifie
les
paramètres
avec
#
lesquels
la
fonction
de
rappel
est
appelée.
0
par
défaut.
#
Element
5:
Le
type
d'entrée.
Indique
quelle
sorte
d'entrée
l'on
souhaite
créer.
#
Voici
les
différentes
valeurs
possibles
:
#
NULL
->
""
#
""
->
""
#
"