20 - Turtle
Ouverture hors programme. Le module
turtlen’est pas au programme de NSI, mais il permet de voir immédiatement ce que fait un programme : chaque instruction trace un trait. C’est un excellent moyen de s’entraîner aux boucles, aux fonctions et aux variables. Il est fourni avec Python : rien à installer. Les programmes de cette page s’exécutent dans Thonny (ou tout autre éditeur Python installé sur l’ordinateur), pas dans le navigateur.
Le principe : une tortue qui tient un crayon
Le module turtle affiche une fenêtre au centre de laquelle se trouve une tortue, représentée par une petite flèche. La tortue tient un crayon : quand elle avance, elle laisse un trait derrière elle. On la pilote avec des instructions simples : avancer de 100 pas, tourner de 90 degrés vers la gauche, lever le crayon…
Le premier programme fait avancer la tortue, la fait tourner, puis avancer encore :
import turtle
t = turtle.Turtle() # création d'une tortue, rangée dans la variable t
t.forward(100) # avancer de 100 pas
t.left(90) # tourner de 90 degrés vers la gauche
t.forward(50) # avancer de 50 pas
turtle.done() # garder la fenêtre ouverte

Résultat : la tortue est partie vers la droite, puis a tourné vers le haut.
Trois lignes méritent une explication.
import turtlecharge le module, commeimport randomcharge les fonctions de hasard.t = turtle.Turtle()crée une tortue et la range dans la variablet. Ensuite, on lui donne des ordres avec un point :t.forward(100)se lit « tortuet, avance de 100 ». C’est la même écriture quenotes.append(12)pour une liste : une méthode appliquée à un objet.turtle.done()laisse la fenêtre ouverte à la fin du programme, jusqu’à ce qu’on la ferme. Sans cette ligne, selon l’éditeur, la fenêtre peut se fermer dès que le dessin est fini.
Question. Au départ, dans quelle direction la tortue regarde-t-elle ?
Réponse
Vers la droite (l’est) : le premier forward(100) trace un trait horizontal vers la droite.
Le repère de la fenêtre
Chaque point de la fenêtre a deux coordonnées (x, y), comme en mathématiques. La tortue part du centre de la fenêtre, le point (0, 0). L’axe des x va vers la droite, l’axe des y vers le haut. Les directions se mesurent en degrés : 0 vers la droite, 90 vers le haut, 180 vers la gauche et 270 vers le bas.
Le point rouge a pour coordonnées (220, 180) : il se trouve 220 pas à droite du centre et 180 pas au-dessus.
Un pas vaut un pixel de l’écran. La taille de la fenêtre dépend de l’écran ; l’instruction turtle.setup(700, 600) la fixe à 700 pixels de large sur 600 de haut. Les points visibles ont alors des coordonnées comprises entre -350 et 350 pour x, entre -300 et 300 pour y.
| Instruction | Effet |
|---|---|
t.forward(d) | avancer de d pas dans la direction de la tortue |
t.backward(d) | reculer de d pas, sans se retourner |
t.left(a) | tourner de a degrés vers la gauche, sur place |
t.right(a) | tourner de a degrés vers la droite, sur place |
t.goto(x, y) | aller directement au point (x, y) |
t.setheading(a) | s’orienter dans la direction a (0 : droite, 90 : haut) |
t.position() | renvoie la position actuelle, un couple (x, y) |
t.heading() | renvoie la direction actuelle, en degrés |
Question. La tortue part de (0, 0) en regardant vers la droite. Où se trouve-t-elle après t.forward(100), t.left(90), t.forward(50) ? Dans quelle direction regarde-t-elle ?
Réponse
Au point (100, 50), et elle regarde vers le haut : sa direction vaut 90. On peut le vérifier en ajoutant print(t.position(), t.heading()) à la fin du programme.
Répéter avec une boucle : le carré
Pour tracer un carré de côté 100, la tortue doit faire quatre fois la même chose : avancer de 100, puis tourner de 90 degrés. Écrire huit lignes serait fastidieux ; une boucle for répète le bloc :
import turtle
t = turtle.Turtle()
for i in range(4):
t.forward(100)
t.left(90)
turtle.done()

Le carré : quatre fois « avancer, tourner ».
Le tableau suit la tortue à la fin de chaque tour de boucle.
| Tour | i | Position après forward(100) | Direction après left(90) |
|---|---|---|---|
| 1 | 0 | (100, 0) | 90 (haut) |
| 2 | 1 | (100, 100) | 180 (gauche) |
| 3 | 2 | (0, 100) | 270 (bas) |
| 4 | 3 | (0, 0) | 0 (droite) |
Après quatre tours, la tortue est revenue à son point de départ, dans sa direction de départ : elle a fait un tour complet sur elle-même, soit 4 × 90 = 360 degrés.
Question. Que dessine ce programme si l’on remplace t.left(90) par t.right(90) ?
Réponse
Le même carré, mais tracé vers le bas : la tortue tourne dans l’autre sens, donc le carré se trouve sous le point de départ au lieu d’être au-dessus.
Les polygones réguliers et l’angle de rotation
Un polygone régulier a tous ses côtés de même longueur et tous ses angles égaux : triangle équilatéral, carré, pentagone, hexagone… Pour en tracer un à n côtés, la tortue avance et tourne n fois. Mais de combien doit-elle tourner ?
À la fin du tracé, elle doit avoir fait exactement un tour complet, soit 360 degrés, en n rotations égales. Chaque rotation vaut donc 360 / n degrés. Ce n’est pas l’angle intérieur du polygone : c’est l’angle dont la tortue change de direction à chaque coin.
Pour un triangle équilatéral, la tortue tourne donc de 360 / 3 = 120 degrés, alors que les angles du triangle mesurent 60 degrés. C’est l’erreur la plus fréquente : avec left(60), la tortue trace la moitié d’un hexagone.
On écrit une fonction qui trace n’importe quel polygone régulier, puis on l’appelle plusieurs fois :
import turtle
def polygone(t, n, cote):
"""La tortue t trace un polygone régulier à n côtés de longueur cote."""
for i in range(n):
t.forward(cote)
t.left(360 / n)
t = turtle.Turtle()
polygone(t, 3, 120) # triangle
polygone(t, 4, 100) # carré
polygone(t, 6, 70) # hexagone
polygone(t, 8, 55) # octogone
turtle.done()

Quatre appels de la même fonction : tous les polygones partent du même coin.
La tortue est passée en paramètre de la fonction : on pourrait ainsi faire dessiner le même polygone par plusieurs tortues différentes.
Question. Que faut-il écrire pour tracer un cercle approché ?
Réponse
Un polygone qui a beaucoup de petits côtés, par exemple polygone(t, 36, 10) : 36 côtés de 10 pas, avec une rotation de 10 degrés à chaque coin. L’œil ne distingue plus les coins. Le module propose aussi t.circle(r), qui trace un cercle de rayon r.
Lever le crayon et se déplacer
Pour dessiner plusieurs figures séparées, la tortue doit se déplacer sans tracer : on lève le crayon avec t.penup(), on se déplace, puis on le repose avec t.pendown().
import turtle
def carre(t, cote):
"""La tortue t trace un carré de côté cote."""
for i in range(4):
t.forward(cote)
t.left(90)
t = turtle.Turtle()
for i in range(5):
t.penup()
t.goto(-250 + i * 110, 0) # on se place sans tracer
t.pendown()
carre(t, 80)
turtle.done()

Une rangée de cinq carrés : le crayon est levé entre deux carrés.
Le coin de départ du carré numéro i est au point d’abscisse -250 + i * 110 : -250 pour le premier, puis 110 pas de plus à chaque carré (80 pour le carré, 30 d’espace).
Question. Que dessine le programme si l’on oublie la ligne t.penup() ?
Réponse
Les mêmes cinq carrés, mais reliés par des traits : goto trace un segment quand le crayon est baissé. On verrait en plus le trait qui va du centre (0, 0) jusqu’au premier carré.
Couleurs, épaisseur et remplissage
La tortue peut changer de crayon et remplir des formes.
| Instruction | Effet |
|---|---|
t.color("red") | couleur du trait et du remplissage |
t.color("black", "gold") | trait noir, remplissage doré |
t.pensize(3) | épaisseur du trait, en pixels |
t.begin_fill() … t.end_fill() | remplit la forme tracée entre ces deux instructions |
t.circle(r) | cercle de rayon r ; le centre est à r pas sur la gauche de la tortue |
t.dot(d, "red") | point de diamètre d à l’endroit de la tortue |
t.write("texte", font=("Arial", 16, "normal")) | écrit un texte à l’endroit de la tortue |
t.speed(0) | vitesse : de 1 (lent) à 10 (rapide), 0 pour la plus rapide |
t.hideturtle() | cache la flèche de la tortue |
turtle.bgcolor("lightblue") | couleur du fond de la fenêtre |
Les couleurs s’écrivent en anglais ("red", "green", "blue", "orange", "gold", "brown"…) ou en code hexadécimal, comme en CSS : "#2a9d8f".
Le programme suivant dessine une maison avec deux fonctions, l’une pour un rectangle plein, l’autre pour un triangle plein :
import turtle
def rectangle(t, x, y, largeur, hauteur, couleur):
"""Trace un rectangle plein dont le coin inférieur gauche est en (x, y)."""
t.penup()
t.goto(x, y)
t.setheading(0)
t.pendown()
t.color("black", couleur)
t.begin_fill()
for i in range(2):
t.forward(largeur)
t.left(90)
t.forward(hauteur)
t.left(90)
t.end_fill()
def triangle(t, x, y, cote, couleur):
"""Trace un triangle équilatéral plein dont la base part de (x, y)."""
t.penup()
t.goto(x, y)
t.setheading(0)
t.pendown()
t.color("black", couleur)
t.begin_fill()
for i in range(3):
t.forward(cote)
t.left(120)
t.end_fill()
t = turtle.Turtle()
t.speed(0)
t.pensize(2)
rectangle(t, -100, -100, 200, 150, "wheat") # les murs
triangle(t, -100, 50, 200, "firebrick") # le toit
rectangle(t, -25, -100, 50, 80, "saddlebrown") # la porte
rectangle(t, -80, -20, 40, 40, "lightblue") # fenêtre de gauche
rectangle(t, 40, -20, 40, 40, "lightblue") # fenêtre de droite
t.hideturtle()
turtle.done()

La maison : cinq appels à deux fonctions.
Chaque fonction commence par placer la tortue (penup, goto, setheading(0), pendown) : ainsi, elle dessine correctement quel que soit l’endroit où la tortue se trouvait avant l’appel. Ici, chaque figure ramène la tortue à la direction 0 (un rectangle tourne de 4 × 90 = 360 degrés, un triangle de 3 × 120 = 360 degrés) ; mais si l’on appelait rectangle après un t.left(30), le rectangle serait tracé de travers sans setheading(0).
Des variables qui changent : spirales et rosaces
Dans une boucle, la longueur ou l’angle peuvent changer à chaque tour. Le motif obtenu devient alors beaucoup plus riche.
Une spirale : la longueur augmente
import turtle
t = turtle.Turtle()
t.speed(0)
longueur = 5
for i in range(60):
t.forward(longueur)
t.left(90)
longueur = longueur + 5
turtle.done()

La spirale carrée : chaque côté est plus long de 5 pas que le précédent.
Question. Quelles sont les longueurs des quatre premiers côtés tracés ?
Réponse
5, 10, 15 et 20 pas. La variable longueur vaut 5 au premier tour et augmente de 5 après chaque trait, comme un accumulateur.
| Tour | longueur au moment du forward | longueur après la mise à jour |
|---|---|---|
| 1 | 5 | 10 |
| 2 | 10 | 15 |
| 3 | 15 | 20 |
| 4 | 20 | 25 |
En remplaçant t.left(90) par t.left(91), chaque coin est décalé d’un degré : la spirale se met à tourner sur elle-même. C’est un bon exercice d’essayer 89, 121 ou 144.
Une rosace : une figure répétée en tournant
Une rosace répète la même figure en faisant tourner la tortue un peu entre deux répétitions. Pour revenir au point de départ après un tour complet, on répète 36 fois avec une rotation de 360 / 36 = 10 degrés :
import turtle
def carre(t, cote):
for i in range(4):
t.forward(cote)
t.left(90)
t = turtle.Turtle()
t.speed(0)
couleurs = ["crimson", "darkorange", "gold", "seagreen", "royalblue", "purple"]
for i in range(36):
t.color(couleurs[i % 6]) # on change de couleur à chaque carré
carre(t, 120)
t.left(10)
t.hideturtle()
turtle.done()

La rosace : 36 carrés, chacun tourné de 10 degrés par rapport au précédent.
L’expression i % 6 donne le reste de la division de i par 6 : elle vaut successivement 0, 1, 2, 3, 4, 5, 0, 1… On parcourt ainsi la liste des six couleurs en boucle.
Le hasard : un ciel étoilé
Le module random fournit des nombres au hasard : random.randint(a, b) renvoie un entier au hasard compris entre a et b, bornes incluses, et random.choice(L) un élément de la liste L. Avec eux, chaque exécution produit un dessin différent.
import random
import turtle
turtle.setup(700, 600) # fenêtre de 700 sur 600
turtle.bgcolor("midnightblue")
t = turtle.Turtle()
t.hideturtle()
t.speed(0)
t.penup()
for i in range(80):
x = random.randint(-330, 330)
y = random.randint(-280, 280)
taille = random.randint(2, 8)
t.goto(x, y)
t.dot(taille, random.choice(["white", "lightyellow", "lightblue"]))
turtle.done()

Quatre-vingts étoiles tirées au hasard ; une nouvelle exécution donne un autre ciel.
La tortue ne trace rien ici : le crayon reste levé, et seule dot dessine.
Dessiner vite
Même avec t.speed(0), un dessin de plusieurs milliers de traits est long, car la fenêtre est redessinée après chaque trait. On peut suspendre l’affichage, tracer tout le dessin, puis l’afficher d’un coup :
turtle.tracer(0) # plus d'affichage pendant le tracé
# ... tout le dessin ...
turtle.update() # affichage du résultat en une fois
C’est indispensable pour l’animation de la balle, plus bas : on déplace les objets, puis on affiche une seule fois l’image complète.
Réagir au clavier et à la souris
Jusqu’ici, le programme se déroule de la première à la dernière ligne, puis s’arrête. Avec les événements, c’est l’utilisateur qui décide de ce qui se passe : on écrit des fonctions, puis on demande à turtle de les appeler quand une touche est enfoncée ou qu’un clic a lieu. Cette façon de programmer, dite événementielle, est aussi celle de Tkinter et de Pygame.
Piloter la tortue avec les flèches
import turtle
ecran = turtle.Screen() # la fenêtre
ecran.title("Flèches pour diriger, espace pour effacer")
t = turtle.Turtle()
t.shape("turtle") # une vraie tortue au lieu de la flèche
t.pensize(3)
def avancer():
t.forward(20)
def gauche():
t.left(30)
def droite():
t.right(30)
def effacer():
t.clear() # efface les traits de la tortue
# Associer chaque touche à une fonction (sans parenthèses !)
ecran.onkey(avancer, "Up")
ecran.onkey(gauche, "Left")
ecran.onkey(droite, "Right")
ecran.onkey(effacer, "space")
ecran.listen() # la fenêtre écoute le clavier
turtle.done() # attendre les événements

Un dessin réalisé avec les flèches du clavier.
Trois points sont essentiels.
- On écrit
ecran.onkey(avancer, "Up")et nonecran.onkey(avancer(), "Up"): on donne à turtle le nom de la fonction, pour qu’il l’appelle plus tard, à chaque appui sur la flèche. Avec les parenthèses, la fonction serait appelée une seule fois, tout de suite, et la touche ne ferait plus rien. ecran.listen()donne le clavier à la fenêtre ; sans cette ligne, les touches ne sont pas reçues.turtle.done()est indispensable : c’est lui qui attend les événements et appelle les fonctions. Le programme ne s’arrête que lorsque l’on ferme la fenêtre.
Les noms de touches les plus utiles sont "Up", "Down", "Left", "Right", "space", "Return" (Entrée), "Escape" et les lettres ("a", "b"…).
Question. On appuie trois fois sur la flèche du haut, une fois sur la flèche de gauche, puis deux fois sur la flèche du haut. Quelle longueur de trait la tortue a-t-elle tracée ?
Réponse
5 × 20 = 100 pas : chaque appui sur la flèche du haut appelle avancer, qui trace 20 pas ; la flèche de gauche fait seulement tourner la tortue.
Dessiner à la souris
ecran.onclick(f) appelle la fonction f à chaque clic, en lui donnant les coordonnées x et y du point cliqué. La fonction doit donc avoir deux paramètres.
import turtle
ecran = turtle.Screen()
ecran.title("Cliquer pour relier les points")
t = turtle.Turtle()
t.pensize(2)
t.color("darkgreen")
def relier(x, y):
"""Trace un segment jusqu'au point cliqué et y pose un point."""
t.goto(x, y)
t.dot(10, "orange")
ecran.onclick(relier)
turtle.done()

Chaque clic ajoute un segment et un point.
Une animation avec une minuterie
ecran.ontimer(f, 20) appelle la fonction f une seule fois, 20 millisecondes plus tard. Pour obtenir une animation, la fonction se reprogramme elle-même à la fin : elle sera rappelée 20 millisecondes après, et ainsi de suite, environ 50 fois par seconde.
import turtle
ecran = turtle.Screen()
ecran.setup(700, 600)
ecran.title("Balle qui rebondit")
turtle.tracer(0)
balle = turtle.Turtle()
balle.shape("circle")
balle.color("crimson")
balle.penup()
vx = 4 # vitesse horizontale, en pas par image
vy = 3 # vitesse verticale
def animer():
global vx, vy
x = balle.xcor() + vx
y = balle.ycor() + vy
if x > 330 or x < -330: # bord gauche ou droit : on inverse vx
vx = -vx
if y > 280 or y < -280: # bord haut ou bas : on inverse vy
vy = -vy
balle.goto(x, y)
turtle.update()
ecran.ontimer(animer, 20) # rappel dans 20 millisecondes
animer()
turtle.done()
La fonction animer modifie les variables vx et vy, définies en dehors d’elle. Pour qu’elle modifie ces variables plutôt que d’en créer des nouvelles, il faut l’annoncer avec le mot-clé global. Sans cette ligne, Python considère vx et vy comme des variables propres à la fonction, et lève l’erreur UnboundLocalError dès le premier appel, à la ligne x = balle.xcor() + vx.
Question. La balle est en (328, 0) avec vx = 4. Que se passe-t-il à l’image suivante ?
Réponse
Le calcul donne x = 332, qui dépasse 330 : vx devient -4. La balle va en (332, 3), puis repart vers la gauche à l’image suivante (x = 328). Changer le signe d’une vitesse, c’est faire rebondir l’objet.
Plusieurs tortues
Chaque appel à turtle.Turtle() crée une nouvelle tortue, avec sa propre position, sa couleur et son crayon. Le programme suivant organise une course entre quatre tortues, dont chacune avance d’une distance tirée au hasard à chaque tour :
import random
import turtle
turtle.tracer(0)
couleurs = ["crimson", "darkorange", "seagreen", "royalblue"]
noms = ["rouge", "orange", "verte", "bleue"]
tortues = []
for i in range(4):
t = turtle.Turtle()
t.shape("turtle")
t.color(couleurs[i])
t.penup()
t.goto(-300, 90 - i * 60) # une ligne de départ par tortue
t.pendown()
tortues.append(t)
arbitre = turtle.Turtle() # tortue qui trace la ligne d'arrivée
arbitre.hideturtle()
arbitre.penup()
arbitre.goto(250, 130)
arbitre.pendown()
arbitre.goto(250, -130)
gagnante = -1
while gagnante == -1:
for i in range(4):
tortues[i].forward(random.randint(1, 10))
if tortues[i].xcor() >= 250 and gagnante == -1:
gagnante = i
turtle.update()
arbitre.penup()
arbitre.goto(0, 160)
arbitre.write(f"La tortue {noms[gagnante]} gagne !", align="center",
font=("Arial", 18, "bold"))
turtle.done()

La course : chaque tortue est un objet indépendant, rangé dans une liste.
Les tortues sont rangées dans une liste : tortues[i] désigne la tortue numéro i. La boucle while tourne tant qu’aucune tortue n’a franchi la ligne, c’est-à-dire tant que gagnante vaut -1.
Erreurs fréquentes
| Symptôme | Cause | Correction |
|---|---|---|
AttributeError: module 'turtle' has no attribute 'Turtle' (selon la version de Python, le message dit aussi partially initialized module ou consider renaming) | le fichier s’appelle turtle.py : Python importe ce fichier au lieu du module | renommer le fichier, par exemple dessin.py, et supprimer le dossier __pycache__ s’il existe |
| la fenêtre s’ouvre et se ferme aussitôt | pas de turtle.done() à la fin | ajouter turtle.done() en dernière ligne |
| un triangle ne se referme pas | rotation de 60 degrés au lieu de 120 | tourner de 360 / n degrés |
| des traits relient les figures | goto avec le crayon baissé | t.penup() avant, t.pendown() après |
| la forme n’est pas remplie | t.end_fill() oublié | encadrer le tracé par begin_fill() et end_fill() |
| les touches ne font rien | ecran.listen() oublié, ou fonction appelée avec parenthèses dans onkey | ecran.listen() et ecran.onkey(avancer, "Up") |
TypeError: relier() takes 0 positional arguments but 2 were given | la fonction donnée à onclick n’a pas de paramètres | lui donner deux paramètres, x et y |
UnboundLocalError dans une fonction appelée par un événement | la fonction modifie une variable du programme principal | déclarer cette variable avec global en tête de la fonction |
turtle.Terminator après avoir fermé la fenêtre | le programme dessine encore alors que la fenêtre est fermée | relancer le programme ; ce n’est pas une erreur du code |
Pour aller plus loin
- Écrire une fonction
etoile(t, cote)qui trace une étoile à cinq branches : la tortue avance et tourne de 144 degrés, cinq fois. - Dessiner un damier de 8 × 8 cases en noir et blanc avec deux boucles imbriquées et la fonction
carre. - Faire un jeu « attrape la tortue » : une tortue se place au hasard, et un clic sur elle augmente le score (la méthode
t.onclick(f)réagit aux clics sur la tortue elle-même). - La documentation de turtle (en français) décrit toutes les instructions.
Solution de l'étoile
import turtle
def etoile(t, cote):
"""La tortue t trace une étoile à cinq branches."""
for i in range(5):
t.forward(cote)
t.right(144)
t = turtle.Turtle()
t.color("darkorange", "gold")
t.begin_fill()
etoile(t, 200)
t.end_fill()
turtle.done()
La tortue fait deux tours complets en traçant l’étoile : 5 × 144 = 720 = 2 × 360 degrés.
À retenir
import turtle, puist = turtle.Turtle()crée une tortue ; on la pilote avec des méthodes :t.forward(100),t.left(90).- La tortue part du centre
(0, 0)en regardant vers la droite ; l’axe desyva vers le haut. - Un polygone régulier à
ncôtés s’obtient avec une boucle dentours et une rotation de360 / ndegrés. penup()etpendown()lèvent et baissent le crayon ;goto(x, y)déplace la tortue directement.- Une fonction qui dessine commence par placer la tortue, pour fonctionner quel que soit l’état laissé avant.
- Une variable modifiée à chaque tour de boucle (longueur, angle, couleur) produit spirales et rosaces.
- Avec
onkey,onclicketontimer, on donne à turtle le nom d’une fonction, qu’il appellera lui-même plus tard ;turtle.done()attend ces événements. - Ne jamais appeler son fichier
turtle.py.
Résumé
| Concept | Syntaxe | Exemple |
|---|---|---|
| Créer une tortue | turtle.Turtle() | t = turtle.Turtle() |
| Avancer, reculer | t.forward(d), t.backward(d) | t.forward(100) |
| Tourner | t.left(a), t.right(a) | t.left(360 / n) |
| Aller à un point | t.goto(x, y) | t.goto(-200, 50) |
| Crayon | t.penup(), t.pendown() | lever avant goto |
| Couleur, épaisseur | t.color(trait, fond), t.pensize(e) | t.color("black", "gold") |
| Remplir | t.begin_fill() … t.end_fill() | autour du tracé |
| Cercle, point | t.circle(r), t.dot(d, couleur) | t.dot(10, "red") |
| Texte | t.write(texte, align=..., font=...) | t.write("Bravo", align="center") |
| Dessin rapide | turtle.tracer(0) puis turtle.update() | animations |
| Clavier | ecran.onkey(f, touche) puis ecran.listen() | ecran.onkey(avancer, "Up") |
| Souris | ecran.onclick(f), f(x, y) | ecran.onclick(relier) |
| Minuterie | ecran.ontimer(f, ms) | ecran.ontimer(animer, 20) |
| Attendre les événements | turtle.done() | dernière ligne |