Nous allons apprendre ici à gérer quelques nouveaux widgets. Pratique mais pas indispensable pour réaliser vos projets.
Rien de mieux qu’un curseur pour rendre un programme interactif.
Il s’agit de la classe Scale (oui oui, avec une majuscule).
Nous allons pouvoir récupérer des données numériques plutôt que texte.
On peut créer des widgets autonomes, non rattachés à une variable externe :
Scale(fen_princ, from_ = -20, to = 300)
=pack()
.On peut alors lire la valeur actuelle du Scale avec la méthode get :
valeur = .get()
Et on peut même forcer la valeur du curseur à une certaine valeur avec la méthode set :
set(10)
.Si on intègre cela dans une interface Tkinter basique (un Scale, un Label et un Button qui modifie l'affichage du Label avec la valeur du Scale), le code devient :
# -*-coding:Utf-8 -*
from tkinter import *
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions utilisées
# - - - - - - - - - - - - - - - - - -
def afficherValeur() :
valeur = .get()
configure(text = valeur)
.# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
# Création d'un Scale nommé curseur1
Scale(fen_princ, from_ = -20, to = 300)
=pack()
.# Création d'un Label nommé monAffichage
Label(fen_princ, text = "C'est ici qu'on affichera le résultat du Scale", width=70)
=pack()
.# Création d'un Button lancant la fonction afficherValeur()
Button(fen_princ, text = "Récupérer la valeur du curseur", command = afficherValeur)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
01° Lancer le programme test et vérifier qu'il fonctionne correctement en faisant bouger le curseur :
Lors du lancement, on obtient :
Après l'activation du bouton :
Voici quelques attributs permettant d'améliorer le visuel de votre interface :
Comment avoir un curseur/scale horizontal ?
Scale(fen_princ, orient='horizontal', from_=0, to=10)
Comment gérer la taille du curseur/scale ?
Scale(fen_princ, from_=0, to=10, length=350)
Comment mettre un nom sur un curseur/scale ?
Scale(fen_princ, from_=0, to=10, label='titre du scale')
02° Modifier le curseur pour que cela ressemble à ceci :
...CORRECTION...
curseur1 = Scale(fen_princ, from_ = -20, to = 300, orient='horizontal', bg='yellow', fg='black')
Rappel : le background (bg) et le foreground(fg) permettent de gérer les couleurs.
On peut également rattacher le curseur à une variable externe. On utilisera ici une méthode constructeur IntVar() qui est l'équivalent du StringVar() mais en integer. Si vous modifiez cette variable, les widgets qui en dépendent vont automatiquement être modifiés.
valeur2 = IntVar
Scale(fen_princ, from_ = -20, to = 300, variable = valeur2)
=pack()
.Le plus fort, c'est qu'on peut associer cet objet de classe IntVar à un Label via l'attribut textvariable. Et Tkinter fera le boulot à votre place dès que ce IntVar va varier.
Label(fen_princ, textvariable = valeur2, width=70)
=03° Utiliser ces nouvelles connaissances pour parvenir à créer un interface sans bouton dans laquelle bouger le curseur modifie automatiquement la valeur affichée dans le Label.
...CORRECTION...
# -*-coding:Utf-8 -*
from tkinter import *
# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
# Création d'un Scale nommé curseur1
valeur2 = IntVar()
Scale(fen_princ, from_ = -20, to = 300, variable = valeur2)
=pack()
.# Création d'un Label nommé monAffichage
Label(fen_princ, textvariable = valeur2, width=70)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
Il s’agit de la classe Spinbox : une entrée où on peut rentrer la valeur à la main ou en utilisant deux boutons + ou -.
Spinbox(fen_princ, from_ = 0, to = 10)
=pack()
.04° Afficher le contenu de la Spinbox en appuyant sur l’un des boutons. Il faudra utiliser la fonction get() appliquée à la spinbox s.
La correction en cliquant sur l'image :
Les remarques faites sur le Scale ayant un attribut variable pointant vers un Scale sont valables ici aussi.
Cette classe se note Listbox, avec une majuscule, oui oui, on a compris.
Listbox(fen_princ)
=insert(1, "Python")
.insert(2, "C++")
.insert(3, "HTML")
.insert(4, "CSS")
.insert(5, "Javascript")
.pack()
.On voit que ça se code un peu différemment mais sans plus.
Pour récupérer la valeur du choix, on utilise :
get('active')
.Pour définir les choix par programmation, on peut utiliser le code ci-dessous. Il crée les choix 'un, 'deux' et 'trois'.
set('un deux trois')
.Beaucoup d'autres choses sont possibles. Il suffit de lire la documentation.
05° Compléter le programme ci-dessous pour qu'il permette de modifier l’affichage du label en fonction des valeurs d’une liste et de l’appui sur un bouton.
La correction en cliquant sur l'image :
# -*-coding:Utf-8 -*
from tkinter import *
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions utilisées
# - - - - - - - - - - - - - - - - - -
def changerAffichage() :
Compléter ici
# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
# Création d'un Label nommé monAffichage
monTexte = StringVar()
monTexte.set("Hello World !")
Label(fen_princ, textvariable = monTexte, width=70)
=pack()
.# Création d'une ListBox
Listbox(fen_princ)
=insert(1, "Python")
.insert(2, "C++")
.insert(3, "HTML")
.insert(4, "CSS")
.insert(5, "Javascript")
.pack()
.# Création d'un Button lancant la fonction changerAffichage()
Button(fen_princ, text = "Change l'affichage !", command = changerAffichage)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
06° Créer un programme qui utilise une liste contenant des strings ('blue', 'green', 'red' ...). Utilisez ensuite les valeurs de cette liste pour changer la couleur d'un Label texte lorsqu'on appuie sur un bouton.
Remarque : comme le texte n'est pas destiné à changer, j'ai utilisé l'attribut text et pas textvariable dans la correction.
...CORRECTION...
# -*-coding:Utf-8 -*
from tkinter import *
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions utilisées
# - - - - - - - - - - - - - - - - - -
def changerCouleur() :
couleur = .get('active')
configure(fg = couleur)
.# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
# Création d'un Label nommé monAffichage
Label(fen_princ, text = "C'est ce texte qui devra changer de couleurs", width=70)
=pack()
.# Création d'une ListBox
Listbox(fen_princ)
=insert(1, "blue")
.insert(2, "red")
.insert(3, "green")
.insert(4, "orange")
.insert(5, "yellow")
.pack()
.# Création d'un Button lancant la fonction changerCouleur()
Button(fen_princ, text = "Change la couleur !", command = changerCouleur)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
Nous allons avoir besoin d'importer les éléments de tkinter.filedialog.
Il faudra ensuite utiliser la fonction askopenfilename et lui transmettre en argument le titre de la boîte de dialogue (title
), les types de fichiers qui seront affichés (filetypes
).
Filetypes
doit recevoir une liste, cela se voit aux crochets de définitions [ et ]. Chaque élément de la liste est un 2-uplets (string affiché, extension correspondante).
filepath = askopenfilename(title = "Ouvrir une image", filetypes = [ ('jpg files','.jpg'),('all files','.*') ])
Une fois le fichier sélectionné, votre variable (ici filepath) contiendra un string correspondant au nom du fichier voulu avec son chemin d'accés.
07° On voudrait activer la fenêtre lorsqu’on appuie sur un bouton. Il faudrait alors afficher le contenu de filepath dans un Label. Un simple monTexte.set(filepath)
devrait suffire. N'oubliez pas non plus qu'on modifie le contenu d'un StringVar à l'aide de la méthode get.
Le programme ci-dessous crée un Label et un Entry qui sont liés au même StringVar nommé monTexte.
# -*-coding:Utf-8 -*
from tkinter import *
from tkinter.filedialog import * # pour les gestions de fichiers
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions utilisées
# - - - - - - - - - - - - - - - - - -
def chercherFichier() :
Compléter ici
# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
monTexte = StringVar()
monTexte.set("Pas de fichier pour l'instant")
# Création d'un Label nommé monAffichage
Label(fen_princ, textvariable = monTexte, width=70)
=pack()
.# Création d'un Entry nommé monEntree
Entry(fen_princ, textvariable = monTexte, width=70)
=pack()
.# Création d'un Button lancant la fonction chercherFichier()
Button(fen_princ, text = "Boîte de dialogue", command = chercherFichier)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
La correction en cliquant sur l'image :
C'est la suite logique : on va associer ce qu’on a vu sur l'affichage d'une image et ce qu'on vient de voir sur le choix d'un fichier.
N’oubliez pas l’importation des classes indispensables :
from tkinter import *
from tkinter.filedialog import * #pour les gestions de fichiers
from PIL import Image as Img
from PIL import ImageTk
Pour affecter l'image au bon Label, utilisons :
filepath = askopenfilename(title = "Ouvrir une image", filetypes = [ ('jpg files','.jpg'),('all files','.*') ])
presentation = Img.open(filepath)
presentationTk = ImageTk.PhotoImage(presentation)
Label(fen_princ, image = presentationTk)
=pack()
.Voici ce que cela donne sur un programme qui demande directement l'image qu'on veut voir s'afficher.
# -*-coding:Utf-8 -*
from tkinter import *
from tkinter.filedialog import * # pour les gestions de fichiers
from PIL import Image as Img
from PIL import ImageTk
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions utilisées
# - - - - - - - - - - - - - - - - - -
# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
monFichier = StringVar()
monFichier.set("Pas de fichier pour l'instant")
# Création d'un Label nommé monAffichage
Label(fen_princ, textvariable = monFichier, width=70)
=pack()
.# Recherche de l'adresse du fichier-image voulu
filename = askopenfilename(title = "Ouvrir une image", filetypes = [ ('jpg files','.jpg'),('all files','.*') ])
# Mise à jour de monFichier
monFichier.set(filename)
# Création d'un Label image associé à l'adresse précédente
presentation = Img.open(filename)
presentationTk = ImageTk.PhotoImage(presentation)
Label(fen_princ, image = presentationTk)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
08° Tester le code pour qu'on puisse ouvrir la fenetre de dialogue, sélectionner un fichier image et l'afficher dans un Label. Et expliquer le, ligne par ligne, c'est très important. Aucune ligne de code ne doit vous paraitre inconnue.
Si on sélectionne une image, ça donne
Et si nous tentions de faire la même chose mais avec un bouton. Pour l'instant, la boîte de dialogue s'affiche dès le départ.
09° Modifier le programme pour que le choix de l'image se fasse après avoir cliqué sur un bouton. Tenter de modifier l'image du Label.
Dans le corps du programme, vous pouvez créer au départ l'image de base sans sélection avec le code suivant : (voir l'activité précédente sur Tkinter)
presentation = Img.new("RGB", (20,20), (255,255,150))
presentationTk = ImageTk.PhotoImage(presentation)
Label(fen_princ, image = presentationTk)
=pack()
.Dans la fonction, il faudra donc recréer un contenu pour presentationTk et il faudra donc modifier l'attribut image avec un code du type :
config(image=presentationTk)
.Tenter d'aller jusqu'au bout et de ne plus avoir d'erreur dans le code. Vous ne devriez néanmoins pas réussir à modifier votre image ... Tout cela à cause du ramasse-miettes : vous tentez de modifier l'image de votre label avec un = depuis une fonction : dès qu'on sort de la fonction, le ramasse-miettes détruit les variables qui ne sont pas liées à des objets du programme principal directement.
Vous devriez néanmoins réussir à afficher l'image de base (un carré coloré) et choisir le fichier et modifier le nom affiché dans le label.
Voici une correction du code non fonctionnel que vous devriez avoir écrit :
...CORRECTION...
# -*-coding:Utf-8 -*
from tkinter import *
from tkinter.filedialog import * # pour les gestions de fichiers
from PIL import Image as Img
from PIL import ImageTk
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions utilisées
# - - - - - - - - - - - - - - - - - -
def mise_a_jour():
# Recherche de l'adresse du fichier-image voulu
filename = askopenfilename(title = "Ouvrir une image", filetypes = [ ('jpg files','.jpg'),('all files','.*') ])
# Mise à jour de monFichier
monFichier.set(filename)
# Modification du Label image associé à l'adresse précédente
presentation = Img.open(filename)
presentationTk = ImageTk.PhotoImage(presentation)
config(image=presentationTk)
.# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
monFichier = StringVar()
monFichier.set("Pas de fichier pour l'instant")
# Création d'un Label nommé monAffichage
Label(fen_princ, textvariable = monFichier, width=70)
=pack()
.# Création d'un Button lancant la fonction mise_a_jour
Button(fen_princ, text = "Mise à jour du texte ci-dessus", command=mise_a_jour)
=pack()
.# Création d'un Label image associé à l'adresse précédente
presentation = Img.new("RGB", (20,20), (255,255,150))
presentationTk = ImageTk.PhotoImage(presentation)
Label(fen_princ, image = presentationTk)
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
Alors, d'où vient le problème ?
Regardons le code :
def mise_a_jour():
# Recherche de l'adresse du fichier-image voulu
filename = askopenfilename(title = "Ouvrir une image", filetypes = [ ('jpg files','.jpg'),('all files','.*') ])
# Mise à jour de monFichier
monFichier.set(filename)
# Modification du Label image associé à l'adresse précédente
presentation = Img.open(filename)
presentationTk = ImageTk.PhotoImage(presentation)
config(image=presentationTk)
.Pourquoi ça ne marche pas ? La documentation explique qu'il faut garder une référence vers votre objet-image. Ici, on ne stocke dans l'attribut image que l'adresse mémoire de l'objet-image lui-même. Or, le ramasse-miette détruit les variables locales créées par la fonction qui ne sont pas stockées dans un objet-créé dans le corps du programme. Le résultat en image :
La technique pour stocker un objet-image qui n'est pas créé dans le programme principal est de le stocker dans l'objet-Label en lui créant un nouvel attribut. On peut le nommmer comme on veut : toto, monEmplacement, memoireImage ...
Voici ce que cela donnera en image :
Et on peut obtenir ceci avec une ligne en plus :
presentationTk
.monEmplacement =def mise_a_jour():
# Recherche de l'adresse du fichier-image voulu
filename = askopenfilename(title = "Ouvrir une image", filetypes = [ ('jpg files','.jpg'),('all files','.*') ])
# Mise à jour de monFichier
monFichier.set(filename)
# Modification du Label image associé à l'adresse précédente
presentation = Img.open(filename)
presentationTk = ImageTk.PhotoImage(presentation)
presentationTk
.monEmplacement =config(image=presentationTk)
.10° Rajouter la ligne qui permet de stocker notre référence à l'image dans l'objet-Label lui-même et relancer votre programme. Cette fois, cela va fonctionner.
Puis :
Nous n'avions pas réglé le probléme des textes trop grands pour le widget-Label dans lequel ils sont affichés :
Voici le code utilisé pour obtenir un résultat proche du précédent :
# -*-coding:Utf-8 -*
from tkinter import *
# - - - - - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - - - - - -
fen_princ = Tk()
# Création d'un Label nommé monAffichage
Label(fen_princ, text = "Mon premier Affichage ! Mon premier Affichage !", width=35, font = ("Helvetica", 32))
=pack()
.# - - - - - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - - - - - -
fen_princ.mainloop()
Tentons de résoudre ce problème : on peut définir des formats d’affichage.
L’attribut height définit la hauteur de l’affichage :
L’attribut width définit la largeur de l’affichage.
11° Tester le code suivant :
from tkinter import *
fen_princ = Tk()
Label(fen_princ, text = "C’est ici que j’affiche mon très très long premier texte !" , font = ("Helvetica", 32), height = 3, width = 15)
=pack()
.fen_princ.mainloop()
Vous devriez voir que le Label prend 3 lignes de hauteur (mais n’en utilise qu’une) et que la chaîne de caractères ne s’affiche que sur 15 caractères. Ce n’est pas ce qui était voulu…
Si le texte ne s’affiche pas correctement, on peut déjà indiquer quelle zone du texte on désire afficher :
Le paramètre anchor permet d’afficher le texte en l’alignant sur :
On notera qu’on peut également utiliser les associations : NW ….
Quelques exemples
12° Tester le code suivant (attention, votre chaîne de caractères doit être réellement longue).
from tkinter import *
fen_princ = Tk()
Label(fen_princ, text = "C’est ici que j’affiche mon très très long premier texte !", font = ("Helvetica", 32), height = 3, width = 15, anchor = W)
=pack()
.fen_princ.mainloop()
Ca fonctionne mieux un peu mieux.
ATTENTION : Il y a des modifications à faire si vous importer Tkinter avec un alias :
import tkinter as tk
Comme vous avez créé un alias, il faudra taper tk devant W, E ...
Label(fen_princ, text = "C’est ici que j’affiche mon très très long premier texte !", font = ("Helvetica", 32), height = 3, width = 15, anchor = tk.W)
=Pensez à modifier les codes ci-dessous en conséquence.
13° Tester le code en modifiant le type de l’ancrage du texte (anchor) pour en comprendre la signification (notamment les associations NW, SE ..)
C’est bien beau tout ça, mais mon texte ne s’affiche toujours pas en entier. Ce ne fait pas très sérieux.
Pour palier à ce petit problème, nous allons lui demander d’accepter de s’afficher sur plusieurs lignes si ça peut vous faire plaisir. Pour cela, on utilise wraplength.
Label(fen_princ, text = "C’est ici que j’affiche mon très très long premier texte !", font = ("Helvetica", 32), height = 3, width = 25, anchor = NW, wraplength = 800)
=
14° Tester cette ligne pour que votre long texte s’affiche maintenant correctement. Attention wraplength est à donner en mesure-écran. Il faudra sûrement jouer sur width pour que cela fonctionne bien. Pensez également à rajouter des lignes avec height si la place n’est pas suffisante. Le problème vient du fait que la police utilisée fait varier la taille de la lettre en fonction de la taille réellement nécessairement en largeur : un a prend plus de place qu'une virgule.
Vous devriez réussir à obtenir quelque chose comme
Il existe beaucoup d’autres attributs modifiables. Le mieux est encore de faire une recherche sur « Python Tkinter Label ».
Il s'agit juste d'une remarque.
Ces deux attributs que nous avons utilisé avec les widgets Label pour du texte sont utilisables sur tous les widgets. Si vous devez modifier la taille d'un widget, pensez à ces deux attributs.
Par exemple pour un bouton :
Ce résultat est obtenu avec :
# Création d'un Button lancant la fonction mise_a_jour
Button(fen_princ, text = "Activer le bouton", width=50, height=5, bg='red')
=pack()
.Comme il s'agit d'un bouton affichant du texte, les dimensions sont en colonnes et lignes, pas en pixels.
Cela marche aussi avec les labels affichant des images. Et cette fois, le résulat est en pixels.
Il reste encore des cas à traiter. Mais vous avez maintenant assez de connaissances pour vous débrouiller avec la documentation de Tkinter.