Projet Python Initiation à Tkinter (Tool Kit Interface)

publicité
Projet Python
Initiation à Tkinter (Tool Kit Interface)
Objectif de ce tutoriel : initiation à Tkinter (gestionnaire d’interface graphique) en vue des projets de fin d’année.
Gestion du projet
Méthodologie
Ce travail de prise main de tkinter doit être réparti en plusieurs séances, en autonomie (chez vous), et doit être mené à
bien d’ici le mois de mars.
Taper et ne pas hésiter à modifier les scripts ci-dessous.
Commentez vos scripts sous peine de perdre un temps considérable : ce travail va s’étaler dans le temps !
Parallèlement à cette prise en main, commencer à définir un jeu ou une application que vous souhaiteriez programmer.
En cas de problème ou pour rechercher des fonctionnalités supplémentaires, vous trouverez sur le web de très
nombreuses ressources (cf. liste non exhaustive ci-dessous).
Ressources (liste non exhaustive !)
http://apprendre-python.com/page-tkinter-interface-graphique-python-tutoriel (synthétique, parfois des problèmes avec Pyzo)
http://fsincere.free.fr/isn/python/cours_python.php (Beaucoup de ressources sur le son, les images…)
http://effbot.org/tkinterbook/ (Exhaustif ! Toute la documentation)
Ne pas hésiter à lancer des requêtes sur un moteur de recherche. Par exemple : tkinter move image
Le site Stack Overflow (forums) proposé dans les réponses à cette requête est généralement très pertinent.
Consignes
Travail par équipe de 2 ou 3 élèves.
Présentation du jeu à la classe en fin d’année et mise à disposition en ligne (espace spécifique sur mon site).
Nous n’utiliserons pas le module pygame.
Le jeu ou l’application devra obligatoirement sauvegarder des informations dans des fichiers et des bases de données (à
venir).
Widgets
Widget : contraction de l'anglais windows gadget (gadget fenêtre). Les widgets sont tous les objets graphiques que l'on peut
insérer dans une interface (fenêtre).
Les principaux widgets sont :
Les boutons :
Les labels :
Les zones de saisie :
Les canevas :
Button
Label
Entry
Canvas
(pour commander une action).
(pour insérer un texte).
(pour permettre l'entrée d'une donnée).
(pour insérer des dessins ou des images).
Chaque widget a des propriétés et des méthodes qui permettent de régler son apparence et les interactions avec l'utilisateur.
https://fr.wikibooks.org/wiki/Programmation_Python/Tkinter
PCSI – IPT G. Monod
Initiation_Tkinter.docx
1/9
Modules
from tkinter import *
from tkinter.messagebox import *
from numpy import random as rd
# Boîtes de dialogue prédéfinies
Création (instanciation) d’une fenêtre : Tk() – Boucle principale : méthode mainloop()
fenetre = Tk()
# Création de la fenêtre, le nom choisi est quelconque : f1 = Tk() convient
""" Instructions pour définir et placer les éléments de la fenêtre, interagir… """
fenetre.mainloop()
# Boucle scrutant les événements (clavier, souris et autres actions)
Il est alors possible de placer des widgets dans la fenêtre et d’interagir avec elle. La méthode mainloop() scrute les
évènements déclenchés au clavier ou par les clics souris (cf. méthode bind() ci-dessous).
Astuces pratiques
Lors de la mise au point d’un script, s’il se produit une erreur qui interrompt l’exécution après l’instruction
fenetre = Tk(), taper fenetre.destroy() dans la console interactive afin de supprimer la fenêtre créée (sinon Python
se bloque).
Contrairement à ce que vous trouverez parfois sur le web, c’est bien fenetre.destroy() et non fenetre.quit()
qu’il faut utiliser avec Pyzo (sinon Python se bloque).
Widgets Label, Entry, Button - Méthodes destroy() et pack() - Focus
# Personnalisation de la fenêtre
fenetre = Tk()
fenetre.title('3 Widgets')
fenetre.geometry('300x100')
fenetre['bg'] = 'lightgrey'
# Titre de la fenêtre
# Dimensions de la fenêtre largeur x hauteur
# Couleur du fond
# bg = background (arrière-plan) fg = foreground (1er plan)
# Widget Label = zone de texte à lire (étiquette, légende, information…)
label1
= Label(fenetre, text='Nom : ',fg='blue', bg='yellow')
# Positionnement automatique du widget avec la méthode pack()
label1.pack(padx=5, pady=5)
# padx et pady = distance par rapport au bord de la fenêtre
# Widget Entry = zone de texte à entrer (zone de saisie)
entree1 = Entry(fenetre, width=100, fg='red')
entree1.pack(padx=5, pady=5)
# Place le focus sur la zone d'entrée de texte (curseur de la souris dans la zone de saisie)
entree1.focus_force()
# Widget Button utilisant la méthode destroy SANS PARENTHESES
boutQ = Button(fenetre, text='Quitter', command=fenetre.destroy)
boutQ.pack(padx=5, pady=5)
fenetre.mainloop()
PCSI – IPT G. Monod
Initiation_Tkinter.docx
2/9
Widget Canvas - Bouton associé à une fonction : command = fonction
# Fonction appelée par le bouton boutM
def dessineDisque():
""" Déplace le disque aléatoirement """
global largeur, hauteur, dessin, disque
xd = rd.randint(30,largeur-30)
yd = rd.randint(30,largeur-30)
# Modification de la position du disque
dessin.coords(disque,xd,yd,xd+30,yd+30)
fenetre = Tk()
fenetre.title('Widget Canvas')
# widget Canvas = zone de dessin (ou fichier image png)
hauteur, largeur = 250, 300
dessin = Canvas(fenetre,bg='dark grey',height=hauteur, width=largeur)
# Positionnment du widget sur le côté gauche
dessin.pack(side=LEFT, padx=5, pady=5)
# Dessin d'un disque dans la zone de dessin
xd, yd = rd.randint(largeur), rd.randint(hauteur)
disque = dessin.create_oval(xd, yd, xd+30, yd+30, width=1, fill='red')
# Les coordonnées sont celles du rectangle circonscrit à l'oval
# width = épaisseur du contour ; fill = couleur de remplissage
# widget Label = zone de texte à lire
label1
= Label(fenetre, text='Déplace le disque')
# Positionnment automatique
label1.pack(padx=5, pady=5)
# widget Button utilisant appelant la fonction dessineDisque SANS PARENTHESES
boutM = Button(fenetre, text='Move !', command=dessineDisque)
# Positionnment automatique
boutM.pack(padx=5, pady=5)
boutQ = Button(fenetre, text='Quitter', command=fenetre.destroy)
# Positionnment du widget en bas
boutQ.pack(side=BOTTOM,padx=5, pady=5)
fenetre.mainloop()
PCSI – IPT G. Monod
Initiation_Tkinter.docx
3/9
Animation
D'après le site de Gérard Swinnen auteur de « Apprendre à programmer avec Python 3 » : http://inforef.be/swi/python.htm
def deplaceDisque():
""" Déplace le disque """
global x, y, dx, dy, anim
x, y = x +dx, y + dy
if x > 210:
x, dx, dy = 210, 0, 15
if y > 210:
y, dx, dy = 210, -15, 0
if x < 10:
x, dx, dy = 10, 0, -15
if y < 10:
y, dx, dy = 10, 15, 0
dessin.coords(oval,x,y,x+30,y+30)
if anim:
fenetre.after(50,deplaceDisque)
# boucler après 50 millisecondes
def stop_anim():
""" Arrêt de l'animation """
global anim
anim = False
def start_anim():
""" Démarrage de l'animation """
global anim
if not anim: # pour éviter que le bouton ne puisse lancer plusieurs boucles
anim =True
deplaceDisque()
x, y
= 10, 10
dx, dy = 15, 0
anim = False
# coordonnées initiales
# Pas du déplacement
# commutateur
fenetre = Tk()
fenetre.title("Animation avec Tkinter")
dessin = Canvas(fenetre,bg='dark grey',height=250, width=250)
dessin.pack(side=LEFT, padx =5, pady =5)
oval = dessin.create_oval(x, y, x+30, y+30, width=2, fill='red')
boutQ = Button(fenetre,text='Quitter', width =8, command=fenetre.destroy)
boutQ.pack(side=BOTTOM,padx =5, pady =5)
boutD = Button(fenetre, text='Démarrer', width =8, command=start_anim)
boutD.pack(padx =5, pady =5)
boutA = Button(fenetre, text='Arrêter', width =8, command=stop_anim)
boutA.pack(padx =5, pady =5)
fenetre.mainloop()
PCSI – IPT G. Monod
Initiation_Tkinter.docx
4/9
Détection des événements : clics souris - event.x, event.y et bind() <Button-1>"
# Fonction appelée lors de la détection d'un clic gauche (<Button-1>)
def souris(event): # event est un évènement (ici clic bouton gauche)
""" Récupération des coordonnées du pointeur de la souris """
xsouris, ysouris = event.x, event.y
# Affichage des coordonnées dans une boîte de dialogue
showinfo('Clic','x =' + str(xsouris) + '\ny =' + str(ysouris))
# Facultatif mais utile pour le débogage ; affichage dans la console
print(xsouris, ysouris)
fenetre = Tk()
fenetre.title('Détection clics')
# Surveillance du bouton gauche (<Button-1>) de la souris et appel de souris()
fenetre.bind("<Button-1>", souris)
fenetre.mainloop()
Détection des événements : clavier - event.char, repr() et bind() <Key>, <Up> , <Down> , <Left>, <Right>
# Fonction appelée lors de la détection d'une touche enfoncée (<Key>)
def clavier(event): # event est un évènement (ici touche enfoncée)
""" Récupération de la touche enfoncée """
touchePressee = event.char
# Affichage de la touche enfoncée
showinfo('Touche pressée',repr(touchePressee))
# Facultatif mais utile pour le débogage ; affichage dans la console
print(repr(touchePressee))
fenetre = Tk()
fenetre.title('Détection clavier')
# Indispensable : place le focus sur la fenêtre qui vient d'être créée
fenetre.focus_force()
# Surveillance du clavier (<Key>) et appel de clavier()
fenetre.bind("<Key>", clavier)
# Autres choix : <Up>, <Down>, <Left>, <Right> (flèches du clavier)
fenetre.mainloop()
PCSI – IPT G. Monod
Initiation_Tkinter.docx
5/9
Déplacer une image au clavier ou à la souris (clics ou drag & drop) <B1-Motion>
def deplaceImage(event,dx,dy):
""" Déplace un objet grâce aux flèches """
global xc, yc, dessin, chat
xc += dx
yc += dy
dessin.move(chat,dx,dy)
def sourisClic(event):
""" Déplace un objet jusqu'au point cliqué """
global xc, yc
xs, ys = event.x, event.y
deplaceImage(None,xs-xc,ys-yc)
# Réutilisation d'une fct dans un autre contexte => event remplacé par None
def sourisDrag(event):
""" Déplace un objet par drag & drop """
global xc, yc, dessin
xs, ys = event.x, event.y
objet = dessin.find_closest(xs,ys)
deplaceImage(None,xs-xc,ys-yc)
fenetre = Tk()
fenetre.title("Déplacement d'un objet")
pas = 15
# Pas de déplacement de l'objet au clavier
hauteur, largeur = 300, 300 # Dimensions de la zone de dessin
dessin = Canvas(fenetre,bg='dark grey',height=hauteur, width=largeur)
dessin.pack(side=LEFT, padx=10, pady=10)
# Coordonnées du centre de l'image
# Personnaliser le chemin (chemin complet si StartDir est resté vide dans Shell/Configuration des Shells)
xc, yc = 100, 100
fichier = 'Projets\\Scratchcat2.png'
# Création d'une image Tkinter (utiliser un fichier png)
imgChat = PhotoImage(file='Projets\\Scratchcat2.png')
# Création d'un objet dans la zone de dessin
chat = dessin.create_image(xc,yc, image=imgChat)
label1 = Label(fenetre, text='Flèches, clic souris ou drag & drop')
label1.pack(padx=5, pady=5)
boutQ = Button(fenetre, text='Quitter', command=fenetre.destroy)
boutQ.pack(side=BOTTOM,padx=10, pady=10)
fenetre.focus_force()
# Syntaxe appel d'une fct AVEC PARAMETRES : utilisation de lambda
fenetre.bind("<Up>",
lambda event : deplaceImage(event, 0, -pas))
fenetre.bind("<Down>", lambda event : deplaceImage(event, 0, pas))
fenetre.bind("<Left>", lambda event : deplaceImage(event, -pas,0))
fenetre.bind("<Right>", lambda event : deplaceImage(event, pas,0))
fenetre.bind("<Button-1>", sourisClic)
# Surveillance du drag & drop
fenetre.bind("<B1-Motion>", sourisDrag)
fenetre.mainloop()
PCSI – IPT G. Monod
Initiation_Tkinter.docx
6/9
Créer, effacer des objets - Activer / désactiver des boutons
def creeCarre():
""" Dessine un carré s'il n'en existe aucun """
global existeCarre, xr, yr, largeur, hauteur, cote, dessin, carre
if not existeCarre:
xr, yr = rd.randint(cote,largeur-cote), rd.randint(cote,hauteur-cote)
carre = dessin.create_rectangle(xr, yr, xr+cote, \
yr+cote, width=1, fill='blue')
existeCarre = True
boutC.configure(state=DISABLED) # Activation / désactivation de boutons
boutE.configure(state=ACTIVE)
def effaceCarre():
""" Efface un carré existant """
global dessin,existeCarre, carre
if existeCarre:
dessin.delete(carre)
existeCarre = False
boutC.configure(state=ACTIVE)
boutE.configure(state=DISABLED)
def deplaceCarre(event,dx,dy):
""" Déplace un carré grâce aux flèches """
global existeCarre, xr, yr, cote, dessin, carre
if existeCarre:
xr += dx
yr += dy
dessin.move(carre,dx,dy) # Ou dessin.coords(carre,xr,yr,xr+cote,yr+cote)
def sourisClic(event):
""" Déplace un carré jusqu'au point cliqué """
global existeCarre, xr, yr, cote
xs, ys = event.x, event.y
if existeCarre:
deplaceCarre(None,xs-xr-cote//2,ys-yr-cote//2)
def sourisDrag(event):
""" Déplace un carré par drag & drop """
global existeCarre, xr, yr, cote, dessin, carre
if existeCarre:
xr, yr = event.x - cote//2, event.y - cote//2
objet = dessin.find_closest(xr,yr)
dessin.coords(carre,xr,yr,xr+cote,yr+cote)
fenetre = Tk()
fenetre.title("Déplacement d'un carré")
existeCarre = False # Initialisation du booléen utilisé par les fonctions
pas = 15
# Pas de déplacement du carré au clavier
cote = 30
# Dimension du côté du carré
hauteur, largeur = 250, 300 # Dimensions de la zone de dessin
dessin = Canvas(fenetre,bg='dark grey',height=hauteur, width=largeur)
dessin.pack(side=LEFT, padx=10, pady=10)
label1 = Label(fenetre, text='Flèches ou souris')
label1.pack(padx=5, pady=5)
boutC = Button(fenetre, text='Crée un carré', width = 15, command= creeCarre)
boutC.pack(padx=5, pady=5)
boutC.configure(state=ACTIVE)
# Bouton actif
boutE = Button(fenetre, text='Efface un carré', width = 15, command=effaceCarre)
boutE.pack(padx=5, pady=5)
boutE.configure(state=DISABLED)
# Bouton désactivé
boutQ = Button(fenetre, text='Quitter', command=fenetre.destroy)
boutQ.pack(side=BOTTOM,padx=10, pady=10)
fenetre.focus_force()
fenetre.bind("<Up>",
lambda event : deplaceCarre(event, 0, -pas))
fenetre.bind("<Down>", lambda event : deplaceCarre(event, 0, pas))
fenetre.bind("<Left>", lambda event : deplaceCarre(event, -pas,0))
fenetre.bind("<Right>", lambda event : deplaceCarre(event, pas,0))
fenetre.bind("<Button-1>", sourisClic)
fenetre.bind("<B1-Motion>", sourisDrag)
fenetre.mainloop()
PCSI – IPT G. Monod
Initiation_Tkinter.docx
7/9
Récupération du contenu de Entry : get() - Version 1
def afficheTexte():
""" Récupération du texte tapé """
# Méthode get()
valeur = int(entree1.get())
if (valeur) > 0:
# Affichage d'une boîte de dialogue "info"
showinfo('Mon info','Bravo !\nA bientôt ?')
fenetre.destroy()
else:
# Affichage d'une boîte de dialogue "warning"
showwarning('Mon avertissement','Raté ! \nTry again...')
monTexte =''
# Effacement du contenu de entree1
fenetre = Tk()
fenetre.title('Ma fenêtre')
label1 = Label(fenetre, text='Entrer un entier : ',fg='blue', bg='yellow')
label1.pack(side=LEFT,padx=10, pady=10)
monTexte = ''
entree1 = Entry(fenetre, textvariable=monTexte, width=50, fg='red')
entree1.focus_set()
entree1.pack(side=LEFT,padx=10, pady=10)
entree1.focus_force()
boutA = Button(fenetre, text='Affiche', command=afficheTexte)
boutA.pack(padx=10, pady=10)
boutQ = Button(fenetre, text='Quitter', command=fenetre.destroy)
boutQ.pack(padx=10, pady=10)
fenetre.mainloop()
Récupération du contenu de Entry : IntVar( ) - Version 2
def afficheTexte():
""" Récupération du texte tapé """
# Utilise une variable typée (cf. ci-dessous)
valeur = monTexte.get()
if (valeur) > 0:
# Affichage d'une boîte de dialogue "info"
showinfo('Mon info','Bravo !\nA bientôt ?')
fenetre.destroy()
else:
# Affichage d'une boîte de dialogue "warning"
showwarning('Mon avertissement','Raté ! \nTry again...')
monTexte.set('')
# Effacement du contenu de entree1
fenetre = Tk()
fenetre.title('Ma fenêtre')
label1 = Label(fenetre, text='Entrer un entier > 0 : ',fg='blue', bg='yellow')
label1.pack(side=LEFT,padx=10, pady=10)
# Création d'une variable Tkinter traçable (modification détectable par get)
monTexte = IntVar()
# Autres types : StringVar, BooleanVar, DoubleVar
# Utilisation de cette variable dans entree1
entree1 = Entry(fenetre, textvariable=monTexte, width=100, fg='red')
Suite identique
PCSI – IPT G. Monod
Initiation_Tkinter.docx
8/9
Disposition avancée des widgets : grid( )
La méthode grid() permet de disposer les widgets sur une « grille »
(une matrice) constituée de lignes et de colonnes.
fenetre = Tk()
fenetre.title('Ma fenêtre')
label1 = Label(fenetre, text='Nom : ',fg='red')
label2 = Label(fenetre, text='Prénom : ')
label3 = Label(fenetre, text='2015', fg='red')
dessin = Canvas(fenetre, width=200, height=200, bg='lightgrey')
entree1 = Entry(fenetre, width=50, fg='blue')
entree2 = Entry(fenetre, width=50)
bout1 = Button(fenetre, text='Appliquer', padx =20)
bout2 = Button(fenetre, text='Quitter', command=fenetre.destroy, padx =20)
# sticky = collé, E = Est, O = Ouest, rowspan = fusion de lignes
label1 .grid(row=1,
entree1.grid(row=1,
label2 .grid(row=2,
entree2.grid(row=2,
dessin .grid(row=1,
bout1 .grid(row=3,
bout2 .grid(row=3,
label3 .grid(row=4,
column=1,
column=2)
column=1,
column=2)
column=3,
column=2)
column=3)
column=2,
sticky=E, padx=10)
sticky=E, padx=10)
rowspan=2, padx=10, pady=10)
pady=10)
fenetre.mainloop()
Autres boîtes de dialogue du module tkinter.messagebox
showinfo(), showwarning(), showerror()
askquestion(), askokcancel(), askyesno(), askretrycancel()
Gestion rigoureuse des exceptions
Il existe des instructions permettant une gestion rigoureuse des erreurs susceptibles de se produire au cours d’un programme :
try / except.
Voir par exemple :
https://openclassrooms.com/courses/les-exceptions-9
http://fsincere.free.fr/isn/python/cours_python_ch5.php
À vous de jouer !
PCSI – IPT G. Monod
Initiation_Tkinter.docx
9/9
Téléchargement