Pour créer une application avec une interface graphique, on peut utiliser le module tkinter
Outre Python, ce module peut être utilisé par les langages : Tcl, Ruby et Perl.
Depuis Python 3 (depuis +/- 2009), le module s'appelle tkinter
. Or, avant, il s'appelait Tkinter
... => Pour que les anciens codes fonctionnent, il
faut les corriger en remplaçant la majuscule par une minuscule
Depuis Python 3.7, lors de son installation, le module tkinter
y est
compris.
PyQt5, abordé plus tard, permet de réaliser des interfaces parfaites. De plus, l'apprentissage de Qt5 servira avec d'autres langages de programmation tel que C++.
Avec Tkinter, l'interface sera "vieille", mais aussi plus légère et plus rapide.
Exécuter python -m tkinter
depuis la ligne de commande ouvre
une fenêtre de démonstration d'une interface Tk simple, vous indiquant que tkinter est
correctement installé sur votre système et indiquant également quelle version de Tcl/Tk
est installée; vous pouvez donc lire la documentation Tcl/Tk.
Le module tkinter
contient les modules suivants : (par ordre
alphabétique)
- colorchooser
- commondialog
- dnd
- filedialog : pour sélectionner facilement des fichiers ou un dossier
- font
- messagebox : pour attendre la réaction de l'utilisateur
- scrolledtext
- simpledialog : pour que l'utilisateur puisse entrer un nombre entier, décimal ou une string
- tix :
Deprecated. This Tk extension should not be used in new code. Use tkinter.ttk instead
- ttk
Depuis tkinter 8.5, lors de son installation, l'extension ttk
est
comprise. Cette extension permet d'améliorer l'apparence des widgets (objets
graphiques : bouton, ...). Documentation
Il existe des tutoriels :
-
Python GUI Programming With
Tkinter (pour
Label, Entry, Button, Text, Frame
) - xavierdupre.fr
- python-course.eu/tkinter
5 lignes de code suffisent pour qu'une fois interprétées s'affiche, par exemple :
Il s'agit d'un logiciel puisque vous pouvez cliquer sur la croix de fermeture (et vous pouvez l'agrandir pour qu'il prenne toute la surface de l'écran, en cliquant sur le carré à gauche de cette croix)
import tkinter # import de tkinter fP = tkinter.Tk () # création de la fenêtre principale (fP) obj = tkinter.Label (text = "Bonjour, Thibaut !") obj.pack () # on ajoute l'objet à la fenêtre principale fP.mainloop () # écouter toute action sur la fenêtre
Boîtes de dialogue
Documentation sur les boîtes de dialogue
Avant de créer un logiciel, sachons que le module tkinter offre des objets tout faits, très utiles, qui peuvent être utilisés aussi dans un programme en ligne de commande ...
Ainsi, on peut utiliser brièvement une interface graphique par commodité (avant de retourner en ligne de commande). Ces objets sont des boîtes de dialogue qui retournent le choix de l'utilisateur. Ce retour est placé dans une variable.
Coder "manuellement" de tels retours d'information serait trop complexe. Ces boîtes de dialogue diminue le nombre de lignes de code et, par conséquent, le nombre d'erreurs. Le développement du projet est également plus rapide et l'utilisateur est ravi par l'apparence connue (au lieu d'apprendre un interface spartiate).
Sélection d'un fichier
import os import platform import tkinter # -------------------------- from tkinter import filedialog # ---------- from tkinter import messagebox # utilisation d'une boîte de dialogue ----------------------------------------- fP = tkinter.Tk () # création de la fenêtre principale fP.withdraw() # Ne pas l'afficher gPath = os.path.abspath("."); SEP="/" if platform.system() == "Windows": SEP="\\"#; gPath=os.path.abspath("C:\\Users"); #else: gPath = os.path.abspath("/home") gFileTypes=(('TEXT files','*.txt'),('HTML files','*.htm'),('All files','*.*')) filename = filedialog.askopenfilename(initialdir=gPath, filetypes=gFileTypes, title="Sélection d'un fichier") if len(filename)==0: messagebox.showwarning("","Pas de fichier sélectionné !") # sortir du programme ? else: CC = filename.replace("/",SEP) fP.destroy() # destruction de la fenêtre (= fermeture du GUI) # fin de l'utilisation d'une boîte de dialogue -------------------------------- # reste du code du programme print(CC) # chemin complet du fichier choisi
askopenfilename()
retourne une string (vide
si l'utilisateur a cliqué sur "Annuler").
askopenfilenames()
retourne l'ensemble des fichiers
sélectionnés sous la forme d'un tuple (()
ou non).
Les retours sont des strings représentant le chemin complet du dossier, du fichier et des fichiers.
Les options facultatives : (extrait de la documentation sur filedialog)
- parent - the window to place the dialog on top of
- title - the title of the window (facultatif)
-
initialdir - the directory that the dialog starts in
(Par défaut, = le dossier du programme Python en cours)import os; import platform gPath = os.path.abspath("."); SEP="/" if platform.system() == "Windows": SEP="\\"#; gPath=os.path.abspath("C:\\Users"); #else: gPath = os.path.abspath("/home")
-
filetypes - a sequence of (label, pattern) tuples, ‘*’ wildcard is allowed
Ce tuple doit contenir au moins deux éléments.gFileTypes=(('TEXT files','*.txt'),('HTML files','*.htm'),('All files','*.*')) filename = filedialog.askopenfilename(initialdir=gPath, filetypes=gFileTypes)
gFileTypes=(('HTML files','*.htm'),('HTML files','*.html')) => la liste déroulante affichera HTML files (*.htm;*.html)
- initialfile - the file selected upon opening of the dialog
- defaultextension - default extension to append to file (save dialogs)
- multiple - when true, selection of multiple items is allowed
Sélection d'un dossier
import os import platform import tkinter # -------------------------- from tkinter import filedialog # ---------- # utilisation d'une boîte de dialogue ----------------------------------------- fP = tkinter.Tk () # création de la fenêtre principale fP.withdraw() # Ne pas l'afficher gPath = os.path.abspath("."); SEP="/" if platform.system() == "Windows": SEP="\\"#; gPath=os.path.abspath("C:\\Users"); #else: gPath = os.path.abspath("/home") dossier = filedialog.askdirectory(initialdir=gPath, title="Sélection d'un dossier") if len(dossier)==0 : exit("Pas de dossier sélectionné !") dossier = dossier.replace("/",SEP) # NB : Sans le slash (ou anti-slash) final fP.destroy() # destruction de la fenêtre (= fermeture du GUI) # fin de l'utilisation d'une boîte de dialogue -------------------------------- # reste du code du programme print(dossier) # chemin complet du dossier choisi
askdirectory()
retourne une string (vide
si l'utilisateur a cliqué sur "Annuler").
Le programme devient logiciel lorsque tout est demandé via une interface graphique.
Le fenêtre modale peut se trouver sous sa fenêtre parent ...
=> déplacer la fenêtre parent (ou regarder dans la barre des tâches)
Le fenêtre qui a perdu le focus a son titre mis en gris.
Sélection d'un nombre entier
Retourne None
ou un nombre entier (positif ou négatif).
None = clic sur la croix de fermeture ou clic sur Cancel
- Premier paramètre = Titre de la boîte de dialogue
- Second paramètre = Message précédent le champ
from tkinter import * from tkinter import simpledialog fP = Tk() fP.withdraw() # Ne pas l'afficher def labelWidth(label, taille = 100): #------------------------------------------ """ La fonction retourne une string ayant la taille minimale voulue, (utile à une largeur correcte de la boîte de dialogue). :param str label: un entier (tel qu'un nombre ou une taille de fichiers) :param int taille: nombre de caractères de la string formatée :return: Retourne une string ayant la taille minimale voulue :rtype: str 2022-10-24 04:42:04 """ nb = len(label); diff = taille-nb if diff>0 : return label+" "*diff return label label=labelWidth("Entrez un nombre entier [0-100] :") int_a = simpledialog.askinteger("Titre",label, minvalue=0, maxvalue=100, initialvalue=7) print(int_a) # affiche None ou un nombre entier (positif ou négatif) fP.mainloop()
Les paramètres-clés - initialvalue, minvalue et maxvalue - sont facultatifs. Si la valeur donnée est égale à la valeur minimale ou maximale, elle sera acceptée.
Sélection d'un nombre décimal
Retourne None
ou un nombre décimal (positif ou négatif).
None = clic sur la croix de fermeture ou clic sur Cancel
from tkinter import * from tkinter import simpledialog fP = Tk() fP.withdraw() # Ne pas l'afficher float_b = simpledialog.askfloat("b", "Entrez un nombre décimal") print(float_b) # affiche None ou un nombre décimal (positif ou négatif) # ou un entier sous forme décimale (3 devient 3.0) fP.mainloop()
Les paramètres-clés - initialvalue, minvalue et maxvalue - sont facultatifs. Si la valeur donnée est égale à la valeur minimale ou maximale, elle sera acceptée.
Sélection d'une string
Retourne None
ou une string (vide ou non).
None = clic sur la croix de fermeture ou clic sur Cancel
from tkinter import * from tkinter import simpledialog fP = Tk() fP.withdraw() titre = "Tapez 'fin' pour quitter" label = "Entrez votre nom :" +" "*80 while True: str_nom = simpledialog.askstring(titre, label, initialvalue="Toto") print(str_nom) # affiche None ou une string (qui peut être vide, # ou contenir un nombre => 7 est un nom valide !) if str_nom is None: print("= None") else: if str_nom == "": print("= Vide") else: print("= Non vide") if str_nom == "fin": print("=> Bye"); break fP.destroy() fP.mainloop()
Le paramètre-clé initialvalue="X"
ou X sera affiché dans le champ.
Messages
L'appel d'une messagebox
suspend le programme, le temps que l'utilisateur
clique sur l'un des boutons de cette fenêtre surgissante (popup).
Il existe 8 types de message :
# À Â Ç É È Ê Ë Î Ï Ô Ù Û Ü Ÿ Æ Œ æ œ # Code source : https://pythonguides.com/python-tkinter-messagebox/ from tkinter import * from tkinter import messagebox fP = Tk() fP.title("Python Guides") fP.geometry('300x200') fP.config(bg='#5FB691') def msg1(): messagebox.showinfo("Titre","Message") messagebox.showerror("Titre","Message") messagebox.showwarning("Titre","Message") re = messagebox.askquestion("Titre","Message"); print(re) # "yes" | "no" re = messagebox.askokcancel("Titre","Message"); print(re) # True | False re = messagebox.askyesno("Titre","Message"); print(re) # True | False re = messagebox.askretrycancel("Titre","Message"); print(re) # True | False re = messagebox.askyesnocancel("Titre","Message"); print(re) # True | False | "None" Button(fP, text="Cliquez ici", command=msg1).pack(pady=50) fP.mainloop()
Le premier bouton est le bouton par defaut. Toutefois, il est possible de modifier ce
comportement avec l'option default="x"
où x vaut, généralement,
"no", "cancel"
Pour afficher un message sur plusieurs lignes, il faut utiliser la séquence /n
et évitez d'utiliser plus de 55 caractères par ligne.
# messagebox n'affiche des lignes que de 69 caractères #msg = "01234567890123456789012345678901234567890123456789012" # = 53 chiffres #msg = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" # = 36 lettres m messagebox.showwarning("Fin de ligne", "Une ligne par chemin.\nLa dernière ligne est vide.")
Documentation sur messagebox - (Tuto, trop de pub !)
Choisir une couleur
import tkinter # -------------------------- from tkinter import colorchooser # ---------- # utilisation d'une boîte de dialogue ----------------------------------------- fP = tkinter.Tk () # création de la fenêtre principale fP.withdraw() # Ne pas l'afficher couleur = colorchooser.askcolor() fP.destroy() # destruction de la fenêtre (= fermeture du GUI) # fin de l'utilisation d'une boîte de dialogue -------------------------------- # reste du code du programme print(couleur) # sous forme d'un tuple # ((214, 104, 217), '#d668d9') ou (None, None) print(couleur[1]) # affiche : #d668d9
Sans fenêtre principale
La méthode .withdraw()
permet de ne pas afficher la fenêtre
(principale). L'utilisation de cette méthode est souvent utilisée lorsqu'on veut utiliser
des fonctionnalités de Tkinter (telles que les boîtes de dialogue), sans avoir besoin
d'une croix de fermeture.
from tkinter import * from tkinter import simpledialog from tkinter import messagebox fP = Tk() fP.withdraw() def labelWidth(label, taille = 100): #------------------------------------------ """ La fonction retourne une string ayant la taille minimale voulue, (utile à une largeur correcte de la boîte de dialogue). :param str label: un entier (tel qu'un nombre ou une taille de fichiers) :param int taille: nombre de caractères de la string formatée :return: Retourne une string ayant la taille minimale voulue :rtype: str 2022-10-24 04:42:04 """ nb = len(label); diff = taille-nb if diff>0 : return label+" "*diff return label label=labelWidth("Entrez un nombre entier [0-100] :") while True: int_a = simpledialog.askinteger("Valeur de a",label, minvalue=0, maxvalue=100, initialvalue=7) print(int_a) if not messagebox.askyesno("Continuer ?", "Souhaitez-vous faire un autre test ?") : break fP.destroy() fP.mainloop()
Si on veut aller plus loin qu'utiliser des boîtes de dialogue et afficher des messages dans le Terminal ...
Fenêtre principale
Un logiciel dispose toujours d'une fenêtre principale. Souvent, elle dispose d'un menu et permet l'affichage d'autres fenêtres.
import tkinter # import de tkinter fP = tkinter.Tk() # création de la fenêtre principale (fP) fP.mainloop() # écouter toute action sur la fenêtre
L'application de la méthode .Tk()
sur tkinter
crée un objet qui
est attribué à une variable (et qui ainsi dispose d'un nom (ici, fP). Cet objet est une
fenêtre qui dispose des trois buttons classiques (réduction, ajustement, fermeture).
L'application de la méthode .mainloop()
crée une boucle (infinie) d'écoute
de tous les événements qui pourraient se produire sur cette fenêtre, tel qu'un clic sur
la croix de fermeture.
On constate que, par défaut, la fenêtre dispose d'une surface, d'une couleur de fond, d'un titre (tk) et qu'on peut utiliser les trois buttons classiques.
On peut modifier l'apparence de cette fenêtre, via différentes méthodes : .title(), .geometry(), .config()
from tkinter import * fP = Tk() fP.title("Fenêtre principale") # titre pour pouvoir glisser la fenêtre fP.geometry("400x100") # largeur suffisante pour afficher le titre ... fP.config(bg='#5FB691') fP.mainloop()
Vous pouvez déplacer une fenêtre en cliquant sur le titre de la fenêtre, puis en maintenant le bouton gauche de la souris enfoncé la déplacer pour déplacer la fenêtre. Il est donc recommandé de donner un titre à la fenêtre.
Cependant pour que le titre s'affiche, la fenêtre doit être suffisamment large. Il est donc recommandé d'indiquer une largeur suffisante. 160 est la largeur minimale pour disposer des trois boutons : _ □ x. Si le titre de la fenêtre n'est pas fourni => titre = tk => largeur minimale = 180.
Outre la dimension de la fenêtre, il est possible de la positionner via un code tel
que
fP.geometry('400x100 + 10 + 100')
où 10 est la position sur
l'axe des X et 100 celle sur l'axe des Y.
Il ne reste plus qu'à remplir cette fenêtre de widgets ...
Widgets
Pour créer une interface, nous disposons de widgets.
Chaque widget est une class.
Une interface est quelque chose entre deux choses. Ici, entre un
utilisateur et de la logique.
- Tkinter utilisent :
Button, Checkbutton, Entry, Frame, Label, LabelFrame, Listbox, Menubutton, PanedWindow, Radiobutton, Scale, Scrollbar, Spinbox, Text
(= 14 natifs) - Ttk comes with 18 widgets, twelve of which already existed in tkinter: Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton, PanedWindow, Radiobutton, Scale, Scrollbar, and Spinbox. The other six are new: Combobox, Notebook, Progressbar, Separator, Sizegrip and Treeview. And all them are subclasses of Widget.
Les widgets Listbox et Text ne sont pas visés par Ttk.
Par ordre de fréquence d'utilisation :
Button
Le bouton est l'élément (le widget) le plus utile. Un clic sur un bouton déclenche un événement qui est traité par une fonction.
Le clic n'est pas le seul événement pouvant se produire. Pour plus d'info.
Syntaxe (tk) : btn = Button(parent, options)
-
parent. Le nom de la variable-objet qui contiendra ce bouton, par exemple fP.
(Donc, la variable-objet fP doit être créé avant le bouton) -
options. Elles permettent d'indiquer le texte sur le bouton et le nom de la fonction qui sera appelée lors du clic. Elles permettent aussi de décrire l'apparence du bouton.
from tkinter import * fP = Tk() # crée une fenêtre bouton = Button(fP,text = "Bouton") # crée un bouton et le lie à la fenêtre bouton.pack() # place le bouton sur la fenêtre (selon la méthode .pack()) fP.mainloop()
Rappel. Le nom d'une variable ne doit pas contenir de caractères spéciaux (tel que "ê").
Il faut définir une fonction pour que le bouton serve à quelque chose.
from tkinter import * # importe toutes les 'class' from tkinter import messagebox # importe le module concerné def afficher(): print("Bouton cliqué") # pour afficher quelque chose en console messagebox.showinfo("Titre", "Bouton cliqué !") fP = Tk() bouton = Button(fP, text="Bouton", command=afficher) bouton.pack() fP.mainloop()
Options communes
La plupart des options sont communes aux widgets. Toutes ces options ont une valeur par défaut. Ce qui évite de devoir les donner pour construire un objet utilisable.
Le nom de toutes les options sont en minuscules.
Certaines options peuvent être spécifiques à un widget.
Tel que l'option command, dont la valeur est le nom d'une fonction (non encadré de
guillemets)
Un bouton dispose de 19 options facultatives.
- activebackground : la couleur de fond lors du clic
- activeforeground : la couleur du texte lors du clic
- bd : la largeur de la bordure en pixels
- bg : la couleur de fond
-
command : le nom (non encadré) de la fonction appelée lors du
clic.
Spécifique aux objets Button. - fg : la couleur du texte
- font : le nom de la variable-objet préalablement créé, représentant une font (police, taille, style)
- height : le nombre de lignes (pour du texte) ou le nombre de pixels (pour une image)
- highlightcolor : la couleur du texte lorsque le bouton a le focus
- image : le chemin du fichier-image
- justify : alignement du texte multi-lignes :
left
,center
,right
oufill
- padx : ajout d'un padding horizontal
- pady : ajout d'un padding vertical
- relief : type de bordure :
sunken
,raised
,flat
,groove
ouridge
. - state : état du bouton :
disabled
ouactive
(par défaut) - text : texte affiché sur le bouton
- underline : soulignement du texte
- width : nombre de lettres (pour un texte), de pixels (pour une image)
- wraplength : nombre (s'il est positif) de lettres minimum, avant un retour à la ligne
Le nom des couleurs est encadré. Par exemple : "red", "#5FB691"
from tkinter import * # importe toutes les 'class' from tkinter import messagebox # importe le module concerné import tkinter.font as font def afficher(): messagebox.showinfo("Mon titre", "Mon message : Bouton cliqué !") fP = Tk(className="Test") # à déclarer après la création de la fenêtre myFont = font.Font(family='Lucida Calligraphy', size=20, weight='bold') bouton = Button(fP, text="Bouton", command=afficher, bg="#5FB691", bd=5, font=myFont, relief=RIDGE, padx=30) bouton.pack() fP.mainloop()
.config()
Les options sont données lors de la construction de l'objet. Toutefois, la méthode
.config()
permet de modifier l'objet après sa construction.
rom tkinter import * fP = Tk() def modifier(): btn.config(text = "Texte de ce bouton modifié") btn = Button(text = "Modifier ce bouton",command = modifier) btn.pack() fP.mainloop()
Exemples de bouton (pour ttk)
Label
L'étiquette est généralement utilisée pour indiquer la nature du contenu du champ qui le suit.
Syntaxe (tk) : lbl = Label(parent, options)
-
parent. Le nom de la variable-objet qui contiendra cette étiquette.
Obligatoire que s'il existe plusieurs conteneurs. -
options. Elles permettent d'indiquer le texte. Elles permettent aussi de décrire l'apparence du bouton.
from tkinter import * # importe toutes les 'class' fP = Tk() lbl = Label(text = "Étiquette") lbl.pack() fP.mainloop()
Hyper lien
Une étiquette peut être transformée en lien
# Source : https://www.tutorialspoint.com/how-to-create-a-hyperlink-with-a-label-in-tkinter #Import the required libraries from tkinter import *; import webbrowser #Create an instance of tkinter frame win = Tk() #Define a callback function def callback(url): webbrowser.open_new_tab(url) #Create a Label to display the link link = Label(win, text="www.tutorialspoint.com", fg="blue", cursor="hand2") link.pack() link.bind("<Button-1>", lambda e: callback("http://www.tutorialspoint.com")) win.mainloop()
Entry
Le champ est utilisé pour que l'utilisateur puisse introduire des données sur une ligne.
Syntaxe (tk) : txt = Entry(parent, options)
from tkinter import * # importe toutes les 'class' fP = Tk() lbl = Label(text = "Nom") txt = Entry() def afficher(): print(txt.get()) btn = Button(text="Afficher le nom dans la console",command=afficher) lbl.pack() txt.pack() btn.pack() fP.mainloop()
Pour ajouter du texte au début : txt.insert(0, "texte")
Text
Ce widget est utilisé pour que l'utilisateur puisse introduire des données sur plusieurs lignes.
from tkinter import * # importe toutes les 'class' fP = Tk() lbl = Label(text = "Description") txt = Text(font=("Times New Roman", 14, "italic")) def afficher(): print(txt.get("0.0","end")) btn = Button(text="Afficher la description dans la console",command=afficher) lbl.pack() txt.pack() btn.pack() fP.mainloop()
Une position est une string au format deux nombres entiers positifs séparés par un point tel que "numLigne.numCarac" où numLigne est un numéro de ligne et numCarac, la position sur cette ligne. La position "end" indique la fin du texte.
La méthode .get("position1","position2")
permet de récupérer les caractères
situés entre deux positions.
La dernière ligne est blanche (lors d'une impression).
La méthode .insert("position","texte")
permet d'ajouter la valeur du second
paramètre à la position indiquée par le premier paramètre.
La méthode .delete("position1","position2")
permet de supprimer les
caractères situés entre deux positions.
Effacer le contenu : .delete("start","end")
L'option commune height
permet d'indiquer la hauteur du widget en
nombre de lignes.
Checkbutton
Chaque option dispose de sa variable réceptrice (du choix de l'utilisateur)
import tkinter from tkinter import messagebox fP = tkinter.Tk () fP.title("Tests : Checkbutton") fP.geometry("350x150") int_chkFR = tkinter.IntVar() chk_FR = tkinter.Checkbutton(variable = int_chkFR) chk_FR.pack() int_chkUS = tkinter.IntVar(value = 1) # Pour pré-sélectionner chk_US = tkinter.Checkbutton(fP, variable = int_chkUS) chk_US.pack() int_chkNL = tkinter.IntVar() chk_NL = tkinter.Checkbutton(fP, variable = int_chkNL, text = "Néerlandais") chk_NL.pack() int_chkXX = tkinter.IntVar() chk_XX = tkinter.Checkbutton(fP, variable = int_chkXX, text="XX", state = tkinter.DISABLED) chk_XX.pack() def afficher(): msg=( "Français : "+str(int_chkFR.get())+"\n" +"Anglais : "+str(int_chkUS.get())+"\n" +"Néerlandais : "+str(int_chkNL.get())+"\n" +"XX : "+str(int_chkXX.get()) ) messagebox.showinfo("Vos choix",msg) btn = tkinter.Button(text="Afficher vos choix", command=afficher) btn.pack() fP.mainloop ()
La variable qui reçoit le choix peut être du type : IntVar, StringVar ou BooleanVar
Plus d'info : pythonguides.com
Radiobutton
Une seule variable (de type IntVar, StringVar ou BooleanVar), pour toutes les options.
import tkinter from tkinter import messagebox fP = tkinter.Tk () fP.title("Tests : Radiobutton") fP.geometry("350x150") str_opt = tkinter.StringVar(value = "US") # Pour éviter que toutes les options soient sélectionnés # => en pré-sélectionner une => en donnant une des valeurs possibles. opt_FR = tkinter.Radiobutton (variable = str_opt, value = "FR") opt_FR.pack() opt_US = tkinter.Radiobutton (fP, variable = str_opt, value = "US") opt_US.pack() opt_NL = tkinter.Radiobutton (fP, variable = str_opt, value = "NL", text="Néerlandais") opt_NL.pack() opt_XX = tkinter.Radiobutton (fP, variable = str_opt, value = "XX", text="XX", state = tkinter.DISABLED) opt_XX.pack() def afficher(): messagebox.showinfo("Votre choix",str_opt.get()) btn = tkinter.Button(text="Afficher votre choix", command=afficher) btn.pack() fP.mainloop ()
Frame
Les Frame
sont des conteneurs d'autres widgets. Ils facilitent la
présentation des widgets dans une fenêtre.
Un relief peut leur être appliqué :
# Source : https://realpython.com/python-gui-tkinter/ import tkinter as tk border_effects = { "flat": tk.FLAT, "sunken": tk.SUNKEN, "raised": tk.RAISED, "groove": tk.GROOVE, "ridge": tk.RIDGE, } fP = tk.Tk() for relief_name, relief in border_effects.items(): frame = tk.Frame(master=fP, relief=relief, borderwidth=5) frame.pack(side=tk.LEFT) label = tk.Label(master=frame, text=relief_name) label.pack() fP.mainloop()
Rappel : Ce relief peut être appliqué à la plupart des widgets.
Un cadre peut recevoir une étiquette.
LabelFrame
# https://www.tutorialspoint.com/python/tk_labelframe.htm from tkinter import * fP = Tk() fP.title("Test : LabelFrame") fP.geometry("350x150") labelframe = LabelFrame(fP, text="Étiquette du cadre") labelframe.pack(fill = "both", expand = "yes") left = Label(labelframe, text = "À l'intérieur du cadre") left.pack() fP.mainloop()
Listbox
Par défaut, une liste affiche 10 lignes.
La hauteur de la liste, exprimée en lignes, peut être modifié, via
.config(height=11)
# https://pythonprogramming.altervista.org/tkinter-15-add-a-scrollbar-to-tkinter/ from tkinter import * fP = Tk(); fP.title("Test : Listbox"); fP.geometry("300x250") listbox = Listbox(fP); listbox.pack() for i in range(100): listbox.insert(END, i) def selected_item(): for i in listbox.curselection(): print(listbox.get(i)) btn = Button(fP, text = 'Print Selected', command = selected_item) btn.pack(side = 'bottom') fP.mainloop()
Scrollbar
# https://pythonprogramming.altervista.org/tkinter-15-add-a-scrollbar-to-tkinter/ from tkinter import * fP = Tk() fP.title("Test : Listbox + Scrollbar") fP.geometry("300x250") # Créer et placer la Scrollbar dans le conteneur scrollbar = Scrollbar(fP) scrollbar.pack(side = RIGHT, fill = Y) # Créer et placer la Listbox dans le conteneur listbox = Listbox(fP) listbox.pack() # Remplir la Listbox for i in range(100): listbox.insert(END, i) # attach listbox to scrollbar listbox.config(yscrollcommand = scrollbar.set) scrollbar.config(command = listbox.yview) #https://www.geeksforgeeks.org/how-to-get-selected-value-from-listbox-in-tkinter/ # Function for printing the selected listbox value(s) def selected_item(): # Traverse the tuple returned by curselection method and print # corresponding value(s) in the listbox for i in listbox.curselection(): print(listbox.get(i)) # Create a button widget and map the command parameter to # selected_item function btn = Button(fP, text = 'Print Selected', command = selected_item) # Placing the button btn.pack(side='bottom') # Lancer l'écoute des événements fP.mainloop()
Focus
Plusieurs objets (widgets) peuvent être placés dans un conteneur (tel qu'une fenêtre), mais un seul écoute les actions de l'utilisateur (via le clavier ou la souris). On dit que ce widget a le focus.
Le widget qui a dispose du focus (qui est visé par les actions de
l'utilisateur) présente une apparence différente. Le bouton est encadré de pointillé, le
champ de saisie dispose du signe indiquant l'attente d'une frappe au clavier,
|
. Sont aussi concernés les Checkbutton,
Radiobutton, ...
L'utilisateur peut taper sur la touche de tabulation (TAB) pour donner le focus au widget suivant (dans l'ordre de création dans le code).
La méthode .focus_set
appliquée sur la variable représentant un widget
permet au développeur de placer initialement le focus.
from tkinter import * fP = Tk() fP.title("Test : .focus_set()") fP.geometry("350x100") lbl = Label(text = "Nom") txt_nom = Entry() def afficher(): nom=txt_nom.get() print(nom) btn = Button(text="Afficher le nom dans la console",command=afficher) lbl.pack() txt_nom.pack() btn.pack() txt_nom.focus_set() fP.mainloop()
Emplacement des widgets dans un conteneur
Un conteneur peut être une fenêtre ou une sous-fenêtre (Frame
).
.pack()
Cette méthode permet d'ajouter (dans le conteneur) le widget sur lequel elle est appliquée (et donc de le rendre visible)
.pack_forget()
Cette méthode permet de retirer le widget sur lequel elle est appliquée (et donc de le rendre invisible)
.grid()
Toplevel
from tkinter import * fP = Tk() # fP pour Fenêtre principale fP.title("Fenêtre principale") fP.geometry("400x100") def ouvrir_F2(): F2 = Toplevel(fP) # F2 pour Fenêtre 2 F2.title("Fenêtre 2") F2.geometry("300x50") fP.withdraw() # efface de l'écran la fP (=> F2 devient modale) def fermer_F2(): F2.destroy() fP.deiconify() # ré-affiche la fP (à ne pas oublier !) btn_F2=Button(F2, text = "Fermer cette fenêtre", command = fermer_F2).pack() F2.mainloop() btn_fP=Button(text = "Ouvrir une seconde fenêtre", command = ouvrir_F2).pack() fP.mainloop()
Mes boîtes de dialogue
Nativement, Python vous offre que 3 boîtes de dialogue : askinterger, askfloat et askstring.
Mais, vous pouvez créer vos boîtes de dialogue : asktext, asklistbox, asklistboxID, ...
asktext
asktext.py
from tkinter import * win=Tk(); win.title("asktext"); win.geometry("300x100"); gRE = "" class MyDialog_asktext: #------------------------------------------------------- # inspiré par manejandodatos.es/2014/10/modal-forms-tkinter-python/ # Modif = Utilise la variable globale, gRE, pour recueillir la valeur fournie def __init__(self, parent, title, labeltext, initialvalue,withBtnOK): self.top = Toplevel(parent); self.top.transient(parent); self.top.grab_set() self.top.title(title); Label(self.top, text=labeltext).pack() self.e = Text(self.top); self.e.insert("0.0",initialvalue) self.e.bind("<Escape>", self.cancel) # 'Escape' vaut un clic sur "Annuler" self.e.pack(padx=15); frame = Frame(self.top) btnOK = Button(frame,text="Valider", command=self.ok) btnCancel = Button(frame,text="Annuler", command=self.cancel) if withBtnOK : btnOK.grid(row=0, column=1); self.e.focus_set(); else: self.e.config(state=DISABLED,bg="#e6e6e6"); btnCancel.focus_set() btnCancel.grid(row=0, column=2); frame.pack() def ok(self, event=None): # ajout automatique d'une ligne vide global gRE; gRE = self.e.get("0.0","end"); self.top.destroy() def cancel(self, event=None): global gRE; gRE=None; self.top.destroy() def asktext(parent, titre="", etiquette="", initialvalue="", withBtnOK=True): #- """ Si withBtnOK=False, la modale ressemble à une messagebox.showinfo() améliorée car plus large et permettant le défilement vertical """ global gRE; gRE=None; d=MyDialog_asktext(parent,titre,etiquette,initialvalue, withBtnOK) parent.wait_window(d.top) # lance la popup (et attend la réponse du user) return gRE def afficher(): #--------------------------------------------------------------- print("=",asktext(win)) Button(win, text="Afficher le texte dans la console", command=afficher).pack() win.mainloop()
Le code de l'application déclare la variable globale : gRE
.
La class MyDialog_asktext
est recopiée tel quel dans le code.
La fonction asktext()
retourne le texte (avec les retours de
ligne) ou 'None'.
asklistbox
from tkinter import * win=Tk(); win.title("listbox"); win.geometry("300x100") re="vide"; liste=["33 : aaa","1 : bb", "9 : cccc"] def choice(option): global re if option: for i in listBox.curselection(): re=listBox.get(i) else: re=None pop.destroy() def createModal(liste): global pop, listBox; pop=Toplevel(win); listBox=Listbox(pop) pop.title("Liste :"); pop.geometry("250x200") for x in liste : listBox.insert(END, x) listBox.pack() frame = Frame(pop) btnOK = Button(frame,text="Valider", command=lambda: choice(True)) btnOK.grid(row=0, column=1) btnCancel = Button(frame,text="Annuler", command=lambda: choice(False)) btnCancel.grid(row=0, column=2) frame.pack() label=Label(text=""); label.pack() Button(win, text="Choisir dans une liste", command=lambda: createModal(liste)).pack() def afficher(): if re is not None : print(re) Button(win, text="Afficher le choix dans la console", command=afficher).pack() win.mainloop()
asklistboxID_multi
Le résultat d'une requête SQL de sélection (SELECT id, ... ORDER BY id) est 'None', une liste (list) de tuples ou un tuple. Ici, la première valeur d'un tuple doit être l'ID.
asklistboxID_multi.py
from tkinter import * win=Tk(); win.title("asklistboxID_Multiple"); win.geometry("300x100") gListTup = None liste=[(3, 'moi', 18), (7, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 19), (8, 'x', 20)] # from .fetchall class MyDialog_asklistboxID_multi: #------------------------------------------- """ inspiré par manejandodatos.es/2014/10/modal-forms-tkinter-python/ Modifie la variable globale, gListTup, pour les éléments sélectionnés (issus d'une requête SQL retournant sous la forme : ID, nom, ...) """ def __init__(self, parent, liste, title, labeltext, id_byDefault, isMultiple ): self.top = Toplevel(parent); self.top.transient(parent); self.top.grab_set() self.top.title(title); Label(self.top, text=labeltext).pack(); i=-1; maxi=0 self.e = Listbox(self.top, activestyle="none") if isMultiple : self.e.config(selectmode="multiple") self.e.pack(padx=15); self.e.focus_set() for x in liste : if len(str(x))>maxi : maxi=len(str(x)) i+=1; self.e.insert(END, x) # x est un tuple if str(x[0])==str(id_byDefault) : self.e.select_set(i) Button(self.top,text="Valider", command=self.ok).pack() self.e.config(width=maxi) #;print(str(maxi)) def ok(self, event=None): global gListTup; gListTup = None if len(self.e.curselection())!=0 : gListTup=[] for i in self.e.curselection(): gListTup.append(self.e.get(i)) self.top.destroy() def cancel(self, event=None): global gListTup; gListTup = None; self.top.destroy() def asklistboxID_multi(parent, liste, titre = "Liste", etiquette = "IDs :", id_byDefault = "", isMultiple = False ): #-------------------- """ return : None ou une liste de Tuples """ global gListTup; gListTup=None if len(liste)>0 : d = MyDialog_asklistboxID_multi(parent, liste, titre, etiquette, id_byDefault, isMultiple) parent.wait_window(d.top) # lance la popup (et attend la réponse du user) return gListTup def afficher(): #--------------------------------------------------------------- y = asklistboxID_multi(win, liste, "", "", "7", True) if y is not None : for x in y: print(str(x[0]), str(x[1])) Button(win, text="Afficher les ID choisis dans la console", command=afficher).pack() win.mainloop()
Le code de l'application déclare la variable globale : gListTup
.
La class MyDialog_asklistboxID_multi
est recopiée tel quel dans le
code.
La fonction asklistboxID_multi()
retourne une liste de tuples
ou 'None', chaque tuple correspond à un ligne de la listbox présentant les données
(issues d'une DB) sous la forme : ID, nom, ...
Menu
Les boîtes de dialogue (simples) sont des fenêtres sans menu.
Une fenêtre peut contenir un menu.
# À Â Ç É È Ê Ë Î Ï Ô Ù Û Ü Ÿ Æ Œ æ œ from tkinter import * fP = Tk(); m = Menu() mFichier = Menu() mAide = Menu() m.add_cascade(label = "Fichier", menu = mFichier) m.add_cascade (label = "Aide", menu = mAide) def menu1(): # à définir avant son utilisation dans le reste du code pass mFichierNouveau = Menu() mFichierOuvrir = Menu() mFichier.add_command (label = "Nouveau", command = menu1) mFichier.add_command (label = "Ouvrir ...", command = menu1) mFichierMenu = Menu() mFichier.add_cascade (label = "Menu", menu = mFichierMenu) mFichier.add_command (label = "Quitter", command = fP.destroy) mAide.add_command (label = "À propos de", command = menu1) mFichierMenu.add_command (label = "Sous-Menu", command = menu1) fP.config(menu = m) fP.mainloop()
Les options de Menu() :
-
activebackground
Couleur de fond au survol de la souris. -
activeforeground
Couleur du texte au survol de la souris. -
activeborderwidth
Largeur de la bordure autour de l'élément choisi. La valeur par défaut est 1 pixel. -
bd
Largeur de bordure du menu en pixels. La valeur par défaut est 2. -
bg
Couleur de fond. -
fg
Couleur du texte. -
font
Police du texte. -
image
Image à afficher sur le menu. -
postcommand
Procédure appelée chaque fois que quelqu'un affiche le menu. -
relief
Type de bordure. Les valeurs sont : SUNKEN, RAISED, GROOVE et RIDGE. -
selectcolor
Couleur de fond lorsqu'un élément est sélectionné.
Les méthodes de Menu() :
-
.add_command(options)
Ajoute un élément au menu.Option "command = nom_d_une_fonction" (et non appel d'une fonction ...)
=> créer des fonctions sans paramètre qui appellent une fonction avec le paramètre souhaitédef insert_fiches(): pass def insert_fiches1(): insert_fiches(True) #------------------------------------- def insert_fiches2(): insert_fiches(False) #------------------------------------ def affichMenus(): mAjouter.add_command (label = "Ajouter une fiche : lowID", command = insert_fiches1) mAjouter.add_command (label = "Ajouter une fiche : myID", command = insert_fiches2)
-
.add_cascade(options)
Crée un nouveau menu hiérarchique en associant un menu donné à un menu parent -
.add_radiobutton(options)
Crée un bouton radio en tant que élément de menu. -
.add_checkbutton(options)
Crée un bouton checkbox en tant que élément de menu. -
.add_separator()
Ajoute une ligne de séparation au menu. -
add(type, options)
Ajoute un type spécifique d'élément au menu. -
delete(startindex [, endindex ])
Supprime les éléments de menu allant de startindex à endindex. -
entryconfig(index, options)
Vous permet de modifier un élément de menu, identifié par l'index, et de changer ses options. -
index(item)
Renvoie le numéro d'index de l'étiquette de l'élément du menu donnée. -
insert_separator(index)
Insérez un nouveau séparateur à la position spécifiée par index.
Les options de Menu() :
- postcommand = permet d'appeler une fonction à chaque clic sur le menu
- ...
.protocol()
WM_DELETE_WINDOW
fP.protocol("WM_DELETE_WINDOW", quitter)
Cette méthode appliquée sur l'objet représentant l'interface Tkinter permet d'appeler une fonction lors du clic sur la croix de fermeture. Ici, quitter est le nom de la fonction appelée lorsque l'utilisateur clique sur le menu Fichier > Quitter.
Presse papier
La méthode .clipboard_clear()
appliqué sur l'objet créé par le
constructeur Tk() permet de vider le contenu textuel du presse-papier.
La méthode .clipboard_append()
appliqué sur l'objet créé par le
constructeur Tk() permet d'ajouter du contenu textuel au presse-papier.
from tkinter import * fP = Tk() texte="Thibaut" fP.clipboard_clear() fP.clipboard_append(texte)
La méthode .clipboard_get()
appliqué sur l'objet créé par le
constructeur Tk() permet de récupérer le contenu textuel du presse-papier.
from tkinter import * fP = Tk() fP.withdraw() # keep the window from showing print(fP.clipboard_get())