fonctions Turtle

Identification

Infoforall

12 - Une tâche une fonction


Nous allons maintenant utiliser la puissance des fonctions pour réaliser des choses sympathiques avec le module Turtle.

Vous allez également voir qu'il faut tenter d'associer chaque tâche avec une fonction dans vos programmes. Et on pourra les emboîter les unes dans les autres.

Logiciel nécessaire pour l'activité : Python 3

Evaluation ✎ : question 10-11-12-13-14-15

Documents de cours : open document ou pdf

1 - Fonction nouveau stylo

AVERTISSEMENT

Dans cette activité, il faudra garder vos différentes fonctions en mémoire puisque le but est de créer de nouvelles fonctions lançant des appels aux vielles fonctions.

Rappel sur la structure d'un programme :

  • Importations
  • Déclaration des CONSTANTES
  • Déclaration des fonctions
  • Programme principal

Nous allons commencer par créer une fonction nouveau_stylo() qui va nous permettre de créer un stylo en définissant ses caractéristiques.

01 ✔° Mettre la fonction en mémoire puis utiliser la fonction via la console en utilisant les instructions fournies en exemple console.

1 2 3 4 5 6 7 8 9 10
import turtle as trt def nouveau_stylo(ecriture, fond, largeur): """Renvoie la référence d'un stylo configuré""" feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre
>>> s1 = nouveau_stylo("red","black", 4) >>> s1.forward(150) >>> s1.left(120) >>> s1.forward(150) >>> s1.left(120) >>> s1.forward(150) >>> s1.left(120)

02-A° Lors de l'appel, dans quel paramètre sera stocké l'argument "red", l'argument "black" et l'argument 4 ? Pour répondre, il suffit d'aller lire la première ligne de la déclaration.

>>> s1 = nouveau_stylo("red","black", 4)

...CORRECTION...

02-B° Que renvoie cette fonction nouveau_stylo() ? Pour répondre, il suffit de chercher son return et comprendre ce qu'il renvoie en allant cherchant plus haut dans les instructions l'affectation de feutre.

  1. Un dessin
  2. La référence d'une "tortue" (Turtle) qui nous servira de crayon
  3. La couleur du crayon
  4. La réference d'un dessin

...CORRECTION...

Ligne 5, on récupère et stocke la référence d'un objet créé avec Turtle() (tortue) :
feutre = trt.Turtle().

  • Lignes 6 à 9 : on configure cet objet d'écriture.
  • Ligne 10 : on renvoie feutre et donc la référence de l'objet Turtle qui va nous servir à dessiner.
  • 2 - Déplacer le crayon avec une fonction

    Nous allons maintenant rajouter une fonction deplacer() qui permet de modifier la position du crayon. Si vous vous souvenez, c'est assez pénible à taper à la main :

    • Il faut soulever le crayon avec penup()
    • Déplacer le crayon avec goto()
    • Abaisser le crayon avec pendown() pour pouvoir dessiner à nouveau

    03 ✔° Mettre le programme complet en mémoire. Lire les instructions pur vérifier qu'il comporte

    1. une partie Importation au début
    2. une partie Déclaration des fonctions
    3. une partie Programme, qui commence en ligne 36

    Le programme permet de tracer trois traits en utilisant deux fonctions qui vont nous simplifier un peu le travail

    Programme complet

    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
    # Importation des modules supplémentaires import turtle as trt # Déclaration des fonctions def nouveau_stylo(ecriture, fond, largeur): """Renvoie la référence d'un stylo configuré :: param ecriture(str) :: une couleur autorisée (pour l'écriture) :: param fond(str) :: une couleur autorisée (pour le fond éventuel) :: param largeur(int) :: la largeur d'écriture :: return (Turtle) :: la référence du crayon """ feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y): """Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour """ feutre.penup() # On lève la pointe feutre.goto(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe # Programme s1 = nouveau_stylo("red","black", 4) s1.forward(150) deplacer(s1, 0, 0) s1.left(120) s1.forward(150) deplacer(s1, 0, 0) s1.left(120) s1.forward(150)

    04° Observer l'appel de la ligne 36 et la déclaration de la ligne 5 puis l'appel de la ligne 38 et la déclaration de la ligne 21.

    Questions

    1. lors de l'appel sur la ligne 36, quels sont les arguments envoyés ? Dans quels paramètres sont-ils stockés dans la fonction ?
    2. lors de l'appel sur la ligne 38, quels sont les arguments envoyés ? Dans quels paramètres sont-ils stockés dans la fonction ?
    5 | | | 36
    def nouveau_stylo(ecriture, fond, largeur): ... s1 = nouveau_stylo("red","black", 4)
    21 | | | 38
    def deplacer(feutre, x, y): ... deplacer(s1, 0, 0)

    ...CORRECTION...

    1. Les arguments "red","black", 4 vont être stockés dans les paramètres ecriture, fond, largeur.
    2. Les arguments s1, 0, 0 vont être stockés dans les paramètres feutre, x, y.
    1 - Modifier l'état par effet de bord

    Modifier par effet de bord veut dire modifier sans utiliser une nouvelle affectation (c'est à dire sans utiliser l'opérateur = en Python).

    Comme on peut le voir, cette fonction modifie l'état du "crayon", puisqu'il est déplacé.

    30 31 32 33
    feutre.penup() # On lève la pointe feutre.goto(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe

    Sans retour. Le crayon est directement modifié.

    Du coup, il faudrait l'indiquer dans la documentation :

    21 22 23 24 25 26 27 28 29 30 31 32 33
    def deplacer(feutre, x, y): """Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre """ feutre.penup() # On lève la pointe feutre.goto(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe

    3 - Une fonction pour chaque tâche

    Nous allons voir ici quelque chose de fondamental au niveau de la conception des fonctions : chaque fonction de base ne doit normalement réaliser qu'une tâche basique.

    Si on a besoin de réaliser une tâche complexe comment faire alors ?

    Réponse : on crée une autre fonction qui va faire appel aux fonctions de base précédentes.

    05° Deux choses à faire :

    1. Placer le nouveau programme ci-dessous en mémoire en remplacement de l'ancien.
    2. Compléter uniquement la fonction trois() de façon à ce qu'elle
      • trace un trait sur la bonne distance puis tourne à gauche de l'angle transmis en paramètre,
      • trace un trait sur la bonne distance puis tourne à gauche de l'angle transmis en paramètre,
      • trace un trait sur la bonne distance puis tourne à gauche de l'angle transmis en paramètre.

    AIDE : allez voir la documentation de la fonction trois() de façon à connaître les paramètres dont elle dispose pour faire ce travail.

    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
    # Importation des modules supplémentaires import turtle as trt # Déclaration des fonctions def nouveau_stylo(ecriture, fond, largeur): """Renvoie la référence d'un stylo configuré :: param ecriture(str) :: une couleur autorisée (pour l'écriture) :: param fond(str) :: une couleur autorisée (pour le fond éventuel) :: param largeur(int) :: la largeur d'écriture :: return (Turtle) :: la référence du crayon """ feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y): """Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre """ feutre.penup() # On lève la pointe feutre.goto(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe def trois(feutre, distance, angle): """Fait avancer le crayon de la distance, tourne de l'angle donné. Trois fois. :: param feutre(Turtle) :: la référence du crayon :: param distance(int) :: la distance à parcourir :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de feutre """ pass def triangle(ftr, cote): """Trace un triangle (equilatéral) à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr """ trois(ftr, cote, 120) def trace_triangle(ecrit, fd, epaisseur, cx, cy, c): """Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner """ pass def rapide_triangle(cx, cy, c): """Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner """ return trace_triangle("blue", "orange", 3, cx, cy, c) # Programme # -- rien pour le moment --

    Une fois que vous pensez que votre fonction trois() est opérationnelle : relancez le programme pour placer la nouvelle version de la fonction en mémoire. Vous pouvez tester votre fonction en mode interactif par exemple (pensez à modifier la taille des écrans pour voir Thonny et l'écran de Turtle au même moment):

    >>> s1 = nouveau_stylo("red","black", 4) >>> trois(s1, 100, 90) >>> s1.setheading(0) >>> deplacer(s1, 200, 50) >>> trois(s1, 50, 45)

    Pour rappel, setheading() sert à faire pointer la tortue dans une direction particulière.

    ...CORRECTION...

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    def trois(feutre, distance, angle): """Fait avancer le crayon de la distance, tourne de l'angle donné. Trois fois. :: param feutre(Turtle) :: la référence du crayon :: param distance(int) :: la distance à parcourir :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de feutre """ feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle)

    06 ✔° Observer l'unique instruction de la fonction triangle() : elle lance un appel à la fonction trois() en fournissant un angle de 120° : cela lui permet de tracer un triangle.

    1 2 3 4 5 6 7 8 9 10
    def triangle(ftr, cote): """Trace un triangle (equilatéral) à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr """ trois(ftr, cote, 120)

    Tester cette fonction via la console :

    >>> s1 = nouveau_stylo("green","black", 4) >>> triangle(s1, 150)

    07° Répondre aux questions suivantes qui sont liées au transfert de paramètres lors de l'utilisation de ces instructions :

    >>> s1 = nouveau_stylo("green","black", 4) >>> triangle(s1, 150)

      Questions

    1. Que contiennent les paramètres ftr et cote lorsqu'on lance triangle(s1150) ?
    2. Que contiennent alors les paramètres feutre, distance et angle lorsqu'on lance trois(ftr, cote, 120) ?

    Rappel : pour pouvoir répondre, il faut regarder la première ligne de la déclaration de la fonction et la ligne d'appel.

    ...CORRECTION...

    Prototype : def triangle(ftr, cote).

    Appel réalisé : triangle(s1, 150).

    On voit donc que ftr reçoit la référence de s1.

    On voit que cote reçoit la valeur 150.

    Prototype : def trois(feutre, distance, angle).

    Appel réalisé : trois(ftr, cote, 120).

    feutre stocke donc la même référence que ftr, à savoir s1, le crayon du départ !

    distance reçoit cote, soit 150.

    angle fait donc référence à 120.

    On trace donc un triangle (car trois traits avec un angle de 120°) avec le crayon du départ.

    08° Compléter maintenant la fonction trace_triangle() dont les instructions internes doivent réaliser ceci :

    1. Créer un nouveau stylo f (en utilisant nouveau_stylo())
    2. Déplacer ce stylo en (x,y) (en utilisant deplacer())
    3. Demander de surveiller le dessin en vu de faire du remplissage ensuite (avec f.begin_fill())
    4. Dessiner un triangle à cet endroit (en utilisant triangle())
    5. Demander de remplir la forme qu'on vient de créer (avec f.end_fill())
    6. Demander de cacher le feutre-tortue (avec f.hideturtle())
    7. Renvoyer la référence f
    Penser à regarder la documentation de trace_triangle() pour voir quels sont les paramètres disponibles.

    Tester votre fonction en utilisant ceci par exemple :

    >>> trace_triangle("blue", "yellow", 4, 50, 50, 200)

    ...CORRECTION...

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
    def trace_triangle(ecrit, fd, epaisseur, cx, cy, c): """Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner """ f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) f.begin_fill() triangle(f, c) f.end_fill() f.hideturtle() return f
    2 - Une fonction, une tâche

    Comme vous l'avez vu, nous sommes parvenus à réaliser cette action assez complexe en la décomposant en de multiples petites fonctions. C'est une manière de faire qu'il faudra prendre l'habitude d'utiliser.

    Comme on peut le voir,

    • on utilise des fonctions basiques et
    • on crée des fonctions plus complexes qui utilisent ces fonctions basiques.

    09 ✔° Vous trouverez à la fin de votre programme cette fonction rapide_triangle() qui lance un appel à trace_triangle() en lui dissant de tracer en bleu avec un fond orange.

    1 2 3 4 5 6 7 8 9 10
    def rapide_triangle(cx, cy, c): """Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner """ return trace_triangle("blue", "orange", 3, cx, cy, c)

    Tester avec des commandes comme :

    >>> t1 = rapide_triangle(0,0,100) >>> t2 = rapide_triangle(50,90,100) >>> t3 = rapide_triangle(-50,90,100)

    Vous devriez obtenir ceci :

    ✎ 10° Rajouter et compléter la fonction dessin1() dans la partie DECLARATION pour que son appel provoque le dessin précédent : trois triangles.

    1 2 3 4 5 6 7
    def dessin1(): """Réalise le dessin voulu :: return (None) :: Aucun retour """ pass

    Vérifier en lançant l'appel à cette fonction depuis la console.

    >>> dessin1()

    On crée donc des fonctions basiques qui seront appeler par d'autres fonctions pour réaliser des choses plus complexes.

    ✎ 11° Rajouter (dans la partie DECLARATION DES FONCTIONS) la fonction quatre(). Compléter les instructions de façon à ce qu'elle trace un trait, tourne de l'angle, trace un trait, tourne de l'angle, trace un trait, tourne de l'angle, trace un trait et tourne de l'angle. Bref, 4 fois.

    1 2 3 4 5 6 7 8 9 10
    def quatre(feutre, distance, angle): """Fait avancer le crayon de la distance, tourne de l'angle donné. Quatre fois. :: param feutre(Turtle) :: la référence du crayon :: param distance(int) :: la distance à parcourir :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour """ pass

    Si vous faites un appel avec un angle de 90°, elle doit donc vous tracer un carré. Pour cela, vous pouvez placer ces instructions dans la zone PROGRAMME de votre programme

    1 2 3
    # Programme s1 = nouveau_stylo("red","black", 4) quatre(s1, 150, 90)

    ✎ 12° Rajouter puis compléter la fonction carre() de façon à ce qu'elle trace un carré en utilisant... la fonction quatre().

    1 2 3 4 5 6 7 8 9 10
    def carre(ftr, cote): """Trace un carre à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr """ pass

    Pour tester votre fonction, il faudra remplacer les instructions de la zone PROGRAMME par ceci :

    1 2 3
    # Programme s1 = nouveau_stylo("green","black", 4) carre(s1, 150)

    Une fois cette fonction carre() créée, on pourrait l'utiliser pour totalement encapsuler la réalisation d'un carré dans une fonction trace_carre() par exemple :

    1. création d'un stylo,
    2. déplacement du stylo au bon endroit,
    3. gestion du fond coloré et traçage du carré en utilisant carre().

    ✎ 13° En appliquant le principe d'une fonction par tâche, quelle est la seule ligne différente entre les fonctions trace_carre() et trace_triangle() (en dehors de la documentation) ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
    def trace_carre(ecrit, fd, epaisseur, cx, cy, c): """Trace un carré en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner """ f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) f.begin_fill() carre(f, c) f.end_fill() f.hideturtle() return f

    ✎ 14° Compléter la fonction tour() pour que son appel provoque le dessin proposé.

    1 2 3 4 5 6 7
    def tour(): """Réalise le dessin voulu :: return (None) :: Aucun retour """ pass
    >>> tour()

    Nous pourrions continuer et refaire le même cheminement pour créer des disques colorés. Voici de quoi réaliser ce type de dessin. Cette fois, j'ai crée les fonctions

    • Tâche basique : arc_de_cercle() à qui on doit fournir un feutre Turtle et un angle,
    • Tâche moyenne : cercle() qui utilise à l'interne arc_de_cercle() en lui demander de tracer sur 360°,
    • Tâche complexe : trace_disque() qui crée elle-même un feutre avec nouveau_stylo(), qu'elle déplace avec deplacer() et qu'elle fournit à cercle()

    Exemple d'utilisation :

    >>> trace_disque("red", "orange", 10, 0, 0, 100) <turtle.Turtle object at 0x7ff58c7d9be0>

    Résultat :

    Les déclarations de fonctions à rajouter (si vous le voulez):

    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
    def arc_de_cercle(feutre, rayon, angle): """Trace l'arc de cercle voulu. :: param feutre(Turtle) :: la référence du crayon :: param rayon(int) :: le rayon de l'arc de cercle :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour """ feutre.circle(rayon, angle) def cercle(ftr, rayon): """Trace un cercle à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param rayon(int) :: la valeur en pixels du rayon :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr """ arc_de_cercle(ftr, rayon, 360) def trace_disque(ecrit, fd, epaisseur, cx, cy, r): """Trace un disque coloré en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param r(int) :: la valeur en pixels du rayon :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner """ f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) f.begin_fill() cercle(f, r) f.end_fill() f.hideturtle() return f

    ✎ 15° Quelle ligne provoque la réponse sous forme de l'adresse du crayon ayant servi à tracer le disque ?

    >>> trace_disque("red", "orange", 10, 0, 0, 100) <turtle.Turtle object at 0x7ff58c7d9be0>

    4 - FAQ

    Et left, forward... Ce sont des fonctions aussi non ?

    Comme vous avez dû le remarquer, on peut agir sur le crayon / feutre en utilsant des sortes de fonctions : left, forward...

    La syntaxe est néanmoins différentes de celles des fonctions : on doit placer le nom de l'objet sur lequel on agit, placer un point puis placer le nom de la fonction.

    1 2
    feutre.forward(distance) feutre.left(angle)

    Si vous regardez bien, vous pourrez d'ailleurs voir qu'elles sont en rouge foncé, contrairement aux fonctions qui sont en rouge clair.

    Pourquoi ? Tout simplement car ces fonctions portent un nom particulier.

    On les nomme des méthodes : ce sont des fonctions incorporées de base à l'intérieur de certaines structures qu'on nomme des objets. Comme notre Turtle.

    Si vous regardez les codes Python que vous pouvez trouver sur le Web, vous risquez de tomber assez régulièrement sur ces drôles d'appels de fonctions avec le point.

    En résumé, il s'agit juste de fonctions incorporées et on les nomme méthodes.

    La codification est donc : objet.methode().

    Il faut donc veiller à ce qu'une fonction ne soit conçue que pour répondre à UNE tâche.

    On retiendra que pour réaliser une tâĉhe un peu complexe, il faut :

    • isoler les tâches basiques dans des fonctions
    • réaliser les tâches "moyennes" à l'aide de fonctions qui utilisent les tâches basiques
    • réaliser les tâches complexes en utilisant les fonctions basiques et moyennes précédentes

    Une sorte d'emboîtement.

    Activité publiée le 01 11 2020
    Dernière modification : 13 07 2023
    Auteur : ows. h.