Vous pouvez me contacter via Facebook pour questions & suggestions :
Page Facebook relative à mon site
Dans ce chapitre je vais surtout évoquer le sous module tkinter.ttk.
Vous allez aussi vous familiariser avec la programmation évènementielle dans python-tkinter.
Ne croyez pas que ce type de widget est réservé à la saisie d' entiers.
Par défaut le pas est de +1 dans l'intervalle fixé, mais avec l'option résolution on peut imaginer
un pas qui soit un nombre décimal.
Thème : interface pour saisir la température voire autres paramètres vitaux (le pouls, la tension artérielle).
def f1():
celsius = float(scale.get())
farhen = (celsius * 9 / 5) + 32.2
farhen = round(farhen,2)
message = f"Votre température est {celsius} °C ou {farhen} °F"
msg.showinfo("Bilan de santé : ", message)
# nom programme : tkinter_scale.p y
# objet : présentation de widgets moins connus
import tkinter as tk
import tkinter.messagebox as msg
# Création d'un objet "fenêtre"
fenetre = tk.Tk()
fenetre.title("Suivi de température")
fenetre.geometry("400x400")
fenetre.config(bg ="ivory")
fonte1 = "{courier new} 14"
#scale
tk.Label(fenetre, text = "Votre poids ? ", font = fonte1).pack(padx =0, pady=10)
scale = tk.Scale(fenetre, from_=35, to=40, resolution=0.1, orient='horizontal',font =fonte1)
scale.pack(padx =0, pady=10)
tk.Button(fenetre, text ="Validez", command =f1, font =fonte1).pack(padx =0, pady=10)
fenetre.mainloop()
Remarquez la couleur de fond : ivoire.
On peut imaginer avec ce type de widget une liste à choix unique ou à choix multiple.
Programmation évènementielle : à chaque changement de sélection dans la liste, j'appelle une fonction qui affiche la ligne sélectionnée.
# ----------fonctions--------------
def fonction(event):
indice = liste.curselection()
item = liste.get(indice)
print(item)
#-------------fin fonctions-----------
# tkinter_listbox.py
# liste à choix unique
import tkinter as tk
fen = tk.Tk()
fen.title("Liste à choix unique")
fen.geometry("400x400")
fen.config(bg ="skyblue")
# une zone de liste à choix unique
tk.Label(fen, text = "Votre sport préféré (un seul choix) ? ")
liste = tk.Listbox(fen,selectmode=tk.SINGLE, height=6)
contenu = ["foot", "vélo", "natation", "course", "musculation", "aucun"]
for ele in contenu:
liste.insert(tk.END, ele)
liste.pack(padx=10, pady=10)
liste.bind('<< ListboxSelect >>', fonction)
fen.mainloop()
import tkinter as tk : Le module tkinter aura l'alias tk.
liste1 = tk.Listbox(fen,selectmode=tk.SINGLE, height=6) : une seule sélection est possible.
items = ["foot", "vélo", "natation", "course", "musculaton", "aucun"] : liste correspondant aux futurs items du
wigdet listbox
for item in items: liste1.insert(tk.END, item) : boucle pour bâtir les items de la liste
liste1.bind('<<ListboxSelect>>', fonction) :
la méthode bind(évènement) lie un widget à un évènement.
Ici un changement de sélection dans la liste provoque l'appel d'une fonction.
Notez la syntaxe de l'évènement : le mot clé est encadré par une paire de chevrons.
Je rappelle que la méthode curselection() appliquée à un Listbox retourne non pas la contenu de la lignes sélectionnée mais l'indice de cette ligne.
On peut réaliser via le widget listbox une liste à choix multiple.
Encore une fois en utilisant la méthode bind(évènement) liée au Listbox on fait l'économie d'un bouton de commande.
# ----------fonctions--------------
def fonction(event):
indices = liste.curselection()
items = [liste.get(i) for i in indices]
for item in items :
print(item)
print("------------")
#-------------fin fonctions-----------
# tkinter_listbox2.py
# liste à choix multiple
import tkinter as tk
fen = tk.Tk()
fen.title("Liste à choix multiples")
fen.geometry("400x400")
fen.config(bg ="skyblue")
# liste à choix multiple
tk.Label(fen, text = "Vos jeux de société (plusieurs choix possibles) ? ")
liste = tk.Listbox(fen,selectmode=tk.MULTIPLE, height=5)
contenu = ["tarot", "bridge", "belote", "manille","bataille"]
for ele in contenu:
liste.insert(tk.END, ele)
liste.pack(padx=10, pady=10)
liste.bind(''<<ListboxSelect>>'', fonction)
fen.mainloop()
Nouveautés par rapport au script précédent :
liste = tk.Listbox(fen,selectmode=tk.MULTIPLE, height=5) : possibilité de sélectionner plusieurs lignes grâce à
la valeur de l'option selectmode.
Sinon vous retrouvez l'appel d'une fonction lors du changement de sélection dans la liste grâce à la méthode bind().
ttk (Themed Tkinter) est un sous‑module de Tkinter introduit avec Tk 8.5.
Il constitue un réel progrès par rapport au module historique.
Par ailleurs avec ttk il est possible de définir et appliquer des styles aux widgets.
Donc le recours aux options de style (font, bg, fg; etc.) dans la définition du widget devient inutile et
remplacé par l'option "style". .
On peut créer et appliquer des styles à des widgets à condition que ceux-ci sont de type "ttk".
Attention les noms de styles ne sont pas libres ...
Dans le code ci-dessus les Label & Button sont des widgets ttk alors
que les Entry sont encore des widgets tkinter.
Donc on applique un style à chaque étiquette et bouton alors qu'on utilise encore des options de style pour les zones de texte.
def fonction():
nom =zt1.get()
prenom =zt2.get()
chaine = "Bonjour cher " + prenom.lower() + " " + nom.upper()
zt3.delete(0,tk.END)
zt3.insert(0,chaine)
#------------------
#tkinter_ttk_styles.py
import tkinter as tk
from tkinter import ttk
fen = tk.Tk()
fen.title("Les styles ttk")
fen.geometry("400x400")
#styles
mafonte = "{courier new} 16 bold"
style = ttk.Style()
style.configure("TLabel", foreground="navy", background="skyblue", font=("Arial", 14))
style.configure("TButton", foreground="red", background="lime",font=("Arial", 14))
ttk.Label(fen, text="Saisir votre nom ", style="TLabel").pack(padx=10, pady=10)
zt1= tk.Entry(fen,width=50,font =mafonte)
zt1.pack(padx=10, pady=10)
ttk.Label(fen, text="Saisir votre prénom ", style="TLabel").pack(padx=10, pady=10)
zt2= tk.Entry(fen,width=50,font =mafonte)
zt2.pack(padx=10, pady=10)
ttk.Button(fen, text="Validez", style ='TButton',command=fonction).pack(padx=10, pady=10)
zt3= tk.Entry(fen,width=50,font=mafonte,bg='skyblue',fg='navy')
zt3.pack(padx=10, pady=10)
fen.mainloop()
Notez que certaines définitions de widgets commencent pas tk (alias de tkinter) et d'autres par ttk.
Donc il y a des contrôles tkinter et des contrôles ttk.
Pour la mise en forme des widgets tkinter, cet exemple est une "piqure de rappel".
Ce widget permet de réaliser des listes déroulantes très facilement.
Le remplissage d'un widget combobox est beaucoup plus simple que pour un widget listbox.
# -----------fonctions-------------
def transfert(event):
# transfert de la sélection vers zone de texte
choix = liste.get()
print(choix)
zt1.delete(0,tk.END)
zt1.insert(0,choix)
#---------------------------
# combobox.py
# liste déroulante
import tkinter as tk
import tkinter.ttk as ttk
fen = tk.Tk()
fen.geometry('300x300')
# légende liste
e1 = tk.Label(fen, text="Sélectionnez une couleur dans la liste déroulante")
e1.pack()
# contenu de la liste
contenu = ["aucune","bleu", "vert", "rouge", "noir", "orange"]
# construction de la liste
liste = ttk.Combobox(fen, values=contenu)
liste.current(0) # option par défaut
liste.pack(padx=10, pady=10)
# associer le widget à un évènement
liste.bind("<<ComboboxSelected>>",transfert)
tk.Label(fen, text ="Couleur sélectionnée").pack()
zt1 = tk.Entry(fen, width=50)
zt1.pack(padx=10, pady=10)
fen.mainloop()
import tkinter.ttk as ttk : utilisez toujours cette syntaxe pour importer ttk (sous module de tkinter).
contenu = ["aucune","bleu", "vert", "rouge", "noir", "orange"] : futures lignes du "combobox"
liste = ttk.Combobox(fen, values=contenu) : création d'un widget "combobox". Notez l'argument "values = contenu".
Donc point besoin d'une boucle pour remplir chaque ligne de la liste.
liste.current(0) : la ligne d'indice 0 est affichée par défaut.
liste.bind("<<ComboboxSelected>>",transfert) : sur changement de sélection dans la liste déroulante : appel de la
fonction nommée "transfert".
Notez la syntaxe de l'évènement avec les doubles chevrons : "<<ComboboxSelected>>"
tk.END est une constante fournie par le module tkinter qui indique la position "fin" du contenu du widget.
Rappel HTML : dans un élément SELECT (liste de formulaire HTML) on fait bien la différence entre la "value" et la légende d'une ligne.
Exemple :
Pour la première ligne on lit dans le formulaire "Moins de 20 ans" mais si on clique dessus on saisit "t1" (la "value").
Peut-on faire la même chose en python-tkinter ? Oui, grâce au widget treeview.
def transfert(event):
id_selection= liste.focus()
ligne = liste.item(id_selection, "values")
zt.delete(0,tk.END)
zt.insert(0,ligne)
#-----------fin fonction--------
# tkinter_treeview.py
# création d'une liste à deux colonnes
import tkinter as tk
import tkinter.ttk as ttk
fen = tk.Tk()
fen.title("Liste à deux colonnes")
# Définition de la liste à deux colonnes
liste = ttk.Treeview(fen, columns=("col1", "col2"), show="headings")
liste.heading("col1", text="Code")
liste.heading("col2", text="Tranche d'âge")
liste.column("col1", width=30, anchor="nw")
liste.column("col2", width=100, anchor="nw")
# futur contenu de la liste
donnees = [
("t1", "Moins de 20 ans"),
("t2", "Entre 20 et 40 ans"),
("t3", "entre 40 et 60 ans"),
("t4", "plus de 60 ans")
]
for ligne in donnees:
liste.insert("",tk.END,values=ligne)
liste.pack(padx=10, pady=10)
# Lier l'événement de sélection
liste.bind("<<TreeviewSelect>>", transfert)
tk.Label(fen,text ="Contenu de la ligne sélectionnée").pack(padx=10, pady=10)
zt =tk.Entry(fen, width =50)
zt.pack(padx=10, pady=10)
fen.mainloop()
Le shell n'est plus utilisé !
Aucune saisie ou affichage dans le shell !
Et si on ne veut afficher que les légendes des lignes (les codes étant masquées).
...
liste.column("col2", width=100, anchor="nw")
# afficher seulement 'col2'
liste["displaycolumns"] = ("col2",)
# futur contenu de la liste
...
Instruction nouvelle insérée : liste["displaycolumns"] = ("col2",)
Cette instruction n'affiche que la colonne nommée "col2".
id_selection= liste.focus()
ligne = liste.item(id_selection, "values")
code_saisi = ligne[0]
zt.delete(0,tk.END)
zt.insert(0,code_saisi)
code_saisi = ligne[0] : je récupère que le "code tranche" (1er élément du tuple retourné).
Il faudrait modifier le titre de la fenêtre : liste à deux colonnes dont première masquée"
Je veux afficher dans une fenêtre tkinter, le résultat d'une requête sélection paramètrée ; requête portant sur la table "especes" de la base "animaux.db".
Téléchargez la base 'animaux.db'
Donc la colonne "code" est le champ de jointure entre les deux tables.
Cette base contient aussi des vues (requêtes sélection complexes sauvegardées).
#nom programme : tkinter_lire_especes.py
import sqlite3
import tkinter as tk
import tkinter.ttk as ttk
connexion = sqlite3.connect('animaux.db')
curseur = connexion.cursor()
curseur.execute("SELECT code FROM taxons order by code;")
lignes = curseur.fetchall()
print("-------------Aide-mémo (les codes des différents taxons)-------------")
for ligne in lignes:
print(ligne[0],end='-')
print("---------fin--------------")
# requête paramétrée
choix = input("saisir un des codes ci-dessus : ")
curseur.execute("select id, nom_espece, code FROM especes WHERE code =?",(choix,))
lignes = curseur.fetchall()
for ligne in lignes :
print(ligne[0], ligne[1],ligne[2])
# ---------- 2. Création de la fenêtre nommée 'fen' ----------
fen = tk.Tk()
fen.geometry("400x400")
# ---------- 3. Création du treeview nommé 'tableau'----------
tableau = ttk.Treeview(fen, columns=(1,2,3), show="headings", height=30)
tableau.heading(1, text="ID")
tableau.heading(2, text="Nom espèce")
tableau.heading(3, text="Code taxon")
tableau.column(1, width=50, anchor="nw")
tableau.column(2, width=150, anchor="nw")
tableau.column(3, width=50, anchor="center")
tableau.pack(padx=10, pady=10)
# ---------- 4. Insertion des données dans le treview ----------
for ligne in lignes:
tableau.insert('', tk.END, values=ligne)
fen.mainloop()
Les affichages dans le shell sont provisoires ; ils disparaitront dans la version améliorée.
Je saisis "deca" comme "décapodes", taxon qui comprend les grands crustacés (crabes, crevettes, etc.)
-------------Aide-mémo (les codes des différents taxons)------------- acti-agna-amph-anne-asci-aste-biva-cart-ceph-cebta-chit-cirr- crin-cten-deca-echi-epon-gast-holo-hydr-medu-ophi-osse-pinn- plat-sire ----------fin-------------- Saisir un des codes ci-dessus : deca 1 homard deca 4 crevette grise deca 5 bouquet deca 6 étrille deca 7 tourteau deca 8 araignée de mer deca 30 langouste deca 31 cigale de mer deca 32 langoustine deca 35 pinothère deca 37 crabe chinois deca 66 Crabe vert deca 67 crabe japonais deca 68 Etrille deca 69 tourteau deca 70 Crabe marbré deca 73 bouquet deca
La liste des espèces pour le taxon sélectionnée apparait provisoirement dans le shell.
Le treeview affiche la liste des espèces pour le taxon sélectionné.
Objectif : ne plus utiliser le shell ni en entrées, ni en sorties
À chaque fois le contenu du treeview est actualisé.
Il faut remplacer la saisie dans le shell(input) par une sélection dans une liste déroulante de la fenêtre tkinter.
#-----------fonctions------------
def actualise(event):
choix = liste.get()
curseur.execute("select id, nom_espece, code FROM especes WHERE code =?",(choix,))
# vidage treeview
for item in tableau.get_children():
tableau.delete(item)
# nouveau contenu treeview
lignes = curseur.fetchall()
for ligne in lignes:
tableau.insert('', tk.END, values=ligne)
#--------------programme principal---------
#nom programme : tkinter_lire_especes2.py
# amélioration du script tkinter_lire_espece.py
import sqlite3
import tkinter as tk
import tkinter.ttk as ttk
connexion = sqlite3.connect('animaux.db')
curseur = connexion.cursor()
choix =""
fen = tk.Tk()
fen.geometry("400x600")
# liste déroulante
tk.Label(fen, text="Les codes taxon dans cette liste : ").pack(padx=10, pady=10)
curseur.execute("SELECT code FROM taxons order by code;")
contenuD = curseur.fetchall()
liste = ttk.Combobox(fen,values=contenuD)
liste.pack(padx=10, pady=10)
liste.bind("<<ComboboxSelected>>",actualise)
# treeview initialement vide
tableau = ttk.Treeview(fen, columns=(1,2,3),show="headings", height=30)
tableau.heading(1, text="ID")
tableau.heading(2, text="Nom espèce")
tableau.heading(3, text="Code taxon")
tableau.column(1, width=50, anchor="nw")
tableau.column(2, width=150, anchor="nw")
tableau.column(3, width=50, anchor="center" )
tableau.pack(padx=10, pady=10)
fen.mainloop()
Dans le programme principal on dessine un combobox et un treeview (initialement vide).
Le combobox aura toujours le même contenu (liste des taxons).
Par contre le contenu du treeview doit être actualisé à chaque nouvelle sélection dans le combobox :
programmation évènementielle !
Les requêtes paramétrées extraient des données de "vue1".
Le code de cette "vue1" :
CREATE VIEW "vue1" AS SELECT nom_espece, taxons.code as code, nom_taxon FROM especes INNER JOIN taxons ON especes.code = taxons.code
Je recherche les poissons osseux. J'ai donc saisi une partie du nom de ce taxon : "osse".
...
tk.Label(fen, text="Saisir le début du nom de l'espèce recherchée : ").pack(padx=10, pady=0)
zt=tk.Entry(fen,width =50)
zt.pack(padx=10,pady=20)
zt.bind("<FocusOut>",actualise1)
tk.Label(fen, text="Saisir le début du nom du taxon : ").pack(padx=10, pady=0)
zt2=tk.Entry(fen,width =50)
zt2.pack(padx=10,pady=0)
zt2.bind("<FocusOut>",actualise2)
# treeview initialement vide : 2 colonnes
tableau = ttk.Treeview(fen, columns=(1,2),show="headings", height=30)
tableau.heading(1, text="nom espece")
tableau.heading(2, text="Nom taxon")
tableau.column(1, width=150, anchor="nw")
tableau.column(2, width=150, anchor="nw")
tableau.pack(padx=10, pady=20)
fen.mainloop()
Notez l'évènement associé aux widgets Entry : <FocusOut> (sur perte de focus).
C'est le principal évènement que l'on peut associer à une zone de texte.
def actualise1(event):
choix = ...
sql ="select nom_espece, nom_taxon FROM vue1 WHERE nom_espece like ? "
curseur.execute(sql,('%'+choix+'%',))
# vidage treeview
...
# nouveau contenu treeview
...
def actualise2(event):
choix = ...
sql ="select nom_espece, nom_taxon FROM vue1 WHERE nom_taxon like ? "
curseur.execute(sql, ('%'+choix+'%',))
# vidage treeview
...
# nouveau contenu du treeview
...
Si vous avez bien assimilé ce chapitre, vous devez être capable de compléter le script sans difficulté.
Vous avez découvert de nombreux évènements que l'on peut associer aux widgets.
Vous savez désormais réaliser des entrées et sorties dans une fenêtre tkinter ; donc vous n'utilisez plus le shell.
Dans le chapitre suivant vous verrez comment regrouper plusieurs scripts afin de réaliser des applications locales.