Les pages qui suivent contiennent des indications et et des exemples complémentaires qui pourront vous être utiles pour le développement de vos projets personnels. Il ne s'agit évidemment pas d'une documentation de référence complète sur "Tkinter". Pour en savoir plus, vous devrez tôt ou tard consulter des ouvrages spécialisés, comme par exemple l'excellent "Python and Tkinter programming" de John E. Grayson, dont vous trouverez la référence complète à la page.
Les « boutons radio ».
Les "widgets" « boutons radio » permettent de proposer à l'utilisateur un ensemble de choix mutuellement exclusifs. On les appelle ainsi par analogie avec les boutons de sélection que l'on trouvait jadis sur les postes de radio. Ces boutons étaient conçus de telle manière qu'un seul à la fois pouvait être enfoncé : tous les autres ressortaient automatiquement.
La caractéristique essentielle de ces "widgets" est qu'on les utilise toujours par groupes. Tous les boutons radio faisant partie d'un même groupe sont associés à une seule et même "variable Tkinter", mais chacun d'entre eux se voit aussi attribuer une "valeur" particulière.
Lorsque l'utilisateur sélectionne l'un des boutons, la valeur correspondant à ce bouton est affectée à la "variable Tkinter" commune.
from Tkinter import *
class RadioDemo(Frame):
 """Démo : utilisation de widgets 'boutons radio'"""
 def __init__(self, boss =None):
 """Création d'un champ d'entrée avec 4 boutons radio"""
 Frame.__init__(self)
 self.pack()
 # Champ d'entrée contenant un petit texte :
 self.texte = Entry(self, width =30, font ="Arial 14")
 self.texte.insert(END, "La programmation, c'est génial")
 self.texte.pack(padx =8, pady =8)
 # Nom français et nom technique des quatre styles de police :
 stylePoliceFr =["Normal", "Gras", "Italique", "Gras/Italique"]
 stylePoliceTk =["normal", "bold", "italic" , "bold italic"]
 # Le style actuel est mémorisé dans un 'objet-variable' Tkinter ;
 self.choixPolice = StringVar()
 self.choixPolice.set(stylePoliceTk[0])
 # Création des quatre 'boutons radio' :
 for n in range(4):
 bout = Radiobutton(self,
 text = stylePoliceFr[n],
 variable = self.choixPolice,
 value = stylePoliceTk[n],
 command = self.changePolice)
 bout.pack(side =LEFT, padx =5)
 def changePolice(self):
 """Remplacement du style de la police actuelle"""
 police = "Arial 15 " + self.choixPolice.get()
 self.texte.configure(font =police)
if __name__ == '__main__':
 RadioDemo().mainloop()
Utilisation des cadres (frames) pour la composition d'une fenêtre.
Vous avez déjà abondamment utilisé la classe de "widgets" codice_1 (« cadre », en français), notamment pour créer de nouveaux "widgets" complexes par dérivation.
Le petit script ci-dessous vous montre l'utilité de cette même classe pour regrouper des ensembles de "widgets" et les disposer d'une manière déterminée dans une fenêtre. Il vous démontre également l'utilisation de certaines options décoratives (bordures, relief, etc.).
Pour composer la fenêtre ci-contre, nous avons utilisé deux cadres codice_2 et codice_3, de manière à réaliser deux groupes de "widgets" bien distincts, l'un à gauche et l'autre à droite. Nous avons coloré ces deux cadres pour bien les mettre en évidence, mais ce n'est évidemment pas indispensable.
Le cadre codice_2 contient lui-même 6 autres cadres, qui contiennent chacun un "widget" de la classe codice_5. Le cadre codice_3 contient un "widget" codice_7 et un "widget" codice_8. Les couleurs et garnitures sont de simples options.
from Tkinter import * #1
 #2
fen = Tk() #3
fen.title("Fenêtre composée à l'aide de frames") #4
fen.geometry("300x300") #5
 #6
f1 = Frame(fen, bg = '#80c0c0') #7
f1.pack(side =LEFT, padx =5) #8
 #9
fint = [0]*6 #10
for (n, col, rel, txt) in [(0, 'grey50', RAISED, 'Relief sortant'), #11
 (1, 'grey60', SUNKEN, 'Relief rentrant'), #12
 (2, 'grey70', FLAT, 'Pas de relief'), #13
 (3, 'grey80', RIDGE, 'Crête'), #14
 (4, 'grey90', GROOVE, 'Sillon'), #15
 (5, 'grey100', SOLID, 'Bordure')]: #16
 fint[n] = Frame(f1, bd =2, relief =rel) #17
 e = Label(fint[n], text =txt, width =15, bg =col) #18
 e.pack(side =LEFT, padx =5, pady =5) #19
 fint[n].pack(side =TOP, padx =10, pady =5) #20
 #21
f2 = Frame(fen, bg ='#d0d0b0', bd =2, relief =GROOVE) #22
f2.pack(side =RIGHT, padx =5) #23
 #24
can = Canvas(f2, width =80, height =80, bg ='white', bd =2, relief =SOLID) #25
can.pack(padx =15, pady =15) #26
bou =Button(f2, text='Bouton') #27
bou.pack() #28
 #29
fen.mainloop() #30
Comment déplacer des dessins à l'aide de la souris.
Le "widget" canevas est l'un des points forts de la bibliothèque graphique "Tkinter". Il intègre en effet un grand nombre de dispositifs très efficaces pour manipuler des dessins. Le script ci-après est destiné à vous montrer quelques techniques de base. Si vous voulez en savoir plus, notamment en ce qui concerne la manipulation de dessins composés de plusieurs parties, veuillez consulter l'un ou l'autre ouvrage de référence traitant de "Tkinter".
Au démarrage de notre petite application, une série de dessins sont tracés au hasard dans un canevas (il s'agit en l'occurrence de simples ellipses colorées). Vous pouvez déplacer n'importe lequel de ces dessins en les « saisissant » à l'aide de votre souris.
Lorsqu'un dessin est déplacé, il passe à l'avant-plan par rapport aux autres, et sa bordure apparaît plus épaisse pendant toute la durée de sa manipulation.
Pour bien comprendre la technique utilisée, vous devez vous rappeler qu'un logiciel utilisant une interface graphique est un logiciel « piloté par les événements » (revoyez au besoin les explications de la page ). Dans cette application, nous allons mettre en place un mécanisme qui réagit aux événements : « enfoncement du bouton gauche de la souris », « déplacement de la souris, le bouton gauche restant enfoncé », « relâchement du bouton gauche ».
Ces événements sont générés par le système d'exploitation et pris en charge par l'interface "Tkinter". Notre travail de programmation consistera donc simplement à les associer à des gestionnaires différents (fonctions ou méthodes).
from Tkinter import *
from random import randrange
class Draw(Frame):
 "classe définissant la fenêtre principale du programme"
 def __init__(self):
 Frame.__init__(self)
 # mise en place du canevas - dessin de 15 ellipses colorées :
 self.c = Canvas(self, width =400, height =300, bg ='ivory')
 self.c.pack(padx =5, pady =3)
 for i in range(15):
 # tirage d'une couleur au hasard :
 coul =['brown','red','orange','yellow','green','cyan','blue',
 'violet', 'purple'][randrange(9)]
 # tracé d'une ellipse avec coordonnées aléatoires :
 x1, y1 = randrange(300), randrange(200)
 x2, y2 = x1 + randrange(10, 150), y1 + randrange(10, 150)
 self.c.create_oval(x1, y1, x2, y2, fill =coul)
 # liaison d'événements <souris> au widget <canevas> :
 self.c.bind("<Button-1>", self.mouseDown)
 self.c.bind("<Button1-Motion>", self.mouseMove)
 self.c.bind("<Button1-ButtonRelease>", self.mouseUp)
 # mise en place d'un bouton de sortie :
 b_fin = Button(self, text ='Terminer', bg ='royal blue', fg ='white',
 font =('Helvetica', 10, 'bold'), command =self.quit)
 b_fin.pack(pady =2)
 self.pack()
 def mouseDown(self, event):
 "Op. à effectuer quand le bouton gauche de la souris est enfoncé"
 self.currObject =None
 # event.x et event.y contiennent les coordonnées du clic effectué :
 self.x1, self.y1 = event.x, event.y
 # <find_closest> renvoie la référence du dessin le plus proche :
 self.selObject = self.c.find_closest(self.x1, self.y1)
 # modification de l'épaisseur du contour du dessin :
 self.c.itemconfig(self.selObject, width =3)
 # <lift> fait passer le dessin à l'avant-plan :
 self.c.lift(self.selObject)
 def mouseMove(self, event):
 "Op. à effectuer quand la souris se déplace, bouton gauche enfoncé"
 x2, y2 = event.x, event.y
 dx, dy = x2 -self.x1, y2 -self.y1
 if self.selObject:
 self.c.move(self.selObject, dx, dy)
 self.x1, self.y1 = x2, y2
 def mouseUp(self, event):
 "Op. à effectuer quand le bouton gauche de la souris est relâché"
 if self.selObject:
 self.c.itemconfig(self.selObject, width =1)
 self.selObject =None
if __name__ == '__main__':
 Draw().mainloop()
Le script contient essentiellement la définition d'une classe graphique dérivée de codice_1.
Comme c'est souvent le cas pour les programmes exploitant les classes d'objets, le corps principal du script se résume à une seule instruction composée, dans laquelle on réalise deux opérations consécutives : instanciation d'un objet de la classe définie précédemment, et activation de sa méthode codice_38 (laquelle démarre l'observateur d'événements).
Le constructeur de la classe codice_39 présente une structure qui doit vous être devenue familière, à savoir : appel au constructeur de la classe parente, puis mise en place de divers "widgets".
Dans le "widget" canevas, nous instancions 15 dessins sans nous préoccuper de conserver leurs références dans des variables. Nous pouvons procéder ainsi parce que "Tkinter" conserve lui-même une référence interne pour chacun de ces objets. (Si vous travaillez avec d'autres bibliothèques graphiques, vous devrez probablement prévoir une mémorisation de ces références).
Les dessins sont de simples ellipses colorées. Leur couleur est choisie au hasard dans une liste de 9 possibilités, l'indice de la couleur choisie étant déterminé par la fonction codice_40 importée du module codice_41.
Le mécanisme d'interaction est installé ensuite : on associe les trois identificateurs d'événements codice_42, codice_43 et codice_44 concernant le "widget" canevas, aux noms des trois méthodes choisies comme gestionnaires d'événements.
Lorsque l'utilisateur enfonce le bouton gauche de sa souris, la méthode codice_45 est donc activée, et le système d'exploitation lui transmet en argument un objet codice_46, dont les attributs codice_47 et codice_48 contiennent les coordonnées du curseur souris dans le canevas, déterminées au moment du clic.
Nous mémorisons directement ces coordonnées dans les variables d'instance codice_49 et codice_50, car nous en aurons besoin par ailleurs. Ensuite, nous utilisons la méthode codice_51 du "widget" canevas, qui nous renvoie la référence du dessin le plus proche. (Note : cette méthode bien pratique renvoie toujours une référence, même si le clic de souris n'a pas été effectué à l'intérieur du dessin).
Le reste est facile : la référence du dessin sélectionné est mémorisée dans une variable d'instance, et nous pouvons faire appel à d'autres méthodes du "widget" canevas pour modifier ses caractéristiques. En l'occurrence, nous utilisons les méthodes codice_52 et codice_53 pour épaissir son contour et le faire passer à l'avant-plan.
Le « transport » du dessin est assuré par la méthode codice_54, invoquée à chaque fois que la souris se déplace alors que son bouton gauche est resté enfoncé. L'objet event contient cette fois encore les coordonnées du curseur souris, au terme de ce déplacement. Nous nous en servons pour calculer les différences entre ces nouvelles coordonnées et les précédentes, afin de pouvoir les transmettre à la méthode codice_55 du "widget" canevas, qui effectuera le transport proprement dit.
Nous ne pouvons cependant faire appel à cette méthode que s'il existe effectivement un objet sélectionné, et il nous faut veiller également à mémoriser les nouvelles coordonnées acquises.
La méthode codice_56 termine le travail. Lorsque le dessin transporté est arrivé à destination, il reste à annuler la sélection et rendre au contour son épaisseur initiale. Ceci ne peut être envisagé que s'il existe effectivement une sélection, bien entendu.
Python Mega Widgets.
Les modules "Pmw" constituent une extension intéressante de "Tkinter". Entièrement écrits en Python, ils contiennent toute une bibliothèque de "widgets" composites, construits à partir des classes de base de "Tkinter". Dotés de fonctionnalités très étendues, ces "widgets" peuvent se révéler fort précieux pour le développement rapide d'applications complexes. Si vous souhaitez les utiliser, sachez cependant que les modules "Pmw" ne font pas partie de l'installation standard de Python : vous devrez donc toujours vérifier leur présence sur les machines cibles de vos programmes. 
Il existe un grand nombre de ces méga-widgets. Nous n'en présenterons ici que quelques-uns parmi les plus utiles. Vous pouvez rapidement vous faire une idée plus complète de leurs multiples possibilités, en essayant les scripts de démonstration qui les accompagnent (lancez par exemple le script all.py , situé dans le répertoire codice_57).
« Combo Box ».
Les méga-widgets s'utilisent aisément. La petite application ci-après vous montre comment mettre en œuvre un "widget" de type codice_58 (boîte de liste combinée à un champ d'entrée). Nous l'avons configuré de la manière la plus habituelle (avec une boîte de liste déroulante).
Lorsque l'utilisateur de notre petit programme choisit une couleur dans la liste déroulante (il peut aussi entrer un nom de couleur directement dans le champ d'entrée), cette couleur devient automatiquement la couleur de fond pour la fenêtre maîtresse.
Dans cette fenêtre maîtresse, nous avons ajouté un libellé et un bouton, afin de vous montrer comment vous pouvez accéder à la sélection opérée précédemment dans le "ComboBox" lui-même (le bouton provoque l'affichage du nom de la dernière couleur choisie).
 from Tkinter import *
 import Pmw
 def changeCoul(col):
 fen.configure(background = col)
 def changeLabel(): 
 lab.configure(text = combo.get())
 couleurs = ('navy', 'royal blue', 'steelblue1', 'cadet blue',
 'lawn green', 'forest green', 'dark red',
 'grey80','grey60', 'grey40', 'grey20')
 fen = Pmw.initialise() 
 bou = Button(fen, text ="Test", command =changeLabel)
 bou.grid(row =1, column =0, padx =8, pady =6)
 lab = Label(fen, text ='néant', bg ='ivory')
 lab.grid(row =1, column =1, padx =8)
 combo = Pmw.ComboBox(fen, labelpos = NW,
 label_text = 'Choisissez la couleur :',
 scrolledlist_items = couleurs,
 listheight = 150,
 selectioncommand = changeCoul)
 combo.grid(row =2, columnspan =2, padx =10, pady =10)
 fen.mainloop()
Remarque concernant l'entrée de caractères accentués.
Nous vous avons déjà signalé précédemment que Python est tout à fait capable de prendre en charge les alphabets du monde entier (grec, cyrillique, arabe, japonais, etc. - voir notamment page ). Il en va de même pour "Tkinter". En tant que francophone, vous souhaiterez certainement que les utilisateurs de vos scripts puissent entrer des caractères accentués dans les widgets "Entry", "Text" et leurs dérivés ("ComboBox", "ScrolledText").
Veuillez donc prendre bonne note que lorsque vous entrez dans l'un de ces widgets une chaîne contenant un ou plusieurs caractères non-ASCII (tel qu'une lettre accentuée, par exemple), "Tkinter" encode cette chaîne suivant la norme UTF-8. Si votre ordinateur utilise plutôt le codage Latin-1 par défaut (ce qui est très souvent le cas), vous devrez convertir la chaîne avant de pouvoir l'afficher.
Cela peut se faire très aisément en utilisant la fonction intégrée codice_65. Exemple :
Essayez ce petit script en entrant des chaînes avec caractères accentués dans le champ d'entrée.
Essayez encore, mais en remplaçant l'instruction codice_66 par codice_67. Concluez. 
« Scrolled Text ».
Ce "méga-widget" étend les possibilités du "widget" Text sandard, en lui associant un cadre, un libellé (titre) et des barres de défilement.
Comme le démontrera le petit script ci-dessous, il sert fondamentalement à afficher des textes, mais ceux-ci peuvent être mis en forme et intégrer des images.
Vous pouvez également rendre « cliquables » les éléments affichés (textes ou images), et vous en servir pour déclencher toutes sortes de mécanismes.
Dans l'application qui génère la figure ci-dessus, par exemple, le fait de cliquer sur le nom « Jean de la Fontaine » provoque le défilement automatique du texte ("scrolling"), jusqu'à ce qu'une rubrique décrivant cet auteur devienne visible dans le "widget" (Voir page suivante le script correspondant).
D'autres fonctionnalités sont présentes, mais nous ne présenterons ici que les plus fondamentales. Veuillez donc consulter les démos et exemples accompagnant "Pmw" pour en savoir davantage.
"Gestion du texte affiché" : Vous pouvez accéder à n'importe quelle portion du texte pris en charge par le "widget" grâce à deux concepts complémentaires, les "indices" et les "balises" :
Note : Pour la bonne compréhension du script ci-dessous, veuillez considérer que le texte de la fable traitée doit être accessible, dans un fichier nommé "CorbRenard.txt". 
from Tkinter import * #1
import Pmw #2
 #3
def action(event=None): #4
 """défilement du texte jusqu'à la balise <cible>""" #5
 index = st.tag_nextrange('cible', '0.0', END) #6
 st.see(index[0]) #7
 #8
fen = Pmw.initialise() #10
st = Pmw.ScrolledText(fen, #11
 labelpos =N, #12
 label_text ="Petite démo du widget ScrolledText", #13
 label_font ='Times 14 bold italic', #14
 label_fg = 'navy', label_pady =5, #15
 text_font='Helvetica 11 normal', text_bg ='ivory', #16
 text_padx =10, text_pady =10, text_wrap ='none', #17
 borderframe =1, #18
 borderframe_borderwidth =3, #19
 borderframe_relief =SOLID, #20
 usehullsize =1, #21
 hull_width =370, hull_height =240) #22
st.pack(expand =YES, fill =BOTH, padx =8, pady =8) #23
 #24
st.tag_configure('titre', foreground ='brown', font ='Helvetica 11 bold italic') #26
st.tag_configure('lien', foreground ='blue', font ='Helvetica 11 bold') #27
st.tag_configure('cible', foreground ='forest green', font ='Times 11 bold') #28
st.tag_bind('lien', '<Button-1>', action) #29
 #30
titre ="""Le Corbeau et le Renard #31
par Jean de la Fontaine, auteur français #32
\n""" #33
auteur =""" #34
Jean de la Fontaine #35
écrivain français (1621-1695) #36
célèbre pour ses Contes en vers, #37
et surtout ses Fables, publiées #38
de 1668 à 1694.""" #39
 #40
st.importfile('CorbRenard.txt') #42
st.insert('0.0', titre, 'titre') #43
st.insert(END, auteur, 'cible') #44
photo =PhotoImage(file= 'Penguin.gif') #46
st.image_create('6.14', image =photo) #47
st.tag_add('lien', '2.4', '2.23') #49
 #50
fen.mainloop() #51
« Scrolled Canvas ».
Le script ci-après vous montre comment vous pouvez exploiter le codice_96 codice_97, lequel étend les possibilités du "widget" codice_98 standard en lui associant des barres de défilement, un libellé et un cadre. Notre exemple constitue en fait un petit jeu d'adresse, dans lequel l'utilisateur doit réussir à cliquer sur un bouton qui s'esquive sans cesse. (Note : si vous éprouvez vraiment des difficultés pour l'attraper, commencez d'abord par dilater la fenêtre).
Le "widget" codice_98 est très versatile : il vous permet de combiner à volonté des dessins, des images "bitmap", des fragments de texte, et même d'autres "widgets", dans un espace parfaitement extensible. Si vous souhaitez développer l'un ou l'autre jeu graphique, c'est évidemment le "widget" qu'il vous faut apprendre à maîtriser en priorité.
Comprenez bien cependant que les indications que nous vous fournissons à ce sujet dans les présentes notes sont forcément très incomplètes. Leur objectif est seulement de vous aider à comprendre quelques concepts de base, afin que vous puissiez ensuite consulter les ouvrages de référence spécialisés dans de bonnes conditions.
Notre petite application se présente comme une nouvelle classe codice_100, obtenue par dérivation à partir de la classe de "méga-widgets" codice_101. Elle contient donc un grand canevas muni de barres de défilement, dans lequel nous commençons par planter un décor constitué de 80 ellipses de couleur dont l'emplacement et les dimensions sont tirés au hasard.
Nous y ajoutons également un petit clin d'œil sous la forme d'une image bitmap, destinée avant tout à vous rappeler comment vous pouvez gérer ce type de ressource.
Nous y installons enfin un véritable "widget" : un simple bouton, en l'occurrence, mais la technique mise en œuvre pourrait s'appliquer à n'importe quel autre type de "widget", y compris un gros "widget" composite comme ceux que nous avons développés précédemment. Cette grande souplesse dans le développement d'applications complexes est l'un des principaux bénéfices apportés par le mode de programmation « orientée objet ».
Le bouton s'anime dès qu'on l'a enfoncé une première fois. Dans votre analyse du script ci-après, soyez attentifs aux méthodes utilisées pour modifier les propriétés d'un objet existant.
from Tkinter import * #1
import Pmw #2
from random import randrange #3
 #4
Pmw.initialise() #5
coul =['sienna','maroon','brown','pink','tan','wheat','gold','orange','plum', #6
 'red','khaki','indian red','thistle','firebrick','salmon','coral'] #7
 #8
class FenPrinc(Pmw.ScrolledCanvas): #9
 """Fenêtre principale : canevas extensible avec barres de défilement""" #10
 def __init__(self): #11
 Pmw.ScrolledCanvas.__init__(self, #12
 usehullsize =1, hull_width =500, hull_height =300, #13
 canvas_bg ='grey40', canvasmargin =10, #14
 labelpos =N, label_text ='Attrapez le bouton !', #15
 borderframe =1, #16
 borderframe_borderwidth =3) #17
 # Les options ci-dessous doivent être précisées après initialisation : #18
 self.configure(vscrollmode ='dynamic', hscrollmode ='dynamic') #19
 self.pack(padx =5, pady =5, expand =YES, fill =BOTH) #20
 #21
 self.can = self.interior() # accès au composant canevas #22
 # Décor : tracé d'une série d'ellipses aléatoires : #23
 for r in range(80): #24
 x1, y1 = randrange(-800,800), randrange(-800,800) #25
 x2, y2 = x1 + randrange(40,300), y1 + randrange(40,300) #26
 couleur = coul[randrange(0,16)] #27
 self.can.create_oval(x1, y1, x2, y2, fill=couleur, outline='black') #28
 # Ajout d'une petite image GIF : #29
 self.img = PhotoImage(file ='linux2.gif') #30
 self.can.create_image(50, 20, image =self.img) #31
 # Dessin du bouton à attraper : #32
 self.x, self.y = 50, 100 #33
 self.bou = Button(self.can, text ="Start", command =self.start) #34
 self.fb = self.can.create_window(self.x, self.y, window =self.bou) #35
 self.resizescrollregion() #36
 #37
 def anim(self): #38
 if self.run ==0: #39
 return #40
 self.x += randrange(-60, 61) #41
 self.y += randrange(-60, 61) #42
 self.can.coords(self.fb, self.x, self.y) #43
 self.configure(label_text = 'Cherchez en %s %s' % (self.x, self.y)) #44
 self.resizescrollregion() #45
 self.after(250, self.anim) #46
 #47
 def stop(self): #48
 self.run =0 #49
 self.bou.configure(text ="Restart", command =self.start) #50
 #51
 def start(self): #52
 self.bou.configure(text ="Attrapez-moi !", command =self.stop) #53
 self.run =1 #54
 self.anim() #55
 #56
 #58
if __name__ == '__main__': #59
 FenPrinc().mainloop() #60
Barres d'outils avec bulles d'aide - expressions lambda.
De nombreux programmes comportent une ou plusieurs « barres d'outils » ("toolbar") constituées de petits boutons sur lesquels sont représentés des pictogrammes (icônes). Cette façon de faire permet de proposer à l'utilisateur un grand nombre de commandes spécialisées, sans que celles-ci n'occupent une place excessive à l'écran (un petit dessin vaut mieux qu'un long discours, dit-on).
La signification de ces pictogrammes n'est cependant pas toujours évidente, surtout pour les utilisateurs néophytes. Il est donc vivement conseillé de compléter les barres d'outils à l'aide d'un système de bulles d'aide ("tool tips"), qui sont des petits messages explicatifs apparaissant automatiquement lorsque la souris survole les boutons concernés.
L'application décrite ci-après comporte une barre d'outils et un canevas. Lorsque l'utilisateur clique sur l'un des boutons de la barre, le pictogramme qu'il porte est recopié dans le canevas, à un emplacement choisi au hasard :
Dans notre exemple, chaque bouton apparaît entouré d'un sillon. Vous pouvez aisément obtenir d'autres aspects en choisissant judicieusement les options codice_115 et codice_116 (bordure) dans l'instruction d'instanciation des boutons. En particulier, vous pouvez choisir codice_117 et codice_118 pour obtenir des petits boutons « plats », sans aucun relief.
La mise en place des bulles d'aide est un jeu d'enfant. Il suffit d'instancier un seul objet codice_119 pour l'ensemble de l'application, puis d'associer un texte à chacun des "widgets" auxquels on souhaite associer une bulle d'aide, en faisant appel autant de fois que nécessaire à la méthode codice_120 de cet objet.
from Tkinter import *
import Pmw
from random import randrange
images =('floppy_2','papi2','pion_1','pion_2','help_4')
textes =('sauvegarde','papillon','joueur 1','joueur 2','Aide')
class Application(Frame):
 def __init__(self):
 Frame.__init__(self)
 # Création d'un objet <bulle d'aide> (un seul suffit) :
 tip = Pmw.Balloon(self)
 # Création de la barre d'outils (c'est un simple cadre) :
 toolbar = Frame(self, bd =1)
 toolbar.pack(expand =YES, fill =X)
 # Nombre de boutons à construire : 
 nBou = len(images)
 # Les icônes des boutons doivent être placées dans des variables
 # persistantes. Une liste fera l'affaire :
 self.photoI =[None]*nBou
 for b in range(nBou):
 # Création de l'icône (objet PhotoImage Tkinter) :
 self.photoI[b] =PhotoImage(file = images[b] +'.gif')
 # Création du bouton.:
 # On utilise une expression "lambda" pour transmettre
 # un argument à la méthode invoquée comme commande :
 bou = Button(toolbar, image =self.photoI[b], relief =GROOVE,
 command = lambda arg =b: self.action(arg))
 bou.pack(side =LEFT)
 # association du bouton avec un texte d'aide (bulle) :
 tip.bind(bou, textes[b]) 
 self.ca = Canvas(self, width =400, height =200, bg ='orange')
 self.ca.pack()
 self.pack()
 def action(self, b):
 "l'icône du bouton b est recopiée dans le canevas"
 x, y = randrange(25,375), randrange(25,175)
 self.ca.create_image(x, y, image =self.photoI[b])
Application().mainloop()
Métaprogrammation. Expressions lambda :.
Vous savez qu'en règle générale, on associe à chaque bouton une "commande", laquelle est une méthode ou une fonction particulière qui se charge d'effectuer le travail lorsque le bouton est activé. Or dans l'application présente, tous les boutons doivent faire à peu près la même chose (recopier un dessin dans le canevas), la seule différence entre eux étant le dessin concerné.
Pour simplifier notre code, nous voudrions donc pouvoir associer l'option codice_121 de tous nos boutons avec une seule et même méthode (ce sera la méthode codice_122), mais en lui transmettant à chaque fois la référence du bouton particulier utilisé, de manière à ce que l'action accomplie puisse être différente pour chacun d'eux.
Une difficulté se présente, cependant, parce que l'option command du "widget" codice_123 accepte seulement une "valeur" ou une "expression", et non une "instruction". Il est donc permis de lui indiquer la référence d'une fonction, mais pas de l'invoquer véritablement en lui transmettant des arguments éventuels (c'est la raison pour laquelle on indique le nom de cette fonction sans lui adjoindre de parenthèses).
On peut résoudre cette difficulté de deux manières :
Nous ne détaillerons pas davantage ici la question des expressions lambda, car elle déborde du cadre que nous nous sommes fixés pour cet ouvrage d'initiation. Si vous souhaitez en savoir plus, veuillez donc consulter l'un ou l'autre des ouvrages de référence cités dans la bibliographie.
Fenêtres avec menus.
Nous allons décrire à présent la construction d'une fenêtre d'application dotée de différents types de menus « déroulants », chacun de ces menus pouvant être « détaché » de l'application principale pour devenir lui-même une petite fenêtre indépendante, comme dans l'illustration ci-dessous.
Cet exercice un peu plus long nous servira également de révision, et nous le réaliserons par étapes, en appliquant une stratégie de programmation que l'on appelle "développement incrémental".