Comme je n'ai pas envie de réinventer la poudre à chaque fois, je mettrai ici des modèles qui marchent pour réaliser une barre de menu, et je les enrichirai au fur et à mesure.
La barre de menu, située en haut de la fenêtre, comporte ici 2 items: “Fichier” et “Aide”. Un clic sur l'un de ces 2 items fait apparaitre une petite fenêtre avec des items qui, lorsqu'ils sont cliqués par la souris, déclenchent des actions décrites dans les fonctions appelées (Ouvrir, Fermer, etc…).
Vous noterez aussi l'option “underline=” qui désigne le caractère de l'item qui sera souligné et qui permettra à l'item d'être appelé directement au clavier par alt-caractère. Les enchainements de plusieurs commandes alt sont possibles, à condition de ne pas relâcher la touche alt entre 2 commandes.
#!/usr/bin/python # -*- coding: utf-8 -*- import Tkinter class Application(Tkinter.Frame): def __init__(self, master=None): Tkinter.Frame.__init__(self, master, background="grey") # creation de la barre de menu: self.barremenu = Tkinter.Menu(self.master) # creation du menu "Fichier" self.fichier = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Fichier", underline=0, menu=self.fichier) self.fichier.add_command(label="Ouvrir", underline=0, command=self.ouvrir) self.fichier.add_command(label="Fermer", underline=0, command=self.fermer) self.fichier.add_command(label="Enregistrer", underline=0, command=self.enregistrer) self.fichier.add_command(label="Quitter", underline=0, command=self.quitter) # creation du menu "Aide" self.aide = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Aide", underline=0, menu=self.aide) self.aide.add_command(label="Manuel", underline=0, command = self.manuel) self.aide.add_command(label="A propos", underline=0, command = self.apropos) # afficher le menu self.master.config(menu=self.barremenu) def ouvrir(self): print "Ouvrir" def fermer(self): print "fermer" def enregistrer(self): print "enregistrer" def quitter(self): self.master.destroy() def manuel(self): print "manuel" def apropos(self): print "apropos" ############################################################################# fen=Tkinter.Tk() fen.title("Ma fenêtre") app=Application(fen) fen.geometry("%dx%d%+d%+d" % (300,300,100,100)) fen.mainloop()
Pour construire un item de menu désactivé au démarrage de la fenêtre, il suffit d'ajouter l'option “state=Tkinter.DISABLED” dans la liste des options d'un add_commande.
Par exemple, pour avoir l'item “Enregistrer” du menu Fichier désactivé au départ:
self.fichier.add_command(label="Enregistrer", underline=0, state=Tkinter.DISABLED, command=self.enregistrer)
Pour réactiver l'item à partir d'une des méthodes appelées (l'item est le 3ème item du menu, donc à l'indice 2):
self.fichier.entryconfig(2,state=Tkinter.NORMAL)
et pour désactiver de nouveau:
self.fichier.entryconfig(2,state=Tkinter.DISABLED)
Pour faire la même chose avec l'un des items de la barre de menu, par exemple ici, l'item “Aide”:
Pour désactiver l'item au départ:
self.barremenu.add_cascade(label="Aide",menu=self.aide, state=Tkinter.DISABLED)
Pour l'activer à partir d'une méthode:
self.barremenu.entryconfigure(3,state=Tkinter.NORMAL)
Et pour le désactiver de nouveau:
self.barremenu.entryconfigure(3,state=Tkinter.DISABLED)
Vous noterez une différence avec le cas précédent: l'item “Aide” de la barre de menu est le 3ème menu, son index devrait être “2” et en fait il est “3” (???).
Il suffit d'ajouter entre deux items du menu, la méthode .add_separator(). Par exemple, dans le code précédent, ajoutez avant l'item “Quitter” du menu Fichier:
self.fichier.add_separator()
Par exemple, entre l'item “Fichier” et l'item “Aide” de la barre de menu, on va ajouter un item “Calculer” qui, lorsqu'il sera cliqué, déclenchera directement une action:
Dans le code de base ci-dessus, avant la création de l'item “Aide”, ajoutez simplement:
self.barremenu.add_command(label="Calculer", command=self.calculer)
Et, bien sûr, la méthode qui va être appelée:
def calculer(self): print "calculer"
Par exemple, on va ajouter dans le menu “Fichier” un item qui s'appelle “Drapeau”, qui pourra avoir une coche devant.
A chaque fois qu'on le sélectionne, il change (coché → non-coché → coché → etc…)
self.drap=Tkinter.IntVar() self.drap.set(0) # initialisation: item non coché au départ self.fichier.add_checkbutton(label="Drapeau", command=self.drapeau, variable=self.drap)
Si la coche doit être mise au départ, il suffit d'initialiser la variable self.drap avec 1:
self.drap.set(1)
Et bien sûr, la méthode appelée qui fait des choses différentes en fonction de la valeur de self.drap.get():
def drapeau(self): #on peut tester ici self.drap.get() qui passe a 0 ou a 1 selon la coche de l'item "drapeau" du menu. print "drapeau"
On va placer 3 items dans le menu Action, qui comporteront une coche devant pour le dernier item sélectionné.
Les choix s'excluent: la sélection d'un item le coche, mais aussi décoche l'item précédemment choisi (comme des boutons radio).
Vous voyez l'utilisation de la variable nitem de type Tkinter.IntVar(). Elle est mise à 1 au départ (=1er choix coché). Elle est ensuite mise à 1, 2 ou 3 selon le dernier choix effectué. Les 3 items du choix exclusif appellent la même méthode (ici item()) qui peut tester le dernier choix fait (=la valeur de nitem) par self.nitem.get().
#!/usr/bin/python # -*- coding: utf-8 -*- import Tkinter class Application(Tkinter.Frame): def __init__(self, master=None): Tkinter.Frame.__init__(self, master, background="grey") # creation de la barre de menu: self.barremenu = Tkinter.Menu(self.master) # creation du menu "Fichier" self.fichier = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Fichier",menu=self.fichier) self.fichier.add_command(label="Ouvrir", command=self.ouvrir) self.fichier.add_command(label="Fermer", command=self.fermer) self.fichier.add_command(label="Enregistrer", command=self.enregistrer) self.fichier.add_command(label="Quitter", command=self.quitter) # creation du menu "Action" self.action = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Action",menu=self.action) self.action.add_command(label="Format", command=self.format) self.action.add_separator() self.nitem=Tkinter.IntVar() self.nitem.set(1) self.action.add_radiobutton(label="item1", command=self.item, variable=self.nitem, value=1) self.action.add_radiobutton(label="item2", command=self.item, variable=self.nitem, value=2) self.action.add_radiobutton(label="item3", command=self.item, variable=self.nitem, value=3) self.action.add_separator() self.action.add_command(label="Configuration", command=self.configuration) # creation du menu "Aide" self.aide = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Aide",menu=self.aide) self.aide.add_command(label="Manuel", command = self.manuel) self.aide.add_command(label="A propos", command = self.apropos) # afficher le menu self.master.config(menu=self.barremenu) def ouvrir(self): print "Ouvrir" def fermer(self): print "fermer" def enregistrer(self): print "enregistrer" def quitter(self): self.master.destroy() def format(self): print "format" def item(self): print "item: ", self.nitem.get() def configuration(self): print "configuration" def manuel(self): print "manuel" def apropos(self): print "apropos" ############################################################################# fen=Tkinter.Tk() fen.title("Ma fenêtre") app=Application(fen) fen.geometry("%dx%d%+d%+d" % (300,300,100,100)) fen.mainloop()
On va faire en sorte que l'item “Titre” du menu “Action” affiche un fenêtre supplémentaire avec des items de menu à sélectionner.
On voit aussi dans cet exemple comment on peut transmettre une valeur en sélectionnant un item de menu avec lambda:
#!/usr/bin/python # -*- coding: utf-8 -*- import Tkinter class Application(Tkinter.Frame): def __init__(self, master=None): Tkinter.Frame.__init__(self, master, background="grey") # creation de la barre de menu: self.barremenu = Tkinter.Menu(self.master) # creation du menu "Fichier" self.fichier = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Fichier",menu=self.fichier) self.fichier.add_command(label="Ouvrir", command=self.ouvrir) self.fichier.add_command(label="Fermer", command=self.fermer) self.fichier.add_command(label="Enregistrer", command=self.enregistrer) self.fichier.add_command(label="Quitter", command=self.quitter) # creation du menu "Action" self.action = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Action",menu=self.action) self.action.add_command(label="Format", command=self.format) # creation du sous-menu "Titre" du menu "Action" self.titre = Tkinter.Menu(self.action, tearoff=0) self.action.add_cascade(label="Titre",menu=self.titre) self.titre.add_command(label="Demo", command=lambda : self.changetitre('Demo')) self.titre.add_command(label="Test", command=lambda : self.changetitre('Test')) self.action.add_command(label="Configuration", command=self.configuration) # creation du menu "Aide" self.aide = Tkinter.Menu(self.barremenu, tearoff=0) self.barremenu.add_cascade(label="Aide",menu=self.aide) self.aide.add_command(label="Manuel", command = self.manuel) self.aide.add_command(label="A propos", command = self.apropos) # afficher le menu self.master.config(menu=self.barremenu) def ouvrir(self): print "Ouvrir" def fermer(self): print "fermer" def enregistrer(self): print "enregistrer" def quitter(self): self.master.destroy() def format(self): print "format" def changetitre(self,choix): self.master.title(choix) def configuration(self): print "configuration" def manuel(self): print "manuel" def apropos(self): print "apropos" ############################################################################# fen=Tkinter.Tk() fen.title("Ma fenêtre") app=Application(fen) fen.geometry("%dx%d%+d%+d" % (300,300,100,100)) fen.mainloop()
On va utiliser la même solution qu'avec les autres widgets.
1- Importer tkFont en plus de Tkinter (attention: les majuscules comptent!)
2- Instancier la classe tkFont.Font(), par exemple policemenu=tkFont.Font(), et lui fournir comme arguments les caractéristiques de la police voulue.
3- ajouter l'option “font=policemenu” à la création de chacun des items de la barre de menu.
NB: on ne change pas la police de la barre des menus elle-même, mais seulement des fenêtres d'items qui sont appelées par eux.
Les options possibles sont:
Exemple: dans le code suivant, les items du menu apparaissent en arial gras italic taille 10:
#!/usr/bin/python # -*- coding: utf-8 -*- import Tkinter, tkFont class Application(Tkinter.Frame): def __init__(self, master=None): Tkinter.Frame.__init__(self, master, background="grey") policemenu=tkFont.Font(self, size=10, family='Arial', weight=tkFont.BOLD, slant=tkFont.ITALIC) # creation de la barre de menu: self.barremenu = Tkinter.Menu(self.master) # creation du menu "Fichier" self.fichier = Tkinter.Menu(self.barremenu, tearoff=0, font=policemenu) self.barremenu.add_cascade(label="Fichier",menu=self.fichier) self.fichier.add_command(label="Ouvrir", command=self.ouvrir) self.fichier.add_command(label="Fermer", command=self.fermer) self.fichier.add_command(label="Enregistrer", command=self.enregistrer) self.fichier.add_separator() self.fichier.add_command(label="Quitter", command=self.quitter) # creation du menu "Aide" self.aide = Tkinter.Menu(self.barremenu, tearoff=0, font=policemenu) self.barremenu.add_cascade(label="Aide",menu=self.aide) self.aide.add_command(label="Manuel", command = self.manuel) self.aide.add_command(label="A propos", command = self.apropos) # afficher le menu self.master.config(menu=self.barremenu) def ouvrir(self): print "Ouvrir" def fermer(self): print "fermer" def enregistrer(self): print "enregistrer" def quitter(self): self.master.destroy() def manuel(self): print "manuel" def apropos(self): print "apropos" ############################################################################# fen=Tkinter.Tk() fen.title("Ma fenêtre") app=Application(fen) fen.geometry("%dx%d%+d%+d" % (300,300,100,100)) fen.mainloop()
On peut changer la couleur de la police, la couleur de fond ainsi que la couleur de la barre de sélection au passage de la souris. Cela ne concerne pas les items de la barre de menu eux-même, mais des fenêtres d'items qui sont appelées par eux.
La couleur peut être définie par les noms habituels ('red' = rouge, 'blue' = bleu, etc…), ou par des valeurs hexadécimales.
Comme pour le changement de police, il faut provoquer la modification pour chacun des items de la barre de menu.
#!/usr/bin/python # -*- coding: utf-8 -*- import Tkinter class Application(Tkinter.Frame): def __init__(self, master=None): Tkinter.Frame.__init__(self, master, background="grey") # creation de la barre de menu: self.barremenu = Tkinter.Menu(self.master) # creation du menu "Fichier" self.fichier = Tkinter.Menu(self.barremenu, tearoff=0, bg='yellow', fg='green', activebackground='red', activeforeground='yellow') self.barremenu.add_cascade(label="Fichier",menu=self.fichier) self.fichier.add_command(label="Ouvrir", command=self.ouvrir) self.fichier.add_command(label="Fermer", command=self.fermer) self.fichier.add_command(label="Enregistrer", command=self.enregistrer) self.fichier.add_separator() self.fichier.add_command(label="Quitter", command=self.quitter) # creation du menu "Aide" self.aide = Tkinter.Menu(self.barremenu, tearoff=0, bg='yellow', fg='green', activebackground='red', activeforeground='yellow') self.barremenu.add_cascade(label="Aide",menu=self.aide) self.aide.add_command(label="Manuel", command = self.manuel) self.aide.add_command(label="A propos", command = self.apropos) # afficher le menu self.master.config(menu=self.barremenu) def ouvrir(self): print "Ouvrir" def fermer(self): print "fermer" def enregistrer(self): print "enregistrer" def quitter(self): self.master.destroy() def manuel(self): print "manuel" def apropos(self): print "apropos" ############################################################################# fen=Tkinter.Tk() fen.title("Ma fenêtre") app=Application(fen) fen.geometry("%dx%d%+d%+d" % (300,300,100,100)) fen.mainloop()
On veut qu'un menu se présente un peu comme cela (exemple des items d'un menu de type “Fichier”):
Ouvrir Ctrl+O Fermer Ctrl+W Enregistrer Ctrl+S Quitter Alt+X
C'est à dire que les raccourcis commandes claviers sont alignés.
Le problème, c'est que dans une police de caractères type “arial”, les caractères n'ont pas tous la même largeur en pixels à l'écran.
J'ai mis pas mal de temps à trouver cette option qui ne se trouve pas dans tous les manuels.
Au lieu de faire par exemple:
self.edition.add_command(label="Copier Ctrl-C", underline=2, command = self.copier)
On fait:
self.edition.add_command(label="Copier", accelerator="Ctrl-C", underline=2, command = self.copier)
Et les différents codes de commande clavier d'un même menu et cités par accelerator, seront alignés
Je cite cette solution pour mémoire et parce que c'est un exemple d'utilisation de la méthode “measure” qui peut servir à d'autres choses.
On va utiliser une des fonctions du module tkFont: “measure(texte)”, qui mesure en pixel la longueur d'un texte. Et on ajustera à la fin avec des espaces.
L'alignement n'est pas parfait: on peut faire une erreur maxi égale au nombre de pixels d'un espace - 1. Mais c'est tout de même très bien!
l'alignement choisi ici est de caler les codes clavier à droite. Mais on pourrait aussi les aligner sur leur 1er caractère en modifiant le code.
La fonction qui ajuste l'item s'appelle ici: ajusteitems().
Voilà le code (il est auto-documenté):
#!/usr/bin/python # -*- coding: utf-8 -*- import Tkinter, tkFont class Application(Tkinter.Frame): def __init__(self, master=None): Tkinter.Frame.__init__(self, master, background="grey") self.policemenu=tkFont.Font(self, size=9, family='Arial') # creation de la barre de menu: self.barremenu = Tkinter.Menu(self.master) # représentation des items du menu "Fichier" sous forme de liste: self.itemmenu=[ ["Ouvrir", "Ctrl+O"], ["Fermer", "Ctrl+W"], ["Enregistrer", "Ctrl+S"], ["Quitter", "Alt+X"] ] items=self.ajusteitems(self.itemmenu) # creation du menu "Fichier" self.fichier = Tkinter.Menu(self.barremenu, tearoff=0, font=self.policemenu) self.barremenu.add_cascade(label="Fichier",menu=self.fichier) self.fichier.add_command(label=items[0], command=self.ouvrir) self.fichier.add_command(label=items[1], command=self.fermer) self.fichier.add_command(label=items[2], command=self.enregistrer) self.fichier.add_separator() self.fichier.add_command(label=items[3], command=self.quitter) # représentation des items du menu "Aide" sous forme de liste: self.itemmenu=[ ["Manuel", "F1"], ["A propos", ""] ] items=self.ajusteitems(self.itemmenu) # creation du menu "Aide" self.aide = Tkinter.Menu(self.barremenu, tearoff=0, font=self.policemenu) self.barremenu.add_cascade(label="Aide",menu=self.aide) self.aide.add_command(label=items[0], command = self.manuel) self.aide.add_command(label=items[1], command = self.apropos) # afficher le menu self.master.config(menu=self.barremenu) def ajusteitems(self, itemmenu): # Calcul de la longueur maxi en pixels des items du menu fichier: lg=0 for i1, i2 in itemmenu: lg1=self.policemenu.measure(i1) lg2=self.policemenu.measure(i2) if lg1+lg2>lg: lg=lg1+lg2 esp=self.policemenu.measure(" ") # = nb de pixels d'un espace lg=lg+2*esp # ajout de 2 espaces # Ajustement des espaces pour que les commandes clavier soient calées à droite lch=[] for i1, i2 in self.itemmenu: lg1=self.policemenu.measure(i1) lg2=self.policemenu.measure(i2) n=(lg-lg1-lg2)/esp lch.append(i1 + " "*n + i2) return lch def ouvrir(self): print "Ouvrir" def fermer(self): print "fermer" def enregistrer(self): print "enregistrer" def quitter(self): self.master.destroy() def manuel(self): print "manuel" def apropos(self): print "apropos" ############################################################################# fen=Tkinter.Tk() fen.title("Ma fenêtre") app=Application(fen) fen.geometry("%dx%d%+d%+d" % (300,300,100,100)) fen.mainloop()