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 :
#apt install python3-tkEn mode super-utilisateur: su - [Enter]
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()
- tkinter.Tk() ouvre l’objet fenêtre racine0 duquel dépendront d’autres objets créés par la suite
- tkinter.Button() crée l’objet bouton0
- pack() s’applique à un l’objet bouton représenté par la variable bouton0, qui ne s’affiche pas sans cela, voir pack / grid
- tkinter.RIGHT est une constante utilisée pour l’alignement, qui peut être remplacée par "right" (sans préfixe)
- mainloop() ferme la boucle définie par Tk(), en s’appliquant sur l’objet racine0
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()
- tkinter.Tk() ouvre l’objet racine, le «parent» le plus élevé. Il s’agit d’un premier objet (une fenêtre redimensionnable) duquel les autres dépendront
- racine0.title() détermine le titre de la fenêtre, qui s’inscrit dans la barre du haut
- geometry("largeurxhauteur+Δ largeur+Δ hauteur") précise les dimensions minimales intérieure de la fenêtre (x minuscule entre les deux dimensions exprimées en pixels), ainsi que l’écartement à gauche et en haut par rapport à l’écran. Attention si votre système comporte une barre supérieure, qui ne sera pas recouverte. Gnome2/Mate-desktop en tout cas repousse la fenêtre vers le bas de la barre supérieure en cas d’écart insuffisant.
- fenetre0.aspect(x1, y1, x2, y2) impose, pour un redimensionnement (par exemple à la souris), un rapport entre x1/y1 et x2/y2.
- d’après exérimentation, il semble que x1/y1 doive être plus petit que x2/y2
- ces rapports doivent être compatibles avec la largeur et la hauteur de geometry, sinon la fenêtre est basée sur la largeur de geometry, la hauteur étant étant limitée à la valeur la plus proche déterminée par x1/y1 et x2/y2
- tkinter.Button() ajoute le widget «bouton à cliquer». C’est un objet «enfant», qui doit préciser son parent direct comme premier paramètre
- pack() est avec grid la méthode qui installe le widget précisé dans son widget-parent
- tkinter.RIGHT est la constante "right", qui aligne le widget à droite, ce qui une façon d’organiser les widgets entre eux
- mainloop() est une méthode toute spéciale, qui attend les événements (commandes liées aux boutons et renvoyant à des fonctions, temporalité…). On en sort le plus souvent avec command =racine0.destroy associé au clic d’un bouton comme dans l’exemple ci-dessus, ou par la fermeture par un clic sur l’icone ✕ de la barre supérieure de la fenêtre créée avec tkinter.Tk()
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()
- le bouton [Autre] active la fonction top(), qui ouvre la seconde fenêtre
- cette nouvelle fenêtre ne requiert ni pack() ni mainloop()
- fermer la principale ferme la seconde (sortie de mainloop()), l’inverse n’est pas vrai.
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()
- La disposition des boutons changera selon l’ordre et l’orientation qu’on leur donne.
- Un cadre est disposé par défaut en haut de la fenêtre. Il est possible de modifier cela avec pack(side =)
- Un cadre resserre autant que possible ses composants. Il est possible de lui ajouter une marge à gauche et à droite avec padx = et en haut et en bas avec pady = (en pixels) dans pack()
tkinter.Frame(parent, bg ="red", border =3, relief =tkinter.GROOVE)
- bg ="" permet une définition de couleur de fond
- border = décide de la largeur en pixels de la ligne de contour
- relief ="" permet de définir un type de relief
Il est possible de donner un bord et un nom à un cadre avec LabelFrame :
cadre0 =tkinter.LabelFrame(parent, text ="")
- text ="" détermine le titre du cadre
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()
- La variable mot0, qui contient l’identificateur du widget, permet de l’installer avec mot0.pack() et éventuellement le modifier ou le supprimer [lien interne]
- side =tkinter.BOTTOM garde le texte au bas de la fenêtre si on l’agrandit.
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()
- file doit être suivi d’un nom de fichier, éventuellement avec un chemin suffisant
- certains préconisent la ligne etiquette0.image =image0 avant etiquette0.pack()
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()
- width est la largeur de la plage en nombre de caractères, height le nombre de lignes
- side =tkinter.RIGHT dans pack() 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.
- insert(tkinter.END, chaine) permet l’insertion d’une chaîne à la fin du texte, tkinter.INSERT (par défaut) à l’endroit du curseur-texte, CURRENT à l’endroit le plus proche du curseur souris?
- state =tkinter.DISABLED interdit l’édition du texte, state =tkinter.NORMAL (par défaut) la permet, mais cela ne peut être précisé qu’après l’insertion du texte avec texte0.config(state =tkinter.DISABLED)
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()
- texte0.tag_add() définit des plages de texte pour une configuration précise, relié à un «tag». Dans notre exemple, première ligne : les caractères de 13 à 18 (0 est le premier); pour la seconde ligne, les caratères de 7 à 11 (\n séparent deux lignes)…
- texte0.tag_config() applique une configuration aux plages de textes taggués.
- foreground = ne peut être abrégé en fg =
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
- entier0 =tkinter.IntVar() permet de saisir une valeur entière, qui n’accepte que les chiffres de 0 à 9, + et -
- reel0 =tkinter.DoubleVar() permet de saisir une valeur «réelle», acceptant également le point décimal ., e ou E pour la notation scientifique de type 43e23 (43*10**23) ou 43e-23 (43/10**23). Si la valeur saisie est entière, la variable se terminera par .0 pour garder son type «réel».
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()
- width = et height = permettent de déterminer la largeur et la hauteur du bouton en hauteur de caractères, ou en pixels en cas d’utilisation d’une image.
- relief = permet de déterminer le style de relief du bouton. Par défaut c’est relief =tkinter.RAISED, avec une animation vers tkinter.SUNKEN lorsqu’il est cliqué.
- l’alignement de pack() avec side = peut également prendre les valeurs tkinter.LEFT, tkinter.RIGHT, tkinter.TOP ou tkinter.CENTER
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
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()
- indicatoron =0 est un paramètre qui permet de transformer une case à cocher en bouton «off» (relevé, =0, texte sur fond gris) / «on» (enfoncé, =1, texte sur fond blanc). Pour apprécier le relief, il vaut mieux prévoir une bordure suffisante, à partir de border =2.
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
- retour0 =tkinter.StringVar() permet d’initialiser une variable pour une valeur de retour sous forme de chaîne. value doit alors recevoir une "chaîne"
- retour0.set() permet de forcer une valeur par défaut, et donc un bouton enfoncé. Ce n’est pas obligatoire, mais un bouton apparaîtra coché si sa valeur de retour est 0
- retour0 =tkinter.IntVar() initialise retour0 comme variable entière; tkinter.DoubleVar() pour un variable «réelle» et tkinter.StringVar() pour un variable-chaîne
- variable =retour0 regroupe les boutons d’un même groupe de radio-boutons : tous ceux qui jouent ensemble doivent avoir la même variable-retour.
- value = différencie chaque bouton d’un groupe de radio-boutons, la valeur est rendue par retour0.get()
- grid() est un équivalent de pack() permettant le placement des widget dans un système de rangées/colonnes
Et encore…
- indicatoron =0 permet de transformer une case à cocher en bouton «off» (relevé, =0, texte sur fond gris) / «on» (enfoncé, =1, texte sur fond blanc). Une bordure à partir de border =2 permet de mieux apprécier le relief.
2.8 Spinbox Nv. 2014.10
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())
- retour0 =tkinter.StringVar() initialise la variable-retour retour0, qui sera ensuite récupérée par retour0.get() sous forme de chaîne même à partir de valeurs entières ou «réelles».
- retour0.set() précise la valeur de la variable-retour avant la définition du widget
- textvariable =retour0 précise la variable-retour dans le widget
- from_ =, to =, increment = définissent respectivement un début, une fin et une éventuelle incrémentation, qui peuvent être entiers ou «réels». Contrairement à to, le début (from_) n’est pas obligatoire, valant 0 par défaut. Des valeurs négatives sont possibles, tant que to est plus grand que from_ ; increment doit être strictement positif. from_ doit son tiret bas au fait que from est un nom réservé en python.
- font ="sans 24" permet d’agrandir la fonte, et de ce fait les flèches à cliquer
- width = précise la largeur du texte en nombre de caractères (20 par défaut)
- spin0.config() permet d’écrire les paramètres en plusieurs fois
Et encore…
- values = alternative à from_ / to, ce paramètre peut recevoir une liste [1, "qsd", "7", 8], un tuple (3, "A", 45) ou une fonction renvoyant un objet itérable, comme range()
- buttonbackground = définit la couleur des flèches
- repeatdelay = nombre de millisecondes d’appui sur une flèche avant que la valeur ne ccommence à changer automatiquement (400 par défaut, semble un peu lent)
- repeatinterval = nombre de millisecondes entre deux changements de valeur (100 par défaut, semble un peu rapide)
- …
2.9 Scale Nv. 2014.10
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())
- retour0 =tkinter.IntVar() initialise une variable-retour entière
- retour0.set(43) fixe la variable-retour à 43
- variable =retour0 assigne la variable retour au widget
- print(retour0.get()) affiche la variable retour lors de la sortie de mainloop()
Paramètres additionnels
- from_ =, to = déterminent les valeurs basse et haute (incluses) entre lesquelles le curseur peut coulisser (0 et 100 par défaut).
- resolution = détermine l’écart entre deux valeurs (1 par défaut). C’est de cet écart que le curseur bouge lors d’un clic sur la coulisse.
- length = définit la longueur de l’échelle (101 pixels par défaut, plus l’épaisseur du curseur).
- s’il y a plus de valeurs que de pixels, certaines valeurs seront manquantes par saisie/déplacement du curseur : il faut dans ce cas cliquer en deçà ou au delà du curseur pour accroître ou diminuer la valeur d’une unité
- s’il y a moins de valeurs que de pixels, le curseur saute aux valeurs justes, sans s’arêter aux valeurs intermédiaires
- width = largeur en pixels du curseur et de la coulisse (15 par défaut)
- sliderlength = épaisseur du curseur (30 par défaut)
- orient = permet de définir le sens du curseur : "horizontal" (accroissement vers la droite) ou "vertical" (par défaut, accroissement vers le bas)
- repeatdelay = nombre de millisecondes d’appui en deçà ou au delà du curseur avant que la valeur ne commence à changer automatiquement (400 par défaut, semble un peu lent)
- repeatinterval = nombre de millisecondes entre deux changements de valeur (100 par défaut, semble un peu rapide)
- command = détermine une fonction/procédure lors de la sollicitation du widget. Attention : la fonction définie par def valeur(): est appelée par command =valeur. Les variables définies par tkinter.IntVar() sont globales.
- showvalue =0 supprime l’étiquette qui indique la valeur courante, à utiliser avec le paramètre command = qui permet d’afficher la valeur ailleurs dans le cadre.
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())
- retour0 =tkinter.DoubleVar() initialise nécessairement une variable-retour «réelle»
- resolution = détermine un écart différent de l’unité, par exemple .05. C’est de cet écart que le curseur bouge lors d’un clic sur la coulisse.
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
- width = nombre de pixels pour épaissir le trait
Il est possible de déterminer un style pour les extrémités de ligne (si l’épaisseur 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
- 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 forme de la flèche - (10, 20, 8) pour un losange
- longueur de la partie centrale (plus courte que la valeur suivante pour une flèche en ancre)
- longueur des ailes à partir de la pointe
- largeur de la flèche à l’extrémité des ailes
Les angles de lignes brisées peuvent également recevoir un style :
- 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)
Il est possible de donner un style à la ligne
Lignes discontinues Aj. 2015.01
- dash =(n, q) force la ligne brisée avec n pixels en couleur suivis de q pixels d’interruption. Il est possible de définir des lignes complexes avec un tuple plus long (n0, q0, n1, q1…)
- dashoff =d définit le début de la séquence «dash» - À vérifier!
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()
- splinesteps = définit un degré de lissage (1 pour (nombre de points -2) traits, 12 approche du lissage maximal).
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()
- create_rectangle : 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
- create_oval : il s’agit des coordonnées du rectangle circonscrit à l’ovale (pour un cercle, largeur et hauteur doivent être égales)
- create_arc : il s’agit des coordonnées du rectangle circonscrit à l’ovale entier, quelques soient les dimensions de la portion
- create_arc : start = défini l’angle de départ, extent = l’angle d’arrivée, exprimés en degrés (sens trigonométrique, «réels» permis)
- style =tkinter.PIESLICE dessine un quartier de tarte (par défaut)
- style =tkinter.CHORD dessine l’arc et sa corde.
- fill = définit la couleur de remplissage de la forme géométrique (transparente par défaut)
- color = définit la couleur de contour de la forme géométrique (noire par défaut)
- width = définit l’épaisseur du contour de la forme géométrique (1 par défaut)
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()
- smooth =True et splinesteps =n sont disponibles pour les polygones.
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()
- pour cet exemple, le fichier uneimage.png doit exister dans le répertoire courant
- photo0 est la variable créée lors du chargement de l’image, elle permet par exemple de déterminer les dimensions de l’image avec photo0.width et photo0.height, utiles pour définir les dimensions du canevas.
- create_image() affiche l’image sur le canevas représenté par la variable fond0
- image0 est la variable créée lors du placement de l’image sur le canevas, et permet de reconfigurer ou supprimer l’image
- image0 =fond0.create_image(0, 0, image =photo0, anchor =tkinter.NW) aligne l’image par rapport en haut et à gauche du canevas (défini par fond0) ; les argument sans paramètres (abscisse, ordonnée, largeur, hauteur…) doivent être donnés avant les argument avec paramètre
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()
- couleur =photo0.get(xx, yy) retourne un tuple des valeurs de rouge, vert et bleu du point xx, yy (0, 0 est en haut à gauche)
- photo0.put("#234a87", (xx, yy)) colore le pixel (xx, yy) de l’image ouverte avec photo0 de la nuance #234a87.
À 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()
- L’encodage de l’image provient de la syntaxe du langage C, qu’il convient plus ou moins de respecter. Après expérimentation, les trois premières lignes doivent commencer par le # et respectivement comporter _width et une largeur en pixels, _height et une hauteur en pixels, et char suivi d’un nom de variable-liste dont le choix semble arbitraire.
- Chaque octet (ici sous forme hexadécimale) contient huit pixels, la séquence 10101010 étant codée 0xaa, selon la conversion par quartet :
0000 → 0 0010 → 2 0100 → 4 0110 → 6 1000 → 8 1010 → a 1100 → c 1110 → e 0001 → 1 0011 → 3 0101 → 5 0111 → 7 1001 → 9 1011 → b 1101 → d 1111 → f
- Les octets définissent les paquet horizontaux de huit pixels de gauche à droite et de haut en bas, mais chaque octet est inversé par rapport à la suite de pixels. Un ensemble de 8 pixels .X.XXX.. se code en fait 00111010, à savoir x03A
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()
-
maskfile n’est pas obligatoire; il s’agit d’un tapis ajouré qui recouvre la couleur du fond à certains endroits :
* * * dessin (rouge) --- --- --- masque (blanc) = = = = = = = = = = = = = = = = fond (gris)
- foreground et background de BitmapImage ne peuvent être remplacés par fg et bg
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()
- itemconfig() peut remplacer itemconfigure()
- puisqu’il y a de la place, il est possible de plaquer le bouton "Rouge" à gauche, le "Bleu" à droite et puis le "Vert" au milieu (par défaut). Malgré les side =, modifier l’ordre de définition des boutons change le résultat. grid() peut être utilisé pour faciliter le placement des widgets.
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()
- les choix sont données à la suite l’un de l’autre
- choix0.set("Rouge") détermine la valeur par défaut
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()
- orient =tkinter.VERTICAL est l’orientation par défaut de Scrollbar
- fill =tkinter.Y met de l’espace entre les deux flèches de l’ascenseur horizontal
- side =tkinter.LEFT aurait placé l’ascenseur à gauche
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().
- side = permet de diriger le widget vers une direction dans son parent, avec les constantes tkinter.RIGHT, tkinter.LEFT, tkinter.TOP ou tkinter.BOTTOM, ou les chaînes "right", "left", "top" ou "bottom".
- fill = permet à un widget d’utiliser l’espace laissé par le parent, respectivement en largeur avec tkinter.X, en hauteur avec tkinter.Y, dans les deux dimensions avec tkinter.BOTH ou les chaînes "x", "y", "both". Par défault, il s’agit de tkinter.None ou "none".
- expand = permet (1) ou non (0, par défaut) à un widget d’utiliser le maximum d’espace
- in_ = désigne un autre widget que le parent comme widget de référence. Cet autre widget doit nécessairement être descendant du parent
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…
- in_ = un widget est en général placé par rapport à son parent. in_ permet de désigner un autre widget (un descendant du parent?)
- bordermode = tkinter.OUTSIDE précise que la taille et la position du widget sont relatives à la taille extérieure du widget de référence ; par défaut, tkinter.INSIDE : taille et position relatives à la surface intérieure
- width = et height = taille du widget en pixels ; par défaut, la taille prévue par tkinter
- relwidth = et relheight = («réels» de 0.0 à 1.0) taille, relative au widget de référence
- x = et y = : position par rapport à la gauche et au haut du widget, en px
- relx = et rely = («réels» de 0.0 à 1.0) position, relative à la taille du widget de référence
- anchor = spécifie le côté ou le sommet du widget à placer à la position définie. Le défaut est tkinter.NW (voir alignements)
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()
- pour réceptionner toutes les touches : <Key> ou <KeyPress>
- la fonction touche() (appelée sans parenthèses) récupère l’événement dans une variable (ici : evt0) et l’interroge par une méthode (ici : evt0.char)
- <KeyPress-w> ou <w> attend le caractère w (minuscule). Tous les caractères sont permis, mais <space> convient pour attendre la barre-espace, <less> pour <, <Return> (mais <Enter> est réservé à la souris : voir exemple suivant) pour la touche Enter
- Les modificateurs Control, Alt… peuvent être combinés : <Control-Alt-w> attend cette combinaison de touche. <Any-w> permet n’importe combinaison de touche modificatrices, mais Shift ne semble pas fonctionner (en tout cas sous Linux : utiliser une majuscule).
- evt0.keycode renvoie le numéro de touche, evt0.keysym un caractère ou sa description, ou une description de la touche, comme (liste non exhaustive) :
- Alt_L, BackSpace, Cancel ([Break]), Caps_Lock, Control_L, Control_R, Delete, Down (flèche), End, Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, Home, Insert, ISO_Level3_Shift ([AltGr]), Left (flèche), Menu, Next ([Pg Dn]), Num_Lock, Pause, Print, Prior, Return ([Enter]), Right (flèche), Scroll_Lock, Shift_L, Super_L ([Win]), Tab ([Pg Up]), Up (flèche)
- agrave, ampersand, apostrophe, asterisk, at [@], backslash, backslash, bar, braceleft [{], braceright [}], bracketleft, bracketright, ccedilla, colon [:], comma, dead_acute [´], dead_diaeresis [¨], dead_grave [`], dead_tilde [~], degree, dollar, eacute, egrave, equal, exclam, guillemotleft [«], guillemotright [»], minus, parenleft, parenright, percent, period, plus, question, quotedbl ["], section [§], semicolon [;], slash, sterling [£], threesuperior, twosuperior, underscore
- Avec NumLock : KP_0, KP_1, KP_2, KP_3, KP_4, KP_5, KP_6, KP_7, KP_8, KP_9, KP_Decimal (point)
- Sans NumLock : KP_Add ([+]), KP_Begin ([Home]), KP_Delete, KP_Down, KP_End, KP_Home, KP_Insert, KP_Left, KP_Next ([PgUp]), KP_Prior ([PgDn]), KP_Right, KP_Up
- Avec ou sansNumLock : KP_Divide, KP_Enter, KP_Multiply, KP_Subtract, KP_Subtract
- evt0.type renvoie le type d’événement : 2 pour un appui sur une touche (également en mode «répétition»), 3 pour une touche que l’on quitte (également en mode «répétition»)
É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()
- bind() lie, pour un widget (défini par sa variable de création), une action à une fonction
- <Button-1> est l’action de cliquer sur le bouton gauche de la souris(voir liste plus bas)
- la fonction souris récupère l’événement (ici, par la variable evt0) et en exprime les attributs :
- .x et .y renvoient les coordonnées de la souris par rapport au widget survolé
- .x_root et .y_root renvoient les coordonnées de la souris par rapport à l’écran
- .num renvoie le numéro du bouton cliqué
- .type renvoie le type d’événement : 4 pour un (ou double, triple) clic, 5 pour un retour de clic, 6 pour un mouvement, 7 pour une entrée sur un widget, 8 pour une fin de survol
- .widget retourne l’identifiant du widget concerné, sous la forme .140524171117416
- .width et .height renvoient les nouvelles dimensions d’un widget (en cas de reconfiguration)
Les actions de la souris peuvent être :
- <Button> pour n’importe quel bouton, <Button-1> ou <1> pour le bouton de gauche et <Button-3> ou <3> pour le bouton de droite
- <Button-2> ou <2> pour le bouton du milieu (s’il existe, ou les boutons gauche et droit en même temps)
- <ButtonRelease>, <ButtonRelease-1>, <ButtonRelease-2>… le déclenchement a lieu avec le retrait du clic
- <Motion> permet un déclenchement lorsque la souris bouge en survolant un widget ; <B1-Motion> attend un clic et un mouvement
- <Double-Button-1>, <Triple-Button-1> : attend deux ou trois clics rapprochés (à ne pas utiliser avec une autre procédure pour un simple clic sur le même widget, qui se déclencherait également).
- <Enter> déclenche une procédure lorsque la souris survole un widget, <Leave> lorsque la souris quitte le widget
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.
- le nombre précisé en premier paramètre précise le nombre de millisecondes entre chaque appel
- La variable tps0 permet de terminer la temporalisation avec after_cancel().
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 module | nom de constante | valeur |
---|---|---|
import tkinter | tkinter.HORIZONTAL | "horizontal" |
import tkinter as alias | alias.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.
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)
- une police de caractère
- un type : serif (avec empattement), sans (sans empattement), monospace (largeur fixe)
- une fonte installée sur votre système : times, helvetica ou arial, courier, …
- une fonte utilisée par tkinter - très petites : TkTooltipFont et TkSmallCaptionFont - moyennes : TkDefaultFont, TkTextFont, TkIconFont, TkMenuFont, TkFixedFont (monospace), TkHeadingFont (gras) - plus grande : TkCaptionFont
- une hauteur de caractère en points d’imprimerie, 72e de pouce (25, 4mm), mais semble en fait être la hauteur en pixels : sur un écran de 100px/pouce, cela apparaîtra plus petit
- une graisse : bold, ou normal (par défaut)
- un style : italic, ou roman (normal, par défaut)
- une décoration : underline, overstrike
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 :
- title ="" donne un titre à la boîte de message ou de choix
- message ="" définit le message en gras, à l’intérieur de la boîte
- detail ="" permet un message secondaire, dans une fonte de taille plus petite
- default ="no" ou default ="cancel" redéfinit le bouton par défaut
- icon et type : voir Forcer le type de boîte
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" :
- initialvalue = détermine une valeur par défaut
- minvalue = et maxvalue = déterminent les valeurs minimale et maximale acceptables
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)
- title = donne un titre à la boîte
- le bouton [OK] de la boîte de sélection de répertoire renvoie le répertoire par défaut, [Ouvrir] et [Sauvegarder] ne fonctionnent que si un répertoire, un fichier à ouvrir ou à sauvegarder ont été sélectionnés ; dans ce dernier cas, avertissement si le nom de fichier saisi existe déjà.
- il n’y a pas de bouton par défaut et pas de default = pour le déterminer, mais [Enter] renvoie le répertoire par défaut pour la boîte de sélection de répertoire.
- le bouton [Annuler] ou la fermeture de la boîte par [x] en haut et à droite renvoient une chaîne vide "" si aucun répertoire ou fichier n’est sélectionné, le tuple vide () si un répertoire ou un fichier est sélectionné
- les variables fichier1 et fichier2 contiennent le nom du fichier à ouvrir ou à sauvegarder, sans traiter le chargement ou la sauvegarde du fichier, même si une boîte de dialogue s’ouvre indiquant qu’ils sont sur le point d’être effacés - pour le traitement même, voir ici.
- multiple =True - pour askopenfilename() seulement - permet le choix de plusieurs fichiers (avec [shft-clic] pour des fichiers contigues et [ctrl-clic] pour les choisirs un à un), retournés sous forme de tuple contenant les noms de fichiers sélectionnés.
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)
- id0 est un identifiant de fichier, pas son nom
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()
- Attention! l’exemple est fonctionnel et susceptible d’écraser un fichier déjà existant!
- id1 est un identifiant de fichier, pas son nom
- une boîte de dialogue avertit qu’un fichier du nom sélectionné existe et va être écrasé
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 :
- title ="" permet de donner un titre à la fenêtre de sélection des couleurs
- color ="#18fd5e" ou color =(24,246,173) impose une couleur de départ (par défaut, c’est le gris léger "#edeceb"). Il est possible de fournir une chaîne de couleur reconnue : ""pink", navy", "green"… initialcolor ="#126578" ou "#126578" tout seul sont équivalent.
- parent =cadre0 (à tester) devrait permettre de désigner un widget réceptionnant la fenêtre.
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
- Penser en tkinter (.fr) de Stephen Ferg
- Construire une interface graphique pas à pas en Python (.fr) doc orientée objet
- docs.python.org/lib/module-tkinter.html (.en) doc officielle
- www.pythonware.com/library/ (.en)
- effbot.org/tkinterbook/ (.en) très complet tout en restant accessible
- infohost.nmt.edu/tcc/help/pubs/tkinter/web/ (.en) et pdf
A.4 Documentation sur papier
- John E. Grayson, Python and tkinter Programming, Manning Publications
- Alex Martelli & David Ascher, eds. : "python cookbook", o’reilly, 2002 (anglais) comporte peu de choses sur le module tkinter.