seance4_4h.md
-
Derrode Stéphane authoredDerrode Stéphane authored
Sommaire
BE #4 : Application de dessin vectoriel
L'objectif de ce TD est d'apprendre à manipuler quelques composants du module Python Tkinter permettant de créer des interfaces graphiques. Vous allez créer une application simple de dessin vectoriel, qui permettra de tracer à la souris les formes définies dans le BE #2.
Quelques éléments de Tkinter (45 min.)
Le module Tkinter ("Tk interface") permet de créer des interfaces graphiques. Il contient de nombreux composants graphiques (ou widgets), tels que les boutons (classe Button), les cases à cocher (classe CheckButton), les étiquettes (classe Label), les zones d'entrée de texte (classe Entry), les menus (classe Menu), ou les zones de dessin (classe Canvas).
Durant ce BE, nous vous recommandons de conserver la documentation de Tkinter ouverte dans un onglet. Elle contient des exemples de code qui vous seront utiles pour utiliser chacun des widgets.
Voici un premier exemple de code Tkinter :
import random
from tkinter import *
def tirage():
nb = random.randint(1, 100)
texteResultat.set('Nombre : ' + str(nb))
if __name__ == '__main__':
# création de l'arbre de scène
racine = Tk() # Appel à une méthode de classe (et non un constructeur, cf slide cours #2)
racine.title('Tirage aléatoire')
racine.geometry('300x100+400+400')
# Les widgets de la scène
boutonLancer = Button(racine, text='Tirage')
boutonLancer.pack(side=LEFT, padx=5, pady=5)
texteResultat = StringVar()
labelResultat = Label(racine, textvariable=texteResultat)
labelResultat.pack(side=LEFT, padx=5, pady=5)
boutonQuitter = Button(racine, text='Quitter')
boutonQuitter.pack(side=LEFT, padx=5, pady=5)
# association des commandes aux widgets
boutonLancer.config(command=tirage) # appel dit callback (pas de parenthèses)
boutonQuitter.config(command=racine.quit) # idem
racine.mainloop() # affichage de l'interface jusqu'à quit
Exercice 1 - Copiez le code suivant dans un fichier appelé Exo1.py et exécutez-le pour observer le résultat.
Attention, utilisateurs de Mac : l'association Spyder+Tkinter ne fonctionne pas bien sous Mac! Lorsque vous quitterez l'interface (par le biais du bouton quitter), la fenêtre va se bloquer (freeze). Deux solutions:
- soit vous forcez l'application à s’arrêter à chaque fois (utilisez le menu contextuel sur l'icône de l'application concernée dans la barre d'outils);
- soit vous exécutez votre programme en ligne de commande. Pour cela, ouvrez un terminal dans le répertoire de travail (clic-droit dessus → Nouveau terminal au dossier). Puis lancer la commande :
python3 Exo1.py
. Vous devriez pouvoir quitter l'application sans difficulté. N'oubliez pas de sauvegarder votre fichier sous Spyder avant toute exécution!
Prenez le temps d'étudier cet exemple, et répondez aux questions suivantes :
- Combien d'éléments contient l'arbre de scène ?
- Que se passe-t-il lorsqu'on clique sur le bouton ?
- Comment peut-on inverser les positions des deux boutons ?
- Comment peut-on augmenter l'espace à gauche et à droite du label ?
- Comment peut-on colorier le texte du label en rouge ?
Squelette de l'application de dessin (45 min.)
On souhaite obtenir l'interface ci-dessous, dans laquelle les utilisateurs sélectionneront le type de forme à dessiner avec les boutons, et créeront une forme en cliquant dans la zone située sous la barre d'outils (widget Canvas de Tkinter). On a donné une couleur grise au fond de la fenêtre pour vous aider à déterminer les widgets présents.

Exercice 2 - Dessinez l'arbre de scène correspondant à cette capture d'écran.
Une pratique courante dans les interfaces graphiques est de créer des classes qui remplacent des nœuds de l'arbre de scène, et d'y mettre le code de l'application. Ces classes héritent des classes de Tkinter (pour pouvoir les remplacer dans l'arbre), et nous leur ajouterons des attributs et méthodes spécifiques à leurs responsabilités dans l'application de dessin. Nous allons ainsi introduire deux classes :
- la classe ZoneAffichage, qui hérite de Canvas et gère toutes les opérations de dessin spécifiques à votre application.
- la classe FenPrincipale, qui hérite de Tk et gère l'initialisation de l'arbre de scène et des callbacks des widgets.
Voici le diagramme UML correspondant :
Exercice 3 - Complétez le code ci-dessous avec l'initialisation de votre arbre de scène. Vous utiliserez une instance de ZoneAffichage à la place de Canvas. À ce stade, on ne vous demande pas de programmer les actions, uniquement de mettre en place le design de l'interface. Vous trouverez des exemples d'utilisation de chacun des widgets dans la documentation référencée plus haut.
from tkinter import *
class ZoneAffichage(Canvas):
def __init__(self, parent, largeur, hauteur):
Canvas.__init__(self, parent, width=largeur, height=hauteur)
class FenPrincipale(Tk):
def __init__(self):
Tk.__init__(self)
# L'initialisation de l'arbre de scène se fait ici
if __name__ == "__main__":
fen = FenPrincipale()
fen.mainloop()
Dessin de formes dans le canevas (60 min.)
Vous trouverez dans le dossier de ce BE le fichier formes.py développé durant le BE #2. Nous avons agrémenté les classes Rectangle et Ellipse pour qu'elles reçoivent un canevas en argument et se dessinent dessus lors de leur initialisation. Téléchargez ce fichier dans votre répertoire de travail.
Les classes seront intégrées selon le diagramme UML suivant :
Exercice 4 - Créez une méthode ajout_forme(..., x, y)
dans ZoneAffichage qui crée un Rectangle dont le centre sera donné par les 2 arguments x et y, de largeur 10 et de hauteur 20. N'oubliez pas de stocker ce rectangle dans ZoneAffichage !
Exercice 5 - À l'aide de la méthode bind
vue en cours, reliez les clics de souris sur le canevas (évènements <ButtonRelease-1>
) à la méthode ajout_forme
. Attention, pour utiliser une méthode comme fonction de callback, il faut la précéder de self.
.