Accueil

Traduction

Tutoriel Python - sommaire

Sommaire partiellement masqué - faites défiler !

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Le module Tkinter

Nous avons déjà réalisé de nombreux programmes avec des traitements de plus en plus complexes. Mais nos entrées et sorties avec les fonctions input() & print() sont toujours "tristounettes" ...

Le module Tkinter nous permet de réaliser d'agréables interfaces graphiques.

Tkinter est un module intégré à la bibliothèque standard de Python. Donc il faut simplement l'importer.
En fait avec ce module nous créons une fenêtre comprenant des éléments ou "widgets" ou "contrôles" : cadres, zones de texte, cases à cocher, boutons radio, zone de listes, boutons de commande, etc.
Nous allons découvrir ce module via des programmes de plus en plus complexes.

Premier programme

Le code

Il faut importer le module tkinter.
On crée une fenêtre, pour cela, on crée une instance de classe tk que l'on nomme "fenetre".
Puis on attribue à cette fenêtre un titre et une taille par défaut.

Les lecteurs qui connaissent les formulaires HTML, ne seront pas surpris ...
Il faut toujours associer un "label" à un "entry" pour préciser à l'utilisateur ce qu'il doit saisir.

Analyse des instructions du programme

label1 = Label(fenetre, text = "Votre identifiant ? ") : définition d'une étiquette à l'intérieur de l'objet " parent. Il faut utiliser la méthode Label() avec deux arguments au moins : l'objet parent, le texte de l'étiquette.

champ1 = Entry(fenetre, width=30) : définition d'une zone de texte à l'intérieur de l'objet parent. Il faut utiliser la méthode Entry() avec deux arguments au moins : l'objet parent (ici "fenetre") puis la largeur du champ (en pixels).

bouton1 = Button(fenetre, text = "connexion") : définition d'un bouton de commande à l'intérieur de l'objet parent. Il faut utiliser la méthode Button() avec au moins deux arguments : l'objet parent, le texte du bouton.

label1.pack() : positionner l'élément "label1" dans la fenêtre.
Il y a 5 widgets donc il y a donc cinq instructions reposant sur la méthode pack().

fenetre.mainloop() : crée une boucle infinie dont on ne sortira que lorsque l'on fermera la fenêtre.

Attention à la "casse".
Les méthodes du module sont sensibles à la casse. Ainsi pour créer une étiquette il faut saisir "Label" (avec un L majuscule) et pour définir une zone de texte il faut écrire "Entry" (avec un E majuscule).

Le rendu de ce programme

module tkinter

Deuxième programme

Le code

Commentaire

Il y a beaucoup de nouveautés dans ce programme.
Passons les en revue.

fenetre.geometry("900x600") : dimensions par défaut de la fenêtre.

fenetre.config(bg ="skyblue") : définition d'une couleur de fond pour la fenêtre.

for i in range(1,6):
____fenetre.rowconfigure(i, pad =15)
 : pour les 5 lignes de la grille on définit une marge externe de 15 pixels.
Ainsi les widgets ne seront pas collés car sinon la hauteur d'une ligne est égale à celle du widget le plus haut dans la ligne.
Par défaut la largeur d'une colonne est égale à celle du widget le plus large dans la colonne. Il existe aussi la méthode columnconfigure pour produire un espacement vertical.

spinbox1 = Spinbox(fenetre, from_=20, to=90, increment=1) : un "spinbox" permet de sélectionner une valeur de l'intervalle défini en cliquant sur des flèches. Mais si l'intervalle est large, la saisie dans ce type de widget devient fastidieuse.

e1.grid(row =1, column =1) : cette étiquette est placée dans la cellule L1C1 de la grille.

scale1 = Scale(fenetre, from_=40, to=100, showvalue=True, label='kilos' ,orient='h') : un widget de type "scale" permet de sélectionner rapidement une valeur de l'intervalle défini. Ici la valeur sélectionnée est affichée et le widget "scale" est dans l'exemple, orienté horizontalement.

rb1 = Radiobutton(fenetre, text="femme", value="f") : définition d'un bouton radio.
Il y a un deuxième bouton radio. Dans un groupe de boutons radio, un seul peut être coché.

cc1 = Checkbutton(fenetre, text="belote") : définition d'une case à cocher.

liste1 = Listbox(fenetre) : création d'une liste.
Il faut ensuite la remplir avec la méthode insert(rang,valeur). Autant d'instructions que le lignes dans la liste ! C'est un peu lourd. Nous verrons plus loin qu'il y a une méthode plus élégante et plus succincte de remplir le contenu d'une Listbox.

Retenons de cet exemple, que la méthode grid() permet le positionnement sur une grille.
Qu'en plus des étiquettes et des zones de texte il existe d'autres types de widgets : "spinbox", "scale", case à cocher, bouton radio, zone de liste.

Le rendu

tutoriel tkinter

Observez bien l'apparence d'un widget de type "spinbox" (toupie) et "scale" (échelle / curseur). Ceux-qui connaissent bien les formulaires HTML ne seront pas surpris. On a le même rendu avec en HTML les instructions : input type ="number" & input type ="range"

Les frames

On peut diviser la fenêtre en différentes zones appelés "frames" (cadres).
Chaque cadre contient des widgets.

Thème : page d'accueil d'une application pour laquelle l'utilisateur doit être inscrit.
L'utilisateur doit donc se connecter ou s'inscrire, si c'est sa première visite.

Le code

Analyse du code

Vous trouvez sans doute que le code est un peu lourd. Mais en fait il se produit très rapidement car il y a 6 étiquettes et 6 zones de texte pratiquement identiques pour leurs arguments donc n'hésitez pas à procéder à des "copier/coller".

f1 =LabelFrame(fenetre,bd=2, text ="connexion", bg ="skyblue", relief="groove") : définition d'un cadre, enfant de "fenetre", borduré avec l'étiquette "connexion", couleur de fond : "skyblue".
Il existe aussi la méthode frame() mais qui ne prévoit pas d'étiquette.
f1.pack(side =LEFT, padx =20, pady=20) : le frame "f1" est positionné à gauche avec des marges par rapport à son parent : "fenetre".
e1 = Label(f1, text ="identifiant ? ").pack(padx =20, pady=20) : chainage de deux méthodes pour définir une étiquette enfant de "f1" et la positionner avec des marges par rapport au parent.

Le rendu

tutoriel tkinter

Récupérer dans une fonction les saisies dans des champs

Arrivé à ce stade, vous devez vous dire : c'est bien gentil tout ça. Mais à quoi ça sert puisqu'il n'y a aucun traitement des données saisies dans la fenêtre.
Je n'ai pas voulu aborder de front le code découlant du design et celui qui permet de transférer des données de l'interface vers le programme et vice-versa.
En d'autres termes j'ai évoqué le "front-end" et maintenant je vais aborder le "back-end".

Thème : via une interface l'utilisateur saisit x et n dans des champs et en retour il y a affichage de x à la puissance n dans un troisième champ.
Une fonction doit donc être capable de récupérer les saisies dans des zones de texte et injecter le résultat dans une troisième ...

Le code

Analyse du code

Un objet de type "Button" peut avoir comme argument "command =nomFonction"
Le code du programme principal ne présente aucune difficulté c'est seulement du design donc une syntaxe déja vue.
Ici le bouton de commande appelle la fonction "fpuissance".

Étudions le code de la fonction qui comprend toutes les nouveautés.
x = zt1.get() : récupération du contenu du champ "zt1" dans la variable x
n = zt2.get() : la variable n de la fonction récupère le contenu du champ "zt2"
resultat = pow(eval(x), eval(n)) : x et n sont convertis en numérique avant d'être les arguments de la méthode pow(). N'oubliez pas d'importer le module math.
zt3.delete(0,END) : effacer le contenu du champ "zt3"
zt3.insert(0,resultat) : insertion du contenu de la variable resultat dans le champ "zt3"

Le rendu

J'ai demandé d'élever à la puissance 64 l'entier 2. Donc le résultat est tellement énorme qu'il est affiché en notation scientifique.

Exercice : une calculatrice basique

Le meilleur moyen de vérifier si vous avez bien assimilé tout ce que je viens de dire, c'est de réaliser vous même un programme avec une UI (User Interface).
Par exemple une calculatrice basique : les 5 opérations de base : addition, multiplication, soustraction, division, élévation à la puissance.

Ce que vous devez obtenir

tutoriel tkinter

Extrait du programme

Je vous communique cependant une partie du programme car j'en profite pour vous montrez comment créer une fenêtre non redimensionnable et aussi comment personnaliser les légendes des widgets.
Ci-dessous début du programme principal :

Je vous indique une fonction sur les cinq.

Dans le programme principal, notez l'instruction fenetre.resizable(False, False) : La fenêtre ne peut être redimensionnée ni en hauteur, ni en largeur.
mafonte = "{courier new} 14" : je définis un objet "font" dénommée "mafonte".
e1 = Label(fenetre, text = " saisir A", font =mafonte) : à chaque widget j'ajoute l'option "font = mafonte" afin que la taille des textes soit plus grande.
zt1 = Entry(fenetre, width=15,font =mafonte) : évitez des champs de saisie trop larges.

Récupérer les saisies via des boutons radio, case à cocher et zones de liste

Vous savez pour le moment, récuperer les contenus des zones de texte (widget de type "Entry").
Pour récupérer les saisies effectuées dans des cases à cocher, boutons radio et zones de liste c'est un peu plus compliqué ... Le programmeur doit utiliser des variables spécifiques à tKinter dites variables de contrôle.

Variables de contrôle

Le code

Reprenons le thème du questionnaire dans lequel on demande le sexe, le poids, le jeu de carte préféré et le sport pratiqué.
Le code est assez long et comprend un programme principal et une fonction de traitement.

Le programme principal

Nous déclarons 5 variables de contrôle nommées "vc1, ... vc5".
"vc1" est relié au widget "scale" et mémorise un entier.
scale1 = Scale(fenetre, from_=40, to=100, showvalue=True, label='kilos' ,orient='h', variable =vc1) : dans les arguments de Scale() j'ai rajouté "variable =vc1".

"vc2" est relié au groupe de boutons radio et mémorise une chaine.
rb1 = Radiobutton(fenetre, text="femme" , value ="féminin", variable = vc2) : pour les boutons radio "rb1" et "rb2" j'ai rajouté dans la méthodeRadiobutton() l'argument "variable =vc2"

Notez bien que pour chaque bouton radio il y a l'argument "value = valeur". Donc si vous sélectionnez "rb1" vous saisissez en fait "féminin" et si vous sélectionnez "rb2" vous saisissez "masculin".

Il y a trois cases à cocher, il faut donc définir trois variables de contrôle qui mémorise un entier (1 ou 0 selon que la case est cochée ou pas) : "vc3, vc4, vc5"
cc1 = Checkbutton(fenetre, text="belote", variable =vc3) : dans chacun des trois cases à cocher je rajoute dans les arguments de Ckeckbutton() : "variable = nomVariableControle".

Pour établir le contenu de la Listbox, j'ai utilisée une boucle qui parcoure une liste.
Donc la maintenance logicielle sera simple ; pour modifier le contenu de la Listbox il suffit de modifier la liste "contenu".

bouton1 = Button(fenetre, text = "Récupérez les valeurs saisies", command=mafonction) :
sur clic sur le bouton de commande j'appelle la fonction "mafonction".

La fonction

Il faut récupérer les valeurs de contrôle dans des variables de la fonction.
monpoids = vc1.get() : récupération du contenu de "vc1" dans variable de fonction "monpoids"
monsexe =vc2.get() : récupération du contenu de "vc2" dans variable de fonction "monsexe"
belote = vc3.get(): récupération dans vc3 de 1 ou zéro. Même remarque pour vc4 et vc5

index = liste1.curselection() : si, dans la liste, j'ai sélectionné par exemple, la cinquième ligne ; la variable "index" récupère 5 grâce à la méthode curselection().
item= liste1.get(index) :la variable "item" récupère l'item d'indice 5 donc "natation".

Notez que la fonction comprend des instructions "print". Donc les sorties se feront dans la fenêtre Shell de l'IDLE.

Le rendu

L'interface graphique

tutoriel tkinter

La fenêtre Shell de l'IDLE

Amélioration du programme

Tout ça c'est bien mais perfectible.
Pour les jeux de cartes pratiqués, à la place des 1 ou zéro (coché ou décoché) on préferait une chaine du genre : "tarot" ou "bridge"
Les informations saisies doivent s'afficher dans une zone de texte de l'interface.
La taille des caractères doit être plus grande avec de la graisse.

Extrait du programme principal modifié

Commentaire

mafonte = "{courier new} 12 bold " : je définis un objet "fonte".
e1 = Label(fenetre, text = "Fiche de renseignements ", font = mafonte) : à chaque widget j'ajoute l'option font = mafonte.

Pour chaque case à cocher, j'ai rajouté un argument à la méthode checkbutton(). Exemple : onvalue = "belote"
Donc si la case est cochée la valeur retournée n'est plus 1 mais la valeur de l'argument onvalue.
Donc les trois variables de contrôle associées aux "Checkbutton" doivent changer de type puisqu'elles doivent désormais retourner une chaine.

zt1 = Entry(fenetre, width=40, font =mafonte) : ajout d'une zone de texte large (40px) qui va afficher la chaine retournée par la fonction.

Extrait de la fonction modifié (dernières lignes)

Les variables "belote", "tarot", "bridge" contiennent désormais une chaine ou zéro (si case non cochée).

Le rendu

tutoriel tkinter

Il aurait été judicieux d'utiliser pour les cases à cocher l'option "offvalue" également.
Dans chacune des trois "Checkbutton" on rajoute :Checkbutton( ... , offvalue =" ", ...) . Ainsi si la case en question n'est pas cochée on récupère une chaine vide (au lieu d'un zéro).

Programme de saisie dans la table 'especes'

Dans le chapitre précédent, je vous ai présenté une version basique de cette thématique : programme basé sur des "input".
Dans une version évoluée présentée ci-dessous, la saisie se réalise dans le cadre d'une interface graphique.

Version évoluée

La saisie sera effectuée via un formulaire (objet Tkinter).

Le code du programme principal

Pour positionner les widgets j'utilise une troisième méthode (après pack() & grid()) : place().
Avec cette technique on dispose d'un repère cartésien donc l'origine et le coin haut gauche de l'objet Tkinter.
Il faut argumenter la méthode avec des coordonnées X & Y.

Attention le contenu de la Listbox (nommée "liste2") n'est pas dynamique.
L'idéal aurait été de bâtir cette liste à partir de la colonne "taxons.code" (champ "code" de la table "taxons"). Mais c'est un peu délicat ; un peu de patience, voir la fin de ce chapitre !

Le code des fonctions

Dans la procédure "validation" il faut récupérer la valeur saisie dans "champ1" et la valeur sélectionnée dans "liste2".
Puis il faut argumenter la requête SQL "insert ..." avec ces valeurs.

Dans la procédure "fin" il y a déconnexion de la base, effacement du formulaire et affichage d'un message dans la fenêtre d'exécution.

Saisie de nouvelles espèces dans "especes" : version définitive

Comme je dis plus haut, le contenu du widget Listbox n'est pas dynamique.
Ci-dessous, je vous propose la solution pour que son contenu soit basée sur la colonne "code" de la table "taxons".

Je crée une liste "contenu" vide.
Cette liste est remplie par l'exécution de la requête "select code from taxons ...".
Il n'y a plus qu'à remplir Listbox avec la liste "contenu" via une boucle basée sur la méthode insert()

Aperçu du formulaire de saisie: tkinter tuto

Travaux pratiques

Si vous avez compris, vous devez désormais être capable de produire un programme avec (interface graphique) qui permet de saisir de nouveaux taxons.
En d'autres termes : saisie de nouveaux enregistrements dans la table "taxons".

Dessiner dans une interface graphique

Avec la méthode Canvas() vous pouvez créer dans une fenêtre une zone de dessin.
Ce canevas peut comprendre des formes géométriques mais aussi intégrer des images matricielles.
Un canevas est un rectangle ayant pour origine le coin supérieur gauche.

Si vous connaissez JavaScript et son API Canvas (qui est implémenté par les tous les navigateurs récents) vous serez très vite familiarisé avec les canevas de Tkinter.

Un programme qui crée un canevas

Dans la fenêtre je dessine un canevas et j'insère dedans un image GIF et du texte au dessus de l'image.
L'image doit être centrée dans le canevas, ses dimensions : celles du canevas /2.

Le code du programme

Analyse

canevas = Canvas(fenetre, width=w, height=h, bg='yellow') : création d'un objet de type "Canvas" dans la fenêtre et qui est stocké dans la variable "canevas". Trois arguments obligatoires : parent, width, height).
monGif = PhotoImage(file="ordinateur.gif") : sélection de l'image gif à insérer avec la méthode PhotoImage(file =chemin de l'image)
canevas.create_image(w/2, h/2, image=monGif) : insertion dans le canevas de l'image avec la méthode create_image(). Les arguments sont : X, Y, image. X et Y sont les coordonnées de l'ancrage.
Par défaut l'image est centrée par rapport à ce point d'ancrage (anchor ="center")
canevas.create_text(w/2, h/2,text="Tkinter, c'est super !", font =mafonte, fill="navy") : insertion dans le canevas d'un texte. Les attributs obligatoires sont : X, Y et text. X et Y sont les coordonnées du point d'ancrage.
Par défaut le texte est centré par rapport à ce point d'ancrage (anchor ="center").

L'attribut anchor peut prendre d'autres valeurs : nw, n,ne,e,se, sw,w c'est à dire les points cardinaux.

Un conseil : les images à insérer et le programme (fichier .py) dans le même répertoire !

Le rendu

tutoriel tkinter

Remarque

On ne se rend pas compte que l'image est deux fois plus petite que le canevas qui la contient ; c'est normal, le format GIF gère la transparence.

Attention l'insertion d'un GIF, comme le montre l'exemple ci-dessus, ne pose pas de difficulté, par contre pour les autres formats d'images matricielles, c'est un peu plus délicat ...
Si vous insérez un gif animé dans un canevas, l'image reste fixe à moins de créer une boucle qui anime chaque image constitutive du gif animé.
Pour insérer, dans un canevas, des images d'autres formats (.jpeg, png), il faudra passer par le module PIL (qui n'est pas présent par défaut dans Python).

Dessiner des formes dans un canevas

Des méthodes

Thème : Uu programme qui dessine dans un canevas le logo de l'entreprise.

Le code

Analyse du code

fenetre.rowconfigure(0,minsize =30)
fenetre.columnconfigure(0,minsize =30)
 : la ligne et la colonne 0 ont une largeur / hauteur de 30px. Le canevas va en effet être positionné avec la méthode grid().

Le rectangle occupe tout le canevas et est rempli de bleu.

Une ellipse de fond rouge avec une bordure blanche épaisse se superpose.

Un texte ancré au milieu du canevas a des caractères blancs.

Le rendu

tutoriel tkinter