Nous avons déjà vu les bases des interfaces graphiques avec Tk à travers deux activités : création et positionnement des widgets.
Nous allons exploiter aujourd'hui le nouveau type de widget (le Canvas) que nous avons vu à la fin de l'activité précédente. Il s'agit d'une zone graphique dans laquelle nous allons pouvoir dessiner ou écrire du texte.
Par exemple, on peut obtenir ceci avec quelques lignes de code :
Cette activité va nous permettre également d'utiliser les connaissances acquises lors de l'activité sur les fonctions. Si vous ne l'avez pas encore faite, il est possible que certaines questions vous semblent obscures.
Commençons par créer le widget que nous allons utiliser pour dessiner : le
.Exemple :
Canvas(fen_princ, width=500, height=500, bg='ivory').
=01° Utiliser le code suivant pour créer un Canvas de 500x500 dans une fenêtre de 600x600. On utilisera la méthode pack pour l'afficher dans la fenêtre fen_princ.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory')
=pack()
.fen_princ.mainloop()
Comme vous pouvez le voir, le Canvas n'est pas très bien centré. Nous allons utiliser la méthode place avec deux arguments : la position x et y du point haut et gauche de votre widget.
Ainsi place(x=100,y=100)
va placer le début du Canvas aux coordonnées (100,100). .
02° Modifier le code en remplaçant la méthode pack par la méthode place de façon à obtenir l'affichage suivant. Il faudra modifier les valeurs des coordonnées x et y.
Pour rappel, voici les axes de coordonnées :
Si vous voulez décaler le widget vers la droite, il faut donc imposer une coordonnée x de plus en plus grande.
Si vous voulez décaler le widget vers le bas, il faut également imposer une coordonnée y de plus en plus grande.
Nous venons de créer notre zone de dessin. Il nous reste à dessiner dessus !
Pour commencer, la ligne droite : create_line(x1, y1, x2, y2, width=2, fill="red")
permet de créer une ligne rouge partant du point (x1,y1) inclus et allant au point (x2,y2) exclu avec une épaisseur de 2 pixels. .
On aura pu également noter la couleur en code RGB hexadécimal :
create_line(x1, y1, x2, y2, width=2, fill="#ff0000").
.03° Utiliser la méthode create_line pour tenter d'obtenir une image semblable à cela plus ou moins :
Voici le résultat des lignes de commandes suivantes (on indique rien pour width et fill, qui prendront leurs valeurs par défaut : 1 pixel et "black") :
Mais où sont passés les deux premiers points (le noir et le rouge)?
create_line(0,0,1,0, fill="black")
.create_line(1,1,2,1, fill="red")
.create_line(2,2,3,2, fill="blue")
.create_line(3,3,4,3, fill="green")
.create_line(4,4,5,4, fill="yellow")
.create_line(5,5,6,5, fill="cyan")
.create_line(5,6,7,6, fill="orange")
.Et bien, voilà le but de cette remarque : par défaut, le Canvas possède une marge vers l'extérieur de 2 pixels ! Voici la même image avec le rajout des coordonnées :
Comme vous le voyez, nous avons tenté par inadvertance de dessiner dans la bordure externe. Donc Tkinter n'a pas dessiné nos deux premiers points.
Alors comment faire ?
Il faut soit gérer ce décalage d'origine dans nos programmes, soit supprimer cette marge lors de la création du Canvas. Voilà comment coder ceci :
Pour supprimer la marge :
Canvas(fen_princ, width=499, height=499, bg='ivory', borderwidth=0, highlightthickness=0)
=Si on relance les instructions précédentes avec une marge de 0 pour le Canvas, on obtient bien le résultat voulu : le carré noir est dans la position (0;0) attendue.
Il faudra donc penser à rajouter borderwidth=0, highlightthickness=0)
si on veut gérer de façon simple les coordonnées des objets qu'on va placer sur le Canvas.
Voilà un programme qui affiche un Canvas et y crée une droite en utilisant une fonction rotation_ligne documentée dans le code de la fonction elle-même. Si vous voulez savoir comment elle fonctionne, allez lire la remarque juste en dessous.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def rotation_ligne( , x0, y0, longueur, angle, couleur):
"""
x0, y0 sont les coordonnées d'origine
longueur est la longueur de la ligne en pixels
couleur est la couleur sous forme de string : "red" ou "#ff0000"
angle est l'angle par rapport à l'horizontal en degré
"""
angle = math.radians(angle) # math.radians permet de convertir en radians
xf = int(x0+longueur*math.cos(angle))
yf = int(y0+longueur*math.sin(angle))
create_line(x0, y0, xf, yf, width=2, fill=couleur)
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.rotation_ligne( , 250, 250, 100, 45, "#ff0000")
fen_princ.mainloop()
04° Regarder la déclaration de la fonction pour trouver la position du paramètre qui gère l'angle de tracage de la droite. Utiliser le programme pour tracer des traits à -45°, 0° et +45°. On prendra le point (x0=250,y0=250) comme origine des lignes.
Cette remarque est plus ou moins optionnelle : elle vous explique briévement comment créer la fonction qui met en rotation. Or, la puissance des fonctions est qu'on n'a pas besoin de savoir comment elle fonctionne pour l'utiliser. Il suffit de savoir ce qu'elle désire comme paramètres.
Faire des rotations, c'est un peu plus compliqué avec Tkinter. Il va falloir sortir le module math car nous allons avoir besoin de faire des projections. Le but de ce chapitre n'est pas de faire un cours de mathématiques mais nous allons devoir faire des projections sur l'axe Ox (horizontal) et l'axe Oy (vertical).
Un trait de longueur L aura une longueur résultante
L*cos(angle)
sur l'axe OxL*sin(angle)
sur l'axe OySi le trait part des coordonnées (x0,y0), les coordonnées finales (xF,yF)seront :
xF = x0+L*cos(angle)
sur l'axe OxyF = y0+L*sin(angle)
sur l'axe OySi on doit créer une fonction qui génére à partir d'un point (x0,y0) une ligne ayant une certaine longueur avec un angle donné, on peut obtenir
def rotation_ligne( , x0, y0, longueur, angle, couleur):
"""
x0, y0 sont les coordonnées d'origine
longueur est la longueur de la ligne en pixels
couleur est la couleur sous forme de string : "red" ou "#ff0000"
angle est l'angle par rapport à l'horizontal en degré
"""
angle = math.radians(angle) # math.radians permet de convertir en radians
xf = int(x0+longueur*math.cos(angle))
yf = int(y0+longueur*math.sin(angle))
create_line(x0, y0, xf, yf, width=2, fill=couleur)
.05° Utiliser une boucle FOR pour tracer les lignes de 0 à 360° par incrémentation de 20°.
...CORRECTION...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def rotation_ligne( , x0, y0, longueur, angle, couleur):
"""
x0, y0 sont les coordonnées d'origine
longueur est la longueur de la ligne en pixels
couleur est la couleur sous forme de string : "red" ou "#ff0000"
angle est l'angle par rapport à l'horizontal en degré
"""
angle = math.radians(angle) # math.radians permet de convertir en radians
xf = int(x0+longueur*math.cos(angle))
yf = int(y0+longueur*math.sin(angle))
create_line(x0, y0, xf, yf, width=2, fill=couleur)
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.for angle in range(0,361,20) :
rotation_ligne( , 250, 250, 100, angle, "#ff0000")
fen_princ.mainloop()
06° Modifier une dernière fois le programme pour obtenir quelque chose qui ressemble à ceci :
On peut même aller plus loin : on peut utiliser le module random pour créer des nombres aléatoires :
random.randint(20,80)
va fournir un nombre entier aléatoire entre 20 inclus et 80 inclus.random.randrange(20,80,10)
fait de même mais uniquement pour 20, 30, 40, 50, 60, 70 et 80.random.choice(liste)
choisit un élément de la liste, par exemple liste = ['red','green','blue','yellow']
.Ce module n'est pas propre aux Canvas ou à Tkinter. Mais, nous allons réutiliser les étoiles ci-dessus pour créer une répartition d'étoiles au hasard justement.
Voilà un programme qui vous montre un peu la puissance des fonctions et du module random :
Il faut savoir lire un programme désormais : on commence par le programme principal et on va voir le contenu des fonctions lorsqu'on les rencontre. Le programme est :
# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.for i in range(6) :
etoile_aleatoire( , "alea")
fen_princ.mainloop()
On constate qu'on va lancer un appel à etoile_aleatoire 6 fois (i valant de 0, 1, 2, 3, 4 et 5).
Voilà le programme total maintenant :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
import random
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def rotation_ligne( , x0, y0, longueur, angle, couleur) :
"""
Cette fonction permet de dessiner une ligne
leCanvas est la référence du canvas dans lequel on veut dessiner la ligne
x0, y0 sont les coordonnées d'origine
longueur est la longueur de la ligne en pixels
couleur est la couleur sous forme de string : "red" ou "#ff0000"
angle est l'angle par rapport à l'horizontal en degré
"""
angle = math.radians(angle) # math.radians permet de convertir en radians
xf = int(x0+longueur*math.cos(angle))
yf = int(y0+longueur*math.sin(angle))
create_line(x0, y0, xf, yf, width=2, fill=couleur)
.def etoile( , x0, y0, couleur) :
"""
Cette fonction permet de dessiner une étoile en utilisant la fonction rotation_ligne
leCanvas est la référence du canvas dans lequel on veut dessiner l'étoile
x0, y0 sont les coordonnées du centre de l'étoile
couleur est la couleur de l'étoile sous forme de string : "red" ou "#ff0000"
"""
for angle in range(0,361,20) :
rotation_ligne( , x0, y0, random.randint(50,100), angle, couleur)
def etoile_aleatoire( , couleur) :
"""
Cette fonction permet de dessiner une étoile de façon aléatoire.
Si le paramètre couleur vaut 'alea', on tire la couleur au hasard.
Les coordonnées du centre sont tirés au hasard.
La fonction utilise la fonction etoile.
"""
if couleur == "alea" :
liste_couleurs = ['red','blue','red','yellow', 'green']
couleur = random.choice(liste_couleurs)
x0 = random.randint(50,450)
y0 = random.randint(50,450)
etoile( , x0, y0, couleur)
# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.for i in range(6) :
etoile_aleatoire( , "alea")
fen_princ.mainloop()
07° Lancer le programme, analyser le pour bien comprendre l'imbrication des fonctions et l'utilisation des éléments aléatoires. Modifier alors deux trois valeurs pour en voir l'influence visuellement.
08° Modifier le programme avec cette seule ligne située dans la fonction rotation_ligne :
create_line(x0, y0, xf, yf, width=2, fill=couleur, dash=(4, 4))
.On peut dessiner d'autres choses que des lignes :
Pour créer des rectangles, on peut utiliser ceci :
create_rectangle(100,200,300,400, fill="red", activefill="yellow", outline="blue", width=5)
.Attention à la coordonnée finale : il s'agit du pixel juste extérieur à la boîte. Ainsi, si vous donnez les coordonnées (0,0,10,5), en réalité vous allez créer un rectangle qui va s'arrêter en (9,4). Le point de coordonnées (10,5) n'est pas dans la forme.
Voici le résultat de create_rectangle(0,0,10,5,width=0,fill="red")
. .
Mais pourquoi agir ainsi ? A cause de la bordure peut-être. Par défaut la bordure a une couleur noire et une largeur de 1 pixel : width=1.
Le code create_rectangle(0,0,10,5, fill="red")
(on ne précise pas width, il vaut donc 1) donne : .
On retrouve donc une raison logique à cette seconde coordonnée : elle est bien intégrée à la forme SI la forme possède une bordure de 1 pixel.
A titre d'exemple, voilà un programme qui crée deux rectangles, l'un avec les valeurs par défaut, l'autre en fournissant certains arguments.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
import random
# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.create_rectangle(20,50,120,250)
.create_rectangle(100,300,300,400, fill="red", activefill="yellow", outline="blue", width=5)
.fen_princ.mainloop()
09° Lancer ce programme pour vérifier que l'un des rectangles change bien de couleur.
Cela va nous permettre de dessiner des traits et des rectangles. Reprenons le code qui permet de dessiner des étoiles aux rayons aléatoires.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
import random
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def rotation_ligne( , x0, y0, longueur, angle, couleur) :
"""
Cette fonction permet de dessiner une ligne
leCanvas est la référence du canvas dans lequel on veut dessiner la ligne
x0, y0 sont les coordonnées d'origine
longueur est la longueur de la ligne en pixels
couleur est la couleur sous forme de string : "red" ou "#ff0000"
angle est l'angle par rapport à l'horizontal en degré
"""
angle = math.radians(angle) # math.radians permet de convertir en radians
xf = int(x0+longueur*math.cos(angle))
yf = int(y0+longueur*math.sin(angle))
create_line(x0, y0, xf, yf, width=2, fill=couleur, activefill = "red", dash=(4, 4))
.# DU CODE SERA A RAJOUTER ICI
def etoile( , x0, y0, couleur) :
"""
Cette fonction permet de dessiner une étoile en utilisant la fonction rotation_ligne
leCanvas est la référence du canvas dans lequel on veut dessiner l'étoile
x0, y0 sont les coordonnées du centre de l'étoile
couleur est la couleur de l'étoile sous forme de string : "red" ou "#ff0000"
"""
for angle in range(0,361,20) :
rotation_ligne( , x0, y0, random.randint(50,100), angle, couleur)
# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.etoile( , 250,250,"red")
fen_princ.mainloop()
10° Utiliser ce programme pour rajouter des carrés aux bouts des droites. On pourrait avoir par exemple :
11° Rajouter un peu de hasard ensuite dans le choix des couleurs des carrés en utilisant la méthode choice du module random (voir la partie précédente 3 - Le module random : gestion du hasard). Un exemple avec trois étoiles de ce type :
Ce n'est pas très pratique de donner les coordonnées. Voici une fonction qui vous permet de donner les coordonnées du point haut-gauche, la largeur et la hauteur. Elle se charge de créer le rectangle, avec une bordure de 0 pixel.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
import random
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def dessine_rectangle( , x0, y0, largeur, hauteur, couleur1, couleur2, couleur3) :
"""
Cette fonction permet de dessiner un rectangle en fournissant :
* les coordonnées xo et yo du point haut à gauche.
* la référence leCanvas du Canvas dans lequel dessiner le rectangle.
* vous donner fournir la taille via les paramètres largeur et hauteur.
* Le paramètre couleur1 correspond à la couleur de remplissage.
* Le paramètre couleur2 correspond à la couleur lorsqu'on survole le rectangle.
* Le paramètre couleur3 correspond du contour (mais attention, la bordure vaut 0 initialement, ce paramètre ne sert donc à rien).
* ATTENTION : la bordure n'est pas gérée dans le calcul de la largeur et de la hauteur.
"""
create_rectangle(x0,y0,x0+largeur,y0+hauteur, fill=couleur1, activefill=couleur2, outline=couleur3, width=0)
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.dessine_rectangle( ,0,0,50,50,""gray", ""yellow", "black")
dessine_rectangle( ,50,0,50,50,""ivory", ""yellow", ""black")
fen_princ.mainloop()
12° Utiliser le programme pour tenter de répondre à la question suivante : Avec la méthode create_rectangle, la bordure est-elle interne ou externe ? Vous pouvez modifier la largeur de la bordure avec le paramètre width :
create_rectangle(x0,y0,x0+largeur,y0+hauteur, fill=couleur1, activefill=couleur2, outline=couleur3, width=20)
.Vous devriez parvenir à la conclusion que les bordures sont à la fois interne et externe. Voici pourquoi :
On crée un rectangle entre (100;250) et (200;350) avec la méthode de base de Tkinter (create_rectangle), on crée une bordure ayant une largeur de 20 pixels.
On crée un point jaune en (100;250) et en (200;350) pour indiquer clairement les deux points d'encrage.
create_rectangle(100,250,200,350,fill="red", outline="black", width=20)
."yellow")
.create_line(100,250,101,250,fill="yellow")
.create_line(200,350,201,350,fill=On voit que :
Comme la largeur de la bordure est de 20 pixels, il faudra rajouter 10 pixels à la largeur et à la hauteur du rectangle :
Si on fait un zoom sur le coin supérieur gauche, on voit qu'on a 10 pixels externes et 10 pixels internes, le point (100;250) faisant partie des pixels internes.
Si on fait un zoom sur le coin inférieur droit, on voit qu'on a 10 pixels externes et 10 pixels internes, le point (200;350) faisant partie des pixels externes.
Voici par exemple le résultat du code create_rectangle(1,1,10,5, fill="red",width=2)
: .
Voici par exemple le résultat du code create_rectangle(1,1,10,5, fill="red",width=3)
: .
Cette partie est assez technique. Si vous préférez passer à la suite, n'hésitez pas. Mais si vous avez un jour un problème avec des bordures larges, pensez à revenir vous documenter ici !
Le programme précédent n'est donc pas très pratique car la taille du rectangle va dépendre de la taille de la bordure ... Voici une nouvelle fonction rectangle_automatique qui gère la taille du rectangle en y intégrant la taille de la bordure. Attention, il faudra vous limiter au cas d'une bordure de taille paire. Aucun test n'est fait non plus si on demande une bordure trop épaisse par rapport à la taille demandée du rectange.
La fonction décale
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
import random
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def rectangle_automatique( , x0,y0,largeur,hauteur,bordure,couleur1,couleur2,couleur3) :
"""
Cette fonction permet de dessiner un rectangle en fournissant :
* les coordonnées xo et yo du point haut à gauche.
* la référence leCanvas du Canvas dans lequel dessiner le rectangle.
* vous donner fournir la taille via les paramètres largeur et hauteur.
* Le paramètre couleur1 correspond à la couleur de remplissage.
* Le paramètre couleur2 correspond à la couleur lorsqu'on survole le rectangle.
* Le paramètre couleur3 correspond du contour (mais attention, la bordure vaut 0 initialement, ce paramètre ne sert donc à rien).
* ATTENTION : la bordure est gérée dans le calcul MAIS necessite une bordure de pixels multiple de 2 et supérieure ou égale à 2 pixelsde la largeur et de la hauteur.
"""
create_rectangle(x0+bordure//2, y0+bordure//2, x0+largeur-bordure//2, y0+hauteur-bordure//2, fill=couleur1, activefill=couleur2, outline=couleur3, width=bordure)
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.rectangle_automatique( ,0,0,50,50,4,"#888888","yellow","black")
rectangle_automatique( ,50,0,50,50,4,"#ffffcc","yellow","black")
rectangle_automatique( ,100,0,50,50,4,"#888888","yellow","black")
fen_princ.mainloop()
Pour savoir s'il fonctionne, il faut l'utiliser. Voici un zoom en largeur avec des ensembles de 10 pixels représentés en jaune. On a bien 50 pixels de large en tout, avec 4 pixels noirs de chaque côté.
Op01° Utiliser ce programme pour visualiser le résultat puis modifier les valeurs données aux x0,y0 dans le corps du programme, sans toucher à la fonction) les valeurs des coordonnées de départ des rectangles pour que leurs bordures soient communes :
Visuellement, je veux ceci :
plutôt que ceci :
Si on veut construire un plateau de jeu de 10 cases sur 10, on peut alors l'afficher en quelques lignes. Il faudra utiliser deux boucles FOR imbriquées pour gérer les coordonnées x et y, en incrémentant de 46 pixels à chaque fois. Il faudra également rajouter une variable binaire de test pour alterner la couleur des cases.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
import random
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def rectangle_automatique( , x0,y0,largeur,hauteur,bordure,couleur1,couleur2,couleur3) :
"""
Cette fonction permet de dessiner un rectangle en fournissant :
* les coordonnées xo et yo du point haut à gauche.
* la référence leCanvas du Canvas dans lequel dessiner le rectangle.
* vous donner fournir la taille via les paramètres largeur et hauteur.
* Le paramètre couleur1 correspond à la couleur de remplissage.
* Le paramètre couleur2 correspond à la couleur lorsqu'on survole le rectangle.
* Le paramètre couleur3 correspond du contour (mais attention, la bordure vaut 0 initialement, ce paramètre ne sert donc à rien).
* ATTENTION : la bordure est gérée dans le calcul MAIS necessite une bordure de pixels multiple de 2 et supérieure ou égale à 2 pixelsde la largeur et de la hauteur.
"""
create_rectangle(x0+bordure//2, y0+bordure//2, x0+largeur-bordure//2, y0+hauteur-bordure//2, fill=couleur1, activefill=couleur2, outline=couleur3, width=bordure)
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=464, height=464, bg='ivory', bd=0, highlightthickness=0)
=place(x=50,y=50)
.test_couleur = True
for y in range(0,460,46) :
test_couleur = not(test_couleur)
for x in range(0,460,46) :
if test_couleur :
rectangle_automatique( ,x,y,50,50,4,"#888888","yellow","black")
else :
rectangle_automatique( ,x,y,50,50,4,"#ffffcc","yellow","black")
test_couleur = not(test_couleur)
fen_princ.mainloop()
Op02° Tester le programme et tenter de le modifier pour que le plateau soit centré puis qu'on affiche uniquement 8x8 cases par exemple.
Nous allons nous arrêter là mais vous pourriez créer un programme qui automatise les calculs en fonction d'une variable nombre_de_cases qui contiendrait le nombre de cases voulues.
Nous allons maintenant survoler rapidement les autres types d'objets qu'on peut créer dans un Canvas.
Commençons par les ellipses, ces formes ovales qui décrivent les trajectoires des planètes par exemple :
Crédit pour l'image gif : Par MADe, CC BY-SA 2.0 be, https://commons.wikimedia.org/w/index.php?curid=3232320
Pour décrire une ellipse avec Tkinter, c'est assez facile : il faut trouver le rectangle qui la contient. On a alors simplement besoin des coordonnées (x0,y0) du point supérieur gauche et des coordonnées (x1,y1) du point inférieur droit du rectangle.
Le code est assez proche du code nécessaire pour créer un rectangle : create_oval(0,0,20,10)
va créer une ellipse de bordure 1 pixel qu'on peut dessiner dans le rectangle d'épaisseur 1 pixel dessinable entre (0;0) et (20;10). .
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.create_rectangle(0,0,20,10,outline="#dddddd")
.create_oval(0,0,20,10)
.fen_princ.mainloop()
13° Utiliser le programme comme base pour générer trois ellipses de dimensions différentes, dont un cercle.
On trouve beaucoup d'arguments permettant de personnaliser les ellipses, comme pour les lignes et les rectangles. Parmi celles que je vous propose ici :
fill="#445566"
ou fill="blue"
, transparent par défaut.width=1
est la valeur par défaut.outline="#dddddd"
par exemple, "black" par défaut.activefill="red"
par exemple.activewidth=4
par exemple.14° Tenter de reproduire les affichages suivants à partir du code proposé (il faudra rajouter des arguments adaptés dans les méthodes create_oval() ).
Au survol :
Le code des rectangles englobants et des ellipses est :
create_rectangle(10,110,150,210,outline="#dddddd")
.create_oval(10,110,150,210, ...)
.create_rectangle(210,110,280,210,outline="#dddddd")
.create_oval(210,110,280,210, ...)
.create_rectangle(310,110,450,210,outline="#dddddd")
.create_oval(310,110,450,210, ...)
.En géométrie euclidienne, un polygone est figure géométrique plane composée d'une ligne brisée fermée sur elle-même.
Dit comme ça, ça parait compliqué mais non. Les exemples courants sont le carré, le triangle, le rectangle, l'hexagone... Voilà pour les cas simples.
On notera qu'un polygone peut être croisé, c'est à dire que ses segments peuvent s'entrecouper.
Pour définir un polygone, il faut donc au moins trois points non alignés.
Pour écrire un polygone dans Tkinter, on utilise create_polygon(x0, y0, x1, y1, x2, y2 ...)
15° Lancer le programme-test suivant pour voir le résultat de n'importe quelle combinaison qui vous viendrait à l'esprit.
Vous pouvez utiliser les paramètres habituels pour gérer la largeur de la bordure (width) , le remplissage (fill) ...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.create_polygon(100,100,100,200,450,300)
.fen_princ.mainloop()
Comme vous pouvez le voir, les options par défaut sont : fill="black"
et pas transparent.
Par contre, la bordure est de base définie par outline=""
, c'est à dire transparent. Le code suivant permet de retrouver les cas précédents (rectangle, oval...) : create_polygon(100,100,100,200,450,300, fill="", outline="black")
. .
Si vous voulez créer n'importe quel polygone ou presque, il faudra trouver les coordonnées à la main, ou presque. Pour vous aiguiller, voici un programme qui trace un cercle en (250;250) avec un rayon de 150 pixels. Puis il trace un certain nombre de droites de 150 pixels partant de (250;250) après avoir fait une rotation d'un certain angle.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
def rotation_ligne( , x0, y0, longueur, angle, couleur) :
"""
Cette fonction permet de dessiner une ligne
leCanvas est la référence du canvas dans lequel on veut dessiner la ligne
x0, y0 sont les coordonnées d'origine
longueur est la longueur de la ligne en pixels
couleur est la couleur sous forme de string : "red" ou "#ff0000"
angle est l'angle par rapport à l'horizontal en degré
"""
angle = math.radians(angle) # math.radians permet de convertir en radians
xf = int(x0+longueur*math.cos(angle))
yf = int(y0+longueur*math.sin(angle))
create_line(x0, y0, xf, yf, width=1, fill=couleur)
.# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.create_oval(100,100,400,400)
.nbr_cotes = 3
for i in range(0,360, int(360/nbr_cotes)) :
rotation_ligne( , 250, 250, 212, i, "red")
fen_princ.mainloop()
16° Utiliser ce progamme pour parvenir à comprendre comment on peut créer la fonction hexagone qui trace un hexagone lorsqu'on lui donne le point central et la distance entre le centre et les points extremes.
On part de ceci normalement :
On remarque que les points extrêmes des droites correspondantes ont les coordonnées des points désirés. On réutilise donc en partie le même code pour créer la fonction. J'utilise la méthode append sur des listes de façon à ne pas créer trop de variables. De plus, cela rendra le code plus facilement adaptable au cas des autres polygones réguliers.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import math
# ----------------------------------------------------------------
# Les fonctions
# ----------------------------------------------------------------
def hexagone( , x0, y0, longueur, couleur) :
"""
x0, y0 sont les coordonnées du point central de la figure
longueur est la longueur entre le centre et les points extrêmes
couleur est la couleur sous forme de string : "red" ou "#ff0000"
Fonctionnement :
-- On stocke dans les listes x[] et y[] les coordonnées des points définissant la forme."
-- On utilise ensuite create_polygone en utilisant les contenus des deux listes."
"""
x = []
y = []
for i in range(6) :
angle = math.radians(i*360/6)
x.append(int(x0+longueur*math.cos(angle)))
y.append(int(y0+longueur*math.sin(angle)))
create_polygon(x[0]y[0],x[1],y[1],x[2],y[2],x[3],y[3],x[4],y[4],x[5],y[5], fill="", outline="black")
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=500, bg='ivory', borderwidth=0, highlightthickness=0)
=place(x=50,y=50)
.hexagone( ,250,250,150,"red")
fen_princ.mainloop()
Et on obtient :
Remarque : rajouter le paramètre smooth=1 (pour "adoucir") lors de l'appel à create_polygon.
17° Question au choix :
Il nous reste encore à voir comment faire un pac-man crédible. En trois questions, pour arriver à 20.
Rien de nouveau pour le début : on utilise la méthode create_arc
en donnant le point supérieur gauche (qu'on nommera x0,y0) et le point inférieur droit (qu'on nommera x1,y1) du rectangle d'épaisseur 1 qui entoure l'arc de cercle ou d'ellipse.
Voici quelques exemples avec le rectangle-conteneur en trait gris clair. Ici (x0;y0) = (10;10) et (x1;y1) = (100;100).
create_rectangle(10,10,100,100,outline="#dddddd")
.create_arc(10,10,100,100)
.create_rectangle(10,10,100,100,outline="#dddddd")
.create_arc(10,10,100,100, fill="yellow")
.create_rectangle(10,10,100,100,outline="#dddddd")
.create_arc(10,10,100,100, outline="#ff0000")
.De base, on pourrait croire qu'on ne crée que des arcs de cercle (ou d'ellipse) portant de l'axe horizontal et avec un angle de +90° (+ car on tourne dans le sens trigonométrique). Par défaut, l'angle de rotation est de 0° : start=0
. C'est pour cela que les arcs du dessus sont tous orientés de la même façon.
Mais on peut faire mieux : on peut indiquer un angle de rotation à l'aide du paramètre start=angle_en_degré.
18° Effectuer différents tests pour parvenir à comprendre le principe de ce paramètre. Tentez par exemple d'obtenir les deux cas ci-dessous (l'image contient trois objets : un rectangle et un arc dont les contours sont très clairs et l'arc de cercle après rotation) :
On peut aller plus loin en définissant l'angle de l'arc de cercle. Le paramètre se nomme extent. De base, on a extent=90
, inutile de préciser l'unité, la méthode vaut des degrés. On crée donc par défaut un quart de cercle. Mais on peut créer des demi-cercle en prenant un angle de 180°, ect ...
19° Effectuer différents tests pour parvenir à comprendre le principe de ce paramètre. Tentez par exemple d'obtenir les deux cas suivants (l'image contient trois objets : un rectangle et un arc de 90° dont les contours sont très clairs et l'arc de cercle après augmentation de l'angle extent) :
20° Il vous reste à créer les deux images de pac-man, l'un avec la bouche ouverte, et l'autre avec la bouche fermée.
Vous savez maintenant créer une interface, créer des boutons, gérer les tests, créer des fonctions et afficher du dessin vectoriel dans les Canvas. Il ne nous reste qu'à savoir créer du mouvement, et vous pourrez faire votre premier jeu ! Nous verrons cela dans le prochain chapitre sur les interfaces graphiques.