From cabf45628236ce8fa1b3ae29dac291465c1eb653 Mon Sep 17 00:00:00 2001 From: Tipragot Date: Thu, 19 Oct 2023 15:29:33 +0200 Subject: [PATCH] Espoir --- .../C06_tp_fractales_arbres.py | 117 ++++ .../__pycache__/dessin.cpython-311.pyc | Bin 0 -> 26245 bytes Chapitre 2 - Récursivité/dessin.py | 511 ++++++++++++++++++ 3 files changed, 628 insertions(+) create mode 100644 Chapitre 2 - Récursivité/C06_tp_fractales_arbres.py create mode 100644 Chapitre 2 - Récursivité/__pycache__/dessin.cpython-311.pyc create mode 100644 Chapitre 2 - Récursivité/dessin.py diff --git a/Chapitre 2 - Récursivité/C06_tp_fractales_arbres.py b/Chapitre 2 - Récursivité/C06_tp_fractales_arbres.py new file mode 100644 index 0000000..f3bf955 --- /dev/null +++ b/Chapitre 2 - Récursivité/C06_tp_fractales_arbres.py @@ -0,0 +1,117 @@ +# coding: utf-8 +# +# Chapitre 6 - TP 2 - Dessiner des fractales (page 129) +# + +from math import cos, sin, radians +from matplotlib.colors import rgb2hex +from dessin import Dessin, Trait, Arc, main_loop + + +# constantes +TAILLE = 800 # taille de la surface de dessin +# la surface de dessin, avec un fond blanc +dessin_arbre = Dessin("white", TAILLE, TAILLE) + + +def ytree( + n, + x, + y, + orientation_arbre, + longueur, + ecart_angulaire_fils=radians(22.5), + ratio_longueur=0.8, + n_max=None, +): + """ Dessine un arbre binaire ytree de hauteur n+1. + Entrée: + - n : hauteur de l'arbre courant -1 + - x,y : coordonnées de la racine de l'arbre + - orientation_arbre : orientation de l'arête partant de la racine du y-tree courant, en radians: radians(0) si horizontale, radians(90) = pi/2 pour orienter verticalement vers le haut + - longueur : longueur de l'arête partant de la racine du y-tree courant + - ratio_longueur : le ratio servant à diminuer la longueur d'un niveau à l'autre. + - n_max : le nombre de niveaux de l'arbre global. Utilisé uniquement pour attribuer la bonne couleur en fonction du niveau du y-tree courant dans l'arbre global. + ------- + Un y-tree de niveau 1 (n=1) est un simple trait. + Un y-tree de niveau n>=2 est formé d'un trait suivi de 2 y-trees de niveau n-1. + Dans un y-tree: + => c'est l'arête rejoignant un noeud à son père qui est l'axe de symétrie entre ses 2 sous-arbres. + => l'angle entre les fils est constant: 2*ecart_angulaire_fils, donc chaque arête s'écarte de l'arête parente d'un angle +/-'ecart_angulaire_fils'. + On a choisi de diminuer la longueur d'un facteur constant à chaque niveau. + Les branches sont colorées proportionnellement à leur niveau dans l'arbre : + => le lien partant de la racine globale est noir + => pour n >= 2, les liens aboutissant aux feuilles sont verts + ------- + Complexité: linéaire dans la taille de l'arbre. + """ + if n >= 0: + if n_max == None: + n_max = n + x1 = x + cos(orientation_arbre) * longueur + y1 = y + sin(orientation_arbre) * longueur + couleur = rgb2hex((0.0, 1.0, 0.0)) + if n_max >= 1: + couleur = rgb2hex((0.0, float(n_max - n) / (n_max), 0.0)) + Trait(dessin_arbre, x, y, x1, y1, couleur) + ytree(n-1, x1, y1, orientation_arbre + ecart_angulaire_fils, longueur*ratio_longueur, ecart_angulaire_fils, ratio_longueur, n_max) + ytree(n-1, x1, y1, orientation_arbre - ecart_angulaire_fils, longueur*ratio_longueur, ecart_angulaire_fils, ratio_longueur, n_max) + # A compléter + # Pour effectuer les appels récursifs : + # - ecart_angulaire_fils, ratio_longueur, n_max ne changent pas + # - la nouvelle origine est x1, y1 + # - la longueur diminue et devient longueur*ratio_longueur + # - l'orientation des sous-arbre est obtenue en fonction de orientation_arbre et ecart_angulaire_fils + + +def dessiner_arbre_binaire_arc(n, x, y, w): + """ Dessine un arbre binaire de hauteur n+1, en représentant les arêtes par des arcs de cercle. + Entrée: + - x,y : coordonnées de la racine de l'arbre + - w : écart horizontal entre les 2 fils de la racine. + - h : écart vertical entre 2 niveaux + - n : hauteur de l'arbre courant -1 + ------- + L'arbre est représenté verticalement de haut en bas. + Les 2 noeuds sont reliés par des arcs de cercles à leurs parents (1/4 de cercle pour chaque fils, de manière à former un demi-cercle d'un fils à l'autre). + => C'est l'axe vertical qui est l'axe de symétrie entre les 2 sous-arbres. + => En divisant w par 2 à chaque niveau, on a la garantie que les branches ne se croisent pas (et comme les noeuds sont de simples points qui n'ont pas de largeur, il n'y a pas de risque que les feuilles se recouvrent). + ------- + Complexité: linéaire dans la taille de l'arbre. + """ + if n > 0: + Arc(dessin_arbre, x - w / 2, y, x + w / 2, y + w, 0, 180) + dessiner_arbre_binaire_arc(n-1, x-w/2, y+w/2, w/2) + dessiner_arbre_binaire_arc(n-1, x+w/2, y+w/2, w/2) + + +def dessiner_arbre_binaire_trait(n, x, y, w, h): + """ Dessine un arbre binaire classique de hauteur n+1. + Entrée: + - x,y : coordonnées de la racine de l'arbre + - w : écart horizontal entre les 2 fils de la racine. + - h : écart vertical entre 2 niveaux + - n : hauteur de l'arbre courant -1 + ------- + L'arbre est représenté verticalement de haut en bas. + => C'est l'axe vertical qui est la bisectrice entre les 2 arêtes rejoignant un noeud à ses fils. + => En divisant w par 2 à chaque niveau, on a la garantie que les branches ne se croisent pas (et comme les noeuds sont de simples points qui n'ont pas de largeur, il n'y a pas de risque que les feuilles se recouvrent). + A vous de modifier pour que cet arbre soit fractal en modifiant l'écart vertical. + Tel qu'il est écrit en l'absence de modification, cet arbre n'est pas réellement fractal car l'angle entre les fils varie à chaque niveau (donc en "zoomant" on n'a pas exactement la même figure que la figure globale). + ------- + Complexité: linéaire dans la taille de l'arbre. + """ + # A modifier : + if n > 0: + Trait(dessin_arbre, x, y, x - w / 2, y + h) + Trait(dessin_arbre, x, y, x + w / 2, y + h) + dessiner_arbre_binaire_trait(n - 1, x - w / 2, y + h, w / 2, h) + dessiner_arbre_binaire_trait(n - 1, x + w / 2, y + h, w / 2, h) + + +# Test : +ytree(10, 100, 200, 0, 50, radians(22.5), .8) +dessiner_arbre_binaire_arc(5, 600, 20, 160) +dessiner_arbre_binaire_trait(5, 600, 400, 160, 40) + +main_loop() diff --git a/Chapitre 2 - Récursivité/__pycache__/dessin.cpython-311.pyc b/Chapitre 2 - Récursivité/__pycache__/dessin.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd00516a7d9ad6cd473cefd07dc78de6c20870fc GIT binary patch literal 26245 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 literal 0 HcmV?d00001 diff --git a/Chapitre 2 - Récursivité/dessin.py b/Chapitre 2 - Récursivité/dessin.py new file mode 100644 index 0000000..9762634 --- /dev/null +++ b/Chapitre 2 - Récursivité/dessin.py @@ -0,0 +1,511 @@ +# coding: utf-8 + +# +# Bibliothèque de dessin pour le TP du C06 et l'activité du C07. +# +# Cette bibliothèque définit un classe Dessin pour créer une surface de dessin : +# s = Dessin() +# et les classes Disque, Trait, Rectangle pour créer des formes que l'on peut ensuite modifier +# d = Disque(s, 40, 50, 10, 'red') # disque de centre (40, 50), de rayon 10 et de couleur rouge +# t = Trait(s, 10, 20, 100, 100, 'blue') # trait de (10, 20) à (100, 100) de couleur bleue +# r = Rectangle(s, 100, 20, 200, 120, 'green') # rectangle de coins (100, 20) et (200, 120), de couleur verte +# a = Arc(s, 10, 20, 100, 120, 45, 90, 'red') # arc de cercle ou d'ellipse contenu dans le rectangle (10, 20), (100, 120), +# commençant à 45º et de longueur 90º, de couleur rouge +# t = Texte(s, 100, 200, "Hello", 'black') # texte "Hello" centré à la position 100, 200, de couleur noire +# +# Note : chaque constructeur peut prendre des paramètres optionnels supplémentaires : +# Dessin: couleur de fond, largeur, hauteur +# Disque, Rectangle : épaisseur du bord, couleur du bord (pas de bord par défaut) +# Trait, Arc : épaisseur du trait (1 par défaut) +# Texte : police (triplet nom de police, taille, style), ancre (point cardinal par rapport auquel le texte est positionné) +# +# Les méthodes disponibles sur une forme f : +# f.couleur('yellow') changer la couleur de remplissage (sauf Trait et Arc) +# f.epaisseur(4) changer l'épaisseur du bord ou du trait +# f.bord('black') changer la couleur du bord (pour le trait et l'arc utiliser f.couleur) +# f.rayon(20) changer le rayon (Disque seulement) +# f.angle_debut(90) changer l'angle de début (Arc seulement) +# f.largeur_angulaire(180) changer la longueur de l'arc (Arc seulement) +# f.police(("Times", 12, "bold")) changer la police de caractères (Texte seulement) +# f.ancre('nord-est') changer la position relative du texte (Texte seulement, voir code ci-dessous) +# +# f.bouge(10, 20) déplacer la forme de 10 pixels en x et 20 pixels en y +# f.bouge_1(10, 20) déplacer le premier point de la forme (Rectangle, Trait et Arc seulement) +# f.bouge_2(10, 20) déplacer le second point de la forme (Rectangle, Trait et Arc seulement) +# +# f.position(50, 70) déplacer la forme pour que son centre soit à la position (50, 70) +# f.position_1(50, 70) déplacer le premier point de la forme à cette position (Rectangle, Trait et Arc seulement) +# f.position_2(50, 70) déplacer le second point de la forme à cette position (Rectangle, Trait et Arc seulement) +# +# f.dessous() mettre la forme f en-dessous de toutes les autres +# f.dessus() mettre la forme f au-dessus de toutes les autres +# f.efface() effacer la forme, qui devient alors inutilisable (les autres méthodes de font rien) +# +# Enfin on peut animer le dessin grâce à la méthode `anime` de la classe Surface : +# s.anime(50, pas) # appelle la fonction `pas` toutes les 50 millisecondes +# s.arrete() # arrête l'animation +# +# IMPORTANT : +# La fonction main_loop() doit être appelée à la fin du programme principal +# pour lancer la boucle d'interaction. + +# Eviter le bug qui plante (certains) MacOS avec TkInter et Python3 ! +import platform + +# if platform.mac_ver()[0] != '' and platform.python_version_tuple()[0] != '2': +# exit('TkInter crashes on this Mac with Python 3. Use Python 2.x') + +# Importer la bonne version de TkInter + +import tkinter as tk + +_root = tk.Tk() + + +def main_loop(): + """ Lancer la boucle d'interaction """ + tk.mainloop() + + +class Dessin: + """ Cette classe représente une surface de dessin. + Toutes les autres classes prennent une instance de cette classe dans leur constructeur. + """ + + def __init__(self, fond="white", largeur=800, hauteur=800): + # créer une surface de dessin qui remplit la fenêtre + self.canvas = tk.Canvas(_root, bg=fond, width=largeur, height=hauteur) + self.canvas.pack(expand=True, fill="both") + self.stop_animation = False + + def anime(self, dt, pas): + """ Appeler la fonction `pas` toutes les dt ms """ + pas() + if not self.stop_animation: + self.canvas.after(dt, lambda: self.anime(dt, pas)) + self.stop_animation = False + + def arrete(self): + """ Arrêter l'animation """ + self.stop_animation = True + + +class Disque: + """ Cette classe représente un disque de centre, rayon et couleur donnés. + Le disque peut avoir un bord d'épaisseur et de couleur donnés. + Si la couleur de remplissage est '', le disque est "vide", + et si son épaisseur de bord n'est pas nulle, il s'affiche comme un cercle. + """ + + def __init__(self, dessin, x, y, r, couleur='black', epaisseur=0, bord="black"): + self.dessin = dessin + self.item = dessin.canvas.create_oval( + x - r, y - r, x + r, y + r, fill=couleur, width=epaisseur, outline=bord + ) + + def couleur(self, c): + """ Changer la couleur du disque """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, fill=c) + + def epaisseur(self, e): + """ Changer l'épaisseur du bord du disque """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, width=e) + + def bord(self, c): + """ Changer la couleur du bord du disque """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, outline=c) + + def rayon(self, r): + """ Changer le rayon du disque """ + if self.item: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + x, y = (x1 + x2)/2, (y1 + y2)/2 + self.dessin.canvas.coords(self.item, x - r, y - r, x + r, y + r) + + def position(self, x, y): + """ Changer la position du disque """ + if self.item: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + r = (x2 - x1)/2 + self.dessin.canvas.coords(self.item, x - r, y - r, x + r, y + r) + + def bouge(self, dx, dy): + """ Déplacer le disque de dx, dy """ + if self.item: + self.dessin.canvas.move(self.item, dx, dy) + + def dessus(self): + """ Placer le disque au-dessus des autres objets """ + if self.item: + self.dessin.canvas.tag_raise(self.item) + + def dessous(self): + """ Placer le disque en dessous des autres objets """ + if self.item: + self.dessin.canvas.tag_lower(self.item) + + def efface(self): + """ Effacer le disque, qui ne peut plus alors être utilisé """ + if self.item != None: + self.dessin.canvas.delete(self.item) + self.item = None + + +class Trait: + """ Cette classe représente un trait d'extrémités et de couleur donnés """ + + def __init__(self, dessin, x1, y1, x2, y2, couleur="black", epaisseur=1): + self.dessin = dessin + self.item = dessin.canvas.create_line( + x1, y1, x2, y2, fill=couleur, width=epaisseur + ) + + def couleur(self, c): + """ Changer la couleur du trait """ + if self.item != None: + self.dessin.canvas.itemconfigure(self.item, fill=c) + + def epaisseur(self, e): + """ Changer l'épaisseur du trait """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, width=e) + + def position(self, x, y): + """ Changer la position du (milieu du) trait """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 + self.dessin.canvas.move(self.item, x - cx, y - cy) + + def position_1(self, x, y): + """ Changer la position de la première extrémité du trait """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x, y, x2, y2) + + def position_2(self, x, y): + """ Changer la position de la seconde extrémité du trait """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1, y1, x, y) + + def bouge(self, dx, dy): + """ Déplacer le trait de dx, dy """ + if self.item != None: + self.dessin.canvas.move(self.item, dx, dy) + + def bouge_1(self, dx, dy): + """ Déplacer la première extrémité du trait de dx, dy """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1 + dx, y1 + dy, x2, y2) + + def bouge_2(self, dx, dy): + """ Déplacer la seconde extrémité du trait de dx, dy """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1, y1, x2 + dx, y2 + dy) + + def dessus(self): + """ Placer le trait au-dessus des autres objets """ + if self.item: + self.dessin.canvas.tag_raise(self.item) + + def dessous(self): + """ Placer le trait en dessous des autres objets """ + if self.item: + self.dessin.canvas.tag_lower(self.item) + + def efface(self): + """ Effacer le trait, qui ne peut plus alors être utilisé """ + if self.item != None: + self.dessin.canvas.delete(self.item) + self.item = None + + +class Arc: + """ Cette classe représente un arc de cercle donné par le carré qui l'encadre, + l'angle de départ, la largeur angulaire, et sa couleur + """ + + def __init__( + self, + dessin, + x_min, + y_min, + x_max, + y_max, + s_angle, + largeur_angulaire, + couleur="black", + epaisseur=1, + ): + """ + x_min, y_min, x_max, y_max : coordonnées du carré contenant le cercle complet + s_angle : angle en degré du départ de l'arc par rapport à l'axe des x : 0 est situé à 3h, 90 à 12h... + largeur_angulaire : largeur angulaire de l'arc, en degré (180 pour un demi-cercle) + """ + self.dessin = dessin + # dans canvas un arc est créé par + self.item = dessin.canvas.create_arc( + x_min, + y_min, + x_max, + y_max, + start=s_angle, + extent=largeur_angulaire, + outline=couleur, + width=epaisseur, + style="arc", + ) + + def couleur(self, c): + """ Changer la couleur du trait """ + if self.item != None: + self.dessin.canvas.itemconfigure(self.item, outline=c) + + def epaisseur(self, e): + """ Changer l'épaisseur du trait """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, width=e) + + def dessus(self): + """ Placer le rectangle au-dessus des autres objets """ + if self.item: + self.dessin.canvas.tag_raise(self.item) + + def angle_debut(self, a): + """ Changer l'épaisseur du trait """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, start=a) + + def largeur_angulaire(self, a): + """ Changer l'épaisseur du trait """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, extent=a) + + def position(self, x, y): + """ Changer la position du centre de l'arc """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 + self.dessin.canvas.move(self.item, x - cx, y - cy) + + def position_1(self, x, y): + """ Changer la position du premier coin du rectangle englobant de l'arc """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x, y, x2, y2) + + def position_2(self, x, y): + """ Changer la position du second coin du rectangle englobant de l'arc """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1, y1, x, y) + + def bouge(self, dx, dy): + """ Déplacer l'arc de dx, dy """ + if self.item != None: + self.dessin.canvas.move(self.item, dx, dy) + + def bouge_1(self, dx, dy): + """ Déplacer le premier coin du rectangle englobant de l'arc """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1 + dx, y1 + dy, x2, y2) + + def bouge_2(self, dx, dy): + """ Déplacer le second coin du rectangle englobant de l'arc """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1, y1, x2 + dx, y2 + dy) + + def dessous(self): + """ Placer le rectangle en dessous des autres objets """ + if self.item: + self.dessin.canvas.tag_lower(self.item) + + def efface(self): + """ Effacer le trait, qui ne peut plus alors être utilisé """ + if self.item != None: + self.dessin.canvas.delete(self.item) + self.item = None + + +class Rectangle: + """ Cette classe représente un rectangle de coordonnées et de couleur donnés. + Le rectangle peut avoir un bord d'épaisseur et de couleur donnés. + Si la couleur de remplissage est '', le rectangle est "vide", + et si son épaisseur de bord n'est pas nulle, il s'affiche comme un cadre. + """ + + def __init__( + self, dessin, x1, y1, x2, y2, couleur="black", epaisseur=0, bord="black" + ): + self.dessin = dessin + self.item = dessin.canvas.create_rectangle( + x1, y1, x2, y2, fill=couleur, width=epaisseur, outline=bord + ) + + def couleur(self, c): + """ Changer la couleur du rectangle """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, fill=c) + + def epaisseur(self, e): + """ Changer l'épaisseur du bord du rectangle """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, width=e) + + def bord(self, c): + """ Changer la couleur du bord du rectangle """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, outline=c) + + def position(self, x, y): + """ Changer la position du (centre du) rectangle """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 + self.dessin.canvas.move(self.item, x - cx, y - cy) + + def position_1(self, x, y): + """ Changer la position du premier coin du rectangle """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x, y, x2, y2) + + def position_2(self, x, y): + """ Changer la position du second coin du rectangle """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1, y1, x, y) + + def bouge(self, dx, dy): + """ Déplacer le rectangle de dx, dy """ + if self.item: + self.dessin.canvas.move(self.item, dx, dy) + + def bouge_1(self, dx, dy): + """ Déplacer le premier coin du rectangle """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1 + dx, y1 + dy, x2, y2) + + def bouge_2(self, dx, dy): + """ Déplacer le second coin du rectangle """ + if self.item != None: + x1, y1, x2, y2 = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x1, y1, x2 + dx, y2 + dy) + + def dessus(self): + """ Placer le rectangle au-dessus des autres objets """ + if self.item: + self.dessin.canvas.tag_raise(self.item) + + def dessous(self): + """ Placer le rectangle en dessous des autres objets """ + if self.item: + self.dessin.canvas.tag_lower(self.item) + + def efface(self): + """ Effacer le rectangle, qui ne peut plus alors être utilisé """ + if self.item != None: + self.dessin.canvas.delete(self.item) + self.item = None + + +# Traduire les directions cardinales en constantes Tk +tk_anchors = { + "n": tk.N, + "nord": tk.N, + "s": tk.S, + "sud": tk.S, + "e": tk.E, + "est": tk.E, + "w": tk.W, + "o": tk.W, + "ouest": tk.W, + "ne": tk.NE, + "nord-est": tk.NE, + "se": tk.SE, + "sud-est": tk.SE, + "nw": tk.NW, + "no": tk.NW, + "nord-ouest": tk.NW, + "sw": tk.SW, + "so": tk.SW, + "sud-ouest": tk.SW, + "c": tk.CENTER, + "centre": tk.CENTER, +} + + +class Texte: + """ Cette classe représente un texte de position, contenu et couleur donnés. + Le texte peut aussi avoir une police et une ancre. + La police est un tuple (famille, taille, style) où famille est le nom de la police + (par exemple Times ou Helvetica), taille la taille en points, et style (optionnel) + une chaîne de caractères contenant les mots "bold", "italic", "underline", et/ou "overstrike". + L'ancre indique la position du texte par rapport à sa position. + Par défaut ('c') l'ancre est au milieu du texte. + On peut aussi indiquer un des points cardinaux ('n', 's', 'e', 'o', 'ne', 'no', 'se', 'so'). + Par exemple 'so' indique que l'ancre est au coin sud-ouest du texte, + donc que le texte est au-dessus et à droite de la position x, y. + """ + + def __init__(self, dessin, x, y, texte, couleur="black", police=None, ancre="c"): + self.dessin = dessin + self.item = dessin.canvas.create_text(x, y, text=texte, fill=couleur) + self.police(police) + self.ancre(ancre) + + def texte(self, t): + """ Changer la contenu du texte """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, text=t) + + def couleur(self, c): + """ Changer la couleur du texte """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, fill=c) + + def police(self, p): + """ Changer la police du texte """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, font=p) + + def ancre(self, a): + """ Changer l'ancre du texte """ + if self.item: + self.dessin.canvas.itemconfigure(self.item, anchor=tk_anchors[a.lower()]) + + def position(self, x, y): + """ Changer la position du texte """ + if self.item: + x, y = self.dessin.canvas.coords(self.item) + self.dessin.canvas.coords(self.item, x, y) + + def bouge(self, dx, dy): + """ Déplacer le texte de dx, dy """ + if self.item: + self.dessin.canvas.move(self.item, dx, dy) + + def dessus(self): + """ Placer le texte au-dessus des autres objets """ + if self.item: + self.dessin.canvas.tag_raise(self.item) + + def dessous(self): + """ Placer le texte en dessous des autres objets """ + if self.item: + self.dessin.canvas.tag_lower(self.item) + + def efface(self): + """ Effacer le texte, qui ne peut plus alors être utilisé """ + if self.item != None: + self.dessin.canvas.delete(self.item) + self.item = None