Les Menus
1. Introduction
Avant toute chose, il faut savoir q'un menu est composé de plusieurs parties distinctes :
- La barre de menu
- Le menu lui même
- Les éléments d'un menu
La barre de menu est l'élément principal qui contiendra des éléments. A chaque élément, nous pouvons associer un menu qui s'ouvrira lorsque l'utilisateur cliquera sur cet élément. Et chaque menu contiendra des éléments qui pourront ouvrir des sous-menus, etc...
En plus d'utiliser tous ces widgets, il faudra aussi le widget
GtkMenuShell qui est une classe de base pour les widgets
GtkMenuBar et
GtkMenu.
1.1 Hiérarchie
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkMenuShell ->
GtkMenuBar
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkMenuShell ->
GtkMenu
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkBin? ->
GtkItem? ->
GtkMenuItem
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkBin? ->
GtkItem? ->
GtkMenuItem ->
GtkImageMenuItem
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkBin? ->
GtkItem? ->
GtkMenuItem ->
GtkCheckMenuItem
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkBin? ->
GtkItem? ->
GtkMenuItem ->
GtkCheckMenuItem ->
GtkRadioMenuItem
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkBin? ->
GtkItem? ->
GtkMenuItem ->
GtkSeparatorMenuItem
GObject ->
GtkObject ->
GtkWidget ->
GtkContainer ->
GtkBin? ->
GtkItem? ->
GtkMenuItem ->
GtkTearoffMenuItem
2. Le principes
2.1 Les différentes étapes
La création d'un menu doit passer par au moins six étapes différentes :
- Etape 1 : création de l'élément GtkMenuBar qui sera la barre de menu;
- Etape 2 : création d'un élément GtkMenu qui sera un menu ;
- Etape 3 : création des éléments GtkMenuItem à insérer dans le menu ;
- Etape 4 : création de l'élément GtkMenuItem qui ira dans l'élément GtkMenuBar ;
- Etape 5 : association de cet élément avec le menu créer précédemment ;
- Etape 6 : ajout de l'élément GtkMenuItem dans la barre GtkMenuBar.
Si par la suite, vous souhaitez ajouter d'autres menu, il suffit de recommencer à partir de l'étape 2.
2.2 Etape 1 : Création de l'élément GtkMenuBar
Cette étape, rapide et simple, se résume en l'utilisation d'une seule fonction :
menu_bar = gtk.MenuBar?()
2.3 Etape 2 : Création d'un élément GtkMenu
Cette fois aussi, une seule fonction est utilisée :
menu = gtk.Menu()
2.4 Etape 3 : Création des éléments GtkMenuItem (ou autres)
Dans un premier temps, il faut créer le
GtkMenuItem grâce cette fonction :
menu_item = gtk.MenuItem?(label)
Maintenant que l'élément est créé, il faut l'insérer dans le menu. Pour cela, il existe plusieurs fonctions possibles, mais nous n'allons en voir que deux :
menu.append(child)
menu.prepend(child)
Ces fonctions ne font pas partie du widget
GtkMenu mais de
GtkMenuShell qui est le widget dont
GtkMenu dérive. La première fonction ajoute les éléments de haut en bas alors que la seconde les ajoute de bas en haut. Le paramètre
menu_shell est le menu dans lequel nous voulons ajouter l'élément et le paramètre
child est l'élément à ajouter.Cette étape peut s'avérer longue à coder, car il faudra la répéter pour chaque élément que nous voulons ajouter au menu.
2.5 Etape 4 : Création de l'élément GtkMenuItem qui ira dans l'élément GtkMenuBar
Il suffit d'utiliser une des fonctions de création du widget
GtkMenuItem.
2.6 Etape 5 : Association de cet élément avec le menu créé précédemment
Il faut maintenant dire que lorsque l'utilisateur cliquera sur l'élément créer pendant l'étape 4, il faudra ouvrir le menu qui a été créé précédemment. La fonction adéquate est celle-ci :
menu_item.set_submenu(submenu)
2.7 Etape 6 : Ajout de l'élément GtkMenuItem dans la barre GtkMenuBar
GtkMenuBar dérivant aussi de
GtkMenuShell, nous allons utiliser la même fonction que lors de l'étape 3 (gtk_menu_shell_append) pour ajouter le sous-menu au menu principal.
2.8 Programme exemple
Nous allons créer une fenêtre simple avec un menu "Fichier" et un menu "?".
# -*- Encoding: Latin-1 -*-
import gtk
def main():
# Creation de la fenetre
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("GtkMenu")
window.set_default_size(320, 200)
window.connect("destroy", gtk.main_quit, None)
# Creation de la GtkVBox
vbox = gtk.VBox(False, 0)
window.add(vbox)
#**** Creation du menu ****
# ETAPE 1
menu_bar = gtk.MenuBar()
#** Premier sous-menu **
# ETAPE 2
menu = gtk.Menu()
# ETAPE 3
menu_item = gtk.MenuItem("Nouveau")
menu.append(menu_item)
menu_item = gtk.MenuItem("Ouvrir")
menu.append(menu_item)
menu_item = gtk.MenuItem("Enregistrer")
menu.append(menu_item)
menu_item = gtk.MenuItem("Fermer")
menu.append(menu_item)
menu_item = gtk.MenuItem("Quitter")
menu_item.connect("activate", OnQuitter, window)
menu.append(menu_item)
# ETAPE 4
menu_item = gtk.MenuItem("Fichier")
# ETAPE 5
menu_item.set_submenu(menu)
# ETAPE 6
menu_bar.append(menu_item)
#* Second sous-menu *
# ETAPE 2
menu = gtk.Menu()
# ETAPE 3
menu_item = gtk.MenuItem("A propos de...")
menu_item.connect("activate", OnAbout, window)
menu.append(menu_item)
# ETAPE 4
menu_item = gtk.MenuItem("?")
# ETAPE 5
menu_item.set_submenu(menu)
# ETAPE 6
menu_bar.append(menu_item)
# Ajout du menu a la fenetre
vbox.pack_start(menu_bar, False, False, 0)
window.show_all()
gtk.main()
def OnQuitter(widget, data):
question = gtk.MessageDialog(data,
gtk.DIALOG_MODAL,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_YES_NO,
"Voulez vous vraiment\n"
"quitter le programme?")
reponse = question.run()
if reponse == gtk.RESPONSE_YES:
gtk.main_quit()
elif reponse in (gtk.RESPONSE_NONE, gtk.RESPONSE_NO):
question.destroy()
def OnAbout(widget, data):
about = gtk.MessageDialog(data,
gtk.DIALOG_MODAL,
gtk.MESSAGE_INFO,
gtk.BUTTONS_OK,
"Cours GTK+ 2.0\n"
"http://www.gtk-fr.org")
about.run()
about.destroy()
if __name__ == '__main__':
main()
3. Les éléments avancées d'un menu
En plus des
GtkMenuItem, GTK+ offre cinq éléments de menu additionnels prêts à l'emploi :
GtkImageMenuItem,
GtkRadioMenuItem,
GtkCheckMenuItem,
GtkSeparatorMenuItem et
GtkTearoffMenuItem.
GtkImageMenuItem est un widget qui permet de créer une entrée de menu textuelle avec une icône juste devant.
Pour créer un
GtkImageMenuItem, on a à disposition la classe :
image_menu_item = gtk.ImageMenuItem?(stock_id=None, accel_group=None)
GtkWidget* gtk_image_menu_item_new_with_label(const gchar *label);
GtkWidget* gtk_image_menu_item_new_with_mnemonic(const gchar *label);
On retrouve encore les mêmes types de fonctions de création, aussi je passe sur l'explication des paramètres. La seule nouveautée est peut être le
GtkAccelGroup? *accel_group de gtk_image_menu_item_new_from_stock, qui sert à ajouter l'entrée à un
GtkAccelGroup? précédemment créé en utilisant le raccourci par défaut de l'icône stock.
Maintenant, il s'agit de définir une icône pour notre entrée, pour cela, on a :
image_menu_item.set_image(image)
Cette fonction permet de définir l'icône (généralement on utilisera un
GtkImage) qui sera affichée par l'entrée passée en paramètre. Cette fonction peut aussi servir à remplacer l'icône que gtk_image_menu_item_new_from_stock définit pour l'entrée lors de sa création.
La dernière fonction spécifique des
GtkImageMenuItem est :
image = image_menu_item.get_image()
Elle sert, comme vous l'aurez deviné, à récupérer ce qui sert d'icône à l'entrée.
Le widget
GtkCheckMenuItem est en fait un
GtkCheckButton qui a été modifié afin de pouvoir êtreinséré dans des menus.
Les
GtkCheckMenuItem émettent le signal "toggled" lorsqu'on les (dé)coche.
Les fonctions de création d'un
GtkCheckMenuItem sont :
GtkWidget* gtk_check_menu_item_new(void);
GtkWidget* gtk_check_menu_item_new_with_label(const gchar *label);
GtkWidget* gtk_check_menu_item_new_with_mnemonic(const gchar *label);
Rien de nouveau à l'horizon, alors nous continuons.
Nous avons plusieurs fonctions qui permettent de changer l'état de notre
GtkCheckMenuItem par programme :
void gtk_check_menu_item_set_active(GtkCheckMenuItem *check_menu_item, gboolean is_active);
void gtk_check_menu_item_set_inconsistent(GtkCheckMenuItem *check_menu_item, gboolean setting);
void gtk_check_menu_item_toggled(GtkCheckMenuItem *check_menu_item);
La première fonction pemet de passer le
GtkCheckMenuItem dans l'état "coché" si le paramètre
is_active est TRUE ou dans l'état "non coché" si
is_active est FALSE. Cette fonction ne permet pas de mettre notre
GtkCheckMenuItem dans le troisième état "demi coché", pour cela, il faut utiliser la deuxième fonction, et grâce au paramètre
setting, on active ou non cet état. La troisième fonction, elle, permet d'alterner entre état "coché" et "non coché" car elle émet en fait le signal "toggled", ce qui a pour effet d'inverser l'état du
GtkCheckMenuItem.
Nous disposons également des fonctions associées pour récupérer l'état d'un
GtkCheckMenuItem :
gboolean gtk_check_menu_item_get_active(GtkCheckMenuItem *check_menu_item);
gboolean gtk_check_menu_item_get_inconsistent(GtkCheckMenuItem *check_menu_item);
La première permet de connaître l'état d'un
GtkCheckMenuItem, elle renvoie TRUE si il est "coché" ou FALSE sinon.
La deuxième permet juste de savoir si le
GtkCheckMenuItem est dans le troisième état "demi coché".
Ici aussi, le widget
GtkRadioMenuItem est un
GtkRadioButton adapté pour les menus.
Les
GtkRadioMenuItem héritent des
GtkCheckMenuItem, donc tout ce qui a été dit juste avant s'applique aussi ici.
Pour créer un
GtkRadioMenuItem, nous pouvons nous servir de :
GtkWidget* gtk_radio_menu_item_new(GSList *group);
GtkWidget* gtk_radio_menu_item_new_with_label(GSList *group, const gchar *label);
GtkWidget* gtk_radio_menu_item_new_with_mnemonic(GSList *group, const gchar *label);
Ce widget fonctionne de la même manière que le widget
GtkRadioButton, et donc le paramètre group de ces fonctions, sert à dire au
GtkRadioMenuItem à quel groupe il va appartenir.
Le premier
GtkRadioMenuItem d'un groupe prendra toujours NULL comme paramètre
group, de cette façon il va en créer un nouveau. Ensuite, il suffira de récupérer ce paramètre
group grâce à la fonction suivante et de le passer à un autre
GtkRadioMenuItem pour que celui ci fasse partie du même groupe que le premier :
GSList* gtk_radio_menu_item_get_group(GtkRadioMenuItem *radio_menu_item);
On peut aussi définir ou remplacer le groupe d'un
GtkRadioMenuItem aprés coup grâce à :
void gtk_radio_menu_item_set_group(GtkRadioMenuItem *radio_menu_item, GSList *group);
Ce widget permet d'insérer une séparation constituée d'une ligne horizontale dans le menu.
Ce widget ne possède qu'une seule fonction de création :
GtkWidget* gtk_separator_menu_item_new(void);
Le widget
GtkTearoffMenuItem permet de détacher le menu de sa barre et d'en faire une fenêtre à part entière.
Pour le créer, il faut utiliser la fonction suivante :
GtkWidget* gtk_tearoff_menu_item_new(void);
Cette fonction va ajouter au menu une ligne en pointillée. Un premier clic sur l'élément
GtkTearoffMenuItem créera une copie du menu dans une fenêtre, et un second clic sur l'élément (que cela soit dans le menu ou dans la fenêtre) supprimera la fenêtre créée.
Pour savoir si le menu est attaché ou détaché, il faut utiliser la fonction :
gboolean gtk_menu_get_tearoff_state(GtkMenu *menu);
Cette dernière renverra TRUE si le menu est détaché et FALSE sinon.
3.6 Programme exemple
Nous avons repris l'exemple précédent en modifiant le menu pour y mettre nos nouveaux items et en y ajoutant trois labels pour indiquer l'état des différents items. Il y a aussi trois callbacks supplémentaires pour la mise à jour de nos labels.
# -*- Encoding: Latin-1 -*-
import gtk
def main():
GSList *pList;
# Creation de la fenetre
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("GtkMenu")
window.set_default_size(320, 200)
window.connect("destroy", gtk.main_quit)
# Creation de la GtkVBox
vbox = gtk.VBox(False, 0)
window.add(vbox)
#*** Creation du menu ***
# ETAPE 1
menu_bar = gtk.MenuBar()
#* Premier sous-menu *
# ETAPE 2
menu = gtk.Menu()
# ETAPE 3
# GtkTearoffMenuItem
menu_item = gtk.TearoffMenuItem()
menu.append(menu_item)
menu_item.connect("activate", OnTearoff, menu)
# GtkImageMenuItem
menu_item = gtk.ImageMenuItem(stock=gtk.STOCK_NEW)
menu.append(menu_item)
menu_item = gtk.ImageMenuItem(stock=gtk.STOCK_OPEN)
menu.append(menu_item)
menu_item = gtk.ImageMenuItem(stock=gtk.STOCK_SAVE)
menu.append(menu_item)
menu_item = gtk.ImageMenuItem(stock=gtk.STOCK_CLOSE)
menu.append(menu_item)
# GtkSeparatorItem
menu_item = gtk.SeparatorMenuItem()
menu.append(menu_item)
# GtkRadioMenuItem
menu_item = gtk.RadioMenuItem(label="Radio 1")
menu.append(menu_item)
liste = menu_item.get_group()
# Il est inutile ici d'utiliser le signal "toggled"
menu_item.connect("activate", OnRadio)
menu_item = gtk.RadioMenuItem(group=liste, label="Radio 2")
menu.append(menu_item)
liste = menu_item.get_group()
menu_item.connect("activate", OnRadio)
menu_item = gtk.RadioMenuItem(group=liste, label="Radio 3")
menu.append(menu_item)
liste = menu_item.get_group()
menu_item.connect("activate", OnRadio)
# GtkSeparatorItem
menu_item = gtk.SeparatorMenuItem()
menu.append(menu_item)
# GtkCheckMenuItem
menu_item = gtk.CheckMenuItem(label="Check")
menu.append(menu_item)
menu_item.connect("toggled", OnCheck, menu)
# GtkSeparatorItem
menu_item = gtk.SeparatorMenuItem()
menu.append(menu_item)
menu_item = gtk.ImageMenuItem(stock=gtk.STOCK_QUIT)
menu_item.connect("activate", OnQuitter, window)
menu.append(menu_item)
# ETAPE 4
menu_item = gtk.MenuItem(label="Fichier")
# ETAPE 5
menu_item.set_submenu(menu)
# ETAPE 6
menu_bar.append(menu_item)
#* Deuxieme sous-menu *
# ETAPE 2
menu = gtk.Menu()
# ETAPE 3
menu_item = gtk.MenuItem(label="A propos de...")
menu_item.connect("activate", OnAbout, window)
menu.append(menu_item)
# ETAPE 4
menu_item = gtk.MenuItem(label="?")
# ETAPE 5
menu_item.set_submenu(menu)
# ETAPE 6
menu_bar.append(menu_item)
# Creation de la deuxieme GtkVBox (pour les labels)
vbox2 = gtk.VBox(False, 0)
radio_label = gtk.Label("Radio 1 est actif")
vbox2.pack_start(radio_label, True, True, 0)
temp_label = u"Check est décoché"
check_label = gtk.Label(temp_label)
vbox2.pack_start(check_label, True, True, 0)
temp_label = u"Menu attaché"
tearoff_label = gtk.Label(temp_label)
vbox2.pack_start(tearoff_label, True, True, 0)
# Ajout du menu a la fenetre
vbox.pack_start(menu_bar, False, False, 0)
# Ajout des labels a la fenetre
vbox.pack_start(vbox2, True, True, 0)
window.show_all()
gtk.main()
def OnRadio(widget, data):
# Recuperer le label du bouton radio active
radio_name = widget.child.get_label()
label = "%s est actif" % radio_name
radio_label.set_label(label)
def OnCheck(widget, data):
# Savoir si le GtkCheckMenuItem est coche ou non
coche = widget.get_active()
if coche:
label = u"Check est coché"
else:
label = u"Check est décoché"
check_label.set_label(label)
def OnTearoff(widget, data):
# Savoir si le menu est detache ou non
detache = data.get_tearoff_state()
if detache:
label = u"Menu détaché"
else:
label = u"Menu attaché"
tearoff_label.set_label(label)
def OnQuitter(widget, data):
question = gtk.MessageDialog(data,
gtk.DIALOG_MODAL,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_YES_NO,
"Voulez vous vraiment\nquitter le programme?")
reponse = question.run()
if reponse == gtk.RESPONSE_YES:
gtk.main_quit()
elif reponse in (gtk.RESPONSE_NONE, gtk.RESPONSE_NO):
question.destroy()
def OnAbout(widget, data):
about = gtk.MessageDialog(data,
gtk.DIALOG_MODAL,
gtk.MESSAGE_INFO,
gtk.BUTTONS_OK,
"Cours GTK+ 2.0\nhttp://www.gtk-fr.org")
about.run()
about.destroy()
Résultat :

