Accueil

Traduction

Tutoriel Python - sommaire

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() restent "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.
Donc le module Tkinter est l'équivalent des formulaires en programmation web.

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.

Il faut toujours associer un "label" à une zone de saisie pour que l'utilisateur sache la nature de ce qu'il doit taper.

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 (des lignes et des colonnes).
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 HTML5 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 il suffit de réaliser 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 d'effectuer des saisies via une belle interface s'il n'y a aucun traitement de ces données ?
Et bien j'y arrive.

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 et injecter le résultat dans une zone de texte.

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" ; toutes les nouveautés sont dans cette fonction.

Étudions le code de la fonction.
x = zt1.get() : récupération du contenu du widget "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") mais quid des saisies effectuées dans des cases à cocher, boutons radio et zones de liste ?
Je vous le dit sans détour ; c'est un peu plus compliqué ! Le programmeur doit utiliser alors 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 cartes préféré et le sport pratiqué.
Le code est assez long et comprend un programme principal pour la description de l'interface 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êmes remarques pour les variables de contrôle 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'

Revenons sur la base de données "animaux.db" qui comprend deux tables : taxons & especes.

Dans le chapitre précédent j'ai présenté un programme basique pour ajouter des lignes à la table 'especes' (avec des input). Je présente maintenant une version améliorée : avec 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()) : la méthode 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é sur la colonne "code" de la table "taxons".

Je crée une liste "contenu" qui est 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

Intérêt du module tKinter ?

Si vous envisagez de créer à partir d'un programme Python un exécutable (voir chapitre suivant), le module tKinter est utile afin que les entrées-sorties soient ergonomiques.
Par contre si vous envisagez de créer une application web (via un framework tel Flask ou Django) vous n'allez pas utiliser Tkinter pour les entreés-sorties mais des templates donc du code HTML !!!

Récupération des programmes

Les programmes de ce chapitre dans un fichier zip