Module graphique tkinter pour Python

LE 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 python 3.7 en GNU/Linux Debian, sont tous concrets et fonctionnels.

Cette page a été commencée en début 2007 pour me servir de notes personnelles, elle évolue donc surtout en fonction de mes nécessités. Elle est néanmoins écrite dans un souci de servir à d’autres : pour plus de clarté, les variables sont en français, écrites en minuscules et terminées par un chiffre ; les explicitations des paramètres sont regroupées dans une liste à puces qui suit chaque exemple.

Menubutton reste à faire. Le puissant Text, qui mériterait un chapitre à lui tout seul, commence à être plus explicite.

python2 n’est plus supporté depuis janvier 2020. Cette page a donc été réécrite du point de vue python3, les différences de Tkinter pour python2 étant décrites sur fond coloré, ce qui pourrait vous être utile pour adapter les vieux scripts.

1. Utilisation de tkinter

1.1 Charger le module
1.2 Ouvrir une fenêtre
1.3 Lancer le script

2. Widgets simples

2.1 Frame et LabelFrame
2.2 Label
2.3 Texte éditable
2.4 Entry
2.5 Button
2.6 Checkbutton
2.7 Radiobutton
2.8 Spinbox
2.9 Scale

3. L’espace Canvas

3.1 Texte graphique
3.2 Ligne et point
3.3 Surfaces
3.4 Afficher un fichier image
3.5 Afficher une image bitmap
3.6 Reconfigurer ou supprimer

4. Widgets complexes

4.1 OptionMenu
4.2 PanedWindow
4.3 Listbox
4.4 Scrollbar
4.5 Menu

5. Structure

5.1 mainloop / destroy
5.2 pack / grid / place
5.3 config
5.4 bind (événements)
5.5 after (temps)

6. Styles

6.1 Alignements
6.2 Reliefs
6.3 Fontes
6.4 Couleurs
6.5 Curseurs souris

7. Sous-modules

7.1 Boîtes à message
7.2 Boîtes de saisie
7.3 Sélecteurs de fichier
7.4 Sélecteurs de couleur

A. Documentation

A.1 dans le logiciel
A.2 sur GNU/Linux
A.3 sur Internet
A.4 sur papier

1. Utilisation du module tkinter

Le module tk pour python doit être installé. En Debian :

#En mode super-utilisateur: su - [Enter] apt install python3-tk

1.1 Charger le module

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()
bouton0 =tkinter.Button(racine0, text ="Quitter", command =racine0.destroy)
bouton0.pack(side =tkinter.RIGHT)
racine0.mainloop()

Tk() et Button(), qui créent des objets, sont précédés du nom du module. Les fonctions créatrices d’objet prennent une capitale.

pack() et mainloop() sont des méthodes qui s’appliquent à des objets déjà créés (bouton0 et racine0) ; la programmation orientée objet est assez intelligente pour ces objets qu’elles a créés : ces méthodes, qui s’écrivent sans capitale, ne sont pas préfixées.

En python2.7, le module s’appelle Tkinter : il faut donc utiliser import Tkinter

Alias

import tkinter as tk permet d’utiliser l’alias tk : racine0 =tk.Tk() remplace racine0 =tkinter.Tk(), etc. Les alias T ou zzz font tout aussi bien l’affaire.

import tkinter as zzz
racine0 =zzz.Tk()
bouton0 =zzz.Button(racine0, text ="Quitter", command =racine0.destroy)
bouton0.pack(side =zzz.RIGHT)
racine0.mainloop()

Importation brute

from tkinter import * permet d’utiliser toutes les fonctions de création d’objet et les constantes de Tkinter sans les préfixer : elle sont considérées au même niveau que les autres fonctions, internes à Python ou définies par l’utilisateur, ce qui peut provoquer des interférences.

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

Par souci de clarté, les exemples de ces pages importent toujours le module avec import tkinter, ce qui rend le préfixage par tkinter toujours nécessaire et explicite.

1.2 Ouvrir une fenêtre

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

import tkinter
racine0 =tkinter.Tk()
racine0.title("Un titre arbitraire")
racine0.geometry("300x150+100+50")
racine0.aspect(3, 2, 5, 3)
bouton0 =tkinter.Button(racine0, text ="Quitter", command =racine0.destroy)
bouton0.pack(side =tkinter.RIGHT)
racine0.mainloop()

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
def top() :
  fenetre0 =tkinter.Toplevel()
  fenetre0.title("Seconde")

racine0 =tkinter.Tk()
racine0.title("Principale")
bouton0 =tkinter.Button(text ="Autre", command =top)
bouton0.pack()
racine0.mainloop()

Attention! les variables représentant les widgets définis dans une fonction ne sont pas globales, puisque définies dans une fonction-utilisateur de python. Il faudra placer une ligne global suivie de ces variables si on veut les utiliser à d’autres endroits du script.

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

Il est possible d’appeler implicitement python (en Unix) en spécifiant sur la première ligne :

#! /usr/bin/python3

Il faut ensuite rendre le fichier exécutable avec chmod 740 nomdufichier.py et le lancer avec ./exemple.py ou chemin/exemple.py

Par l’interface graphique

Voir la section Lancement du manuel.

Caractères non ASCII

Python3 code les chaînes en UTF-8 par défaut, ce qui permet d’utiliser les accents dans les chaînes. Pour utiliser l’ISO-8859-15 / latin-9 :

#! /usr/bin/python3
# -*- coding:iso8859-15 -*-

Pour python2, il faut toujours préciser l’encodage : latin-1 (ou iso8859-1), latin-9 (ou iso8859-15)… Pour l’UTF-8 :

#! /usr/bin/python
# -*- coding:utf-8 -*-

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 Frame et LabelFrame

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

import tkinter

racine0 =tkinter.Tk()
racine0.geometry("400x300")
cadre0 =tkinter.Frame(racine0)

bouton1 =tkinter.Button(cadre0, text ="Bouton 1")
bouton1.pack(side =tkinter.LEFT)
bouton2 =tkinter.Button(cadre0, text ="Bouton 2")
bouton2.pack(side =tkinter.TOP)
bouton3 =tkinter.Button(cadre0, text ="Bouton 3")
bouton3.pack()

cadre0.pack()

racine0.mainloop()

tkinter.Frame(parent, bg ="red", border =3, relief =tkinter.GROOVE)

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

2.2 Label

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 =tkinter.BOTTOM)
racine0.mainloop()

Label peut également recevoir une image sous format PNG, GIF ou PNM ou bitmap (voir également 3.4 Afficher un fichier image) :


import tkinter
racine0 =tkinter.Tk()
image0 =tkinter.PhotoImage(file ="chambrenoire.png")  # ouverture d’un fichier PNG
etiquette0 =tkinter.Label(image =image0)
etiquette0.pack()
racine0.mainloop()

2.3 Texte éditable

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()

tag_config() permet de préciser un style à des portions de texte préalablement définies avec add_tag()

import tkinter

racine0 =tkinter.Tk()

texte0 =tkinter.Text(racine0)
texte0.config(font ="sans 12", width =17, height =4)
texte0.pack()
texte0.insert(tkinter.END, "Les sanglots longs\nDes violons\nDe l'automne")

texte0.tag_add("accent", "1.13", "1.18")
texte0.tag_add("accent", "2.7", "2.11")
texte0.tag_add("accent", "3.7", "3.10")
texte0.tag_config("accent", font ="monospace 12 underline", foreground ="#aa0055")

racine0.mainloop()

2.4 Entry

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.5 Button

Button définit un bouton cliquable

import tkinter
racine0 =tkinter.Tk()
bouton0 =tkinter.Button(racine0, text ="Quitter", command =racine0.destroy)
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 aide1: 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 =tkinter.RIGHT)
bouton0.pack()
racine0.mainloop()

compound =tkinter.TOP, tkinter.BOTTOM, tkinter.LEFT, tkinter.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 personnelle initialisée avec BitmapImage() et spécifiée avec image =.

2.6 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.7 Radiobutton

Les RadioButton sont un ensemble de CheckButton reliés par la même variable, et dont un n’est sélectionné qu’à l’exclusion des autres. Cette variable-retour rendra la valeur proposée par value qui aura été 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.8 Spinbox Nv. 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.9 Scale Nv. 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())

Avec length =50 from_ =0 et to =1, on peut simuler un interrupteur.

import tkinter
racine0 =tkinter.Tk()
retour0 =tkinter.DoubleVar()
retour0.set(18.15)
echelle0 =tkinter.Scale(racine0, variable =retour0, length =50, from_ =0, to =1)
echelle0.pack()
racine0.mainloop()
print(retour0.get())

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

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

import tkinter
racine0 =tkinter.Tk()
fond0 =tkinter.Canvas(racine0, width =350, height =200, background ="darkgray")
fond0.pack()
ligne =fond0.create_line(40, 190, 250, 110, 270, 170, 180, 120)
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).

Styles de ligne

Il est possible de déterminer un style pour les extrémités de ligne (si l’épaisseur est suffisante) :

Les angles de lignes brisées peuvent également recevoir un style :

Il est possible de donner un style à la ligne

Lignes discontinues Aj. 2015.01

import tkinter
racine0 =tkinter.Tk()
fond0 =tkinter.Canvas(racine0, width =200, height =100, background ="darkgray")
line0 =fond0.create_line(30, 50, 170, 50, dash =(7, 2, 2, 2), dashoff =2)
fond0.pack()
racine0.mainloop()

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()
racine0.title("Regardez au centre!")
fond0 =tkinter.Canvas(racine0, width =350, height =200, background ="darkgray")
ligne0 =fond0.create_line(175, 100, 176, 100)
fond0.pack()
racine0.mainloop()

spline

smooth =True lisse une ligne brisée (spline). La courbe commence et termine aux points extrêmes, ne passant qu’exceptionnellement par les points intermédiaires.

#! /usr/bin/python3
import tkinter
racine0 =tkinter.Tk()
fond0 =tkinter.Canvas(racine0, width =400, height =400)
fond0.pack()
# points
point1 =fond0.create_line(9, 9, 12, 12, width =3) ; t1 =fond0.create_text(22, 12, text ="1")
point2 =fond0.create_line(9, 379, 12, 382, width =3) ; t2 =fond0.create_text(22, 382, text ="2")
point3 =fond0.create_line(379, 9, 382, 12, width =3) ; t3 =fond0.create_text(368, 12, text ="3")
point4 =fond0.create_line(379, 379, 382, 382, width =3) ; t4 =fond0.create_text(368, 382, text ="4")
# différents lissage
ligne0 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True, fill ="grey")
ligne1 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True, splinesteps ="1", fill ="purple")
ligne2 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True, splinesteps ="2", fill ="blue")
ligne3 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True, splinesteps ="3", fill ="green")
ligne4 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True, splinesteps ="4", fill ="orange")
ligne5 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True, splinesteps ="5", fill ="red")
ligne43 =fond0.create_line(10, 10, 10, 380, 380, 10, 380, 380, smooth =True)
racine0.mainloop()

3.3 Surfaces

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)
ellipse0 =fond0.create_oval(30, 120, 150, 180)
quartier0 =fond0.create_arc(160, 130, 230, 200, start =30, extent =120, style =tkinter.PIESLICE)
arc0 =fond0.create_arc(250, 130, 320, 200, start =30, extent =120, style =tkinter.CHORD)
fond0.pack()
racine0.mainloop()

Notes Aj. 2015.01

Contrairement au point terminal d’une ligne qui n’est pas dessiné, ces formes géométriques sont dessinées jusqu’aux coordonnées extrêmes.

Comme pour les lignes, dash et dashoff sont disponibles pour le contour des formes géométriques, mais la version tkinter 2.7.3 ajoute un pixel visible (1 vaut 2, 0 n’est pas permis : un pixel unique n’est donc pas définissable) et enlève un pixel d’interruption (1 est réduit à 0, ce qui donne une ligne continue) : dash =(2, 2) définit la ligne XXX XXX XXX !

import tkinter

racine0 =tkinter.Tk()

fond0 =tkinter.Canvas(racine0, width =200, height =100, background ="darkgray")
line0 =fond0.create_line(30, 20, 170, 20, dash =(7, 1, 1, 1))
rectangle0 =fond0.create_rectangle(30, 30, 170, 50, dash =(7, 2, 1, 2))
ellipse0 =fond0.create_oval(30, 60, 170, 80, dash =(7, 2, 1, 2))
fond0.pack()

racine0.mainloop()

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()

3.4 Afficher un fichier-image

tkinter ne reconnaît que les formats PNG, GIF et certains PNM (voir plus bas). L’affichage se fait en deux étapes : d’abord PhotoImage() qui charge un fichier avec file ="" ou qui précise la chaîne de données avec data =, et puis l’affichage de l’image avec create_image(), dans un canevas préalablement défini.

Pour une image existante sur le disque dur, on utilise donc file ="votre_image" (avec un chemin d’accès si l’image est dans un autre répertoire).


import tkinter
racine0 =tkinter.Tk()
photo0 =tkinter.PhotoImage(file ="uneimage.png")  # ouverture d’un fichier PNG existant
largeur =photo0.width(); hauteur =photo0.height() # détermination des dimensions
racine0.geometry(str(largeur+2)+"x"+str(hauteur+2))
fond0 =tkinter.Canvas(racine0, bg ="gray")
fond0.pack()
image0 =fond0.create_image(largeur //2+1, hauteur //2 +1, image =photo0) # image à centrer
racine0.mainloop()

Couleur d’un pixel d’une image

Il est possible d’interroger la valeur d’un pixel d’une image ou d’en modifier sa couleur. get() et put() s’appliquent sur la variable générée par l’ouverture du fichier avec PhotoImage()

À vérifier : tkinter ne gère pas les .GIF animées.

Formats PNM : PGM et PPM

Seuls 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.

data = permet de définir une chaîne contenant les données d’une image que vous produisez vous-même :


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()

Vous trouverez sur la page des recettes une façon de créer une image d’un cercle à partir d’un rayon quelconque.

3.5 Afficher une image bitmap

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 noir du dessin correspond à un bit mis à 1 (couleur définie par foreground), et chaque pixel blanc correspond à un bit mis à 0, qui laisse la couleur de fond du canevas inchangée, ou celle définie 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 et supprimer un élément

L’instance récupérée lors de la création d’un élément d’un widget "Canvas" permet de manipuler cet élément.

Suppression d’item

canevas0.delete(rectangle1) détruit l’élément rectangle1

Déplacements

canevas0.move(rectangle, 20, -10) déplace un élément de Canvas
canevas0.coords(image0, (50, 100)) modifie les coordonnées d’un élément de Canvas (voir images)

Hiérarchie

Chaque élément créé dans le widget "Canvas" recouvre les précédents. Pour modifier la hiérarchie de la pile :

canevas0.tag_lower(ovale0) envoie l’élément "ovale0" tout en bas de la pile (remplace l’ancien lower)
canevas0.tag_raise(rectangle0) envoie l’élément "rectangle0" tout en haut de la pile (remplace l’ancien lift)

#! /usr/bin/python3
import tkinter

racine0 =tkinter.Tk()
racine0.geometry("300x200")

canevas0 =tkinter.Canvas(racine0, width =300, height =200)
canevas0.pack()
rectangle0 =canevas0.create_rectangle(50, 50, 150, 100, fill ="red")
ellipse0 =canevas0.create_oval(75, 75, 150, 125, fill ="blue") # se place au-dessus
canevas0.tag_lower(ellipse0) # replace le rectangle devant l’ellipse

racine0.mainloop()

itemconfigure

canevas0.itemconfigure(ellipse0, fill ="green") colore l’objet ellipse0 en vert. L’exemple suivant permet de changer la couleur d’un rectangle par un clic sur un bouton :

#! /usr/bin/python3
import tkinter
racine0 =tkinter.Tk()
racine0.geometry("300x200")
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")
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()
canevas0.pack()
racine0.mainloop()

4. Widgets complexes

4.1 OptionMenu

Ce premier widget est une composition d’un bouton et d’un menu, qui permet de choisir une option dans une liste. C’est pourtant un des widgets les plus simples ne contenant pas beaucoup d’options.

import tkinter

def ecran(dummy):
  print(choix0.get())

racine0 =tkinter.Tk()

choix0 =tkinter.StringVar(); choix0.set("Rouge")
option0 =tkinter.OptionMenu(racine0, choix0, "Rouge", "Vert", "Bleu", command =ecran)
option0.pack()

racine0.mainloop()

4.2 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()

4.3 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()

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.

4.4 Scrollbar

Scrollbar permet de faire défiler dans une surface limitée quelques widgets, comme Text, Entry, Listbox et, avec une méthode quelque peu différente, Canvas.

import tkinter

fenetre0 =tkinter.Tk()

ascenseur0 =tkinter.Scrollbar(fenetre0)
ascenseur0.pack(side =tkinter.RIGHT, fill =tkinter.Y)

texte0 =tkinter.Text(fenetre0, yscrollcommand =ascenseur0.set)
texte0.insert(tkinter.END, "bla bla bla "*443)
texte0.pack(side =tkinter.LEFT, fill =tkinter.BOTH)

ascenseur0.config(command =texte0.yview)

fenetre0.mainloop()

Text et Scrollbar se réfèrent l’un à l’autre. Il convient de d’abord définir Scrollbar en récupérant sa variable de création (dans cet exemple, ascenseur0), de définir le texte en s’y référant avec yscrollcommand = ascenseur0.set, puis de reconfigurer l’ascenseur avec command =texte0.yview

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

import tkinter

fenetre0 =tkinter.Tk()

# ces deux lignes changent :
ascenseur0 = tkinter.Scrollbar(fenetre0, orient =tkinter.HORIZONTAL)
ascenseur0.pack(side =tkinter.TOP, fill =tkinter.X)

texte0 =tkinter.Text(fenetre0, yscrollcommand =ascenseur0.set)
texte0.insert(tkinter.END, "bla bla bla "*443)
texte0.pack(side =tkinter.LEFT, fill =tkinter.BOTH)

ascenseur0.config(command =texte0.yview)

fenetre0.mainloop()

Par contre, la commande texte0.yview doit rester la même, puisque le défilement reste 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

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. Merci à effbot.org/tkinterbook/ pour cette précision.

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(12, 12, text =texte, 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()

4.5 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.destroy)

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.

5. Structure

5.1 mainloop / destroyRév. 2020.01

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()

L’interface graphique tkinter contient une boucle ouverte par racine0 =tkinter.Tk() et fermée par racine0.mainloop(). Le nom de la variable racine0, qui définit le widget principal (une fenêtre) est arbitraire, les anglophones l’appellent en général root. Il est possible de quitter l’interface graphique en cliquant le [X] en haut et à droite, mais il est possible (et conseillé) d’associer la sortie à une commande qui demande par exemple de sauvegarder quelque chose. On peut utiliser une commande quitter, généralement associée à un bouton, qui envoie vers une procédure .

import tkinter

def quitter():
  # demande de sauvegarde d’un fichier, voir 7.3 filedialog
  racine0.destroy()

racine0 =tkinter.Tk()

bouton0 =tkinter.Button(racine0, text ="Quitter", command =quitter)
bouton0.pack()

racine0.mainloop()

5.2 pack, grid et place

À part Tk(), qui utilise mainloop(), et TopLevel(), qui en est exempté, les fonctions de création d’objet doivent être confirmées par une méthode spéciale, à choisir entre pack(), grid() et place().

pack()

Les exemples en général très simples de cette page contiennent le plus souvent la méthode pack(), que l’on utilise pour l’affichage effectif du widget, précisé par sa variable de création : variable0.pack().

grid()

grid permet de placer des widgets dans les cases d’une grille, selon les coordonnées rangée (row) / colonne (column). Ne jamais mélanger pack() et grid dans un même conteneur ("frame" ou fenêtre-racine).

import tkinter

def afficher(): # interroge et affiche la valeur retour
  print(retour0.get())

racine0 =tkinter.Tk()
cadre0 =tkinter.Frame(racine0)

# titre pour chaque ligne
tkinter.Label(cadre0, text =" 1er").grid(row =0, column =0)
tkinter.Label(cadre0, text =" 2nd").grid(row =0, column =1)
tkinter.Label(cadre0, text =" 3e").grid(row =0, column =2)

retour0 =tkinter.IntVar()
bouton1 =tkinter.Radiobutton(cadre0, text ="Oui", variable =retour0, value =1)
bouton2 =tkinter.Radiobutton(cadre0, text ="Bof", variable =retour0, value =3)
bouton3 =tkinter.Radiobutton(cadre0, text ="Non", variable =retour0, value =2)

bouton1.grid(row =1, column =0)
bouton2.grid(row =2, column =1)
bouton3.grid(row =1, column =2)

cadre0.pack(side =tkinter.TOP)

bouton0 =tkinter.Button(racine0, text ="Cliquer", command =afficher)
bouton0.pack(side =tkinter.BOTTOM)

racine0.mainloop()

place()

place() est un mode assez simple de positionnement pour un nombre restreint de widgets.

  Exemple à intégrer… 

Les positionnements étant individuels, l’usage de place() devient vite ingérable avec l’accroissement du nombre de widgets.

5.3 Modifier

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

def colorer() :
  texte0.config(text ="Ce texte change et prend de la couleur", fg ="blue", bg ="red")

racine0 =tkinter.Tk()

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()

Cela peut être utilisé pour basculer de l’état DISABLED d’un widget à son état ACTIVE.

import tkinter

def activer() :
  bouton0.config(state =tkinter.ACTIVE)

racine0 =tkinter.Tk()

bouton0 =tkinter.Button(racine0, text ="Quitter", state =tkinter.DISABLED, command =racine0.destroy)
bouton0.pack()

bouton1 =tkinter.Button(racine0, text ="Activer", command =activer)
bouton1.pack()

racine0.mainloop()

Les widgets en état DISABLED ne sont pas tous grisés. Il faut parfois agir sur les paramètres de couleurs (fg, bg…), ce qui peut se faire par le même config().

5.4 événements Nv. 2014.11

bind lie un événement (clic de souris, touche…) à une action, l’événement à attendre est codé par une chaîne commençant par < et terminant par >.

Événements liés au clavier

L’exemple suivant attend un clic de touche et l’affiche dans un label :

import tkinter

def touche(evt0):
  texte0.config(text =evt0.char)

racine0 =tkinter.Tk()

texte0 =tkinter.Label(racine0, text ="…", font ="Arial 64", width =3)
texte0.pack()
racine0.bind("<Key>", touche)

racine0.mainloop()

Événements liés à la souris

Les clics et la position de la souris peuvent être déterminés lors d’événements sur un widget. En guise d’exemple, pour récupérer les coordonnées de la souris lors d’un clic :

import tkinter

def souris(evt0) :
  print("X: %03d - Y: %03d" %(evt0.x_root, evt0.y_root))
  print("x: %03d - y: %03d" %(evt0.x, evt0.y))
  print(evt0.num, evt0.type, evt0.widget)

racine0 =tkinter.Tk()

cadre0 =tkinter.Frame(racine0, width =400, height =300)
cadre0.bind("<Button-1>", souris)
cadre0.pack()

racine0.mainloop()

Les actions de la souris peuvent être :

5.5 after (temporalisation) Nv. 2014.11

La méthode after() permet de réaliser des tâches en fonction du temps sans bloquer le script, comme le ferait la commande time.sleep() :

import tkinter, time

def temps() :
  global tps0
  heure =time.strftime("%Y.%m.%d - %H:%M:%S")
  texte0.config(text =heure)
  tps0 =racine0.after(1000, temps)

def stop() :
  global tps0
  racine0.after_cancel(tps0)

racine0 =tkinter.Tk()

texte0 =tkinter.Label(racine0, text ="")
texte0.pack()

bouton0 =tkinter.Button(racine0, text ="Stop", command =stop)
bouton0.pack()
temps()

racine0.mainloop()

Une fois la fonction temps() lancée, elle s’appelle avec régularité, sans empêcher les autres widgets d’être appelables.

Un autre exemple ici

À suivre…

bell ne semble pas fonctionner ; peut-être est-ce dû à la configuration du système ou de la console.

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 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 constante est généralement remplaçable par la même chaîne sans préfixage, en minuscules et avec guillemets, soit, pour notre exemple, "horizontal" :

importation du modulenom de constantevaleur
import tkintertkinter.HORIZONTAL"horizontal"
import tkinter as aliasalias.HORIZONTAL
from tkinter import *HORIZONTAL

Il existe quelques exceptions : SEL_FIRST vaut "sel.first" et SEL_LAST vaut "sel.last"

Quelques constantes 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

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

La majorité des wigdets 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 l’épaisseur des traits. L’image ci-contre a été réalisée avec bd =3 (ou bd ="3").

6.3 Fontes

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) Fontes par défaut de tkinter

6.4 Couleurs

On utilise la forme "#RRGGBB" où les lettres représentent le rouge, le vert et le bleu en chiffres hexadécimaux (de 0 à 9 puis de a à f). Cette chaîne est à fournir aux paramètres foreground ="", background ="", fill ="" et outline =""

Il existe des couleurs toutes faites parmi lesquelles :

white, black, gray1 à gray99, light-gray, dimgray, gray, dark-gray
red (~2 / ~3 / ~4), pink, deep pink, magenta, violet, purple
yellow, gold, orange
green (dark~ / lawn ~ / lime ~ / forest ~ / yellow ~), olive drab
blue (medium ~ / midnight ~ / steel ~ / dark~ / deep~ / light~ / sky~), navy, blue, cyan

Voir également la boîte de choix de couleur.

6.5 Curseurs graphiques Ajout 2014.10

Différents curseurs graphiques sont possibles lorsqu’on survole un widget. La règle générale est de spécifier cursor ="" lors de la définition du widget. Sans rien préciser, un widget adopte le curseur de son parent.

Certains widgets imposent leur curseur, comme xterm lorsqu’on survole un champ éditable, d’autres sont imposés par le système, comme le bureau Gnome a remplacé la montre bracelet de "watch" par une rosace tournante. Ils ne sont pas tous très réussis, ceux écrits en gras sont les plus intéressants ou élégants.

arrow, based_arrow_down, based_arrow_up, boat, bogosity, bottom_left_corner, bottom_right_corner, bottom_side, bottom_tee, box_spiral, center_ptr, circle, clock, coffee_mug, cross, crosshair, cross_reverse, diamond_cross, dot, dotbox, double_arrow, draft_large, draft_small, draped_box, exchange, fleur (4 directions), gobbler, gumby, hand1, hand2, heart, icon, iron_cross, leftbutton, left_ptr, left_side, left_tee, ll_angle, lr_angle, man, middlebutton, mouse, pencil, pirate, plus, question_arrow, rightbutton, right_ptr, right_side, right_tee, rtl_logo, sailboat, sb_down_arrow, sb_h_double_arrow, sb_left_arrow, sb_right_arrow, sb_up_arrow, sb_v_double_arrow, shuttle, sizing, spider, spraycan, star, target, tcross, top_left_arrow, top_left_corner, top_right_corner, top_side, top_tee, trek, ul_angle, umbrella, ur_angle, watch, X_cursor, xterm

7. Sous-modules

Quatre sous-modules permettent d’ouvrir des boîtes toutes faites, qui permettent d’avertir, de demander une confirmation, d’entrer un texte ou de préciser une valeur, de sélectionner un fichier ou même de définir une couleur.

En python2.7, il s’agit de modules indépendants, même s’ils sont installés avec python-tkinter.

7.1 Boîtes à message

Si python3-tk est installé, on importe messagebox comme un sous-module de tkinter. Comme cela fait des noms de fonction à rallonge, mieux vaut utiliser un alias, MB par exemple :

import tkinter.messagebox
q0 =tkinter.messagebox.askquestion(title ="Titre", message ="Message")
print(q0)

Selon le clic sur [Oui] ou [Non], la variable q0 renvoie "yes" ou "no"

Pour mémoire, en python 2.7, un module supplémentaire est installé avec Tkinter, nommé tkMessageBox (majuscules obligatoires), qui contient les fonctions de boîtes à messages, il ne faut donc pas le préfixer par Tkinter :

import tkMessageBox
q0 =tkMessageBox.askquestion(title ="Titre", message ="Message")
print(q0)

En python3, cela produit des noms de fonction à rallonge, mieux vaut utiliser un alias, par exemple MB :

import tkinter.messagebox as MB
q0 =MB.askquestion(title ="Titre", message ="Message")
print(q0)

Options :

Retournent ok, bouton nécessairement par défaut :

import tkinter.messagebox as MB
q0 =MB.showinfo(title ="Information", message ="There's spam")
q1 =MB.showwarning(title ="Avertissement", message ="There's no spam")
q2 =MB.showerror(title ="Erreur", message ="There's no spam")

Retourne yes (par défaut) ou no ; [tab] permet de changer de bouton par défaut, sensible à la touche [Enter] :

import tkinter.messagebox as MB
q =MB.askquestion(title ="Question", message ="Is there spam?")
print(q)

Retournent True ou False - ou encore None pour [cancel] le cas échéant :

import tkinter.messagebox as MB
oc0 =MB.askokcancel()     # default ="ok" - sinon, ajouter default ="cancel"
print(oc0)
yn0 =MB.askyesno()        # default ="yes" - sinon, ajouter default ="no"
print(yn0)
ync0 =MB.askyesnocancel() # default ="yes" - sinon, ajouter default ="no" ou "cancel"
print(ync0)
rc0 =MB.askretrycancel()  # default ="retry" - sinon, ajouter default ="cancel"
print(rc0)

Forcer le type de boîtes

Changement d’icone :

icon ="" permet de forcer le type d’icone "error", "info", "question" ou "warning", quelle que soit le type de réponse possible. L’exemple ci-dessus permet un choix Oui/Non mais avec l’icone du i dans un phylactère.

import tkinter.messagebox as MB
MB.askyesno(title ="Dubitatif", message ="Il n'y a pas de spam", icon ="info")

Changement de boutons de choix :

type = permet de forcer d’autres choix dans une boîte particulière avec les valeurs "ok", "yesno", "okcancel", "retrycancel", or "yesnocancel". L’exemple suivant permet d’afficher la boîte d’erreur (la x blanche sur le disque rouge de showerror), mais avec un choix entre [Oui] et [Non]

import tkinter.messagebox as MB
MB.showerror(type ="yesno")

type ="abortretryignore", inédit parmi les boîtes toutes faites, permet de renvoyer les choix "abort", "retry" ou "ignore" :

import tkinter.messagebox as MB
sh0 =MB.showerror(type ="abortretryignore")
ayn0 =MB.askyesno(title ="Dubitatif", message ="Il n'y a pas de spam", icon ="info", type ="abortretryignore")

7.2 Boîtes de saisie Rév. 2020.01.01

Si le paquet Debian python-tk est chargé, import tkinter.simpledialog charge un sous-module permettant l’affichage de boîtes de saisie de données. Bien que ces boîtes soient indépendante de Tk(), il faut ouvrir une fenêtre-racine.

import tkinter, tkinter.simpledialog as SD

racine0 =tkinter.Tk()
texte0 ="???"
etiquette0 =tkinter.Label(text =texte0)
etiquette0.pack()
texte0 =SD.askstring("Espace de parole", "Exprimez-vous!")
etiquette0.config(text =texte0)
racine0.mainloop()

En python2, le module s’appelle tkSimpleDialog et est installé avec le paquet python-tk. Par exemple :

import tkSimpleDialog
racine0 =tkinter.Tk()
chaine =SD.askstring("Exprimez-vous!", "Veuillez dire ce que vous pensez", parent =racine0)
racine0.mainloop()

tkinter.simpledialog.askinteger("Titre", "Invitation") permet de saisir un entier (uniquement les chiffres de 0 à 9, + et -)

import tkinter.simpledialog as SD

racine0 =tkinter.Tk()
etiquette0 =tkinter.Label(text ="A lancer dans une console; fermer\ncette fenetre en fin de saisie")
etiquette0.pack()
texte0 =SD.askinteger("Un nombre entier", "un signe (+ ou -) et des chiffres")
racine0.mainloop()
print(texte0+1000000000)

tkinter.simpledialog.askfloat("Titre", "Invitation") permet de saisir un «réel» (chiffres, point décimal . (pas de virgule), +, -, E et e pour la notation exponentielle, mais pas les j / J pour les nombres complexes (faut-il prévoir deux boîtes?)

On peut ajouter quelques options valables pour entiers, décimaux ou chaînes, où "ac" est inférieur à "d" :

7.3 Sélectionneur de fichier Rév. 2020.01.01

En python3, filedialog est un sous-module de tkinter permettant de choisir un répertoire, de charger un ou plusieurs fichiers ou d’en sauvegarder un à travers une boîte de sélection de fichiers ou de répertoire :

import tkinter.filedialog
repertoire0 =tkinter.filedialog.askdirectory()
fichier1 =tkinter.filedialog.askopenfilename()
fichier2 =tkinter.filedialog.asksaveasfilename(defaultextension ="png")
print(repertoire0, fichier1, fichier2)

En python2, si le paquet Debian python-tk est chargé : import tkFileDialog importe le module

import tkFileDialog
repertoire0 =tkFileDialog.askdirectory()   # pour choisir un repertoire
fichier1 =tkFileDialog.askopenfilename()   # pour selectionner le nom d’un fichier a ouvrir
fichier2 =tkFileDialog.asksaveasfilename() # pour selectionner le nom d’un fichier a sauvegarder
print(repertoire0, fichier1, fichier2)

filetypes =[] permet de définir des associations permettant de filtrer les fichiers selon leur extension. Les tuples ("description", ".ext"), ou ("description",(".ext1",".ext2")) pour deux variantes de la même description, sont collectés dans une liste :

import tkinter.filedialog as FD
association0 =[("Fichiers de texte", ".txt"), ("Fichiers de données", ".csv"), ("Fichiers HTML", (".htm", ".html")), ("Tous les fichiers", ".*")]
fichier1 =FD.askopenfilename(filetypes =association0)
print(fichier1)

Deux autres boîtes permettent de combiner un choix de fichier et de son chargement ou de sa sauvegarde :

askopenfile(mode ="r") permet de combiner la navigation dans une arborescence, la sélection du nom et le chargement effectif d’un fichier :

import tkinter.filedialog
id0 =FD.askopenfile(mode ="r", title ="Chargement d’un fichier texte")
texte =id0.read()
id0.close()
print(texte)

asksaveasfile(mode ="w") permet de combiner la navigation dans une arborescence, la sélection du nom et la sauvegarde effective d’un fichier :

import tkinter.filedialog as FD
id1 =FD.asksaveasfile(mode ="w", title ="Sauvegarde d'un fichier texte")
id1.write("Un texte court")
id1.close()

7.4 Sélectionneur de couleur Rév. 2020.01.01

En python3, le module tkinter contient le sous-module colorchooser, dont l’unique fonction s’appelle askcolor() :

import tkinter.colorchooser
(rouge0, vert0, bleu0), couleur0 =tkinter.colorchooser.askcolor()
print(rouge0, vert0, bleu0, couleur0)

L’exemple ci-dessus montre qu’en python3, les valeurs des couleurs fondamentales rouge0, vert0 et bleu0 sont sous forme de réels de 0 à 255.99609375. Si besoin est, il faut les traiter de cette façon (en python3, round(reel) arrondit en véritable entier) :

import tkinter.colorchooser
(rouge0, vert0, bleu0), couleur0 =tkinter.colorchooser.askcolor()
print(round(rouge0), round(vert0), round(bleu0), couleur0)

En python2, si le paquet Debian python-tk est installé sur votre système, il est possible d’importer le module import tkColorChooser(). askcolor() renvoie les fondamentales exprimées sous forme entière, de 0 à 255.

import tkColorChooser
(rouge0, vert0, bleu0), couleur0 =tkColorChooser.askcolor(color ="purple", title ="Choisir une couleur!")
print(rouge0, vert0, bleu0, couleur0)

L’expression (rouge0, vert0, bleu0), couleur0 est un tuple composé de deux éléments. Le premier est lui-même un tuple composé de trois valeurs : les composantes fondamentales, sous forme de «réel» de 0 à 255 : rouge signal, vert émeraude et bleu outremer. Le second est une chaîne de forme #18f6ad, composé des trois couleurs sous forme hexadécimale : #18 pour le rouge (1*16+8 =24), #f6 pour le vert (15*16+6 =246) et #ad (10*16+13 =173) pour le bleu. Cette forme est donc moins précise.

askcolor() accepte trois paramètres, facultatifs :

Voici une façon de traduire "#18fd5e" en (24,246,173) :

couleur0 ="#18fd5e"
r0, v0, b0 = int(couleur0[1:3], 16), int(couleur0[3:5], 16), int(couleur0[5:], 16)

Attention! en cas de clic sur le bouton [Annuler], la fonction renvoie le tuple (None, None), ce qui peut interrompre le script, qui s’attend à recevoir un tuple de type ((r, v, b), chaine). Il faut donc se prémunir de ce problème avec un script de ce genre :

import tkinter.colorchooser
defaut =127, 127, 127
couleurs, chaine =tkinter.colorchooser.askcolor(color =defaut)
if couleurs: # bouton [OK]: le tuple existe
  rouge, vert, bleu =couleurs
else:        # bouton [Annuler]
  rouge, vert, bleu =defaut
print(rouge, vert, bleu)

A. Documentation

A.1 Documentation dans le logiciel python

Dans le mode interactif, obtenu en saisissant python3 dans une console et après avoir importé le module :

>>> import tkinter

>>> print(dir()) liste les modules chargés
>>> print(tkinter.__file__) renvoie la localisation de tkinter sur le système
>>> print(dir(tkinter)) liste les instructions du module tkinter
>>> print(tkinter.fct.__doc__) documente l’instruction précisée de tkinter

On lance python2 avec la commande python ; le module s’écrit toujours avec une majuscule : Tkinter

La commande help informe sur une fonction d’un module importé :

>>> help(tkinter.Button)

Pour générer un fichier lisible avec un éditeur de texte, saisir dans une console:

python3 -c "import tkinter; help(tkinter)" > tk.txt
python3 -c "import tkinter.filedialog; help(tkinter.filedialog)" > tkfd.txt
 etc.

python3 -c interprète une chaine, constituée ici de l’importation du module et du lancement de l’aide sur ce module. Au lieu d’utiliser la sortie standard (la console), > tk.txt sauvegarde le texte dans le fichier tk.txt

En python2, il faudra saisir :

python -c "import Tkinter; help(Tkinter)" > Tk.txt
python -c "import tkFileDialog; help(tkFileDialog)" > Tkfd.txt
 etc.

A.2 Documentation sur votre système GNU/Linux

Si python est installé, saisir man python dans une console renseigne sur les différentes manières de lancer python.

Le script /usr/bin/pydoc permet la consultation d’information de fonctions, modules, mots-clés :
pydoc -g lance une interface graphique pour une navigation dans le système d’aide

A.3 Documentation sur Internet

A.4 Documentation sur papier