32 - Projet avec tkinter
Cette activité présente un début de projet guidé utilisant une Interface Graphique Tkinter en tant qu'Interface Homme Machine.

Le programme va être séparé en deux parties distinctes :
- La partie qui traite de l'interaction avec l'IHM Tkinter
- La partie qui gère les données et sans lien direct avec l'interface graphique
En fin d'activité, plusieurs sujets de projet Tkinter seront proposés.
Prérequis :
- l'activité Données : encodage des matrices
- l'activité Python : les dictionnaires
- l'article FICHE Tkinter
Logiciel nécessaire pour l'activité : Python 3 : Thonny, IDLE ...
1 - Création de l'application graphique
RAPPEL
Contrairement au système d'axes Oxy classique en mathématiques, dans le système d'axes de Tkinter, l'axe y est orienté vers le bas et le point d'origine (0,0) correspond au point en haut à gauche.

Rappel des notions vues dans l'article Tkinter
La FICHE sur Tkinter contient les explications sur les notions suivantes. Si cela ne vous dit vraiment plus rien, commencez par lire l'article en activant les programmes au fur et à mesure de façon à bien comprendre l'utilisation de ces différentes notions.
- Partie 1 : Créer la fenêtre
- Constructeur Tk
- Méthode geometry
- Méthode title
- Méthode configure pour modifier après création
- Méthode mainloop pour lancer la surveillance des événements
- Partie 2 : Créer, modifier et lire les attributs des widgets
- Constructeur Label pour créer un widget Texte
- Méthode pack pour afficher et placer simplement le widget
- Méthode place pour afficher et placer à des coordonnées précises
- Méthode configure pour modifier après création
- Méthode cget pour récupérer la valeur d'un attribut
- Partie 3 : Utiliser le gestionnaire d'événements
- Méthode bind pour utiliser le gestionnaire d'événements
- Description des différents événements
- Fonction événementielle et paramètre event pour récupérer les informations de l'événement
- Partie 4 : Placer précisement les widgets
- Méthode place pour afficher et placer le widget à des coordonnées précises
- Partie 5 : Animer automatiquement
- Méthode after pour programmer un appel retardé à une fonction
- Partie 6 : Utilisation avancée
- Fonction lambda pour envoyer l'adresse d'une fonction créée à la volée (hors programme, à n'utiliser que dans le cadre des interfaces graphiques)
Nous allons voir comment réaliser une fenêtre graphique dans laquelle nous allons placer nos éléments graphiques. Par exemple ceci :

Nous prendrons comme support de travail le plateau de jeu que nous avions modélisé dans l'activité Matrice de la partie Données.

Pour travailler proprement,nous allons clairement séparer :
- la partie "Interface" : les variables et les fonctions qui dépendent de l'Interface Homme Machine (IHM) (l'interface graphique Tkinter ici)
- la partie "Données" : les variables et les fonctions qui contiennent les données brutes sur le plateau de jeu (tout ce qui n'aura pas à être modifié si on décide de passer de l'interface Tkinter à l'interface Qt ou autre par exemple)

Nous allons construire peu à peu une interface de ce type dans cette activité. L'activité suivante consiste à sa "transformation" de façon à en faire un véritable jeu. Commençons par la création de :
- la fenêtre servant d'écran principal fenetre
- la matrice qui contient les données encodant les cases du plateau (sous forme d'un simple entier)
- d'un dictionnaire infos contenant des informations complémentaires diverses sur le jeu (quel joueur, l'état actuel du jeu...).
01° Lancer simplement le programme suivant pour visualiser qu'il parvient bien à construire une interface graphique (noire et vide pour le moment).
Inutile de l'analyser, c'est l'objet des questions suivantes.
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 | # ----- 1 - IMPORTATIONS ------------------------------------
import tkinter as tk
# ----- 2 - CONSTANTES --------------------------------------
# ----- 3 - FONCTIONS liées à la partie DONNEES ----------------
# ----- 4 - FONCTIONS liées à la partie INTERFACE -----------
def creer_fenetre():
"""Renvoie la référence d'une nouvelle fenêtre
:: return (tkinter.Tk) :: la référence de la nouvelle fenêtre
"""
fe = tk.Tk() # on crée la nouvelle fenêtre
configurer_fenetre(fe) # on configure sa apparence
return fe # on renvoie sa référence
def configurer_fenetre(fe):
"""Modifie les paramètres de la fenêtre reçue en paramètre
:: param fe(tkinter.Tk) :: la référence d'une fenêtre de classe Tk
:: return (None) :: "procédure" Python, modifie fe par effet de bord
"""
fe.geometry("1200x600")
fe.title("Mon super jeu")
fe.configure(bg="black")
# ----- 5 - PROGRAMME PRINCIPAL ------------------------------
if __name__ == '__main__':
# --- Activation des tests sur les fonctions du module
import doctest
doctest.testmod()
# Une seule fonction ? doctest.run_docstring_examples(initialiser_les_donnees, globals())
# --- Données du jeu
# informations diverses
infos = {"joueur": -1, "état": 0}
# matrice encodant le contenu des cases du plateau
matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
# --- Création de la fenêtre principale
fenetre = creer_fenetre()
# --- Activation de la surveillance sur l'application graphique
fenetre.mainloop()
|
Que fait le programme ?
Ligne 3 : importation du module sous l'alias tk
3 | import tkinter as tk
|
Lignes 14-33 : passons pour l'instant sur la partie déclaration des fonctions. Nous verrons ce qu'elles font lorsque nous aurons besoin de le savoir.
Lignes 41-32 dans le programme principal : on commence par l'utilisation des docstrings en tant que jeu de tests :
38
39
40
41
42 | if __name__ == '__main__':
# --- Activation des tests sur les fonctions du module
import doctest
doctest.testmod()
|
Lignes 45-58 : créations de 2 variables globales.
45
46
47
48
49
50
51
52
53
54
55
56
57
58 | # --- Données du jeu
# informations diverses
infos = {"joueur": -1, "état": 0}
# matrice encodant le contenu des cases du plateau
matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
- Un dictionnaire infos contenant des données générales sur l'état du jeu
- La clé "joueur" permet d'accéder au joueur actif : 1, 2 ou -1 si aucun
- La clé "état" permet d'accéder à l'état du jeu : 0 pour inactif, 1 pour actif et 2 pour fini par exemple.
- Une matrice donnees qui encode le contenu des cases du plateau de jeu. Dans la version présentée ici :
- 0 veut dire que la case contient une case vide
- 1 veut dire que la case contient un mur
- d'autres valeurs pour l'aventurier, le dragon...
J'utilise un integer ici mais, dans votre projet, vous pourrez y mettre un tuple, un string, un autre tableau...
Tableaux et dictionnaires étant muables dans Python , nous pourrons modifier le contenu de ces variables depuis les fonctions en leur transférant la référence-mémoire.
02° Que renvoie matrice[2][4] ?
...CORRECTION...
On se souviendra que l'indice de la première ligne est 0.
matrice[2] est donc le contenu de la case-ligne d'indice 2.
51
52
53
54
55
56
57
58 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
matrice[2][4] : il ne reste qu'à chercher la colonne d'indice 4 sur cette ligne : elle fait référence à la valeur 3.
51
52
53
54
55
56
57
58 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
03° Que renvoie len(matrice) ?
...CORRECTION...
On parvient à trouver le nombre de lignes en cherchant la longueur de matrice. Si vous regarder ci-dessous, vous pourrez constatez qu'on dénombre 6 éléments car il y a 6 tableaux-lignes.
51
52
53
54
55
56
57
58 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
04° Que renvoie len(matrice[0]) ou len(matrice[1]) ?
...CORRECTION...
matrice[2] renvoie le tableau encodant la première ligne.
La longueur de ce tableau correspond donc au nombre de colonnes sur cette ligne.
Quelque soit la ligne transmise, on obtient 6 colonnes.
83
84
85
86
87
88
89
90 | donnees = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
Lignes 61 : création de la fenêtre graphique.
61 | fenetre = creer_fenetre()
|
Pour comprendre comment cela fonctionne, il faut bien entendu aller voir la déclaration de la fonction creer_fenetre(), qui utilise d'ailleurs à l'interne la fonction configurer_fenetre().
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | def creer_fenetre():
"""Renvoie la référence d'une nouvelle fenêtre
:: return (tkinter.Tk) :: la référence de la nouvelle fenêtre
"""
fe = tk.Tk() # on crée la nouvelle fenêtre
configurer_fenetre(fe) # on configure sa apparence
return fe # on renvoie sa référence
def configurer_fenetre(fe):
"""Modifie les paramètres de la fenêtre reçue en paramètre
:: param fe(tkinter.Tk) :: la référence d'une fenêtre de classe Tk
:: return (None) :: "procédure" Python, modifie fe par effet de bord
"""
fe.geometry("1200x600")
fe.title("Mon super jeu")
fe.configure(bg="black")
|
- Ligne 20 : Création d'un objet de classe Tk et stockage de sa référence dans la variable locale fe.
- Ligne 21 : Appel à configurer_fenetre qui reçoit ici l'argument fe et le place dans son paramètre qui se nomme également fe.
- Ligne 31 : geometry permet de fournir les dimensions en pixels.
- Ligne 32 : title permet de faire de même pour le titre de la fenêtre
- Ligne 33 : configure permet de modifier la couleur de fond (bg) à qui on affecte la couleur "black"
- Ligne 22 : On renvoie la référence de la nouvelle fenêtre stockée dans fe.
05° Lancer l'interface pour visualiser ce que cela donne. Modifier quelques valeurs de la fonction configurer_fenetre() pour voir le résultat.
On rappelle que les couleurs d'une image sont définies en RVB (ou RGB en anglais). On peut donc soit donner le nom d'une couleur prédéfinie ou fournir les valeurs RGB en hexadécimal. Chaque composante peut prendre une valeur allant de 00 à FF.
Exemples :
"#000000"
équivalent à "black"
"#FF0000"
équivalent à "red"
"#00FF00"
équivalent à "green"
"#FFFF00"
équivalent à "yellow"
A partir de là, on peut lancer l'interface graphique avec cette dernière partie :
Activation de la surveillance en boucle sur l'interface graphique jusqu'à fermeture
63
64 | # --- Activation de la surveillance sur l'application graphique
fenetre.mainloop()
|
2 - Les carrés colorés
Voyons maintenant comment créer des carrés colorés sur cette interface. Ici :
- le carré numéro 0 est aux coordonnées x = 0 et y = 0
- le carré numéro 1 est aux coordonnées x = 1 et y = 0
- le carré numéro 5 est aux coordonnées x = 1 et y = 1

Dans ce programme,
- x correspondra donc à l'indice de la colonne (0 est la colonne de gauche)
- y correspondra à l'indice de la ligne (0 est la ligne du haut).
05° Lancer le code ci-dessous pour voir la nouveauté qu'il apporte. Lire ensuite la suite des explications.
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 | # ----- 1 - IMPORTATIONS ------------------------------------
import tkinter as tk
# ----- 2 - CONSTANTES --------------------------------------
# ----- 3 - FONCTIONS liées à la partie DONNEES ----------------
# ----- 4 - FONCTIONS liées à la partie INTERFACE -----------
def creer_fenetre():
"""Renvoie la référence d'une nouvelle fenêtre
:: return (tkinter.Tk) :: la référence de la nouvelle fenêtre
"""
fe = tk.Tk() # on crée la nouvelle fenêtre
configurer_fenetre(fe) # on configure sa apparence
return fe # on renvoie sa référence
def configurer_fenetre(fe):
"""Modifie les paramètres de la fenêtre reçue en paramètre
:: param fe(tkinter.Tk) :: la référence d'une fenêtre de classe Tk
:: return (None) :: "procédure" Python, modifie fe par effet de bord
"""
fe.geometry("1200x600")
fe.title("Mon super jeu")
fe.configure(bg="black")
def creer_cases(fe, m):
"""Renvoie une matrice ayant les mêmes dimensions que m mais contenant des widgets Labels
:: param fe(tkinter.Tk) :: la référence d'une fenêtre de classe Tk
:: param m(list(list)) :: la matrice contenant les données des cases
:: return (list(list)) :: une matrice contenant des tkinter.Label
"""
nbl = len(m) # Nombre de lignes
nbc = len(m[0]) # Nombre de colonnes
# On crée une matrice w qui servira à contenir les widgets représentant les cases
w = [ [None for x in range(nbc)] for y in range(nbl) ]
# On crée les Labels un par un et on les stocke dans la matrice
for y in range(nbl):
for x in range(nbc):
# Calcul du numéro de la case
numero = x + y * nbc
# Création et stockage d'un Label
w[y][x] = tk.Label(fe, text=numero, fg="white", bg='grey', width=10, height=5)
# Affichage et placement du widget contenu dans w[y][x]
px = 20 + x*90 # position px en pixels
py = 20 + y*85 # position py en pixels
w[y][x].place(x=px, y=py)
# Option : rajout d'attributs pour les récupérer facilement
w[y][x].numero = numero
w[y][x].colonne = x
w[y][x].ligne = y
# Option : modification du widget (juste pour montrer qu'on peut !!)
if x == 1: # nous sommes sur la 2e colonne (indice 1)
w[y][x].configure(bg="red")
elif y == 2: # nous sommes sur la 3e ligne (indice 2)
w[y][x].configure(bg='#AA0000')
# On renvoie la matrice
return w
# ----- 5 - PROGRAMME PRINCIPAL ------------------------------
if __name__ == '__main__':
# --- Activation des tests sur les fonctions du module
import doctest
doctest.testmod()
# Une seule fonction ? doctest.run_docstring_examples(initialiser_les_donnees, globals())
# --- Données du jeu
# informations diverses
infos = {"joueur": -1, "état": 0}
# matrice encodant le contenu des cases du plateau
matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
# --- Création de la fenêtre principale
fenetre = creer_fenetre()
# --- Création des widgets représentant graphiquement la matrice de données
widgets = creer_cases(fenetre, matrice)
# --- Activation de la surveillance sur l'application graphique
fenetre.mainloop()
|
Création de la matrice de stockage des Labels
Ligne 107, on crée (à l'aide de la fonction creer_cases()) la nouvelle matrice nommée widgets qui contient les différents widgets colorés.
L'appel à la fonction se fait ainsi :
107 | widgets = creer_cases(fenetre, matrice)
|
On envoie donc les argmuments fenetre et matrice.
35
36
37
38
39
40
41
42
43
44
45
46
47
48 | def creer_cases(fe, m):
"""Renvoie une matrice ayant les mêmes dimensions que m mais contenant des widgets Labels
:: param fe(tkinter.Tk) :: la référence d'une fenêtre de classe Tk
:: param m(list(list)) :: la matrice contenant les données des cases
:: return (list(list)) :: une matrice contenant des tkinter.Label
"""
nbl = len(m) # Nombre de lignes
nbc = len(m[0]) # Nombre de colonnes
# On crée une matrice w qui servira à contenir les widgets représentant les cases
w = [ [None for x in range(nbc)] for y in range(nbl) ]
...
|
Ligne 107 puis 35 : le paramètre fe reçoit donc fenetre et le paramètre m reçoit matrice.
Lignes 43 et 44 : on récupère le nombre de lignes nbl et le nombre de colonnes nbc de la matrice m.
Ligne 47 : on crée une matrice puisqu'on crée un tableau contenant des tableaux.
47 | w = [ [None for x in range(nbc)] for y in range(nbl) ]
|
06° QCM : Cette matrice w (pour dire widgets représentant graphiquement les cases du plateau) est construite par :
- A : Compréhension
- B : Déception
- C : Incompréhension
- D : Extension et ajouts successifs
...CORRECTION...
Création des cases et remplissage de cases
Nous avons donc une belle matrice vide nommée w. Voyons comment nous parvenons à la remplir.
Lignes 50-51 : double boucle permettant d'accéder aux coordonnées des cases de la matrice dans l'ordre habituel de lecture : de gauche à droite et avec un passage à la ligne suivante :
..
49
50
51
52
53
54
55
56
57
..
| ...
# On crée les Labels un par un et on les stocke dans la matrice
for y in range(nbl):
for x in range(nbc):
# Calcul du numéro de la case
numero = x + y * nbc
# Création et stockage d'un Label
w[y][x] = tk.Label(fe, text=numero, fg="white", bg='grey', width=10, height=5)
...
|
On va donc boucler plusieurs fois et obtenir la suite de valeurs suivantes pour la ligne y et la colonne x :
Si on prend l'exemple d'une petite matrice 3x3 avec des indices valant donc 0, 1 ou 2, les indices successifs pris par les variables de boucle y et x sont donc :
(y=0, x=0) puis (y=0, x=1) puis (y=0, x=2) puis
(y=1, x=0) puis (y=1, x=1) puis (y=1, x=2) puis
(y=2, x=0) puis (y=2, x=1) puis (y=2, x=2) et c'est fini.
07° QCM : Cela revient donc à parcourir les cases du plateau :
- A : En commençant en haut à gauche et en partant d'abord vers la droite
- B : En commençant en haut à gauche et en partant d'abord vers le bas
- C : En commençant en bas à gauche et en partant d'abord à droite
- D : En commençant en bas à gauce et en partant d'abord en haut
...CORRECTION...
La question n'était pas difficile puisque les cases sont numérotées de base dans l'ordre de création !

Reprenons la création du Label en ligne 57 :
57
| w[y][x] = tk.Label(fe, text=numero, fg="white", bg='grey', width=10, height=5)
|
- Un paramètre obligatoire : la réference d'une fenêtre Tkinter qui contiendra ce Label : fe ici
- text, le texte qu'on veut voir s'afficher dans le widget Label
- fg pour foreground, fond du premier plan, le texte.
- bg pour background, fond coloré.
- width pour width, la largeur (en largeur de caractères puisqu'il s'agit d'un texte)
- height pour height, la hauteur (en hauteur de caractères puisqu'il s'agit d'un texte).
On place donc dans w[y][x] la référence d'un widget-Label. On lui transmet différents paramètres nommés, dont on peut trouver la description dans la documentation de Tkinter :
08° Répondre aux deux questions en analysant la ligne 57.
- Quel est le texte qu'on veut voir s'afficher dans notre carré ?
- Quelle devrait être la couleur de fond de tous nos carrés ?
...CORRECTION...
On veut afficher le numéro de la case : text=numero
Toutes les cases devraient être grises : bg='grey'
Remarque
Nous avons ceci :
57
| w[y][x] = tk.Label(fe, text=numero, fg="white", bg='grey', width=10, height=5)
|
Nous aurions pu l'écrire en plusieurs étapes si vous préférez :
57
58
59
60
61
62 | w[y][x] = tk.Label(fe)
w[y][x].configure(text=numero)
w[y][x].configure(fg="white")
w[y][x].configure(bg='grey')
w[y][x].configure(width=10)
w[y][x].configure(height=5)
|
Avantage : plus clair.
Désavantage : moins concis.
Les widgets sont maintenant créés et stockés dans la matrice w mais si nous arrêtions ici, ils n'apparaitraient pas à l'écran : il faut encore dire précisement où les placer dans l'interface.
Placement des widgets
Voici donc pourquoi nous calculons leurs coordonnées en pixels px et py en fonction des positions théoriques x et y et que nous faisons appel à la méthode place() pour placer les widgets.
59
60
61
62 | # Affichage et placement du widget contenu dans w[y][x]
px = 20 + x * 90 # position px en pixels
py = 20 + y * 85 # position py en pixels
w[y][x].place(x=px, y=py)
|
Si les écartements entre cases ne vous conviennent pas, il suffit de modifier les valeurs.
Rajout de quelques informations
On rajoute ensuite quelques informations liées à la position de la case qui vont être assez utiles faire le lien entre la matrice widgets et la matrice matrice : son numéro de case, son numéro de ligne et de colonne. L'explication profonde du mécanisme n'arrivera qu'en Terminale. Mais c'est facile à lire :
64
65
66
67 | # Option : rajout d'attributs pour les récupérer facilement
w[y][x].numero = numero
w[y][x].colonne = x
w[y][x].ligne = y
|
Ainsi si on veut récupérer le numéro de ligne d'un Label sur lequel on vient de cliquer et dont la référence est stockée
- dans case : il suffit de taper case.ligne.
- dans w[y][x] : il suffit de taper w[y][x].ligne.
Pratique, non ?
Quelques modifications optionnelles supplémentaires
On trouve enfin une modification des caractéristiques de la case. Nous aurions pu les placer directement lors de la création : elles ne servent qu'à vous montrer comment modifier avec configure() les attributs d'un Label qui existe déjà.
69
70
71
72
73 | # Option : modification du widget (juste pour montrer qu'on peut !!)
if x == 1: # nous sommes sur la 2e colonne (indice 1)
w[y][x].configure(bg="red")
elif y == 2: # nous sommes sur la 3e ligne (indice 2)
w[y][x].configure(bg='#AA0000')
|
Comme nous l'avons vu, les carrés devraient être gris. Or, ils ne sont pas tous gris. Pourquoi ?
- Ligne 70 : on teste si notre case se situe sur la colonne d'indice 1
- Ligne 71 : si c'est le cas, on utilise la méthode configure() qui modifie le fond coloré (bg) en rouge vif (red).
- Ligne 72 : sinon, on teste sur notre case se situe sur la ligne d'indice 2
- Ligne 73 : si c'est le cas, on utilise la méthode configure() pour modifier le fond coloré en rouge foncé.
09° Modifier le script pour afficher le texte 'C1' dans les carrés de la colonne 1.
...CORRECTION...
69
70
71
72
73
74 | # Option : modification du widget (juste pour montrer qu'on peut !!)
if x == 1: # nous sommes sur la 2e colonne (indice 1)
w[y][x].configure(bg="red")
w[y][x].configure(text='C1')
elif y == 2: # nous sommes sur la 3e ligne (indice 2)
w[y][x].configure(bg='#AA0000')
|
10° Pourquoi la case de colonne 1 et de ligne 2 a-t-elle la couleur imposée par sa colonne alors que le test de la ligne est vrai
59
60
61
62
63 | # Option : modification du widget (juste pour montrer qu'on peut !!)
if x == 1: # nous sommes sur la 2e colonne (indice 1)
w[y][x].configure(bg="red")
elif y == 2: # nous sommes sur la 3e ligne (indice 2)
w[y][x].configure(bg='#AA0000')
|
...CORRECTION...
Justement parce que le test de la ligne se fera APRES celui de la colonne et SI le bloc de la colonne n'a pas été activé : dans une séquence IF-ELIF-ELSE, on n'effectue que le premier bloc détécté comme valide.
Et une fois que nous sommes sortis des deux boucles FOR et avons stocké tous les labels dans la nouvelle matrice, il faut la renvoyer puisqu'il s'agit d'une variable locale : si on ne la renvoie pas, elle disparaitrait dès qu'on sort de cette fonction.
65
66 | # On renvoie la matrice
return w
|
Faciliter la lecture du code
Les variables globales n'ont pas vocation à être directement lues ou modifiées depuis une fonction si on peut faire autrement.
Les transmettre en arguments et les placer dans des paramètres est la meilleur possibilité. Par contre, changer le nom des paramètres à chaque fois est ennuyeux pour la compréhension du code.
Vous remarquerez que j'ai fait le choix que garder constamment les mêmes noms pour les paramètres contenant des références vers des variables globales.
- fe dans le paramètre de fonction qui doit recevoir la variable globale fenetre
- d comme variable locale associée à la variable globale donnees : il s'agit de la matrice contenant les données encodant le contenu des cases.
- w comme variable locale associée à la variable globale widgets : il s'agit de la matrice contenant les widgets représentant graphiquement les cases.
- c comme variable locale associée à la variable globale complement : il s'agit du tableau contenant les informations complémentaires sur le jeu.
On pourrait leur donner exactement le même nom mais on évitera : celui permet de créer momins de confusion entre variable globale et variable locale.
Variable d'une seule lettre ?
Attention aux variables d'une seule lettre.
Pratiques
- car elles allègent la lecture.
Dangereuses
- car on aura vite fait de créer une autre variable portant le même nom et écrasant l'ancien contenu.
- car elles ne sont pas vraiment explicites.
3 - Partie Données vers partie Interface graphique
Constantes
Nous allons utilisé ici des CONSTANTES, des "variables" dont le contenu ne sera pas variable mais constant : on affecte un contenu au début et ensuite il ne changera jamais lors du déroulement du programme.
Par convention, on utilise uniquement des majuscules dans le nom des constantes. Elles indiquent clairement qu'il ne faudra pas les modifier ensuite.
En Python, elles n'existent pas vraiment : nos constantes ne sont en réalité que des variables dont le nom est composé de majuscules. Rien ne vous empêche de modifier le contenu après initialisation du contenu. Il existe par contre des langages qui distinguent variables et constantes et empêchent les modifications ultérieures.
Dans nos programmes, nous nous autoriserons à LIRE les valeurs des constantes globales depuis les fonctions plutôt que de les transférer via des paramètres.
Pour l'instant l'affichage n'a aucun rapport avec ce qu'on a placé dans la matrice donnees. Nous allons ici nous intéresser à la liaison DONNEES vers INTERFACE : on veut que les données stockées dans la matrice donnees soient visibles à l'écran. Il faudrait ainsi que les 0, 1, 3 et 8 stockés dans donnees soient transformés en effet visible à l'écran. Voici le début du nouveau code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
|
11° Mettre ce code en mémoire : il place en mémoire les 5 constantes, dont APPARENCE qui est une CONSTANTE-DICTIONNAIRE.
Lancer ensuite ces évaluations dans la console.
Questions
- Quel est le type des clés du dictionnaire ?
- Quel est l'intérêt de taper DRAGON plutôt que juste 8 ?
- Quel est le type de valeurs du dictionnaire ?
- Que va renvoyer la dernière évaluation ?
>>> DRAGON
8
>>> APPARENCE[DRAGON]
('D', '#FF5555', '#FF7777')
>>> APPARENCE[8]
('D', '#FF5555', '#FF7777')
>>> APPARENCE[AVENTURIER][1]
...CORRECTION...
- Les clés sont des integers puisque les constantes MUR, COULOIR... sont des integers.
- Utiliser les constantes plutôt que les entiers directement permet de centraliser les valeurs des entiers. Si on décide de changer et de mettre 7 pour DRAGON, il suffit de modifier la ligne 12 et pas TOUTES les lignes où le 8 apparaît.
- Les valeurs du dictionnaire sont toutes des puplets (tuples) de 3 valeurs. Des triplets.
- Lorsqu'on tape APPARENCE[AVENTURIER], on récupère le triplet ('A', '#8888FF', '#AAAAFF')
Lorsqu'on tape APPARENCE[AVENTURIER][1], on récupère donc l'élément d'indice 1 du triplet ('A', '#8888FF', '#AAAAFF') et donc '#8888FF'
Nous utiliserons aussi les constantes dans la matrice donnees. Mais comme les noms des constantes sont longs, nous allons utiliser des alias :
1
2
3
4
5
6
7
8
9
10
11
12
13 |
|
Nous voudrions maintenant créer la fonction modifier_apparence_cases. Cette fonction devra
- regarder chaque valeur contenue dans donnees et
- modifier en conséquence l'apparence du widget correspondant contenu dans widgets.
Comme il s'agit de modifier l'interface à partri des données, il s'agit bien d'une fonction à placer dans la partie Interface. Nous allons partir du principe "une tâche - une fonction":
- modifier_apparence_case se charge de modifier l'apparence d'un widget en fonction d'une valeur de contenu qu'on lui transmet (0 ou MUR par exemple).
- modifier_apparence_cases se charge de le faire sur toutes les cases, en utilisant modifier_apparence_case.

12° Mettre ce code en mémoire pour vérifier qu'il fonctionne sur votre système.
Vous devriez obtenir ceci :

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149 |
|
13° L'appel à la fonction modifier_apparence_cases avec un s se fait ligne 146 :
146 |
|
Et voici la fonction modifier_apparence_cases pour le moment :
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 | def modifier_apparence_cases(w, d):
"""Modifie les widgets pour refléter les données du plateau de jeu
:: param w (list(list)) :: matrice contenant les références des widgets
:: param d (list(list)) :: matrice contenant les données des cases
:: return (None) :: "procédure" Python
.. effet de bord :: modifie la matrice w
"""
# On récupére les dimensions de la matrice d
nbl = len(d) # Nombre de lignes
nbc = len(d[0]) # Nombre de lignes
# On lit les données une par une et on modifie les widgets
modifier_apparence_case(w[5][0], AVENTURIER)
modifier_apparence_case(w[0][0], d[0][0])
|
Questions
- Dans quel paramètre est stocké l'argument widgets envoyé en ligne 146 ?
- Dans quel paramètre est stocké l'argument donnees envoyé en ligne 146 ?
- Sur quelle ligne de la fonction modifier_apparence_cases demande-t-on de modifier en l'apparence du widget ligne 5 et colonne 0 ?
- Sur quelle ligne de la fonction modifier_apparence_cases demande-t-on de modifier en l'apparence du widget ligne 5 et colonne 0 ?
- Sur quelle ligne de la fonction modifier_apparence_cases demande-t-on de modifier en l'apparence du widget ligne 0 et colonne 0 ?
- Que renvoie l'évaluation d'AVENTURIER ?
- Que renvoie l'évaluation de d[0][0] ?
...CORRECTION...
- Dans quel paramètre est stocké l'argument widgets envoyé en ligne 146 ?
- Dans quel paramètre est stocké l'argument donnees envoyé en ligne 146 ?
- Sur quelle ligne de la fonction modifier_apparence_cases demande-t-on de modifier en l'apparence du widget ligne 5 et colonne 0 ?
- Sur quelle ligne de la fonction modifier_apparence_cases demande-t-on de modifier en l'apparence du widget ligne 0 et colonne 0 ?
- Quelle valeur est obtenue par l'évaluation d'AVENTURIER ?
- Que valeur est obtenue par l'évaluation de d[0][0] ?
Dans le premier paramètre, celui qui se nomme w
Dans le deuxième paramètre, celui qui se nomme d
109 | modifier_apparence_case(w[5][0], AVENTURIER)
|
110 | modifier_apparence_case(w[0][0], d[0][0])
|
AVENTURIER rest évaluée à 8.
d[0][0] veut dire d'aller voir la ligne 0 puis la colonne 0 dans la matrice d qui est un alias pour la matrice globale donnees. On y trouve M qui vaut 1. d[0][0] est donc évalué à 1.
14° Regardons maintenant pourquoi les deux cases s'affichent pour l'une en noir et pour l'autre en bleu ciel.
Voici les deux appels à modifier_apparence_case, sans s :
109
110 | modifier_apparence_case(w[5][0], AVENTURIER)
modifier_apparence_case(w[0][0], d[0][0])
|
Et voici le code de la fonction modifier_apparence_case :
82
83
84
85
86
87
88
89
90
91
92
93 | def modifier_apparence_case(case, v):
"""Modifie l'apparence du widget en fonction de la valeur v
:: param case (tkinter.Label) :: la référence du widget-case à modifier
:: param v (int) :: le code du contenu à afficher dans la case
:: return (None) :: "procédure" Python
.. effet de bord :: modifie le widget case
"""
if v in APPARENCE.keys():
case.configure(text=APPARENCE[v][0])
case.configure(bg=APPARENCE[v][1])
|
Question
- Lors de l'appel ligne 109, quel est le widget reçu dans le paramètre case : celui de la ligne 0 du haut ou celui de la ligne 5 du bas ?
- Lors de l'appel ligne 109, quelle est la valeur associée au paramètre v ?
- Que contient le tableau obtenu lorsqu'on utilise APPARENCE.keys ?
- Que veut dire en français if v in APPARENCE.keys ?
- Expliquer alors les modifications faites au widget lors de l'appel ligne 109.
- Expliquer alors les modifications faites au widget lors de l'appel ligne 110.
...CORRECTION...
- Lors de l'appel ligne 109, quel est le widget reçu dans le paramètre case : celui de la ligne 0 du haut ou celui de la ligne 5 du bas ?
- Lors de l'appel ligne 109, quelle est la valeur associée au paramètre v ?
- Que contient le tableau obtenu lorsqu'on utilise APPARENCE.keys ?
- Que veut dire en français if v in APPARENCE.keys ?
- Expliquer alors les modifications faites au widget lors de l'appel ligne 109.
- Expliquer alors les modifications faites au widget lors de l'appel ligne 110.
Celui en bas à gauche. C'est celui qui est bleu ciel.
8 puisqu'on transmet AVENTURIER.
On obtient un tableau qui contient toutes les clés disponibles dans le dictionnaire.
[0, 1, 3, 8].
On teste si la valeur v reçue (8) est bien l'une des clés du dictionnaire-constant APPARENCE. Puisque c'est vrai ici, le test est validé et on effectuera les actions visibles sur les lignes 92 et 93.
Ligne 92 : on modifie le texte de ce widget par le texte présent à l'indice 0 de la valeur associée à la clé 8. On y trouve un 'A'.
Ligne 93 : on modifie la couleur de fond de ce widget par la couleur présente à l'indice 1 de la valeur associée à la clé 8. On y trouve un '#8888FF' : Rouge et Vert à 88 et le bleu à fond sur FF. On obtient donc du bleu ciel.
L'appel montre qu'on envoie le widget présent en ligne 0 et colonne 0 et que la valeur associé est celle de d[0][0] qui se trouve être 1 (voir la question précédente).
Ligne 92 : on modifie le texte de ce widget par le texte présent à l'indice 0 de la valeur associée à la clé 1. On y trouve un ''.
Ligne 93 : on modifie la couleur de fond de ce widget par la couleur présente à l'indice 1 de la valeur associée à la clé 1. On y trouve un 'black'.
15° Modifier maintenant la fonction modifier_apparence_cases pour qu'elle fasse ce qu'on lui demande en réalité : lire la matrice d case par case et imposer la bonne apparence aux widgets stockés dans la matrice w.
Il faudra bien entendu utiliser deux boucles imbriquées. Vous noterez que j'ai déjà récupéré le nombre de lignes et le nombre de colonnes.
Résultat attendu de ce type :

...CORRECTION...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
|
4 - Evénements
Tout d'abord : qu'est-ce qu'un événement pour l'interface ? Il s'agit d'une modification de l'environnement extérieur : une touche de clavier sur laquelle on appuie, le déplacement de la souris, l'appui sur une touche de la souris...
Rappel de l'article Tkinter : gestionnaire d'événement et fonction-événement Tkinter
C'est ce gestionnaire qui va donner l'ordre d'activer une fonction particulière si cet événement survient.
Pour faire la liaison événement à surveiller ↦ fonction à réaliser, on utilise la méthode bind, appliquée au widget qu'on surveille.
Codification de la liaison avec bind
- On active sur le widget voulu (fenetre ici, mais c'est souvent un label directement)
- la surveillance d'une utilisation de la flèche Gauche, identifiée par
'<KeyPress-Left>'
- qui provoque l'appel à la fonction nommée evt_fleche_g
...
fenetre.bind("<KeyPress-Left>", evt_fleche_g) # Lorsqu'on appuie sur la flèche Gauche
Fonction événementielle
La fonction evt_fleche_g est une fonction normale si ce n'est que :
- c'est le gestionnaire d'événements qui la lance lorsqu'il détecte que l'événement est présent.
- elle doit avoir un premier paramètre (souvent nommé event) permettant d'accueillir l'événement.
Que peut faire cette fonction ?
Ce qu'on veut. C'est d'ailleurs pour cela qu'on a le fameux paramètre event. On peut notamment récupérer l'identifiant du widget ayant déclenché l'événement en tapant event.widget.
...
...
...
... | def evt_fleche_g(event):
reference_widget = event.widget
print("Vous venez d'appuyer sur la flèche de gauche")
reference_widget.configure(bg='#440000')
|
Rien d'original : lorsqu'on détecte l'appui sur la FLECHE GAUCHE du clavier alors que reference_widget est actif, on affiche un petit message dans la console et on modifie la couleur du fond du widget.
On notera qu'on ne place par les parenthèses de la fonction : on ne donne que son adresse interne. Sous cette forme, on ne peut donc pas fournir d'arguments.
Le transfert d'arguments est possible mais à travers quelques notions que nous n'aborderons pas ici. Voir FAQ si vous voulez plus de détails.
Liste d'événements
Pas de blabla. Juste la description de l'événement et la façon de le gérer.
Evénements liés à la souris
1
2
3
4
5
6
7
8
9
10
11 | ref_widget.bind('<Button-1>', agir1) # Appui sur clic-gauche sur ce widget
ref_widget.bind('<Button-2>', agir2) # Appui sur clic-molette sur ce widget
ref_widget.bind('<Button-3>', agir3) # Appui sur clic-droit sur ce widget
ref_widget.bind('<ButtonRelease-1>', agir4) # Relachement après clic-gauche
ref_widget.bind('<ButtonRelease-2>', agir5) # Relachement après clic-molette
ref_widget.bind('<ButtonRelease-3>', agir6) # Relachement après clic-droit
ref_widget.bind('<Enter>', agir7) # Pointeur souris rentre sur ce widget
ref_widget.bind('<Leave>', agir8) # Pointeur souris sort de ce widget
ref_widget.bind('<Motion>', agir9) # Pointeur souris sort de ce widget
|
Si vous voulez que la détection soit appliquée sur toute votre fenêtre d'application, il faut lier l'événement à votre fenêtre principale (fenetre dans le code de l'activité présentée ici).
Evénements liés aux touches du clavier
On notera qu'il faut cette fois lié l'événement à un widget-fenêtre. C'est par exemple le widget fenetre de notre activité.
Voici pour les flèches.
1
2
3
4
5
6
7
8
9 | fenetre.bind('<KeyPress-Left>', agir1) # Appui sur la flèche Gauche
fenetre.bind('<KeyPress-Right>', agir2) # Appui sur la flèche Droit
fenetre.bind('<KeyPress-Up>', agir3) # Appui sur la flèche Haut
fenetre.bind('<KeyPress-Down>', agir4) # Appui sur la flèche Bas
fenetre.bind('<KeyRelease-Left>', agir5) # Relachement de la flèche Gauche
fenetre.bind('<KeyRelease-Left>', agir6) # Relachement de flèche Droit
fenetre.bind('<KeyRelease-Up>', agir7) # Relachement de flèche Haut
fenetre.bind('<KeyRelesase-Down>', agir8) # Relachement de la flèche Bas
|
On peut aussi demander à agir si n'importe quelle touche est activée :
1 | fenetre.bind('<Any-KeyPress>', agir9) # Appui sur n'importe quelle touche
|
De façon générale pour les autres touches du clavier, cette utilisation basique demande de faire attention à l'utilisation conjointe de majuscule ou pas :
1
2
3
4
5
6
7
8
9 | fenetre.bind('<KeyPress-a>', agir10) # Appui sur la touche A sans majuscule activée
fenetre.bind('<KeyPress-b>', agir11) # Appui sur la touche B sans majuscule activée
fenetre.bind('<KeyPress-c>', agir12) # Appui sur la touche C sans majuscule activée
fenetre.bind('<KeyPress-d>', agir13) # Appui sur la touche D sans majuscule activée
fenetre.bind('<KeyPress-A>', agir14) # Appui sur la touche A avec majuscule activée
fenetre.bind('<KeyPress-B>', agir15) # Appui sur la touche B avec majuscule activée
fenetre.bind('<KeyPress-C>', agir16) # Appui sur la touche C avec majuscule activée
fenetre.bind('<KeyPress-D>', agir17) # Appui sur la touche D avec majuscule activée
|
Si vous voulez d'autres explications :
- Activité sur l'ancien site : Interface Tk - GESTION DES EVENEMENTS
- Directement sur Effbot.org : Vers Effbot.org : les événements
Passons à la pratique : nous allons rajouter une partie à notre programme de test : après la création de widgets, il est temps de surveiller quelques événements sur ceux-ci.
16° Mettre le programme en mémoire.
Questions :
- Sur quelle ligne se trouve la mise en place de la surveillance de certains événements ?
- Quel est l'événement surveillé ? Sur quels widgets ?
- Que provoque l'événement ?
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 |
|
...CORRECTION...
- Sur quelle lignes se trouve la mise en place de la surveillance de certains événements ?
- Quel est l'événement surveillé ? Sur quels widgets ?
- Que provoque l'événement ?
Ligne 156
L'événement correspond à l'entrée de la souris dans un widget ('<Enter>'). La déclaration s'applique sur tous les widgets contenus dans la matrice widgets à l'aide de la double boucle.
Il faut aller voir le code de la fonction colorier. Lignes 113-114, on voit qu'elle ne fait que colorier le widget survoler en gris.
Nous voulons maintenant rendre la couleur des widgets plus claire lorsqu'on les survole :
17° Cette fois, la fonction-événementielle eclaircir est plus complexe car on a besoin de recevoir les matrices donnees et widgets de façon à pouvoir :
- Obtenir l'indice de ligne y et de colonne x du widget qu'on vient de survoler
- D'aller lire la donnée stockée dans donnees[y][x].
- De retrouver la couleur éclaircie à appliquer en cherchant dans APPARENCE (en utilisant la clé trouvée à l'étape précédente et en lisant l'indice 2 du tuple obtenu)
- D'appliquer cette couleur sur le widget en utilisant la méthode configure.
Voici le prototype de la fonction eclaircir :
113 |
|
Et la façon dont on lie cette fonction à un événement :
183 |
|
Questions liées au code ci-dessous (notamment le code surligné)
- Pourquoi a-t-on dû utiliser une fonction lambda lors de la déclaration de la fonction-événement ?
- Quelle est la fonction qui gère au final l'événement 'entrée dans la case' ?
- Quelle est la fonction qui gère au final l'événement 'sortie hors de la case' ?
- Sur quelles lignes avait-on créé les attributs ligne et colonne que nous parvenons ensuite à lire sur les lignes ... et ...
- Pourquoi aller chercher l'indice 2 du triplet stocké dans le dictionnaire ?
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 |
|
...CORRECTION...
Rappel
Les fonctions lambda ne sont pas au programme : nous ne les utiliserons que comme moyen commode de transmettre des paramètres lors de l'utilisation de la méthode bind ou after.
Hors du contexte "Projet Tkinter", inutile d'essayer de placer une solution qui les utilise. Ce n'est pas attendu.
18° En vous basant sur la fonction précédente, créer la fonction assombrir.
...CORRECTION...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
|
Imaginons maintenant qu'on ne veuille pas animer les murs au survol. Les murs ne bougeront pas dans notre jeu. On peut donc juste récupérer la valeur du fond coloré de la case et s'il correspond à celui d'un mur, on ne fait rien. Pour cela, il faut utiliser la méthode cget.
Méthode cget
C'est une méthode propre aux widgets de Tkinter. Elle n'est donc pas à connaître. Elle permet de récupérer l'une des valeurs graphiques d'un widget : couleur du fond, couleur du texte, texte...
Un exemple : on récupère la couleur du fond du widget dont la référence est stockée dans w_get.
1 | couleur = case.cget('bg')
|
Du coup, on peut l'utiliser pour modifier la couleur d'une des cases lorsqu'on la survole.
19° Remplacer la fonction par la version ci-dessous. Expliquer ce que fait la ligne 127.
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 |
|
Question supplémentaire : quel est le type de MUR ?
...CORRECTION...
On réalise l'éclaircissement SI :
- Le fond coloré de la case graphique n'est pas noir (APPARENCE[MUR][1])
- La clé fournie existe bien dans le dictionnaire APPARENCE
MUR est une constante faisant référence à l'entier 1. Voir ligne 10. Il ne s'agit pas d'un string attention.
5 - Très Mini projet décrit : déplacer l'aventurier
Avant de commencer, réfléchissons à la manière de procéder lorsque réalise un projet basé sur une IHM (Interface Homme Machine). Vous allez avoir besoin d'appliquer cette méthode de réflexion pendant votre projet.
Un bon moyen de faire un projet en équipe est de :
- En groupe : réflechir avec un texte écrit en français qui décrit le déroulé des actions. On y trouve le nom des fonctions et ce qu'elles doivent faire
- En groupe : créer le schéma du projet pour vérifier qu'il est bien conçu (la partie Interface n'agit que sur l'Interface et la partie Données n'agit que sur les Données). Les interactions entre les deux catégories sont encapsulées dans certaines fonctions bien ciblées.
- En groupe : Réaliser les prototypages
- Individuel : Travailler sur sa fonction, créer sa documentation et des tests si possible.
Un exemple - Parvenir à déplacer l'aventurier :
On commence par réflechir aux fonctions qu'on désire, à ce qu'elle doive recevoir et à ce qu'elle doit faire ou répondre.
- Partie Données : on veut une fonction coordonnees_aventurier qui renvoie les x et y de la case-donnée contenant l'aventurier dans donnees.
- Partie Données : on veut une fonction est_case_proche qui renvoie True si les coordonnées-paramètres x et y correspondent à un élément adjacent verticalement ou horizontalement à la case-donnée cible (celle de l'aventurier ici).
- Partie Données : on veut une fonction intervertir qui intervertit les contenus de deux éléments de donnees dont on fournit les quatre coordonnées x1, y1, x2 et y2.
- Partie Données : on crée une fonction tentative_deplacement qui reçoit des coordonnées x et y, qui vérifie si elles sont bien proches de l'aventurer et qui intervertit les éléments dans ce cas. Elle renvoie True si on fait une interversion.
- Partie Interface : on crée une fonction gerer_clic_case liée aux clics sur l'une des cases et qui
- récupère la référence de la case-widget, sa ligne et sa colonne
- envoie cette ligne et cette colonne à la fonction tentative_deplacement appartenant à la partie Données.
- si tentative_deplacement répond True, on actualise l'affichage de l'interface avec la fonction modifier_apparence_cases (déja codée)
1 2 3 4 5 6 7 8 9 10 11
def gerer_clic_case(e, w, d): """Tente de faire bouge l'aventurier sur le plateau :: param e (tkinter.Event) :: contient les informations sur l'événement :: param w (list(list)) :: matrice contenant les références des widgets :: param d (list(list)) :: matrice contenant les données des cases :: return (None) :: "procédure" Python .. effet de bord :: modifie la matrice w et la matrice d """ modifier_apparence_cases(w, d) # en attendant mieux
1
2
3
4
5
6
7
8 |
|
1
2
3
4
5
6
7
8
9
10
11
12 |
|
1
2
3
4
5
6
7
8
9
10
11
12
13 |
|
1
2
3
4
5
6
7
8
9
10
11 |
|
Si on représente la répartition entre partie Interface et partie Données, cela donne :

Pour travailler en équipe, vous allez donc devoir avoir au moins le prototype des fonctions (avec la documentation précises des préconditions et postconditions).
Chaque personne du groupe peut ensuite commencer à réfléchir sur sa propre fonction.
20° Quel est l'intérêt de placer de "fausses réponses" dans les fonctions devant répondre plutôt qu'un simple pass ?
...CORRECTION...
Cela permet simplement de commencer à travailler même si on a besoin d'une réponse de l'une de ces fonctions. On sait que la réponse est fausse mais on n'a pas à entendre la version finale pour avancer.
Voici le code final du très mini-projet Déplacement Aventurier. Vous pouvez le lancer, le tester et voir comment je propose de résoudre les problèmes. Bien entendu, il a d'autres manières de coder une même fonction.
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 |
|
Activité publiée le 29 11 2019
Dernière modification : 13 12 2020
Auteur : ows. h.