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() 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" : 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 éléments.

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 dans les "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".