Formation I.S.N.

Les canevas - Exercices

Attention ! Les erreurs de programmation avec les interfaces graphiques sont souvent nombreuses. Lorsqu'elles se produisent, il faut redémarrer le shell courant afin d'éviter «d'empiler les erreurs». Avec l'interface Pyzo, cela se fait en cliquant sur le bouton .

Un récapitulatif des instructions usuelles étudiées dans la page Module tkinter est disponible, au format .pdf en cliquant ici.

Pour faire travailler les élèves sur les interfaces graphiques, il est fortement conseillé de donner des programmes à compléter contenant déjà l'architecture de base de la fenêtre. Cela leur permettra de se concentrer sur le contenu (la programmation proprement dite) plutôt que sur la mise en forme (l'apparence).

Liste de listes

Cet exercice permet de se rendre compte d'une erreur fréquente commise par les élèves lors de la création d'un plateau de jeu.

On considère une grille de puissance 4 contenant 6 lignes de 7 colonnes. On modélise donc cette grille par une liste contenant 6 listes de 7 éléments et on initialise chacun des éléments à 0. Comment faire pour que l'élément central de la ligne du bas (et uniquement lui) prenne la valeur 1 ?

Corrigez la réponse qu'un élève a formulé ci-dessous.


					
					
  • Une modification qui ne change rien
  • Une solution
  • Une meilleure solution

L'erreur de programmation provient de l'utilisation de l'opérateur « * » :

  • L'instruction [0]*7 crée une liste dans un emplacement mémoire ;
  • L'instruction [[0]*7]*6 crée en fait 6 alias de la liste [0]*7.

En corrigeant cette instruction avec la méthode .append(), il n'y aura pas de changement (cf. la page Portée des variables).


cases=[]
ligne = [0]*7
for i in range(6):
    cases.append(ligne)

##----- Affichage avant modification -----##
for i in range(len(cases)):
    print(cases[i])

##----- Modification -----##
print('\n')
cases[5][3] = 1

##----- Affichage après modification -----##
for i in range(len(cases)):
    print(cases[i])
						

En n'affectant plus chaque nouvelle ligne à une variable, on résout le problème.


cases=[]
for i in range(6):
    cases.append([0]*7)

##----- Affichage avant modification -----##
for i in range(len(cases)):
    print(cases[i])

##----- Modification -----##
print('\n')
cases[5][3] = 1

##----- Affichage après modification -----##
for i in range(len(cases)):
    print(cases[i])
						

Il faut amener les élèves à stocker les constantes globales dans des variables facilement modifiables à l'intérieur du programme.


##----- Constantes et Variables globales -----##
nb_lignes = 6
nb_colonnes = 7
cases = []
for i in range(nb_lignes):
    cases.append([0]*nb_colonnes)

##----- Affichage avant modification -----##
for i in range(nb_lignes):
    print(cases[i])

##----- Modification -----##
print('\n')
cases[5][3] = 1

##----- Affichage après modification -----##
for i in range(nb_lignes):
    print(cases[i])
						

Carrés et cercles

La fenêtre ouverte par le code ci-dessous contient un canevas et un bouton [Quitter].


##-----Importation des Modules-----##
from tkinter import *

##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Carrés et Cercles')

##-----Création des boutons-----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##-----Création des canevas-----##
dessin = Canvas(fen, width=400, height=200)
dessin.grid(row = 0, column = 0, columnspan=2)

##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
				
  1. Le canevas ne se distingue pas du fond de la fenêtre. Changez sa couleur de fond (attribut bg pour « background ») lors de la création du canevas dessin.
  2. Encadrer le canevas par un rectangle noir d'épaisseur de côté 1 et de mêmes dimensions que le canevas. Les coordonnées (x0 ; y0) seront (0 ; 0). Que constate-t-on ?
    Modifier les coordonnées (x0 ; y0) et (x1 ; y1) de sorte que le rectangle «colle» parfaitement au contour du canevas.
  3. Dans le canevas, tracer un carré de bords noirs mesurant 100 pixels, d'épaisseur 2 avec un fond jaune.
  4. .create_oval() permet de tracer un cercle en l'insérant dans un carré de côté 2R. Compléter les pointillés de la figure ci-contre pour déduire de cette figure l'instruction permettant de tracer un cercle rouge dans le carré jaune définie à la question précédente.
  5. Un cercle peut aussi être défini par son centre I(xI ; yI) et son rayon R. Compléter la 2nde figure pour en déduire l'instruction permettant de tracer un cercle ayant pour centre le point de coordonnées (350 ; 100) et de rayon 40.
Image à compléter de la question 4°/ Image à compléter de la question 5°/
  • Étape n°1
  • Étape n°2
  • Étape n°3
  • Étape n°4
  • Étape n°5

Attribut bg (background) lors de la création du canevas dessin :


##-----Importation des Modules-----##
from tkinter import *

##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Carrés et Cercles')

##-----Création des boutons-----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##-----Création des canevas-----##
dessin = Canvas(fen, width=400, height=200, bg='white')
dessin.grid(row = 0, column = 0, columnspan=2)

##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

Méthode .create_rectangle() appliquée au canevas dessin. Selon le système d'exploitation, le coin haut-gauche du canevas aura pour coordonnées (1, 1) (sous Linux) ou bien (2, 2) (sous Windows).


##-----Importation des Modules-----##
from tkinter import *

##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Carrés et Cercles')

##-----Création des boutons-----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##-----Création des canevas-----##
dessin = Canvas(fen, width=400, height=200, bg='white')
dessin.grid(row = 0, column = 0, columnspan=2)

##-----Création des figures-----##
entoure = dessin.create_rectangle(2, 2, 401, 201)

##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

Attributs outline et fill de la méthode .create_rectangle() :


##-----Importation des Modules-----##
from tkinter import *

##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Carrés et Cercles')

##-----Création des boutons-----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##-----Création des canevas-----##
dessin = Canvas(fen, width=400, height=200, bg='white')
dessin.grid(row = 0, column = 0, columnspan=2)

##-----Création des figures-----##
entoure = dessin.create_rectangle(2, 2, 401, 201)
carre = dessin.create_rectangle(50, 50, 100, 100, width=3, outline='black', fill='yellow')

##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

##-----Importation des Modules-----##
from tkinter import *

##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Carrés et Cercles')

##-----Création des boutons-----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##-----Création des canevas-----##
dessin = Canvas(fen, width=400, height=200, bg='white')
dessin.grid(row = 0, column = 0, columnspan=2)

##-----Création des figures-----##
entoure = dessin.create_rectangle(2, 2, 401, 201)
carre = dessin.create_rectangle(50, 50, 100, 100, width=3, outline='black', fill='yellow')

cercle1 = dessin.create_oval(50, 50, 100, 100, width=3, outline='red')

##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

##-----Importation des Modules-----##
from tkinter import *

##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Carrés et Cercles')

##-----Création des boutons-----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##-----Création des canevas-----##
dessin = Canvas(fen, width=400, height=200, bg='white')
dessin.grid(row = 0, column = 0, columnspan=2)

##-----Création des figures-----##
entoure = dessin.create_rectangle(2, 2, 401, 201)
carre = dessin.create_rectangle(50, 50, 100, 100, width=3, outline='black', fill='yellow')

cercle1 = dessin.create_oval(50, 50, 100, 100, width=3, outline='red')

cercle2 = dessin.create_oval(350-40, 100-40, 350+40+1, 100+40+1, width=3, outline='#e46f0a')

##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

Une liste de carrés

Le quadrillage ci-dessus (à gauche) rentre parfaitement dans le canevas. Pour cela, on a défini comme variables la longueur c d'un côté d'une case et le nombre n de cases par ligne et par colonne :


##----- Importation des Modules -----##
from tkinter import *

##----- Variables globales -----##
c = 30                          # Longueur d'un côté d'une case
n = 8                           # Nombre de cases par ligne et par colonne
cases = []                      # Liste contenant les objets cases

##----- Création de la fenêtre -----##
fen = Tk()
fen.title('Damier')

##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.quit)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##----- Création des canevas -----##
dessin = Canvas(fen, width= , height= , bg='white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)

##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    for colonne in range(n):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle( ))
    cases.append(transit)       # Ajout de la ligne à la liste principale

##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
				
  1. Le programme ci-dessus est incomplet. Complétez les paramètres width et height du canevas à la ligne 18 (ne pas oublier le décalage constaté lors de l'exercice précédent, décalage qui dépend du système d'exploitation).
  2. Les carrés qui constituent les cases doivent être stockés dans une liste de listes. A la ligne 25, déterminez les coordonnées permettant de tracer chaque carré en fonction des numéros de ligne, des numéros de colonne et de la variable c (ne pas oublier le décalage constaté lors de l'exercice précédent, décalage qui dépend du système d'exploitation).
  3. La question précédente permet d'obtenir le quadrillage. En utilisant la méthode .itemconfigure() qui permet de modifier les paramètres d'un item déjà créé, afficher le damier désiré.
  • Étape n°1
  • Étape n°2
  • Étape n°3

Solution sous système d'exploitation Windows, avec un décalage de +2.


##----- Importation des Modules -----##
from tkinter import *

##----- Variables globales -----##
c = 30                          # Longueur d'un côté d'une case
n = 8                           # Nombre de cases par ligne et par colonne
cases = []                      # Liste contenant les objets cases

##----- Création de la fenêtre -----##
fen = Tk()
fen.title('Damier')

##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##----- Création des canevas -----##
dessin = Canvas(fen, width = n*c+2, height = n*c+2, bg = 'white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)

##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    for colonne in range(n):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle( ))
    cases.append(transit)       # Ajout de la ligne à la liste principale

##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
						

Solution sous système d'exploitation Windows, avec un décalage de +2.


##----- Importation des Modules -----##
from tkinter import *

##----- Variables globales -----##
c = 30                          # Longueur d'un côté d'une case
n = 8                           # Nombre de cases par ligne et par colonne
cases = []                      # Liste contenant les objets cases

##----- Création de la fenêtre -----##
fen = Tk()
fen.title('Damier')

##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##----- Création des canevas -----##
dessin = Canvas(fen, width = n*c+2, height = n*c+2, bg = 'white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)

##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    for colonne in range(n):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle(colonne*c+2, ligne*c+2, (colonne+1)*c+2, (ligne+1)*c+2))
    cases.append(transit)       # Ajout de la ligne à la liste principale

##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
						

Solution sous système d'exploitation Windows, avec un décalage de +2.


##----- Importation des Modules -----##
from tkinter import *

##----- Variables globales -----##
c = 30                          # Longueur d'un côté d'une case
n = 8                           # Nombre de cases par ligne et par colonne
cases = []                      # Liste contenant les objets cases

##----- Création de la fenêtre -----##
fen = Tk()
fen.title('Damier')

##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##----- Création des canevas -----##
dessin = Canvas(fen, width = n*c+2, height = n*c+2, bg = 'white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)

##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    for colonne in range(n):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle(colonne*c+2, ligne*c+2, (colonne+1)*c+2, (ligne+1)*c+2))
    cases.append(transit)       # Ajout de la ligne à la liste principale

##----- Modification des figures créées -----##
for ligne in range(n):
    for colonne in range(n):
        if (ligne+colonne)%2 == 0:      # Parité
            dessin.itemconfigure(cases[ligne][colonne], outline='black', fill='black')
        else:
            dessin.itemconfigure(cases[ligne][colonne], outline='white')
	
##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements