Aller au contenu

Algo & Prog 1 Python : TP séance 07

Rendu du TP sur Ametice :
Suivez les instructions pour le rendu du TP en mettant le bon numéro de séance dans le nom du répertoire.
N'oubliez pas de rendre votre travail à la fin de la séance comme indiqué, même si vous n'avez pas fini la planche de TP, puis de rendre la version finale avant le début de la séance suivante.

Exercice 1 : Affichage de polygones

1.a. Mise en place

Recopier dans votre répertoire de travail les modules sommet.py et polygone.py du TP précédent. Écrire un nouveau module app1.py, qui importe les deux autres modules dans l'espace de nom courant. Dans le programme principal de app1.py, recopier l'exemple de dessin en TkInter de la planche de TP01. Vérifier en exécutant app1.py qu'il n'y a pas d'erreur et qu'une fenêtre apparaît.

1.b. Dessin du polygone

Rajouter dans la classe Polygone une méthode dessiner_segments qui prend en paramètre la zone de dessin TkInter zone_dessin.

La méthode affiche le polygone dans zone_dessin en reliant le sommet numéro \(i\) au sommet numéro \(i+1\) pour chaque \(i\), à l'aide de la méthode create_line de zone_dessin.

Dans le programme principal (sous-entendu, de app1.py), instancier un polygone ouvert avec quelques sommets, puis appeler sa méthode dessiner_segments de manière à le dessiner dans la zone de dessin.

1.c. Paramètres optionnels

Rajouter deux paramètres optionnels à la méthode dessiner_segments :

  • un paramètre couleur valant par défaut "black" ;
  • un paramètre fermer valant par défaut false.

Tracer les segments dans la couleur reçue en paramètre. Lorsque fermer est true, tracer le segment reliant le dernier point au premier, de manière à obtenir un polygone fermé.

Tester dans le programme principal en affichant un polygone fermé en "blue".

1.d. Dessin des sommets

Dans la classe Sommet, rajouter une méthode dessiner_sommet qui prend en paramètre la zone de dessin TkInter zone_dessin, et en paramètre optionnel une couleur valant par défaut "black".

La méthode dessiner_sommet dessine un petit carré de rayon 4 pixels, centré autour du sommet, dans la couleur demandée.

Dans la classe Polygone, rajouter une méthode dessiner_sommets qui dessine tous les sommets du polygone dans la zone de dessin. Cette méthode reçoit les mêmes paramètres que la méthode dessiner_sommet d'un Sommet.

Tester dans le programme principal en affichant les sommets de votre polygone en "red".

1.e. Dessin des noms des sommets

Dans la classe SommetNom, rajouter une méthode dessiner_sommet qui prend en paramètre la zone de dessin TkInter zone_dessin, et en paramètre optionnel une couleur valant par défaut "black".

La méthode dessiner_sommet affiche le nom un peu en dessous du sommet, en gris ("grey"), puis appelle la méthode dessiner_sommet de la classe mère en lui transmettant les paramètres.

Tester dans le programme principal en remplaçant quelques Sommet par des SommetNom dans votre Polygone.

Exercice 2. Manipulation de polygones

On se propose maintenant de créer une petite interface graphique en TkInter pour manipuler les sommets du polygone, avec une classe App qui contient toute l'interface.

2.a. Classe App

Copier-coller le code suivant dans un fichier app2.py :

Module app2.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import tkinter, sys
from sommet import *
from polygone import *


class App :

    def __init__ (self) :

        self.creer_fenetre_principale()
        self.creer_donnees()
        self.dessiner_tout()


    def creer_fenetre_principale (self) :

        self.fen_princ = tkinter.Tk()
        self.fen_princ.title("Polygones")

        frame_top = tkinter.Frame(self.fen_princ)
        frame_top.pack(anchor=tkinter.NW)

        b_quit = tkinter.Button(frame_top, text="QUIT", fg="red",
            command=self.fen_princ.destroy)
        b_quit.pack(side=tkinter.LEFT)

        b_vider = tkinter.Button(frame_top, text="Vider",
            command=self.on_bouton_vider)
        b_vider.pack(side=tkinter.LEFT)

        self.zone_dessin = tkinter.Canvas(self.fen_princ, bg="white", 
            width=800, height=600)
        self.zone_dessin.pack(side=tkinter.TOP)

        # Gestion des événements
        self.zone_dessin.bind('<Button-1>', self.on_button1_press)
        self.zone_dessin.bind('<B1-Motion>', self.on_button1_motion)
        self.zone_dessin.bind('<ButtonRelease-1>', self.on_button1_release)


    def run (self) :

        self.fen_princ.mainloop()          # Attend la fermeture de la fenêtre


    def on_bouton_vider (self) :

        print ("Bouton vider enfoncé")


    def on_button1_press (self, event) :

        print ("Bouton1 enfoncé en ", event.x, ",", event.y)


    def on_button1_motion (self, event) :

        print ("Bouton1 déplacé en ", event.x, ",", event.y)


    def on_button1_release (self, event) :

        print ("Bouton1 relaché en ", event.x, ",", event.y)


    def creer_donnees (self) :

        self.polyg1 = Polygone()
        self.polyg1 += SommetNom(30, 40, "0")
        self.polyg1 += SommetNom(200, 70, "1")
        self.polyg1 += SommetNom(100, 150, "2")


    def dessiner_tout (self) :

        self.zone_dessin.delete(tkinter.ALL)  # vide zone_dessin

        self.polyg1.dessiner_segments (self.zone_dessin, couleur="blue")
        self.polyg1.dessiner_sommets (self.zone_dessin, couleur="red")


if __name__ == "__main__" :

    app = App()
    app.run()

Étudier le code ; observer ce qui est affiché dans le terminal lorsqu'il y a un des événements suivants :

  • on clique sur le bouton Vider ;
  • le bouton gauche de la souris est cliqué dans la zone de dessin, déplacé bouton enfoncé, puis relâché.

Repérer dans le code les méthodes qui sont automatiquement appelées par TkInter lorsqu'il y a un événement et qui effectuent ces affichages. On appelle ces méthodes des callbacks.

2.b. Bouton Vider

Dans la classe Polygone, rajouter une méthode vider qui vide la liste sommets en appelant la méthode clear propre aux listes.

Dans la callback on_bouton_vider de App, appeler la méthode vider du polygone polyg1, puis la fonction dessiner_tout.

Vérifier que le bouton permet de supprimer le dessin du polygone dans la fenêtre.

2.c. Insertion de sommets

Dans la callback on_button1_press, instancier un SommetNom aux coordonnées de la souris event.x et event.y, et dont le nom est la représentation str de longueur courante de polyg1. Ajouter ce sommet à la fin de polyg1 puis appeler dessiner_tout.

Vérifier que l'on peut bien rajouter des sommets dans le polygone en cliquant dans la zone de dessin.

2.d. Déplacement d'un sommet

Dans la méthode creer_donnees de la classe App, rajouter un attribut numero_sommet_clique initialisé à -1, pour signifier qu'il n'y a aucun sommet cliqué pour le moment.

Dans la classe Polygone, ajouter une méthode calculer_distance_point recevant en paramètre les coordonnées xA, yA, xB, yB de deux points \(A\) et \(B\). La méthode renvoie la distance euclidienne \(d(A,B)\).

Dans la classe Polygone, ajouter une méthode trouver_sommet_clique qui reçoit en paramètres les coordonnées ev_x et ev_y de la souris. La méthode renvoie le numéro du sommet de sommets qui est le plus proche de la souris, si la distance est inférieure à un seuil valant 8.0, sinon -1 pour signaler qu'aucun sommet n'a été trouvé.

Au début de la callback on_button1_press, mémoriser dans l'attribut numero_sommet_clique le résultat de trouver_sommet_clique. Dans la suite de on_button1_press, on ne doit instancier et rajouter un sommet que si la valeur de numero_sommet_clique est -1. Dans ce cas, on mémorisera de plus le numéro du nouveau sommet dans numero_sommet_clique.

Dans la callback on_button1_motion, si numero_sommet_clique est différent de -1, mémoriser les coordonnées de la souris dans le sommet cliqué, puis appeler dessiner_tout.

Dans la callback on_button_release, mettre systématiquement numero_sommet_clique à -1.

Vérifiez que vous pouvez insérer de nouveaux sommets en cliquant dans la zone de dessin ailleurs que sur un sommet, et que vous pouvez déplacer un sommet en le cliquant et en tirant la souris.

2.e. Suppression d'un sommet

Rajouter dans la classe App une callback on_button_press3, et la rattacher à l'événement '<Button-3>' à l'aide de la méthode bind de zone_dessin.

Dans la callback, chercher le numéro de sommet cliqué, le supprimer de polyg1 ; renommer les sommets suivants, puis appeler dessiner_tout.

Vérifiez que vous pouvez supprimer un sommet en cliquant dessus avec le bouton droit de la souris.