TP — Prise en main de Tkinter : une cible interactive⚓︎
Objectifs⚓︎
🎯 Objectifs
Dans ce TP, on réalise progressivement une petite application graphique représentant une cible.
L'utilisateur peut :
- cliquer dans la zone de dessin pour placer un impact ;
- marquer un nombre de points qui dépend de la position de l'impact ;
- consulter son score total ;
- effacer tous les impacts à l'aide d'un bouton.
Ce TP permet de découvrir :
- la structure d'un programme utilisant Tkinter ;
- quelques composants graphiques appelés widgets ;
- le dessin de formes géométriques dans un
Canvas; - l'association d'une fonction à un bouton ;
- la gestion d'un événement provoqué par un clic de souris.
Prérequis
Variables, expressions, instructions conditionnelles, boucles, fonctions et listes.
1. Une première fenêtre⚓︎
Exercice 1
Tkinter est une bibliothèque qui permet de créer des interfaces graphiques en Python. Elle est installée avec la plupart des distributions de Python.
Recopier puis exécuter le programme suivant dans un environnement de programmation Python (Spyder, Thonny, Pyzo ...).
from tkinter import *
# Création de la fenêtre principale
fenetre = Tk()
fenetre.title("Cible interactive")
# Création d'un texte dans la fenêtre
titre = Label(fenetre, text="Cliquez sur la cible !")
titre.pack()
# Lancement de la boucle de gestion des événements
fenetre.mainloop()
Résultat attendu

Question 1
Repérer l'instruction qui crée la fenêtre principale.
Question 2
Modifier le titre affiché dans la barre supérieure de la fenêtre.
Question 3
Modifier le texte du widget Label.
Question 4
Commenter temporairement la dernière instruction, puis exécuter de nouveau le programme. Que constate-t-on ?
La structure d'un programme Tkinter
Un programme Tkinter comporte généralement quatre étapes :
- importer la bibliothèque Tkinter ;
- créer la fenêtre principale avec
Tk(); - créer et placer les widgets dans cette fenêtre ;
- lancer la boucle événementielle avec
mainloop().
L'appel à mainloop() place le programme en attente. Tkinter surveille alors les actions de l'utilisateur : clic sur un bouton, clic dans le canevas, appui sur une touche, fermeture de la fenêtre, etc.
On parle de programmation événementielle : l'ordre d'exécution dépend en partie des événements provoqués par l'utilisateur.
Créer et placer un widget
L'instruction suivante crée un widget de type Label contenu dans fenetre :
titre = Label(fenetre, text="Cliquez sur la cible !")
L'instruction titre.pack() demande ensuite à Tkinter de placer ce widget dans la fenêtre.
La méthode pack() empile les widgets les uns à la suite des autres. Elle suffit pour les interfaces simples de ce TP.
2. Créer une zone de dessin⚓︎
Exercice 2
On souhaite placer sous le titre une zone de dessin de 600 pixels de largeur et 400 pixels de hauteur.
Ajouter les constantes suivantes au début du programme, après l'importation de Tkinter.
LARGEUR = 600
HAUTEUR = 400
Ajouter ensuite les instructions suivantes avant l'appel à mainloop().
canvas = Canvas(
fenetre,
width=LARGEUR,
height=HAUTEUR,
background="#dce8f2"
)
canvas.pack()
Résultat attendu

Question 1
Exécuter le programme et vérifier que la zone de dessin apparaît sous le titre.
Question 2
Modifier la couleur de fond du canevas.
Question 3
Remplacer temporairement LARGEUR par 400. Quelle dimension de la zone est modifiée ?
Le widget Canvas
Un Canvas, appelé aussi canevas, est une zone dans laquelle on peut dessiner des formes géométriques, du texte ou des images.
Son origine est située dans son coin supérieur gauche.
- l'abscisse
xaugmente vers la droite ; - l'ordonnée
yaugmente vers le bas.
Ainsi, le point de coordonnées (0, 0) est le coin supérieur gauche du canevas.
3. Dessiner la cible⚓︎
Exercice 3 : dessiner un premier disque
On choisit pour centre de la cible le point de coordonnées (300, 200).
Ajouter les constantes suivantes au début du programme.
X_CENTRE = LARGEUR // 2
Y_CENTRE = HAUTEUR // 2
Pour dessiner un disque de rayon 150 pixels, ajouter avant mainloop() et après la création du Canevas :
canvas.create_oval(
X_CENTRE - 150,
Y_CENTRE - 150,
X_CENTRE + 150,
Y_CENTRE + 150,
fill="white",
outline="black"
)
Résultat attendu

Question 1
Exécuter le programme et vérifier que le disque est centré dans le canevas.
Question 2
Les quatre premières valeurs transmises à create_oval sont les coordonnées de deux points. Quels sommets du rectangle encadrant le disque représentent-elles ?
Question 3
Modifier le rayon du disque pour obtenir un disque de rayon 120 pixels.
Dessiner dans un Canvas
Les méthodes suivantes permettent de dessiner quelques formes usuelles :
canvas.create_rectangle(x1, y1, x2, y2, fill="blue")
canvas.create_oval(x1, y1, x2, y2, fill="red")
canvas.create_line(x1, y1, x2, y2, fill="black")
canvas.create_text(x, y, text="Bonjour")
Pour un rectangle ou un ovale, (x1, y1) et (x2, y2) sont deux sommets opposés du rectangle qui délimite la forme.
Exercice 4 : trois disques concentriques
On veut obtenir trois disquesde rayons 150, 100 et 50 pixels, colorées respectivement en blanc, rouge et jaune.
Résultat attendu

Question 1
- Compléter la fonction suivante et insérer son code juste avant la création de la fenêtre.
def dessiner_cible():
canvas.create_oval(
X_CENTRE - 150, Y_CENTRE - 150,
X_CENTRE + 150, Y_CENTRE + 150,
fill="white", outline="black"
)
# À compléter : disque rouge de rayon 100
# À compléter : disque jaune de rayon 50
Question 2
Supprimer le premier appel isolé à canvas.create_oval, puis appeler dessiner_cible() avant mainloop().
Question 3
Ajouter deux lignes noires, l'une horizontale et l'autre verticale, passant par le centre de la cible.
Ordre des dessins
Les formes sont dessinées les unes au-dessus des autres dans leur ordre de création. Il faut donc dessiner le plus grand disque en premier et le plus petit en dernier.
4. Réagir à un clic dans le canevas⚓︎
Exercice 5
Ajouter la fonction suivante avant la création des widgets.
def tirer(event):
x = event.x
y = event.y
print("Clic aux coordonnées", x, y)
Puis ajouter l'instruction suivante après la création du canevas :
canvas.bind("<Button-1>", tirer)
Question 1
Cliquer à plusieurs endroits dans le canevas et observer la console.
Question 2
Cliquer près du coin supérieur gauche, puis près du coin inférieur droit. Vérifier que les coordonnées affichées sont cohérentes avec le repère du canevas.
Question 3
Quelle instruction associe l'événement « clic gauche » à la fonction tirer ?
Un gestionnaire d'événement
La méthode bind associe un événement à une fonction :
canvas.bind("<Button-1>", tirer)
Lorsque l'utilisateur clique dans le canevas, Tkinter appelle automatiquement tirer(event).
Le paramètre event est un objet créé par Tkinter. Il contient des informations sur l'événement. Pour un clic de souris :
event.xcontient l'abscisse du clic ;event.ycontient l'ordonnée du clic.
Dessiner un impact⚓︎
Exercice 6
On représente un impact par un petit disque noir de rayon 4 pixels.
Question 1
Compléter la fonction tirer afin qu'un impact apparaisse à l'endroit du clic.
def tirer(event):
x = event.x
y = event.y
canvas.create_oval(
# coordonnées à compléter
fill="black",
outline="black"
)
Question 2
Tester le programme en plaçant plusieurs impacts.
Question 3
Modifier la taille et la couleur des impacts.
5. Calculer le score d'un tir⚓︎
Exercice 7
Le nombre de points dépend de la distance entre l'impact et le centre de la cible :
| Zone atteinte | Nombre de points |
|---|---|
| disque jaune | 5 |
| disque rouge | 3 |
| disque blanc | 1 |
| extérieur de la cible | 0 |
Pour éviter de calculer une racine carrée, on compare le carré de la distance au carré du rayon :
Question 1
Compléter la fonction suivante et insérer son code juste avant la fonction tirer.
def calculer_points(x, y):
distance_carre = (x - X_CENTRE) ** 2 + (y - Y_CENTRE) ** 2
if distance_carre <= 50 ** 2:
return 5
# À compléter pour les trois autres cas
Question 2
Pourquoi faut-il tester la zone jaune avant la zone rouge ?
Question 3
Donner les résultats des appels suivants sans les exécuter, puis vérifier vos réponses dans la console.
calculer_points(300, 200)
calculer_points(360, 200)
calculer_points(420, 200)
calculer_points(500, 200)
6. Afficher et mettre à jour le score⚓︎
Exercice 8
Ajouter les variables globales suivantes avant la définition des fonctions.
score_total = 0
texte_score = None
Après la création de la fenêtre, remplacer la valeur None par une variable Tkinter et créer un nouveau Label :
texte_score = StringVar()
texte_score.set("Score : 0")
label_score = Label(fenetre, textvariable=texte_score)
label_score.pack()
Résultat attendu

Question 1
Remplacer la fonction tirer par la version suivante, puis compléter les deux lignes indiquées.
def tirer(event):
global score_total
x = event.x
y = event.y
canvas.create_oval(
x - 4, y - 4,
x + 4, y + 4,
fill="black",
outline="black"
)
points = ... # à compléter
score_total = ... # à compléter
texte_score.set("Score : " + str(score_total))
Question 2
Tester plusieurs tirs et vérifier le cumul des points.
Question 3
Expliquer pourquoi la conversion str(score_total) est nécessaire.
Question 4
Expliquer le rôle de l'instruction global score_total.
Une variable Tkinter associée à un widget
Une StringVar contient un texte susceptible de changer pendant l'exécution du programme.
texte_score = StringVar()
texte_score.set("Score : 0")
label_score = Label(fenetre, textvariable=texte_score)
Lorsque la méthode set modifie le contenu de texte_score, le texte affiché par le Label est automatiquement actualisé.
7. Commander une action avec un bouton⚓︎
Exercice 9
On souhaite effacer tous les impacts et remettre le score à zéro. Pour cela, il faut d'abord mémoriser dans une liste les identifiants des impacts créés.
Ajouter cette variable globale avant la définition des fonctions :
impacts = []
La méthode create_oval renvoie un entier qui identifie la forme créée dans le canevas.
Dans la fonction tirer, remplacer l'appel à create_oval par :
impact = canvas.create_oval(
x - 4, y - 4,
x + 4, y + 4,
fill="black",
outline="black"
)
impacts.append(impact)
Compléter la fonction suivante et l'insérer après la fonction tirer.
def effacer_impacts():
global score_total
for impact in impacts:
# Supprimer du canevas la forme identifiée par impact
...
impacts.clear() # vider la liste `impacts`
score_total = ... # à compléter
texte_score.set(...) # à compléter
Pour supprimer une forme du canevas à partir de son identifiant, on utilise :
canvas.delete(identifiant)
Créer le bouton après le canevas et avant mainloop().
bouton_effacer = Button(
fenetre,
text="Effacer les impacts",
command=effacer_impacts
)
bouton_effacer.pack()
Résultat attendu

Question 1
Tester le bouton après avoir placé plusieurs impacts.
Question 2
Dans l'argument command, pourquoi écrit-on effacer_impacts et non effacer_impacts() ?
Le paramètre command d'un Button
Le paramètre command reçoit la fonction que Tkinter devra appeler lors d'un clic sur le bouton.
Button(fenetre, text="Effacer", command=effacer_impacts)
Il ne faut pas ajouter de parenthèses après le nom de la fonction. Avec command=effacer_impacts(), la fonction serait appelée immédiatement lors de la création du bouton.
Deux façons de déclencher une fonction
| Interaction | Association | Paramètre reçu par la fonction |
|---|---|---|
clic sur un Button |
command=fonction |
aucun |
| événement dans un widget | widget.bind(evenement, fonction) |
un objet event |
8. Organisation du programme final⚓︎
Exercice 10
Vérifier que le programme respecte l'organisation générale suivante.
from tkinter import *
# Constantes
# ...
# Variables globales représentant l'état de l'application
# ...
# Fonctions
def dessiner_cible():
pass
def calculer_points(x, y):
pass
def tirer(event):
pass
def effacer_impacts():
pass
# Création et configuration de la fenêtre principale
fenetre = Tk()
# ...
# Création et placement des widgets
# ...
# Dessin initial et association des événements
dessiner_cible()
canvas.bind("<Button-1>", tirer)
# Boucle événementielle
fenetre.mainloop()
L'ordre reste important
Une fonction peut utiliser une variable globale créée plus loin dans le fichier, à condition que cette variable existe au moment où la fonction est réellement appelée.
En revanche, les fonctions doivent être définies avant les instructions qui les utilisent pour créer un bouton ou associer un événement.
9. Bilan⚓︎
Exercice 11
Répondre aux questions suivantes sans exécuter le programme.
Question 1
Quel widget constitue la fenêtre principale ?
Question 2
Quel widget permet de dessiner des formes géométriques ?
Question 3
Quelle méthode place simplement un widget dans une fenêtre ?
Question 4
Quel est le rôle de mainloop() ?
Question 5
Quelle différence y a-t-il entre command=effacer_impacts et canvas.bind("<Button-1>", tirer) ?
Question 5
Comment récupère-t-on les coordonnées d'un clic dans le canevas ?
Question 6
À quoi sert l'entier renvoyé par canvas.create_oval(...) ?
Question 7
Quelles sont les variables qui représentent l'état de l'application ? Quels objets appartiennent plutôt à son interface graphique ?