(site sans réclame)
Python  –  Manuel  –  Cont@ct  –  S'abonner

Module graphique Tkinter pour Python

L

E module graphique de référence de python est Tkinter. Il en existe d'autres: wxPython, PyQt ou PyGTK. Cette page comporte des répétitions à l'attention des programmeurs pressés qui brûlent les étapes. Les exemples, testés sur GNU/Linux Debian, sont fonctionnels au moins jusqu'à python 2.7.

Cette page a été commencée en 2007 et est continûment complétée et corrigée. 2014.10 Début de systématisation: variables écrites en minuscule et se terminent avec un chiffre, explicitation des paramètres dans une liste à puces... ; Spinbox et Scale ont été ajoutés.

1. Utilisation du module Tkinter

Tout d'abord, le module Tk pour python doit être installé. Pour Debian-Lenny, Squeeze ou Wheezy:

apt-get install python-tk

Attention: lors de l'installation sous Debian Wheezy, il a fallu un reboot pour que les scripts ci-dessous fonctionnent.

Pour python3, le paquet nécessaire est python3-tk, du moins avec GNU/Linux Debian.

1.1 Charger le module Rév. 2014.10

Pour utiliser le module Tkinter, il faut d'abord choisir un mode d'importation.

import Tkinter charge le module, dont le nom doit précéder les méthodes de création d'objet et les constantes (explications au point suivant):

import Tkinter
racine0=Tkinter.Tk()
racine0.geometry("200x100")
bouton0=Tkinter.Button(racine0, text="Quitter", command=racine0.quit)
bouton0.pack(side=Tkinter.RIGHT)
racine0.mainloop()

import Tkinter as tk permet d'utiliser un alias: racine0=tk.Tk() remplace racine0=Tkinter.Tk(), etc. Les alias T ou zzz peuvent faire l'affaire.

from Tkinter import * permet d'utiliser toutes les méthodes ou constantes sans les préfixer:

from Tkinter import *
racine0=Tk()
bouton0=Button(racine0, text="Quitter", command=racine0.quit)
bouton0.pack(side=LEFT)
racine0.mainloop()

Par souci de clarté:

En python3, le module est rebaptisé tkinter. Il faudra alors choisir un de ces chargements de module:

import tkinter
import tkinter as Tkinter  # pour la compatibilité avec cette page (sauf sous-modules, voir 7.)
import tkinter as tk       # pour un préfixage tk
from tkinter import *      # pour éviter tout préfixage

1.2 Ouvrir une fenêtre Rév. 2014.10

L'utilisation d'une fenêtre se résume schématiquement à cette procédure (les majuscules sont toujours importantes):

import Tkinter      # pour python3: import tkinter as Tkinter
racine0=Tkinter.Tk()
racine0.title("Un titre arbitraire")
racine0.geometry("400x300")
racine0.mainloop()

Tk() et Button(), qui créent des objets, sont précédés du nom du module (selon la méthode d'import) et prennent une capitale.

geometry(), pack() et mainloop() sont des méthodes qui s'appliquent à des objets déjà créés; la programmation orientée objet est assez intelligente pour savoir qu'il s'agit d'objets créés par Tkinter: il est interdit de préfixer ces méthodes, qui s'écrivent sans capitale.

1.3 Lancer le script

Tous les exemples proposés sur cette page sont fonctionnels. Il y a plusieurs méthodes pour lancer un script écrit en python.

Sur un système UNIX, copier-coller chaque exemple dans un fichier-texte, nommé par exemple exemple.py. Ouvrir une console, naviguer vers le répertoire contenant le fichier (ici: scripts-py) et le lancer avec

cd scripts-py
python exemple.py

ou le lancer avec son chemin complet (chemin selon votre nom d'utilisateur)

python /home/dudule/scripts-py/exemple.py

Par l'interface graphique

Voir la section Lancement du manuel.

Caractères non ASCII

Par ailleurs, pour disposer des accents dans les chaînes et les commentaires d'un script, il faut placer au plus tôt (après un éventuel #! /usr/bin/python, voir lancer):

# -*- coding:utf-8 -*-

ou tout autre codage que vous utilisez pour la console: coding:latin-1 (ou iso8859-1), coding:latin-15 (ou iso8859-15)...

2 Les widgets simples

«widget» est paraît-il une contraction de «window gadget». Il s'agit d'éléments simples, comme un titre, un bouton, un texte, un champ éditable... ou plus complexes, comme un système de menu. Lors de la mise en place d'un widget, la récupération de son identifiant dans une variable permet par la suite de le contrôler.

2.1 Label Rév. 2014.08

Label permet un affichage simple de texte:

import Tkinter
racine0=Tkinter.Tk()
mot0=Tkinter.Label(racine0, text="Premier texte\ndans une fenetre")
mot0.pack(side="bottom")
racine0.mainloop()

La variable mot0, qui contient l'identificateur du widget, permet de l'installer avec mot0.pack() et éventuellement le modifier ou le supprimer

side="bottom" garde le texte au bas de la fenêtre si on l'agrandit.

Label peut également recevoir une image sous format GIF/PNM ou bitmap:

import Tkinter
racine0=Tkinter.Tk()
dessin0=Tkinter.PhotoImage(file="nomdefichier.gif")
etiquette0=Tkinter.Label(image=dessin0)
etiquette0.pack()
racine0.mainloop()

file désigne un nom de fichier, éventuellement avec une adresse précise.

2.2 Text Rév. 2014.08

Text définit une plage permettant l'insertion et la manipulation d'un texte.

import Tkinter
racine0=Tkinter.Tk()
texte0=Tkinter.Text(racine0, width=25, height=5)
texte0.insert("end","bla bla bla")
texte0.pack(side=Tkinter.RIGHT)
racine0.mainloop()

width est la largeur de la plage en nombre de caractères, height le nombre de lignes
state="normal" permet l'édition du texte
state="disabled" en interdit l'édition

insert() permet l'insertion à un endroit du texte: Tkinter.END à la fin du texte, à l'endroit du curseur texte, à l'endroit le plus proche du curseur souris. "end""insert""current"

Dans pack(), side="right" concerne l'alignement de la plage de texte: en agrandissant la fenêtre, on verra que la plage de texte se situe à droite, même si le texte est aligné à gauche.

2.3 Entry Rév. 2014.10

Entry crée un champ permettant de saisir une chaîne ou un nombre, entier ou «réel». Il faut donc prévoir une variable permettant de recevoir le texte saisi, que l'on peut définir par défaut avec texte0.set().

import Tkinter
racine0=Tkinter.Tk()
invite0=Tkinter.Label(racine0, text='Cliquer et saisir:', width=20, height=3, fg="navy")
invite0.pack()
texte0=Tkinter.StringVar()  # definition d'une variable-chaine pour recevoir la saisie d'un texte
texte0.set("Sans commentaire")  # facultatif: assigne une valeur à la variable
saisie0=Tkinter.Entry(textvariable=texte0, width=30)
saisie0.pack()
racine0.mainloop()
print texte0.get() # affiche le texte saisi à la fermeture de la fenêtre

2.4 Button Rév. 2014.10

Button définit un bouton cliquable

import Tkinter
racine0=Tkinter.Tk()
bouton0=Tkinter.Button(racine0, text="Quitter", command=racine0.quit)
bouton0.pack(side=Tkinter.BOTTOM)
racine0.mainloop()

Il est à noter que le nom de la fonction-cible de Tkinter.Button s'écrit sans guillemet ni parenthèse. Des parenthèses (par exemple pour envoyer un paramètre à la fonction) ne sont pas utilisables parce qu'elles ont pour effet de lancer la commande au chargement du script. S'il y a peu de paramètres (par exemple trois boutons différents qui envoient chacun une valeur, il est possible de définir chaque bouton avec command=aide0, command=aide1... et les fonctions correspondantes: def aide0: aide(0), def aide0: aide(1)... renvoyant à def aide(n):. Une autre manière serait d'utiliser une variable globale, récupérée avec global à l'intérieur de la fonction appelée.

bitmap

Tkinter: bitmap pour button

Il existe une série d'images toutes faites (pour les nostalgiques des premières icones N/B) pour ces boutons: bitmap="gray12", "gray25", "gray50", "gray75", "warning", "hourglass", "info", "questhead", "question", "error", à utiliser de la sorte:

import Tkinter
racine0=Tkinter.Tk()
bouton0=Tkinter.Button(racine0, text="Attention", bitmap="error", compound="right")
bouton0.pack()
racine0.mainloop()

compound="top", "bottom", "left", "right", indique la position de l'icone par rapport au texte, et peut également prendre la valeur "center" (superposition): voir également Alignements.

Il est - fort heureusement! - possible d'utiliser une image bitmap personnelle initialisée avec BitmapImage().

2.5 Checkbutton

Chekbutton est une "case à cocher". Une méthode est proposée pour récupérer l'information sur l'état de la "case à cocher".

import Tkinter
racine0=Tkinter.Tk()
retour0=Tkinter.IntVar() # creation de variable-retour
bouton0=Tkinter.Checkbutton(racine0, variable=retour0, text="Cochez-moi")
bouton0.pack()
racine0.mainloop()

# recuperation de la valeur lors de la sortie de la boucle mainloop():
if retour0.get(): # la variable 'retour0' = 1 si la case est cochee, 0 sinon
  print "Tilt!"
else:
  print "Vide!"

Pour cocher (ou décocher) un CheckButton par voie de script, retour0.set(1) positionne la valeur à 1 (ou à 0). Doit être suivi de bouton0=Tkinter.Checkbutton ou de bouton0.config()

2.6 Radiobutton Rév. 2014.08

RadioButton est un ensemble de bouton radio (un seul sélectionné à la fois) pour une même variable. La variable retour0 rendra la valeur proposée par value du bouton sélectionné.

import Tkinter
racine0=Tkinter.Tk()
retour0=Tkinter.IntVar() # cree une variable entiere pour recevoir la valeur retour
retour0.set(2) # le bouton [Bof] mis par defaut (value=2)
bouton1=Tkinter.Radiobutton(racine0, text="Oui", variable=retour0, value=1, bd=2)
bouton2=Tkinter.Radiobutton(racine0, text="Bof", variable=retour0, value=2, bd=3)
bouton3=Tkinter.Radiobutton(racine0, text="Non", variable=retour0, value=3, bd=3)
bouton1.grid(row=0, column=0)
bouton2.grid(row=0, column=1)
bouton3.grid(row=0, column=2)
racine0.mainloop()

print retour0.get() # retourne 1, 2 ou 3 selon le bouton choisi, ou 0 si pas de choix

Et encore...

2.7 Spinbox Nouv. 2014.10

Un exemple de boîte Spinbox

Widget permettant de choisir entre plusieurs valeurs que l'on fait défiler en cliquant sur des flèches. Les valeurs peuvent être des nombres, entiers ou réels, ou des chaînes, définis soit entre deux bornes (nombres) ou dans des objets itérables (nombres ou chaînes), ou des fonctions les produisant.

import Tkinter
racine0=Tkinter.Tk()
retour0=Tkinter.StringVar()
retour0.set(37.2)
spin0=Tkinter.Spinbox(racine0, from_=35, to=43, increment=.2, width=4)
spin0.config(textvariable=retour0, font="sans 24", justify="center")
spin0.pack()
racine0.mainloop()

print retour0.get()

Et encore...

2.8 Scale Nouv. 2014.10

Exemple de widget Scale

Le widget Scale permet de déterminer à la souris une valeur numérique entre deux bornes. Le curseur se manipule par saisie/déplacement (drag and drop) ou en cliquant sur la coulisse en deçà ou au-delà du curseur pour un déplacement plus fin. Avis aux distraits: la figure ci-contre n'en est qu'une image figée, pas le widget lui-même.

L'exemple suivant, très simple, définit un curseur vertical balayant les valeurs de 0 à 100 sur... 101 pixels.

import Tkinter
racine0=Tkinter.Tk()
retour0=Tkinter.IntVar()
retour0.set(43)
echelle0=Tkinter.Scale(racine0, variable=retour0)
echelle0.pack()
racine0.mainloop()

print retour0.get()

Paramètres additionnels

avec valeurs réelles

Il est possible de déterminer un Scale avec valeurs réelles.

import Tkinter
racine0=Tkinter.Tk()
retour0=Tkinter.DoubleVar()
retour0.set(18.15)
echelle0=Tkinter.Scale(racine0, variable=retour0, length=300, resolution=.05)
echelle0.pack()
racine0.mainloop()

print retour0.get()

2.9 Modifier / détruire Rév. 2014.10

configure() ou config() permet de modifier complètement un paramètre (dans l'exemple, la chaîne affichée par un «Label») et/ou d'en ajouter (la couleur du texte et celle du fond).

import Tkinter
racine0=Tkinter.Tk()
def colorer():
  texte0.config(text="Ce texte change et prend de la couleur", fg="blue", bg="red")
texte0=Tkinter.Label(racine0, text="Ceci est un texte en noir et blanc")
texte0.pack()
bouton0=Tkinter.Button(racine0, text="Colorer le texte", command=colorer)
bouton0.pack()
racine0.mainloop()

destroy() supprime un widget. Dans l'exemple ci-dessous, le bouton en appelle à son autodestruction (racine0 doit être défini avant la fonction detruire()):

import Tkinter
racine0=Tkinter.Tk()
def detruire():
  bouton0.destroy()
  texte0=Tkinter.Label(racine0, text="Destruction accomplie")
  texte0.pack()
bouton0=Tkinter.Button(racine0, text="Autodestruction", command=detruire)
bouton0.pack()
racine0.mainloop()

3. L'espace graphique Canvas

Canvas crée une surface sur laquelle on peut placer des éléments graphiques (l'exemple suivant n'est pas fonctionnel par manque de commandes):

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, width=300, height=200, background='darkgray')
fond0.pack()
# ... votre script
racine0.mainloop()

Pour la plupart des éléments graphiques présentés ci-dessous, fill définit la couleur (le texte pour creat_text), width l'épaisseur, outline la couleur du bord, anchor l'alignement.

3.1 Texte graphique

Pour positionner un texte au pixel près sur un canevas (attention: le positionnement par défaut est le centre du texte, comme le montrent les deux lignes)

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, width=150, height=120, background='darkgray')
ligne1=fond0.create_line(75, 0, 75, 120)
ligne2=fond0.create_line(0, 60, 150, 60)
texte0=fond0.create_text(75, 60, text="Spam?", font="Arial 16 italic", fill="green")

fond0.pack()
racine0.mainloop()

Rappel: la couleur du texte est définie ici par fill!

3.2 Lignes et points Rév. 2014.05

Avec fond0 représentant l'ouverture d'un canevas:

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, width=350, height=200, background='darkgray')
ligne=fond0.create_line(40,190, 250,110, 270,170, 180,120, width=5)
fond0.pack()
racine0.mainloop()

Les arguments commencent par une série de paires qui sont des coordonnées de points (deux paires minimum pour un segment de droite). Les paramètres peuvent être ajoutés:

smooth=True pour une courbe au lieu d'une ligne brisée ("spline")
splinesteps=12 nombre de brisures pour un lissage imparfait de la courbe si smooth=True

arrow=Tkinter.BOTH place une flèche aux bouts de la ligne (Tkinter.FIRST pour le départ, Tkinter.LAST pour la fin)
arrowshape=(8,10,3) permet de modifier la longueur de la flèche (sur la ligne), la longueur et la largeur des ailes. arrowshape=(20,10,5) pour un losange

Si la largeur de la ligne est suffisante:

capstyle=Tkinter.ROUND fin de ligne arrondie
capstyle=Tkinter.BUTT fin de ligne coupée
capstyle=Tkinter.PROJECTING fin de ligne coupée, une demi-largeur au delà des coordonnées du point

joinstyle=Tkinter.ROUND angle de deux segments arrondi
joinstyle=Tkinter.MITER angle de deux segments pointu
joinstyle=Tkinter.BEVEL angle de deux segments coupé (orthogonale de la bissectrice)

Dessiner un point

Pour afficher un point x,y, il faut afficher une ligne qui va de x,y à x+1,y ou x,y+1 (le dernier point d'une ligne n'est pas affiché):

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, width=350, height=200, background='darkgray')
ligne0=fond0.create_line(100,100, 101,100)
fond0.pack()
racine0.mainloop()

3.3 Formes géométriques Rév. 2014.05

Pour fond0 représentant l'ouverture d'un canevas (les couleurs ont été exécutées pour l'exemple):

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, width=350, height=200, background='darkgray')
rectangle0=fond0.create_rectangle(50,40,300,90, fill='white', outline='red', width=10)
ellipse0=fond0.create_oval(30,120,150,180, fill='green', outline='blue', width=5)
quartier0=fond0.create_arc(160,130,230,200, start=30, extent=120, fill='yellow', outline='purple', width=5, style=Tkinter.PIESLICE)
arc0=fond0.create_arc(250,130,320,200, start=30,extent=120, fill='black', outline='magenta', width=5, style=Tkinter.CHORD)
fond0.pack()
racine0.mainloop()

Pour les formes géométriques, les deux premiers entiers représentent les coordonnées du point en haut à gauche du rectangle, les deux suivants celles du point en bas à droite. Pour create_oval et create_arc, il s'agit des coordonnées du rectangle circonscrit.

Pour create_arc, l'angle de départ et l'extension sont donnés en degrés (progression trigono­mé­trique, anti-horaire). Tkinter.PIESLICE dessine un quartier de tarte, Tkinter.CHORD un arc et sa corde.

Polygones

Pour dessiner un polygone, éventuellement arrondi, il faut en déterminer les différentes coordonnées de points (sans se préoccuper de répéter les coordonnées du premier):

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, width=150, height=150, background='darkgray')
polygone0=fond0.create_polygon(35,105, 120,85, 95,25, 80,75, 25,60, 65,30, fill="cyan", width=5, outline='black')
fond0.pack()
racine0.mainloop()

smooth=True et splinesteps=n sont disponibles pour les polygones.

3.4 Afficher un fichier-image

Tkinter ne reconnaît que les formats GIF et certains PNM (voir plus bas). Voici la façon d'afficher une image (le fichier exemple.gif doit exister dans le répertoire du script):

import Tkinter
racine0=Tkinter.Tk()
photo0=Tkinter.PhotoImage(file="Capture.gif")  # ouverture du fichier existant image.gif
largeur=photo0.width(); hauteur=photo0.height() # determination des dimensions
racine0.geometry(str(largeur+2)+'x'+str(hauteur+2))
racine0.title(str(largeur)+'x'+str(hauteur))
fond0=Tkinter.Canvas(racine0, bg='gray')
fond0.pack()
image0=fond0.create_image(largeur//2+1,image=photo0,hauteur//2+1) # image a centrer
racine0.mainloop()

Dans cet exemple, les dimensions de l'image servent à définir la surface utile pour le canevas.

Par défaut, l'image est centrée sur le point de coordonnées précisé (avant-dernière ligne). Il est possible de changer cela en précisant le point supérieur gauche et ajoutant l'alignement anchor=Tkinter.NW:

image0=fond0.create_image(0, 0, image=photo0, anchor=Tkinter.NW)

Formats PNM: PGM et PPM Ajout 2014.05

Seul les PNM au format brut (binaire) en nuances de gris (PNM ou PGM) ou couleurs (PNM ou PPM), à savoir ceux dont l'entête commence par P5 ou P6, peuvent être affichés par Tkinter. Pour des renseignements sur ces formats non compressés et faciles à générer: P5 et P6.

Il est par ailleurs possible de coder un fichier PNM dans un script:

import Tkinter
p5="P5 8 8 255 "+((chr(255)*2+chr(0)*2)*4+(chr(0)*2+chr(255)*2)*4)*2
# (2px blancs suivis de 2px noirs)x4 + (2px noirs suivis de 2px blancs)x4,
# le tout x2, fait un damier de 4x4 carrés de 2x2px
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0)
damier0=Tkinter.PhotoImage(data=p5)
image1=fond0.create_image(120, 120, image=damier0)
fond0.pack()
racine0.mainloop()

Ce n'est pas trop pratique, le format suivant convient probablement mieux.

3.5 Afficher une image bitmap Ajout 2014.05

Tkinter permet d'utiliser une image bitmap bicolore, par exemple en exportant une image noir et blanc avec l'extention .xbm avec Gimp. Chaque pixel correspondant à un bit mis à 1 donne la couleur définie par foreground et chaque pixel correspondant à un bit mis à 0 laisse la couleur de fond du canevas inchangée, ou celle précisée par background de BitmapImage().

import Tkinter
dessin0="""
# define im_width 24
# define im_height 16
# static char im_data[] = {
0x0f,0x0,0xcc,0x0f,0x0,0xcc,0x0f,0x0,0x33,0x0f,0x0,0x33,0xf0,0x0,0xcc,0xf0,
0x0,0xcc,0xf0,0x0,0x33,0xf0,0x0,0x33,0x0,0xff,0x0,0x0,0xff,0x0,0x0,0xc3,
0x0,0x0,0xc3,0x0,0x0,0xc3,0x0,0x0,0xc3,0x0,0x0,0xff,0x0,0x0,0xff,0x0
};
"""
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, background='gray')
damier0=Tkinter.BitmapImage(data=dessin0, foreground="red", background="blue")
print damier0.width(),damier0.height()
image0=fond0.create_image(20, 20, image=damier0)
fond0.pack()
racine0.mainloop()

Il est possible de charger un fichier, au même format, avec damier0=Tkinter.BitmapImage(file="image.xbm")

Comme pour les fichiers-images, damier0.width() et damier0.width() retourne la largeur et la hauteur de l'image bitmap, pour la variable damier0 issu de Tkinter.BitmapImage().

Si background a été précisé, il est possible de définir un masque, au même format, qui décide des pixels affichés:

masque0="""
# define mask_width 24
# define mask_height 16
# static char mask[] = {
0xaa,0xaa,0xaa,0x55,0x55,0x55,0xaa,0xaa,0xaa,0x55,0x55,0x55,0xaa,0xaa,0xaa,0x55,
0x55,0x55,0xaa,0xaa,0xaa,0x55,0x55,0x55,0xaa,0xaa,0xaa,0x55,0x55,0x55,0xaa,0xaa,
0xaa,0x55,0x55,0x55,0xaa,0xaa,0xaa,0x55,0x55,0x55,0xaa,0xaa,0xaa,0x55,0x55,0x55
};
"""

et l'appel doit être complété:

damier0=Tkinter.BitmapImage(data=dessin0, maskdata=masque0, foreground="black", background="white")

Il est possible de définir une image par un fichier reprenant le codage vu plus haut, avec file="" et maskfile="" (les adresses utilisées devraient être valides sur un système UNIX/X11):

import Tkinter
racine0=Tkinter.Tk()
fond0=Tkinter.Canvas(racine0, bg='gray')
dessin0="/usr/include/X11/bitmaps/star"
masque0="/usr/include/X11/bitmaps/starMask"
definition0=Tkinter.BitmapImage(file=dessin0, maskfile=masque0, foreground="red", background="white")
image0=fond0.create_image(20, 20, image=definition0)
fond0.pack()
racine0.mainloop()

3.6 Reconfigurer un élément

L'instance récupérée lors de la création permet de manipuler les objets créés. Pour une ellipse créée avec la variable ellipse0, sur un canevas représenté par la variable canevas0:

canevas0.itemconfigure(ellipse0, fill="green") colore l'objet ellipse0 en vert
canevas0.delete(rectangle1) détruit l'élément rectangle1

L'exemple suivant permet de changer la couleur d'un rectangle par un clic sur un bouton):

racine0=Tkinter.Tk()
def rouge():
  canevas0.itemconfig(rectangle0, fill='red')
def vert():
  canevas0.itemconfig(rectangle0, fill='green')
def bleu():
  canevas0.itemconfig(rectangle0, fill='blue')
canevas0=Tkinter.Canvas(racine0, width=300, height=200, background='darkgray')
canevas0.pack()
rectangle0=canevas0.create_rectangle(50,50, 250,150, fill='gray')
bouton1=Tkinter.Button(racine0, text="Rouge!", command=rouge)
bouton1.pack(side=Tkinter.LEFT)
bouton3=Tkinter.Button(racine0, text="Bleu!", command=bleu)
bouton3.pack(side=Tkinter.RIGHT)
bouton2=Tkinter.Button(racine0, text="Vert!", command=vert)
bouton2.pack()
racine0.mainloop()

4. Fenêtres, cadres et panneaux

4.1 TopLevel

TopLevel permet l'ouverture d'une nouvelle fenêtre. Afin de ne pas surcharger l'exemple, la nouvelle fenêtre n'affiche rien de plus que la première.

import Tkinter
racine0=Tkinter.Tk()
racine0.title("Principale")
fenetre0=Tkinter.Toplevel()
fenetre0.title("Seconde")
fenetre0.grid()
racine0.mainloop()

Deux fenêtres vont s'ouvrir: la principale, nommée pour l'occasion Principale, et la seconde, appelée Seconde. On subordonne le plus souvent la création d'une seconde fenêtre à un événement de la première, par exemple associée au clic d'un bouton. Fermer la principale ferme la seconde, l'inverse n'est pas vrai.

4.2 Frame et LabelFrame

Frame est un cadre, permettant de regrouper géographiquement les widgets dans une fenêtre.

import Tkinter
racine0=Tkinter.Tk()
cadre0=Tkinter.Frame(racine0)
bouton1=Tkinter.Button(cadre0,text="Bouton 1")
bouton2=Tkinter.Button(cadre0,text="Bouton 2")
bouton3=Tkinter.Button(cadre0,text="Bouton 3")
bouton1.pack(side=Tkinter.LEFT)
bouton2.pack(side=Tkinter.TOP)
bouton3.pack()
cadre0.pack()
racine0.mainloop()

Tkinter.Frame(parent, bg="red", border=3, relief="groove")

Il est possible de donner un bord et un nom à un cadre avec LabelFrame:
cadre0=Tkinter.LabelFrame(parent,text="", font="", fg="")

4.3 PanedWindow

PanedWindow permet de diviser une fenêtre en plusieurs panneaux adaptables.

import Tkinter
racine0=Tkinter.Tk()
racine0.geometry("400x300")
division0=Tkinter.PanedWindow(orient=Tkinter.VERTICAL)
division0.pack(expand="yes",fill="both")
panneau1=Tkinter.Label(division0,text="Panneau Un")
division0.add(panneau1)
panneau2=Tkinter.Label(division0,text="Panneau Deux")
division0.add(panneau2)
panneau3=Tkinter.Label(division0,text="Panneau Trois")
division0.add(panneau3)
racine0.mainloop()

On adapte cette fonction dans l'autre direction avec les paramètres suivants: orient=Tkinter.HORIZONTAL.

Il est possible de créer des subdivisions dans un des panneaux. Dans l'exemple suivant, c'est le panneau bas0 qui devient l'objet à diviser par PanedWindows: c'est donc à lui que les sous-panneaux gauche et droite doivent se référer.

import Tkinter
racine0=Tkinter.Tk()
racine0.geometry("400x300")
division0=Tkinter.PanedWindow(orient=Tkinter.VERTICAL)
division0.pack(expand="yes",fill="both")
haut0=Tkinter.Label(division0,text="Panneau du haut")
division0.add(haut0)
milieu0=Tkinter.Label(division0,text="Panneau du milieu")
division0.add(milieu0)
bas0=Tkinter.PanedWindow(orient=Tkinter.HORIZONTAL) # nouvelle division
bas0.pack(expand="yes",fill="both")
gauche=Tkinter.Label(bas0,text="Panneau bas-gauche")
bas0.add(gauche)
droit=Tkinter.Label(bas0,text="Panneau bas-droit")
bas0.add(droit)
division0.add(bas0) # on acheve la declaration du panneau bas
racine0.mainloop()

5.1 Listbox

Le script suivant permet le transfert dans la zone texte d'un mot dans une liste, par un double clic gauche.

import Tkinter

racine0=Tkinter.Tk()
liste0=Tkinter.Listbox(racine0, width=10)
liste0.pack()
texte0=Tkinter.Text(racine0, width=10)
texte0.pack()

for element in ["Monthy", "Python", "Flying", "Circus"]:
  liste0.insert(Tkinter.END, element)
def clic(inutile):
  texte0.insert(Tkinter.INSERT, liste0.get(liste0.curselection())+" ")

liste0.bind('<Double-1>', clic)
racine0.mainloop()

La boucle for remplit la liste des éléments dans la Listbox.
inutile est une variable nécessaire mais qu'on n'utilise pas.
insert() permet d'ajouter l'élément cliqué à l'endroit du curseur, défini par Tkinter.INSERT, END pour la fin du texte, CURRENT pour le début)

Il est possible de remplacer le double-clic par une confirmation par bouton:

import Tkinter
racine0=Tkinter.Tk()

liste0=Tkinter.Listbox(racine0,width=10,selectmode=Tkinter.MULTIPLE)
liste0.pack()

bouton0=Tkinter.Button(racine0,text='Confirmer')
bouton0.pack()

texte0=Tkinter.Text(racine0,width=10)
texte0.pack()

for element in ["Monthy", "Python", "Flying", "Circus"]:
  liste0.insert(Tkinter.END, element)

def clic(inutile):
  for i in liste0.curselection():
    texte0.insert(Tkinter.INSERT,liste0.get(i)+" ")

bouton0.bind('<Button-1>', clic)
racine0.mainloop()

2014.05 De plus, Listbox accepte selectmode=Tkinter.MULTIPLE pour un mode de sélection multiple en cliquant successivement sur plusieurs items, et EXTENDED qui permet Ctrl-Clic pour une succession d'items et Maj-Clic pour une suite d'items consécutifs. Il a fallu modifier la fonction clic pour qu'elle accepte une réponse multiple, sous forme de tuple contenant les index des items choisis.

Le mode par défault est selectmode=Tkinter.SINGLE, qui fonctionne mal avec "<Button-1>" (simple clic gauche): l'élément cliqué arrive avec un coup de retard.

Pour être complet, il faudrait également parler de selectmode=Tkinter.BROWSE censé déplacer un item en le tirant (drag'n drop), mais cela ne semble pas fonctionner.

5.2 Scrollbar

Scrollbar permet de faire défiler dans une surface limitée quelques widgets, comme Text, Entry, Listbox, et Canvas comme expliqué plus bas.

import Tkinter
racine0=Tkinter.Tk()

def afficher(dummy):
  print liste0.curselection()

ascenseur0= Tkinter.Scrollbar(racine0, orient=Tkinter.VERTICAL)
ascenseur0.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)

liste0=Tkinter.Listbox(racine0, yscrollcommand=ascenseur0.set)
for i in range(0,128):
  liste0.insert(Tkinter.END, str(i))

liste0.pack(fill=Tkinter.Y)
ascenseur0.config(command=liste0.yview)

liste0.bind('<Double-1>', afficher)

racine0.mainloop()

orient=Tkinter.VERTICAL est l'orientation par défaut
fill=Tkinter.Y met de l'espace entre les deux flèches de l'ascenseur horizontal
side=Tkinter.LEFT aurait placé l'ascenseur à gauche

Il était possible de garder le défilement vertical de la liste de nombres avec un ascenseur horizontal, en changeant ses deux lignes de définitions:

ascenseur0= Tkinter.Scrollbar(racine0, orient=Tkinter.HORIZONTAL)
ascenseur0.pack(side=Tkinter.TOP, fill=Tkinter.X)

Par contre, la commande liste0.yview doit rester la même, puisque le défilement est vertical

On utilise indifféremment les constantes Tkinter.Y, Tkinter.N, Tkinter.VERTICAL et Tkinter.HORIZONTAL et leurs valeurs-chaînes "y", "n", "vertical" ou "horizontal"

Ascenseur avec Canvas

Merci à http://effbot.org/tkinterbook/

Pour le widget Canvas, c'est un peu plus compliqué. Premièrement, il semble qu'il faille l'inclure dans un cadre Frame(). Deuxièmement, le paramètre scrollregion=() doit préciser les dimensions de la plage entière concernée par le défilement.

import Tkinter
fenetre0=Tkinter.Tk()

cadre0=Tkinter.Frame(fenetre0,width=300,height=300)
cadre0.pack()
canevas0=Tkinter.Canvas(cadre0,bg='#FFFFFF',width=250,height=200,scrollregion=(0,0,250,250))

ascenseur0=Tkinter.Scrollbar(cadre0)
ascenseur0.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
ascenseur0.config(command=canevas0.yview)

canevas0.config(width=250,height=200)
canevas0.config(yscrollcommand=ascenseur0.set)
canevas0.pack(side=Tkinter.LEFT,expand=True)

texte="""
T'es fou
Tire pas
C'est pas des corbeaux
C'est mes souliers
Je dors parfois dans les arbres"
"""
texte1=canevas0.create_text(text=texte,12, 12, width=280, anchor=Tkinter.NW)
texte2=canevas0.create_text(12, 200, text="'Moi dans l'arbre'\nPaul Vicensini", width=280, anchor=Tkinter.NW)

fenetre0.mainloop()

5.3 Menu

Voici un exemple commenté d'un système de menu fonctionnel comportant des cascades (sous-menus). Pour la fonction Menu, deux méthodes sont nécessaires: add_cascade pour ajouter un menu ou un sous-menu, et add_command pour décider de la commande associée au clic. Les actions sont ici limitées à l'affichage d'un texte.

import Tkinter
racine0=Tkinter.Tk()

texte0=Tkinter.Text(racine0) # prevoit une place pour l'affichage des textes
texte0.pack()

def ecran(var): # fonction servant a l'affichage des textes:
  texte0.insert(Tkinter.END,var)

sysdemenu0=Tkinter.Menu(racine0) # Creation du systeme de menu

menu1=Tkinter.Menu(sysdemenu0, tearoff="0") # Creation du premier menu:
sysdemenu0.add_cascade(label="Menu 1", menu=menu1)

# addition des deux items pour le premier menu et leur commande associee
menu1.add_command(label="Credit", command=lambda: ecran('Credit: www.jchr.be\n'))
menu1.add_command(label="Quitter", command=racine0.quit)

menu2=Tkinter.Menu(sysdemenu0) # Creation du second menu
sysdemenu0.add_cascade(label="Menu 2", menu=menu2)

# addition du premier item pour le second menu et leur sous-items associes
item1=Tkinter.Menu(menu2)
menu2.add_cascade(label="Item 1", menu=item1)

# addition des sous-items du premier item du second menu et leur commande associee
item1.add_command(label="Action 1", command=lambda: ecran('Item 1 / Action 1\n'))
item1.add_command(label="Action 2", command=lambda: ecran('Item 1 / Action 2\n'))

item2=Tkinter.Menu(menu2) # addition du second item pour le second menu et leur sous-items associes
menu2.add_cascade(label="Item 2", menu=item2)

# addition des sous-items du second item du second menu et leur commande associee
item2.add_command(label="Action 1", command=lambda: ecran('Item 2 / Action 1\n'))
item2.add_command(label="Action 2", command=lambda: ecran('Item 2 / Action 2\n'))
item2.add_command(label="Action 3", command=lambda: ecran('Item 2 / Action 3\n'))
racine0.config(menu=sysdemenu0)
racine0.mainloop()

Par défaut, chaque menu commence par une ligne discontinue, et un clic sur celle-ci transfère le menu dans une petite fenêtre indépendante. Pour supprimer cette ligne et cette possibilité, ajouter le paramètre tearoff=0 dans la fonction Menu(), comme cela a été fait dans l'exemple pour menu1.

6. Styles

En important Tkinter avec import Tkinter (comme dans les exemples de cette page), les constantes du système doivent être préfixée de Tkinter, par exemple Tkinter.HORIZONTAL. Avec un alias, par exemple import Tkinter as TK, la valeur s'appelle avec le préfixe TK: TK.HORIZONTAL. Avec le mode d'importation de module from Tkinter import * le préfixage est suprimé: la constante est tout simplement HORIZONTAL.

Quel que soit le mode d'importation (et donc de préfixage), la valeur d'une constate est remplaçable par la même chaîne sans préfixage, en minuscules et avec guillemets: HORIZONTAL vaut la chaîne "horizontal". Il existe quelques exceptions:

SEL_FIRST vaut "sel.first" et SEL_LAST vaut "sel.last"

Quelques-unes contiennent une valeur numérique, qui s'écrit donc sans guillemets:

FALSE, NO et OFF valent 0, et TRUE, YES et ON valent 1

READABLE vaut 2 - WRITABLE vaut 4 - EXCEPTION vaut 8

6.1 Alignements Rév. 2014.08

side= pour pack() et les ascenseurs, ou justify="" pour les textes, acceptent Tkinter.TOP, Tkinter.RIGHT, Tkinter.BOTTOM et Tkinter.LEFT, ou le contenu de ces constantes "top", "right", "bottom" et "left".

compound (voir Boutons) accepte en outre les constantes Tkinter.CENTER et Tkinter.NONE, ou leur contenu "center" et "none".

anchor=, utilisé dans le positionnement des images reçoit les constantes basées sur les points cardinaux: Tkinter.N, Tkinter.NE, Tkinter.E, Tkinter.SE, Tkinter.S, Tkinter.SW, Tkinter.W, Tkinter.NW, Tkinter.CENTER ou leur contenu: "n", "ne", "e", "se", "s", "sw", "w", "nw" et "center".

orient= reçoit les constantes Tkinter.VERTICAL ou Tkinter.HORIZONTAL, qui correspondent aux chaînes "vertical" ou "horizontal".

6.2 Reliefs Rév. 2014.08

Les boutons à cocher et radio-boutons peuvent afficher un type de relief avec le paramètre relief= défini par les constantes Tkinter.RAISED (élevé), Tkinter.SUNKEN (enfoncé), Tkinter.FLAT (plat, par défaut), Tkinter.GROOVE (rainure) ou Tkinter.RIDGE (crête), ou leur contenu: "raised", "sunken", "flat", "groove" ou "ridge". Les simples boutons à cliquer disposent déjà du type "raised" avec une animation "sunken" lors du clic gauche.

relief=raised, sunken, flat, groove, ou ridge

borderwidth= ou bd= permet de préciser la grosseur des traits. L'image ci-contre a été réalisée avec bd=3 (ou bd="3").

6.3 Fontes Rév. 2014.08

Valable pour Label, Text et create_text, il y a plusieurs manières d'imposer une fonte, une hauteur et une décoration:

font=("Courier", "16", "bold italic")
font="Courier 30 bold"
font=("-*-Helvetica-medium-r-*-*-*-200-*-*-*-*-*-*")

import Tkinter
racine0=Tkinter.Tk()
etiquette0=Tkinter.Label(racine0, text="Texte gras et italique", font="Courier 32 bold underline")
etiquette0.pack()
racine0.mainloop()

Le paramètre font permet de préciser (dans une seule chaîne, sans virgule)