Ma première interface utilisateur
Pour créer une interface utilisateur ( en anglais GUI ; Graphical User Interface), vous allez utiliser la bibliothèque Tkinter de python.
Pour cela créez un nouveau programme appelé Hello.py avec le code ci-dessous. Par ailleurs, vous devez travailler avec IDLE, car cela ajoutera automatiquement tkinter dans Python.
Vous enregistrerez chacun de vos programmes dans google Doc ( google drive) et vous le partagerez avec moi (
Merci de bien présenter vos création sur google Doc en mettant le code avec des copies d'écran des fenêtres réalisées.
Remarque : Pour réaliser facilement des copies d'écrans, vous utiliserez l'outil capture d'écran de windows. Celui-ci se trouve dans l'onglet Accessoires windows du bouton fenêtre windows.
-
-
from tkinter import *
from tkinter import ttk
def main():
fen1 = Tk()
label = ttk.Label( fen1, text="Hello World!" )
label.pack()
fen1.mainloop()
main()
-
- Exécutez le programme. Soyez prudent, car cela va créer une petite fenêtre sur votre écran, ce qui peut être difficilement visible. Mais elle devrait être là!
- Normalement, vous devriez voir afficher "Hello World!".
Explications sur le code :
1. Création de la fonction main() avec l'instruction def.
2. Contenue de la fonction main()
Attention
Vous avez remarqué qu’à la deuxième ligne, on n’a pas commencé à écrire au début de la ligne. On dit qu’on fait une indentation. Et cette indentation est indispensable pour que Python fasse son travail. En règle générale, le bloc d’instructions (une ou plusieurs lignes) qui dépend d’une ligne (devant elle se terminer par :) doit être indenté. C’est obligatoire et en plus cela a l’avantage de rendre le script plus lisible.
3. Déclaration de la fenêtre
4. Déclaration du label (étiquette).
5. Déclaration du gestionnaire d'événements avec mainloop(). Cela permet à la fenêtre de gérer la souris, le claver, ..
6. Appelle à la fonction main()
Exercice 1 :
|
Ajout d' un bouton
Maintenant, essayez d'ajouter un peu plus de code à votre application:
-
-
from tkinter import *
from tkinter import ttk
def main():
fen1 = Tk()
label = ttk.Label( fen1, text="Hello World!" )
label.pack()
button1 = ttk.Button( fen1, text="Change Label" )
button1.pack()
fen1.mainloop()
main()
-
- Exécutez le code. Normalement un nouveau bouton est apparu. Cliquez sur le bouton plusieurs fois. Pourquoi ne se passe t-il rien?
- Si votre réponse est que nous n'avons pas définit une action pour le bouton, vous avez raison!
- Ajoutons une nouvelle fonction au programme, et «attacher» au bouton.
-
-
from tkinter import *
from tkinter import ttk
def change():
print( "la fonction change() est appelée" )
def main():
global label
fen1 = Tk()
label = ttk.Label( fen1, text="Hello World!" )
label.pack()
button1 = ttk.Button( fen1, text="Change Label", command=change )
button1.pack()
fen1.mainloop()
main()
-
- Exécutez à nouveau votre code, et vérifiez que lorsque vous cliquez sur le bouton, quelque chose apparait dans la console.
- La console n'est pas nécessaire pour des applications graphiques, mais, dans ce cas, elle est un bon moyen pour débuguer notre programme et vérifier qu'il fonctionne comme prévu.
Exercice 2: Ajouter un deuxième bouton
|
from tkinter import *
from tkinter import ttk
def change():
print( "la fonction change() est appelée" )
def main():
global label
fen1 = Tk()
label = ttk.Label( fen1, text="Hello World!" )
label.pack()
button1 = ttk.Button( fen1, text="Bouton 1", command=change )
button1.pack()
button2 = ttk.Button( fen1, text="Bouton 2", command=change )
button2.pack()
fen1.mainloop()
main()
Bouton Changer le texte de l'étiquette
Réalisons un bouton qui permettra de modifier le texte du label quand vous cliquerez dessus:
-
-
from tkinter import *
from tkinter import ttk
label = None # this variable will hold the label created by the GUI, and will be accessible
# by the change1() function.
def change1():
global label
label.config( text = "Goodbye World!" )
def main():
global label
fen1 = Tk()
label = ttk.Label( fen1, text="Hello World!" )
label.pack()
button1 = ttk.Button( fen1, text="Bye!", command=change1 )
button1.pack()
fen1.mainloop()
main()
-
- Exécutez votre code.
- Vérifiez quand cliquant sur le bouton le contenu du label change.
Exercice 3:: Ajouter un deuxième bouton qui change le contenu du label
|
Mise en place Widgets dans une grille
- plack() est un moyen très simple de mettre les widgets sur la fenêtre de l' interface graphique. Une option plus puissante est d'utiliser de grid(), ce qui nécessite que vous organisiez la position des widgets sur le papier avant de coder.
- Mettons les trois widgets (label, button1 et button2) sur la même ligne. Cela correspond à une grille avec 1 ligne et 3 colonnes.
- Seuls 3 lignes doivent changer. Ils sont indiqués ci-dessous:
-
-
-
-
label.grid( row=0, column=0 )
button1.grid( row=0, column=1 )
button2.grid( row=0, column=2 )
-
-
-
- Faire la modification et exécutez votre application. Est-ce que les 3 widgets s'affichent dans un alignement horizontal?
Exercice 4 : Réorganiser les Widgets
|
L' organisation de l'interface graphique en tant que classe
La nécessité de mettre le label en variable Global est lourde. Si vous avez de nombreux widgets, vous pourrez avoir à faire cela plusieurs fois. Il est préférable d'utiliser une classe pour la gestion des widgets, et de toutes les actions et événements qui leur sont associés.
-
-
from tkinter import *
from tkinter import ttk
class GUI:
def __init__( self, fen1 ):
self.label = ttk.Label( fen1, text="Hello World!" )
self.label.grid( row=0, column=0 )
self.button1 = ttk.Button( fen1, text="Hello",
command=self.hello )
self.button1.grid( row=0, column=1 )
self.button2 = ttk.Button( fen1, text="Bye",
command=self.bye )
self.button2.grid( row=0, column=2 )
def bye( self ):
self.label.config( text = "Goodbye World!" )
def hello( self ):
self.label.config( text = "Hello World!" )
def main():
global label
fen1 = Tk()
gui = GUI( fen1 )
fen1.mainloop()
main()
-
Ajout d'un Checkbox ( case à cocher )
Un checkbox est un widget qui a une boîte que l'utilisateur peut cocher pour mettre en ON ou OFF.
Un checkbox doit être associé à un objet spécial dans le code. En effet, si l'utilisateur change la case en cliquant dessus, celui-ci modifiera automatiquement sa valeur.
Cela semble compliqué. Un exemple va illustrer ce point.
Ajoutez une case à votre GUI. Mettez le code ci-dessous pour modifier votre interface graphique :
-
-
-
-
# create an object to keep the value of the checkbox
self.buttonsEnabled = IntVar()
self.buttonsEnabled.set( 1 )
# create a checkbox and associate it with the object above.
self.check1 = ttk.Checkbutton( fen1, text="Enable", variable=self.buttonsEnabled )
self.check1.grid( row=1, column=0 )
-
-
-
- Notez que pour modifier la valeur de l'objet buttonsEnable, qui est instancié par IntVar. Vous utiliserez la méthode set(). Pour obtenir la valeur d'un tel objet, il faudra utiliser la méthode get().
- Modifiez les fonctions bye() etbhello() de la classe de votre interface graphique, comme indiqué ci - dessous:
-
-
-
-
def bye( self ):
self.label.config( text = "Goodbye World!" )
print( "buttonsEnabled = ", self.buttonsEnabled.get() )
def hello( self ):
self.label.config( text = "Hello World!" )
self.buttonsEnabled.set( 1-self.buttonsEnabled.get() )
-
-
-
- Cliquez sur le bouton hello plusieurs fois. Expliquez le comportement que vous observez. Si vous n'obtenez aucun changement, appelez le professeur.
- Cliquez sur le bouton Bye plusieurs fois, et regardez la console. Le code ci-dessus, et le comportement que vous observez doivent avoir un sens.
- Enfin, cliquez sur le checkbox Enable et uncheckes, puis cliquez sur le bouton approprié pour voir le contenu de l'objet self.buttonsEnabled.
Exercice 5: Contrôle des boutons avec le Checkbox
|
Ajout d' un Text Area ( une zone de texte)
Un widget text area est une zone rectangulaire qui peut agir comme un simple éditeur de texte. En fait, l'éditeur de code que vous utilisez est probablement écrit en python avec la bibliothèque tkonter et son widget Text Area.
Avant d'ajouter la zone de texte, assurez-vous que vos 4 widgets (label, 2 boutons, checkbox) sont organisés dans une grille où ils sont tous alignés sur la ligne 0. Vous allez ajouter la zone de texte sur la ligne 1, et la quelle couvre les 4 colonnes.
Ajouter d'une zone de texte est très simple. Il faut mettre les de lignes de code suivantes à votre classe de GUI.
-
-
self.text = Text( fen1, width=80, height=30, background="ivory" )
self.text.grid( row=1, column=0, columnspan=4 )
-
- Exécutez votre code. Vous devriez voir une fenêtre plus grande qu'auparavant. En effet, elle contient désormais une zone de texte qui peut contenir 30 lignes de 80 caractères.
- Cliquez sur la zone de texte et entrez du texte à l'intérieur.
Effacement de la zone de texte
La zone de texte est entièrement documenté sur cette Page . Il est difficile de comprendre comment accéder aux différentes lignes. Mais heureusement, les instructions associés à l'effacement de la zone de texte sont simple à comprendr :
-
-
self.text.delete(1.0, END)
-
Explications:
-
-
- 1.0 signifie Ligne 1, colonne 0. La numérotation de Tkinter pour les lignes commence à 1 et 0 pour les colnnes.
- END est une constante spéciale utilisée par Tkinter qui signifie la fin du texte dans la zone de texte. Donc , delete(1.0, END) effacera toute la zone de texte.
-
Exercice 7: bouton pour effacer la zone de texte
|
Ajout d' un Canevas
Dans la bibliothèque Tkinter, on ne peut pas dessiner ni insérer d'images en dehors d'un canevas.
Le canevas représente la surface pour dessiner ( = le container de notre dessin).
La ligne de commande peut être la suivante :
can = Canvas(fen,bg='black', height=200, width=300)
can.pack()
- De nombreuses couleurs sont disponible : red, blue, yellow, white, black, green, gray, pink,orange mais aussi dark gray, dark red, magenta, turquoise, gold, ...
- l’option bg='black' donne la couleur du fond de votre canevas.
- can.pack() va ajuster notre fenêtre à la taille du canevas.
- can est le nom de notre canvas, vous pouvez en changer à condition de garder ce même nom à chaque fois que vous y ferez référence.
Le canvas est alors muni d’un repère. Pour repérer vos points sur l'écran :
- Le point en haut à gauche a pour coordonnées (0 ; 0);
- le point en bas à droite par exemple dans une fenêtre de 200 * 300 sera le point(200 ; 300);
- le point central a pour coordonnées : ( largeur de fenêtre /2 ; Hauteur de fenêtre / 2), etc.
Dessiner des lignes La ligne de commande peut être la suivante :
can.create_line(x1,y1,x2,y2,fill='red', width=5)
- Cela permet de tracer une ligne sur notre canvas (can) du point (x1,y1) au point (x2,y2)
- fill : pour préciser la couleur choisie
- width : pour préciser la largeur de ligne.
Tous les paramètres sont facultatifs, ils doivent être séparés par des virgules. Par défaut, le tracé se fera en noir sans remplissage.
Vous pouvez créer plusieurs lignes en une seule ligne de commande donnant d'autres points de coordonnées (x,y). Par exemple :
can.create_line(x1,y1,x2,y2,x3,y3)
Dessiner du texte
can.create_text(x=200, y=200, text="mon texte ici", font="Arial 12",fill="cyan")
- x,y : précise le centre du texte à écrire;
- font : précise la police choisie;
- fill : précise la couleur de l'écriture;
Dessiner un rectangle
can.create_rectangle(x1,y1,x2,y2, outline="red ", fill="pink ")
- (x1,y1) représente le point en haut à gauche;
- (x2,y2) le point en bas à droite;
- outline =couleur du tour du tracé entre guillemets,
- fill = la couleur de remplissage entre guillemets.
Dessiner une ellipse
can.create_oval(x1,y1,x2,y2,fill='blue', outline='green')
- (x1,y1,x2,y2) coordonnées du rectangle circonscrit (qui contient l'ellipse)
Donc pour tracer un cercle vous aurez:
can.create_oval(x-rayon,y-rayon,x+rayon,y+rayon,fill='orange', outline='gold')
Dessiner un polygone
C'est comme des tracés de lignes, mais le dernier tracé viendra rejoindre le premier par une dernière ligne :
can.create_polygon(x1,y1,x2,y2,x3,y3)
option possible : smooth=True #pour arrondir les lignes.
Exécutez le script ci-dessous :
from tkinter import*
from random import randrange
# --- définition des fonctions gestionnaires d'événements : ---
def drawline(): #Tracé d'une ligne dans le canevas can1
global x1, y1, x2, y2, coul # Nouveau !
can1.create_line(x1,y1,x2,y2,width=2,fill=coul)
# modification des coordonnées pour la ligne suivante :
y2, y1 = y2+10, y1-10
def changecolor():
#Changement aléatoire de la couleur du tracé
global coul
pal=['purple','cyan','maroon','green','red','blue','orange','yellow']
c = randrange(8) # => génère un nombre aléatoire de 0 à 7
coul = pal[c]
#------ Programme principal ------
# Les variables suivantes seront utilisées de manière globale :
x1, y1, x2, y2 = 10, 190, 190, 10 # coordonnées de la ligne
coul = 'dark green' # couleur de la ligne
# Création du widget principal ("maître") :
fen1 = Tk()
# Création des widgets "esclaves" :
can1 = Canvas(fen1,bg='dark grey',height=200,width=200)
can1.pack(side=LEFT)
bou1 = Button(fen1,text='Quitter',command=fen1.destroy)
bou1.pack(side=BOTTOM)
bou2 = Button(fen1,text='Tracer une ligne',
command=drawline)
bou2.pack()
bou3 = Button(fen1,text='Autre couleur',command=changecolor)
bou3.pack()
fen1.mainloop()
A retenir ! La commande Global : permet lors de l’appel d’une fonction d’utiliser et surtout de modifier des variables créées dans le programme principal faisant appel à cette fonction.
Exercice 8 :
Vous allez avoir à modifier le programme précédent. 3. Ajouter une fonction drawline2( ) qui tracera deux ligne rouges en croix au centre du canevas : l'une horizontale et l'autre verticale. Ajouter également un bouton portant l'indication « viseur ». Un clic sur ce bouton devra provoquer l'affichage de la croix.
4. Reprendre le programme initial. Remplacer la méthode create_line par la méthode create_rectangle. Que se passe-t-il ? Qu' indique les coordonnées fournies en paramètres ? Appeler le professeur pour vérification. |
Pour dessiner un carré à l'écran, on utilisera ces outils. Mais pour créer nos personnages de jeux, nous allons gagner du temps et de la précision en utilisant des outils de dessins (Photoshop, Gimp, ...) pour ensuite simplement importer ces images dans notre canevas.
Pour tout effacer dans un canevas? La commande
can.delete(ALL)
Insérer une image dans le canevas
Tkinter ne permet pas d'insérer n'importe quelles types d'images. La principale extension acceptée est .gif.
Nous allons donc créer ou transformer toutes nos images en GIF afin de pouvoir les utiliser.
La bonne nouvelle c'est que vous pouvez utiliser des images transparentes au format GIF.
Apprenez à créer des images transparentes (c'est à dire dont le fond se confondra avec le fond de votre fenêtre quelle que soit sa couleur), pour cela il vous faudra préciser ou cocher "fond transparent" dans votre outil de dessin préféré quand vous créerez vos images.
Source : https://media2.giphy.com/media/nOoM9YZ2QuYQE/200_s.gif
fichier_img=PhotoImage(file='mon_image.gif') #pour charger l'image depuis un fichier
mnImage = can1.create_image(x,y,image=fichier_img) # pour copier l'image sur le canevas
- x ,y coordonnées du centre de l'image
Exercice 9
|
from tkinter import*
fen = Tk()
#Création du cadre
cadre = Canvas(fen, width=1000, height=1000, bg="yellow")
cadre.pack()
fichier_img=PhotoImage(file='200_s.gif') #pour charger l'image depuis un fichier
mario = cadre.create_image(500,500,image=fichier_img) # pour copier l'image sur le canevas
fen.mainloop()
Détection et positionnement d'un clic de souris
Copier puis exécuter le programme est le suivant :
# Détection et positionnement d'un clic de souris dans une fenêtre :
from tkinter import*
from tkinter import*
#Déclaration de la fonction position pour afficher les cordonnées du clic souris
def position(event):
chaine.configure(text="Clic détecté en X = " + str(event.x) + ", Y = " + str(event.y))
fen = Tk()
#Création du cadre
cadre = Canvas(fen, width=200, height=150, bg="yellow")
#gestion de l'événement clic souris
cadre.bind("<Button-1>", position)
cadre.pack()
chaine = Label(fen, text="VIDE")
chaine.pack()
fen.mainloop()
Le script fait apparaître une fenêtre contenant un cadre (frame) rectangulaire de couleur jaune pâle.
La méthode bind( ) du widget cadre associe l'événement au gestionnaire d'événement « pointeur ».
Ce gestionnaire d'événement peut utiliser les attributs x et y de l'objet event généré automatiquement par Python, pour construire la chaîne de caractères qui affichera la position de la souris au moment du clic. Exercice : Modifier le script ci-dessus de manière à faire apparaître un petit cercle rouge à l'endroit où l'utilisateur a effectué son clic (il faut d'abord remplacer le widget Frame par un widget Canvas).
Animation: déplacer un objet
Tkinter vous offre la possibilité de déplacer un objet (une image ou un dessin fait à la main) par la fonction :
can.coords(nom_objet,x1,y1,x2,y2) # efface et redessine l'objet
- nom_objet désigne le nom de l'objet à déplacer,
- x1,y1,x2,y2 sont les nouvelles coordonnées de l'objet, cette instruction est magique : elle efface l'objet et le recrée plus loin, sans qu'on ait à écrire ces deux étapes.
Exemple : Copier et exécuter le programment suivant :
from tkinter import*
# Procédure générale de déplacement :
def avance(gd, hb):
global x1, y1
x1 = x1+gd
y1 = y1+hb
can1.coords(oval1, x1, y1, x1+30, y1+30)
# Gestionnaire d'événements :
def depl_gauche():
avance(-10, 0)
def depl_droite():
avance(10, 0)
def depl_haut():
avance(0, -10)
def depl_bas():
avance(0, 10)
##### Programme principal #####
# Les variables suivantes seront utilisées de manière globale :
x1 = 10
y1 = 10 # coordonnées initiales
# Création du widget "maître" :
fen1 = Tk()
fen1.title("Exercice d'animation avec Tkinter")
# Création des widgets "esclaves":
can1 = Canvas(fen1, bg='dark gray', height=300, width=300)
oval1=can1.create_oval(x1,y1,x1+30,y1+30,width=2,fill='red')
can1.pack(side=LEFT)
Button(fen1,text='Quitter',command=fen1.destroy).pack(side=BOTTOM)
Button(fen1,text='Gauche',command=depl_gauche).pack()
Button(fen1,text='Droite',command=depl_droite).pack()
Button(fen1,text='Haut',command=depl_haut).pack()
Button(fen1,text='Bas',command=depl_bas).pack()
# Démarrage du réceptionnaire d'événement :
fen1.mainloop()
La fonction avance( ) redéfinit les coordonnées de l'objet « cercle coloré » (oval1) à chaque fois que l'on clique sur un des boutons.
Ce qui provoque son animation.
Remarque ! Les boutons ont été définis de manière plus compact (pas d'utilisation de variables).
Exercice 10:.
Modifier le programme précédent de manière à ce que le cercle oval1 se place à l'endroit où l'on clique avec la souris
|
Notion de fonction récursive - animation avec la commande after()
Exercice 11 : Copier et exécuter le programme suivant puis compléter les lignes de commentaires :
from tkinter import *
def anime():
# ……………………………………………………………….. :
global x, y
if x<=250:
x=x+1
y=y+1
# .............................................................................................................................. :
canevas.coords(bille,x,y, x+50,y+50)
#............................................................................................................................... :
fen.after(10, anime)
#### programme principal ####
fen=Tk()
fen.title('animation avec tkinter')
# ………………………………………………………………………………………….. :
canevas=Canvas(fen,bg='dark gray',height=300, width=300)
canevas.pack()
x,y=0,0
# ……………………………………………………………………………………………………………………:
bille= canevas.create_oval(x,y,x+50,y+50,fill='orange')
anime()
fen.mainloop()
L'instruction fen.after(10, anime) redéclenche la fonction anime au bout de 10 millisecondes, comme cette instruction se trouve à la fin de la fonction anime(), elle va se rappeler elle-même, on l'appelle une fonction récursive (fonction qui s'appelle elle-même). On obtient ainsi l'animation attendue avec un affichage toutes les 10 ms.
D’autres instructions sont possibles avec Tkinter comme la détection du clavier, l’ouverture de boîtes de dialogue ou encore la gestion du temps. Vous trouverez des compléments de cours aux liens suivants :
http://www.jchr.be/python/tkinter.htm#image
http://fsincere.free.fr/isn/python/cours_python_tkinter.php
http://softdmi.free.fr/tuto_python/tuto-python.php?chapitre=2&page=1
http://www.cgmaths.fr/Atelier/Programmation/InterfacesGraphiques.pdf
http://python.developpez.com/cours/TutoSwinnen/?page=Chapitre8
Sources :
https://maths.ac-noumea.nc/IMG/pdf/tp2_tkinter.pdf
http://www.python.org
http://cs.smith.edu/dftwiki/index.php/Tutorial:TKInter_Lab
http://tkinter.fdex.eu/doc/caw.html