Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Dessiner avec Python : turtle et tkinter

Python propose un module dénommé "turtle" (tortue en français) qui permet de dessiner des figures géométriques dans une fenêtre.
Ces dessins peuvent être sauvegardés sous forme d'images PNG.

Dans une deuxième partie de ce chapitre, je vous montre qu'il existe une autre solution : le module tkinter.
Vous pouvez aborder directement la deuxième partie : dessiner avec tkinter. En effet le module turtle est surtout destiné à l'apprentissage de la programmation par des "kids". C'est une alternative à langage Scratch.
Maintenant un survol de la première partie n'est pas inintéressant. En le lisant vous pouvez redécouvrir certains notions de géométrie. Ainsi, savez-vous dessiner une étoile à 5,7,9 branches ???

Dessiner avec turtle

Turtle signifie en anglais "tortue". C'est avec ce module que l'on peut réaliser dans Python de jolies figures géométriques.
Le principe est simple : on dessine en déplaçant un crayon (ou "turtle" ou tortue ou curseur).
Dans ce chapitre j'emploie le plus souvent le terme "curseur". Ce curseur a la forme d'une pointe de flèche.

Créez un nouveau dossier à la racine de C: et nommez le "python_dessins". Tous les scripts de ce chapitre seront stockés dans ce répertoire.

Le répère cartésien dans la fenêtre graphique

Turtle met en place un repère à deux dimensions dans la fenêtre de dessin. Attention ce repère est invisible.
Les axes X & Y (invisibles) et coupent hauteur et largeur de la fenêtre en leur milieu. L'origine du repère est donc le centre de la fenêtre turtle.

Commandes dans la console

La tortue est au départ situé au point(0,0) du repère et apparait sous forme d'une flèche orientée vers la droite. Ce qui signifie que tout déplacement avant du crayon se fera vers la droite.

>>>from turtle import *
>>>pen()
>>>speed(1)
>>>forward(200)    
>>>left(90)
>>>forward(100)
>>>right(90)
>>>forward(100)
>>>left(90)
>>>forward(100)
>>>left(90)
>>>forward(300)
>>>

from turtle import * : importation de toutes les fonctions du module turtle.
pen() : une zone de dessin apparait avec la "tortue" au milieu sous forme d'une flèche, la fenêtre graphique a pour titre : "Python turtle graphics".
speed(1) : réglage de la vitesse de déplacement de la tortue ; une fenêtre légendé "Python Turtle Graphic" est alors affiché.
forward(200) : trait horizontal de 200 pixels
Left(90) : la tortue tourne à gauche de 90° ; flèche orientée désormais vers le haut
forward(100) : trait vertical de 100px

Le graphique

module turtle de python

Dessiner des carrés

Un carré est un polygone régulier de 4 côtés égaux et 4 angles égaux (angles droits).
Pour sauvegarder notre travail nous passons en mode programme !

Thème : dessiner un carré unique

Première version

#nom programme : tortue_carre.py
#objet : dessiner un carre
from turtle import *
pen()
speed(1)
pencolor('red')
c =input('côté du carré ? : ')
c=eval(c)       
#convertir en numérique la saisie
forward(c)
left(90)
forward(c)
left(90)
forward(c)
left(90)
forward(c)
exitonclick()

Par défaut la couleur du trait est noir. Ici je demande qu'il soit rouge avec la fonction pencolor().

Le côté du carré est saisi dans la variable c.
Rappel : toute saisie avec la fonction input() est de type string. Il faut donc convertir le contenu de c en un numérique avec la fonction eval().
Ensuite vous avez 4 fois le même couple d'instructions : forward(c) et left(90)
Ne pas oubliez de terminer le programme avec l'instruction mainloop() / exitonclick() pour redonner la "main à l'utilisateur".

Différences : exitonclick() maintient la fenêtre ouverte jusqu'à un clic, tandis que mainloop() la garde ouverte indéfiniment jusqu'à sa fermeture manuelle.

Script amélioré

On aurait pu écrire le code d'une façon beaucoup plus concise grâce à une boucle.

...
for i in range(5) : #pour i allant de  0 à 5 exclu
	forward(c)
	left(90)

Dessiner un labyrinthe (carrés qui s'imbriquent)

Le script

# nom programme : tortue_carre_bis.py
from turtle import *
pen()
pencolor('purple')
speed(5)
for i in range(50):
    forward(20 + (i*10) )
    left(90)
mainloop()

A chaque passage dans la boucle l'argument de la commande forward est plus grand de 10, donc on dessine un labyrinthe.

Le rendu

module turtle de python

Dessiner des polygones réguliers et des étoiles

Je veux réaliser un programme très généraliste capable de produire n'importe quel polygone régulier : carré, pentagone, hexagone, octogone.

Dessiner des polygones réguliers

Dans tous les cas la somme des "virages à gauche" doit être égale à 360° afin que la tortue revienne à sa position de départ.

Le code

#nom prog. : tortue_polygone.py
#objet : dessiner un polygone régulier de n côtés (4 ou 5 ou 6 ou 8)

from turtle import *
pen()
n ="2"
while n not in "4568":  
    n = input ("nombre de côtés du polygone : 4/5/6/8: ")
while True:
    cote = input("Entrez le côté du polygone régulier : ")
    if cote.isdigit():               
        break                       
    else:
        print("Ce n'est pas un entier !")

# conversions
n =int(n) #conversion
cote= int(cote)
              
angle=360 / n 
for i in range(n):
    forward(cote)
    left(angle)
exitonclick()

Notez les contrôles de saisie efficaces.
La fonction isdigit() s'applique à une chaine et retourne True si celle-ci ne contient que des chiffres. Donc cette fonction est idéale pour vérifier si vous avez bien saisi un entier positif.

Le graphique

Le shell

nombre de côtés du polygone : 4/5/6/8: 6 côtés
nombre de côtés du polygone : 4/5/6/8: 6
Entrez le côté du polygone régulier : 150pixels
Ce n'est pas un entier !
Entrez le côté du polygone régulier : 150

Malgré des erreurs de saisie, le script ne plante pas !

Dessiner des étoiles

Le graphique

Ci-dessous une étoile à 7 branches.
Il faut tracer 7 segments.
Après avoir desssiné un segment il faut tourner à droite de 154° (180-(180/7)
De façon générale l'angle de virage après chaque segment est : 180-(180/N) (N le nombre de bras de l'étoile).

Le code

from turtle import *
pen()

setup(600,600)
speed(1)      
pensize(2)

n ="10"
while n not in "579":  
    n = input ("nombre de branches de l'étoile 5 ou 7 ou 9: ")

# Paramètres de l'étoile
branches = int(n)
angle = 180 - (180 / branches)   # angle de rotation entre deux traits

# Dessin
for i in range(branches):
    forward(150)   # longueur du segment (à ajuster)
    right(angle)

hideturtle()
mainloop()

Ce script permet de dessiner une étoile à 5 ou 7 ou 9 bras.

Dessiner des cercles et arcs de cercles

Il faut utiliser la fonction cercle(rayon, [angle]).
Le seul argument obligatoire est le rayon.

Exemple : un rond de plus en plus grand

Le script

# nom programme : tortue_cercles.py
import turtle as tu
tu.pen()
tu.pencolor('orange')
tu.speed("slow")
for i in range(20):
  tu.circle(i +30)
tu.exitonclick()

En raison de la technique d'importation du module avec un alias, je dois préfixer les fonctions du module avec cet alias.

L'argument de la fonction speed() peut être un entier (entre 0 et 10) mais aussi une chaine : "fast", "normal", "slow", …
Remarque : les cercles sont dessinés dans le sens contraire des aiguilles d'une montre.

Le rendu

module turtle de python

Je dessine des cercles donc la fonction circle() n'a qu'un argument : le rayon !

Dessiner 15 cercles

Dessiner 15 ronds de la gauche vers la droite.

Le script

# nom programme : tortue_cercles_bis.py
import turtle as tu
tu.pen()
tu.setup(500,500)
tu.pencolor('orange')
tu.pensize(5)
tu.speed(5)
tu.up()
tu.backward(250)
tu.down()
for i in range(15):
  tu.circle(-30)
  tu.forward(30)
tu.exitonclick()

Le graphique

module turtle de python

Comment éviter le trait entre chaque cercle ?
Il suffit de lever le crayon pour ce trajet.

Dessiner des arcs de cercle

La fonction circle() accepte un deuxième paramètre : l'angle (par défaut 360°).

Le code pour dessiner un bonhomme de neige

# tortue_arcs.py
# dessiner un bonhomme de neige

from turtle import *
pen()

setup(600,600)
speed('slow')      
pensize(2)

penup()
goto(0,-200)   
pendown()

circle(80, 180,) 
circle(-50,180)
circle(-50,180)
circle(80,180)

mainloop()

Les méthodes up() & penup() sont équivalentes. Il en est de même pour pendown() & down().
Je dessine 4 arcs de cercle de rayon 180° mais notez que le rayon est tantôt positif et tantôt négatif.

Le graphique

Remplir une aire

Vous pouvez souhaitez que la forme dessinée soit remplie d'une certaine couleur.
Reprenons le programme "tortue_polygone.py" et améliorons le !

La nouvelle version du programme qui dessine des polygones

#nom prog. : tortue_polygone_plus.py
#objet : dessiner un polygone régulier de n côtés (4 ou 6 ou 8)
# polygone rempli de violet
setup(600,600)
speed('slow')
pencolor('purple')
pensize(5)
fillcolor('lime')
bgcolor('pink')

# conversions
n =int(n) #conversion
cote= int(cote)
angle=360 / n

begin_fill()
for i in range(n):
    forward(cote)
    left(angle)
end_fill()
       
exitonclick()

Analyse du code

Le rendu

Le graphique obtenu après avoir saisi 5 et 100 :

Connaitre la position de la tortue

Code à saisir

# nom programme : tortue_infos py
from turtle import *
pen()
hideturtle()
for i in range(4):  #boucle effectuée 4 fois (0 à 3)
    print ("position tortue : ", position())
    forward(200)
    left(90)
# fin boucle
print("-----")
up()
home()
print ("position tortue : ", position())   
mainloop()

La fonction position() retourne les coordonnées x et y de la tortue à un stade donné.
La fonction home() déplace le curseur à l'origine du repère (x = 0 ; y = 0).
La fonction hideturtle() masque le curseur

Le rendu dans le shell

position tortue :  (0.00,0.00)
position tortue :  (200.00,0.00)
position tortue :  (200.00,200.00)
position tortue :  (0.00,200.00)
-----
position tortue :  (0.00,0.00) 

En parenthèse la valeur de l'abscisse puis celle de l'ordonnée de chaque angle.

Rendu dans la fenêtre graphique

La flèche n'apparait pas car elle est masqués.
Il existe aussi la fonction showturtle() pour réafficher la flèche.

Autre technique pour dessiner

Plutôt que d'utiliser les fonctions forward(), backward() left(), right() nous allons utiliser la fonction goto(x,y). qui est beaucoup plus intiutive et plus mathématique.

Le repère cartésien

Pour utiliser la fonction goto() il faut bien avoir en tête qu'un repèren cartésien (mais masqué) divise la fenêtre en 4 zones comme le rappelle le croquis ci-dessous : module turtle de python

Un point situé à gauche de l'axe des Y et sous l'axe des X a donc des coordonnées négatives !

Exemple de script avec goto()

Ce programme utilise un fonction et un boucle afin de réduire sensiblement le nombre de lignes.

def carre(d):
    up()
    goto(-d,-d)
    down()
    goto(d,-d)
    goto(d,d)
    goto(-d,d)
    goto(-d,-d)
    up()

# ----------fin fonction
# nom programme : tortue_goto.py
from turtle import *
pen()
setup(500,500)
speed('slow')
for cote in range(240,0,-40) :
    carre(cote)
    print(cote)
home()
down()
write ("hello !", font=("Arial", 14, "normal"))
mainloop()

Notez la fonction nommée "carre()" qui est appelée 6 fois via une boucle.
Si je n'avais pas utilisé de boucle et de fonction le programme serait beaucoup plus long : 40 lignes de plus.
Donc avec un peu d'astuce les programmes turtle ne sont pas trop verbeux.
Le texte est affiché dans le centre de la fenêtre ; home() équivalent à goto(0,0)

Le graphique

Dans le shell affichage des valeurs successives de cote (côté du carré).

Appliquer une image de fond à la fenêtre turtle

Je vous montre aussi comment écrire du texte dans la fenêtre turtle.

Le graphique

Extraits du code

setup(800,800)
pen()
hideturtle()
title("Appel du 18 juin 1940")
bgpic("degrade.png")
fillcolor('white')
speed('slow')
pensize(1)
up()
goto(50,350)
begin_fill()
down()
goto(-50,350)
...
goto(50,350)
end_fill()
up()
home()
down()
write ("Appel du 18 juin 1940 par le Général de Gaulle", align ="center",  \
       font=("Arial", 24, "normal"))
mainloop()

La fonction bgpic("chemin image") permet de remplir le fond de la fenêtre turtle avec une image.
Cette image doit être au format GIF ou PNG.
Ce degradé est un document SVG (image vectorielle) exporté au format PNG.
La fonction write() permet d'écrire dans la fenêtre turtle.
Je vous laisse compléter ce code : une série de goto() ; faites avant un croquis sur une feuille à petits carreaux (5 carreaux = 100px).

Dessiner avec Python : utiliser tkinter

Le module tkinter fait partie de la bibliothèque de base de Python.
Il est surtour destiné à créer de belles interfaces utilisateurs car les entrées et sorties dans le shell c'est pas terrible ...
Cependant il est aussi possible de dessiner avec tkinter.

Le rendu graphique est désormais instantané ; on ne voit plus la progression du dessin par le crayon.

Premier canevas

Dessiner lignes, rectangles et polygones.

Le programme

# canvas1.py
from tkinter import *
fen = Tk()
# définition et affichage d'un canevas
canevas = Canvas(fen,width = 600, height = 600)
canevas.pack()

# dessin d'une ligne
canevas.create_line(0,0,600,600)

# dessin d'un rectangle
canevas.create_rectangle(300,300,500,600)

# dessin d'un carré rempli de rouge
canevas.create_rectangle((50,50),(110,110), fill ='red', outline ='')

# dessin d'un polygone
canevas.create_polygon((400,400),(600,400),(600,600),fill ='', outline ='red', width =3)

from tkinter import * : il faut importer le module tkinter.
fen = Tk() : puis on crée une instance de la classe Tk que l'on nom "fen" (comme fenêtre) car l'objet tk crée une fenêtre.
canevas = Canvas(fen,width = 600, height = 600): définition d'une zone de dessin (ou "canevas") de 500 par 500 pixels via la méthode Canvas().
canevas.pack() : affichage du canevas (objet de type Canvas) dans la fenêtre tkinter

Dessiner une ligne oblique :
canevas.create_line(0,0,600,600):
il faut appliquer à la méthode create_line() les coordonnées x,y du point départ et coordonnées x,y du point d'arrivée.
Pour réaliser la même ligne avec turtle il y aurait eu plusieurs instructions ...ici une seule !

Attention l'origine du canevas n'est plus son centre (comme avec une fenêtre turtle) mais le coin haut gauche de cette zone de dessin.

Dessins de rectangles et carrés :
canevas.create_rectangle(300,300,500,600) : dessin d'un rectangle avec la méthode create_rectangle(x1,y1,x2,y2)
Les arguments sont les coordonnées du coin supérieur gauche puis les coordonnées x,y du coin inférieur droit. Donc les deux derniers arguments ne sont surtout pas les largeur et hauteur du rectangle.
canevas.create_rectangle((50,50),(110,110), fill ='red', outline ='') :
dessin d'un carré de largeur 60 (110-50) et de hauteur 60 (110-50) rempli de rouge et sans bordure.

Dessin d'un polygone :
canevas.create_polygon((400,400),(600,400),(600,600),fill ='', outline ='red', width =3) :
dessin d'un triangle non rempli avec une bordure rouge de 3px d'épaisseur.
Pour une meilleure lisibilité regrouper chaque couple de coordonnées dans un tuple.

Le graphique (enregistré au format PNG

Le graphique s'affiche dans une fenêtre avec par défaut le titre "tk".
Dans tkinter le rendu graphique est instantané alors que dans turtle on voit le crayon réaliser progressivement le dessin.

Deuxième canevas : dessiner cercles, ellipses et arcs

Le script

# canvas2.py
from tkinter import *
fen = Tk()
fen.title("Cercles, ellipses, arcs")
# canevas de 400 par 700
canevas = Canvas(fen,width =  400, height = 700)
canevas.pack()

# ellipse
canevas.create_arc(50,50,250,150,extent = 359, style = ARC, outline ="black", width ="3")
canevas.create_text(200,30, /
	text ="Une ellipse : arc de 359° qui s'inscrit dans un rectangle", font =('Times',12))

# cercle
canevas.create_arc(200,200,400,400,extent = 359, style =ARC, outline ="red", width ="3" )
canevas.create_text(180,200, /
	text ="Un cercle : arc de 359° qui s'inscrit dans un carré",anchor="w", font =('Times',12) )

# arc d'ellipse haut
canevas.create_arc(100,400,300,500,extent = 180, style = ARC, outline ="green", width ="3")
canevas.create_text(50,380, /
	text ="Arc d'ellipse haut : extent inférieur à 359",anchor="w",font =('Times',12) )

# arc de cercle bas
canevas.create_arc(100,500,300,700,extent = -180,style =ARC, outline ="blue", width ="3")
canevas.create_text(200,480, /
	text ="Arc de cercle bas : extent est négatif", anchor ="center",font =('Times',12) )

# segment de corde
canevas.create_arc(100,500,300,700,extent = -180,style =CHORD, outline ='', fill ='navy')
canevas.create_text(250,580, /
	text ="Segment de corde", font =('Times',12) )

fen.mainloop()

fen.title("Cercles, ellipses, arcs") : je personnalise le titre de la fenêtre tkinter.

La méthode create_arc() permet de dessiner des ellipses, cercles, arcs de cercle ou d'ellipse, segments de corde, secteurs.

canevas.create_text(200,30, text ="Une ...", font =('Times',12)) :
on peut afficher du texte dans un canevas avec la méthode create_text(x,y, text = ...,font=(...))
par défaut anchor = 'center' ce qui veut dire que le texte se répartit par rapport aux coordonnées x,y.
Notez les valeurs de l'attribut font : utilisation de la police "Times New Roman" avec une taille de 12

Le graphique

Un canevas animé

c'est très facile de créer un dessin animé avec Python-tkinter.

Le code de "canvas3.py"

#canvas3.py

from tkinter import *
import time

fen = Tk()
fen.title("Canevas animé")
fen.geometry("600x600")
fen.config(bg ="skyblue")

canevas = Canvas(fen, width = 500, height = 500,bg='lime')
canevas.pack()
tricolore = canevas.create_oval(200,200,300,300, fill ="red" , outline ='')

while True : 
    liste = ['green','orange','red']
    for i in (range(0,3)):
        couleur = liste[i]
        canevas.itemconfig(tricolore,fill =couleur)
        fen.update()
        time.sleep(5)

fen.mainloop()

La boule au centre du canevas change de couleur de remplissage toutes les 5 secondes : vert puis orange puis rouge et de nouveau vert ...

Pour dessiner des cercles et des ellipses complètes il y a plus simple que create_arc() : la méthode create_oval(). Ici je dessine un cercle car l'ovale est inscrit dans un carré de 100 par 100.

Le graphique

Bien évidemment l'animation illimitée n'est pas rendue par cette capture d'écran.

On voit bien la différence entre la fenête tkinter (bleu ciel) et l'objet Canvas (vert clair).

Insérer des images dans le canevas

Le programme

#canvas4.py

from tkinter import *

fen = Tk()
fen.title("Les dents de la mer")
fen.geometry("600x400")
fen.config(bg ="skyblue")

canevas = Canvas(fen, width = 600, height = 400)
canevas.pack()

fond = PhotoImage(file ='fond.png')
canevas.create_image(0,0,anchor =NW, image = fond)

sprite = PhotoImage(file ='plongeur2.gif')
canevas.create_image(0,0,anchor =NW, image = sprite)

requin = PhotoImage(file ='requin.gif')
canevas.create_image(400,300,anchor =NW, image = requin)

fen.mainloop()

fond = PhotoImage(file ='fond.png') : chargement de l'image ; la fonction n'accepte que les formats GIF & PNG.
canevas.create_image(0,0,anchor =NW, image = fond) : le coin supérieur gauche de l'image (anchor = NW) est positionnée dans le coin haut gauche du canevas.
Il n'est pas possible de redimensionner l'image insérée via la fonction create_image() ...
Comme l'image "fond.png" fait 600 par 400, elle occupe tout le canevas.
Deux autres images sont insérées au dessus ; elles ont été redimensionnées auparavant avec Paint.

Le graphique

Conclusion

Turtle et tkinter se complètent. Il est plus facile de dessiner un polygone à 5 côtés (ou plus), une étoile à N branches avec turtle qu'avec tkinter.
Par contre c'est très compliqué de dessiner une ellipse avec turtle alors que c'est un jeu d'enfant avec tkinter.

On peut réaliser des animations graphiques avec tkinter voire des jeux vidéo basiques ...