Infoforall

Python 18 : Les données, les listes, les piles et les files

1 - Rappel sur les listes

Nous allons voir ici comment parvenir à stocker les données et les resortir dans l'ordre voulu.

Mais d'abord, commençons par revoir ce que vous savez déjà des listes, si vous en avez besoin.

Rappel de la première rencontre avec les listes ?

Qu'est qu'une liste ? C'est une suite ordonnée et modifiable (mutable) d'objets ou variables quelconques.

Les éléments d'une liste sont séparés par des virgules et la définition de la liste commence avec [ et s'arrête avec ].

 maListe = [1,'a',45.2,"bonjour",'b']  crée une liste de 5 éléments contenant l'integer 1, le char a, le float 45.2, le string bonjour et le char b.

Si on veut afficher une liste, print(maListe) fonctionne.

>>> maListe = [1,'a',45.2,"bonjour",'b']

>>> print(maListe)

[1,'a',45.2,'bonjour','b']

Si on veut connaitre le nombre d'éléments dans une liste, len(maListe) fonctionne.

>>> maListe = [1,'a',45.2,"bonjour",'b']

>>> len(maListe)

5

Si on veut accéder à l'un des éléments d'une liste, on tapera maListe[2], mais attention le premier élément est l'élément 0.

Si on demande print(maListe[2]) avec maListe = [1,'a',45.2,"bonjour",'b'], on obtient :

>>> maListe = [1,'a',45.2,"bonjour",'b']

>>> maListe = [2]

45.2

Si on veut accéder à un ensemble d'éléments d'une liste, on tapera ma_liste[1:4], et on aura les éléments 1,2 et 3 (car cela veut dire qu'on commence à l'élément 1 et qu'on s'arrête avant le 4.).

Si on demande print(ma_liste[1:4]) avec ma_liste = [1, 'a', 45.2,"bonjour", 'b'], on obtient :

>>> maListe = [1,'a',45.2,"bonjour",'b']

>>> print( ma_liste[1:4] )

['a',45.2,'bonjour']

Pour parcourir une liste, il suffit d'utiliser un for.

for element in maListe :

    print(element)

Et on obtient alors

1

a

45.2

bonjour

b

Nous avons également vu qu'on peut utiliser plutôt une boucle for numérique :

for numero in range(len(maListe)) :

    print(maListe[numero])

Enfin, nous avions vu comment rajouter des éléments dans une liste avec la méthode append qui rajoute des éléments en fin de liste.

>>> a = [1,2,3]

>>> a

[1, 2, 3]

>>> a.append('4')

>>> a

[1, 2, 3, '4']

>>> b = [7,8,9]

>>> a.append(b)

>>> a

[1, 2, 3, '4', [7, 8, 9]]

Cette méthode append permet donc de considérer les listes comme des piles de livres : on rajoute les éléments au dessus des derniers éléments.

append() rajoute au dessus de la pile

Rappel sur la deuxième rencontre avec les listes

Voyons d'autres manières de rajouter des éléments dans une liste.

Pour rajouter des éléments à une liste, on peut également utiliser un simple + entre deux listes. Attention néanmoins : on crée alors une nouvelle liste qui n'aura pas la même adresse/référence/id que la précédente contrairement à l'utilisation de la méthode append. Comme la liste est mutable, c'est même plutôt une façon de faire à éviter SAUF si vous voulez clairement créer une nouvelle liste à partir d'une autre liste.

Si on utilise maListe = maListe + ['nouveau'] avec maListe = [1,'a',45.2,"bonjour",'b'], on obtient également :

>>> maListe = [1,'a',45.2,"bonjour",'b']

>>> maListe = maListe + ['nouveau']

>>> print(maListe)

['a',45.2,'bonjour','b','nouveau']

Par contre, cette fois, on a recréé une liste qui porte le même nom : ce n'est pas la même liste au niveau de son id.

Si on veut rajouter un élément à une position particulière d'index i dans la liste, on utilise maListe.insert(i,x) où x est l'élément à rajouter et i l'index dans la liste.

>>> maListe = [1,'a',45.2,"bonjour",'b']

>>> maListe.insert(1,'nouveau')

>>> print(maListe)

[1,'nouveau','a',45.2,'bonjour','b']

Souvenez-vous que l'index du premier élément est 0, pas 1.

Si i dépasse l'index maximum, x sera juste ajouté en fin de liste, comme avec un append.

Si on veut supprimer un élément x, on utilise maListe.remove(x) où x est l'élément à supprimer dans la liste.

Si on utilise maListe.remove('nouveau') avec maListe = [1,'nouveau','a',45.2,"bonjour",'b','nouveau'], on obtient :

>>> maListe = [1,'nouveau','a',45.2,"bonjour",'b','nouveau']

>>> maListe.remove('nouveau')

[1,'a',45.2,"bonjour",'b','nouveau']

On remarquera donc qu'elle ne supprime que le premier élément 'nouveau' rencontré. S'il y en a d'autres, il faudra faire d'autres remove.

Attention, si l'élément n'est pas présent dans la liste, la méthode va lever une exception de type valueError. Il faudra donc utiliser un while et un try pour supprimer tous les éléments identiques.

Justement, si on veut connaitre le nombre de fois qu'un élément x apparait dans une liste, on utilise maListe.count(x) où x est l'élément à surveiller dans la liste.

Si on utilise maListe.count('nouveau') avec maListe = [1,'nouveau','a',45.2,"bonjour",'b','nouveau'], on obtient :

>>> maListe = [1,'nouveau','a',45.2,"bonjour",'b','nouveau']

>>> maListe.count('nouveau')

2

Enfin, deux méthodes qui sont pratiques et un peu l'inverse l'une de l'autre :

Si on utilise maListe.sort(), on ordonne les éléments de la liste maListe.

Exemple 1:

>>> maListe = ['un','deux','trois','quatre']

>>> maListe.sort()

>>> maListe

['deux', 'quatre', 'trois', 'un']

Exemple 2:

>>> maListe = [5,1,3,2]

>>> maListe.sort()

>>> maListe

[1, 2, 3, 5]

Exemple 3: mélange d'int et de str

>>> maListe = ['un','deux','trois','quatre',5,1,3,2]

>>> maListe.sort()

TypeError: '<' not supported between instances of 'int' and 'str'

Donc attention : ce n'est pas une méthode miracle : elle doit pouvoir sélectionner et trier les éléments de type différents entre eux. Si elle ne sait pas faire, ça ne fonctionnera pas.

L'inverse, c'est la fonction shuffle : cette fonction du module random va mélanger les éléments de la liste et les répartir au hasard...

>>> import random as random

>>> maListe = ['un','deux','trois','quatre',5,1,3,2]

>>> random.shuffle(maListe)

>>> maListe

['trois', 'un', 'deux', 2, 1, 'quatre', 3, 5]

Pratique non ?

Nous allons aujourd'hui nous focaliser sur les méthodes permettant de rajouter ou d'extraire des éléments dans une liste.

2 - Rajouter ou extraire les éléments d'une liste

Pour l'instant, nous avons surtout utiliser :

Commençons par voir si vous avez bien compris la limitation du append :

01° Trouver (sans le lancer) ce que doit afficher le code suivant. Vérifiez votre avis en lançant les commandes.

>>> listeA = [1,2,3]

>>> elementB = 4

>>> listeC = [4,5,6]

>>> listeA.append(elementB)

>>> print(listeA)

???

>>> listeA.append(listeC)

>>> print(listeA)

???

...CORRECTION...

La méthode append rajoute directement l'élément. Si cet élément contient des éléments, il ne rajoute pas les éléments mais bien l'élément lui-même.

Ainsi si on rajoute  4 ,

on obtient  [ 1, 2, 3, 4 ] 


Ainsi si on rajoute  [4,5,6] ,

on obtient  [ 1, 2, 3, [4,5,6] ] 

Si on veut rajouter en fin de liste les éléments d'un objet qui en contient d'autres, il faut changer de méthode. On utilise alors plutôt extend.

02° Trouver (sans le lancer) ce que doit afficher le code suivant. Vérifiez votre avis en lançant les commandes.

>>> listeA = [1,2,3]

>>> elementB = 4

>>> listeC = [4,5,6]

>>> listeA.extend(elementB)

>>> print(listeA)

???

>>> listeA.extend(listeC)

>>> print(listeA)

???

...CORRECTION...

Voilà pourquoi je ne l'avais pas encore utilisé plus que cela : la méthode extend rajoute les éléments mais on doit nécessairement lui fournir un objet iterable. Sinon, ça déclenche une erreur !

Ainsi si on tente de rajouter  4 , on obtient  TypeError: 'int' object is not iterable 


Par contre si on tente de rajouter  [4,5,6] ,

on obtient bien  [ 1, 2, 3, 4, 5, 6 ] 

Voyons maintenant comment remplir puis sortir les données d'une liste.

3 - La pile (stack en anglais)

Principe de la pile de données :

Le nom PILE correspond bien au principe de rajout/lecture : on place les éléments les uns au dessus des autres.

Lorsqu'on rajoute une donnée, on la place donc après les autres : son indice est donc le plus grand indice de la pile.

Lorsqu'on veut lire et extraire l'une des données, on prend donc celle qui a l'indice le plus haut, comme dans une pile de livre.

Rajout de données dans une pile :

Entrée Sortie Elément 2 a Elément 1 b Elément 0 c

Lorsqu'on choisit de gérer les données à stocker en pile, on rajoute les nouveaux éléments au dessus des autres : avec un élément d, j'obtiens :

Entrée Sortie Elément 3 d Elément 2 a Elément 1 b Elément 0 c

Les méthodes append et extend font donc exactement cela sans aucun problème.

Lecture et extraction de données :

Si on travaille avec des piles de données, il faut donc récupérer l'élément ayant le plus grand indice, le livre du haut de la pile.

On ne peut alors pas simplement lire le contenu du haut et utiliser la méthode remove : cette méthode supprime le premier élément qui a le bon contenu. Ce ne sera pas nécessairement le plus haut dans la pile.

Non, pour faire cela, on utilise la méthode pop qui fait la lecture et l'extraction en même temps : lancer le commande lecture = maListe.pop() va stocker l'élément de plus haut indice dans la variable lecture ET va supprimer cet élement de la liste.

Exemple :

Si la liste contient initialement :

maListe Entrée Sortie Elément 3 d Elément 2 a Elément 1 b Elément 0 c

On lance lecture = maListe.pop(). La variable lecture contient alors  d  et dans la liste, il reste ceci :

maListe Entrée Sortie Elément 2 a Elément 1 b Elément 0 c

Avec un nouveau lecture = maListe.pop(), on obtient une variable lecture qui contient  a  et la liste suivante :

Entrée Sortie Elément 1 b Elément 0 c

Moralité : on utilise la notion de pile(stack) lorsqu'il vaut mieux traiter en premier la donnée la plus récénte. Ca peut être le cas pour un système de maintenance automatisée (autant traiter la dernière erreure détectée) ou l'historique dea actions à ANNULER dans un programme (on annule d'abord la dernière action enregistrée).

03° Compléter le code des fonctions rajouterPile et extrairePile de l'interface Tk ci-dessous pour qu'elles gèrent la liste de données comme une pile. Vous n'avez à modifier que le code de ces deux fonctions. Le choix de la méthode d'extraction et de rajout dans la liste listeDesDonnees est donc ici l'élément important.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


from tkinter import *


# - - - - - - - - - - - - - - - - - -

# Variables du programme

# - - - - - - - - - - - - - - - - - -


listeDesDonnees = ["A","B","C"]

grandePolice = ("Helvetica", 14)

petitePolice = ("Helvetica", 10)


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -


def extrairePile() :

    """

    On travaille à partir de la liste listeDesDonnees

    Il faudra stocker le résultat de l'extraction dans le StringVar lecture avec lecture.set()

    On pourra mettre l'affichage de la liste à jour à l'aide de la fonction afficherListe

    """

    # A MODIFIER ET COMPLETER

    elementLu = "Un simple test"

    lecture.set(elementLu)

    afficherListe()


def rajouterPile() :

    """

    On travaille avec listeDesDonnees

    On récupère le contenu du StringVar nouvelleEntree avec nouvelleEntree.get()

    On pourra mettre l'affichage de la liste à jour à l'aide de la fonction afficherListe

    """

    rajout = nouvelleEntree.get()

    # A MODIFIER ET COMPLETER

    afficherListe()


def afficherListe() :

    # NE PAS MODIFIER

    affichageListeH.set(str(listeDesDonnees))

    copieInverse = (listeDesDonnees.copy())[::-1]

    affichageVertical = ""

    for element in copieInverse :

        affichageVertical += element + '\n'

    affichageListeV.set(affichageVertical)


# - - - - - - - - - - - - - - - - - -

# Création de la fenêtre et des objets associés la fenêtre

# - - - - - - - - - - - - - - - - - -


fenetre = Tk()

fenetre.geometry("800x600")


# Création et initialisation des objets StringVar


nouvelleEntree = StringVar()

nouvelleEntree.set("")


lecture = StringVar()

lecture.set("")


affichageListeH = StringVar()

affichageListeH.set("")


affichageListeV = StringVar()

affichageListeV.set("")


afficherListe()


# Création et placement des 3 widgets principaux


Label(fenetre, text = "GESTION EN PILE", bg = '#FFAAFF', fg = "black", font = grandePolice).pack(fill = X, side = TOP)


zoneGauche = Frame(fenetre)

zoneGauche.pack(side = LEFT, fill = BOTH, expand = TRUE)


Label(fenetre, textvariable = affichageListeV, bg = "black", fg = "yellow", font = grandePolice, width = 20).pack(fill = Y, expand = TRUE)


# Création et placement des 3 zones dans le Frame zoneGauche


zoneEcriture = Frame(zoneGauche, bg = '#AAAAFF', padx = 10, pady = 10)

zoneEcriture.pack(fill = X, side = TOP)


zoneAffichage = Frame(zoneGauche, bg = '#555599', padx = 10, pady = 10)

zoneAffichage.pack(expand = TRUE, fill = BOTH, side = TOP)


zoneLecture = Frame(zoneGauche, bg = '#AAAAFF', padx = 10, pady = 10)

zoneLecture.pack(fill = X, side = BOTTOM)


# Création et placement des widgets dans le frame zoneEcriture


Button(zoneEcriture, text = "Rajouter dans la pile", bg = "#AA3333", fg = "ivory", font = petitePolice, command = rajouterPile).pack(ipadx = 20, ipady = 10, pady = 5)

Entry(zoneEcriture, textvariable = nouvelleEntree, bg = "black", fg = "yellow", font = grandePolice, insertbackground = "yellow").pack(fill = X, ipadx = 20, ipady = 20)


# Création et placement des widgets dans le frame zoneAffichage


Label(zoneAffichage, text = "Contenu de la liste", bg = "ivory", fg = "black", font = petitePolice).pack(ipadx = 20, ipady = 10)

Label(zoneAffichage, textvariable = affichageListeH, bg = "black", fg = "yellow", font = grandePolice).pack(fill = BOTH, ipadx = 20, ipady = 20, expand = TRUE)


# Création et placement des widgets dans le frame zoneLecture


Button(zoneLecture, text = "Extraire de la pile", bg = "#AA3333", fg = "ivory", font = petitePolice, command = extrairePile).pack(ipadx = 20, ipady = 10, pady = 5)

Label(zoneLecture, textvariable = lecture, bg = "black", fg = "yellow", font = grandePolice).pack(fill = X, ipadx = 20, ipady = 20, side = BOTTOM)


# - - - - - - - - - - - - - - - - - -

# Bouclage de la fenêtre fen_princ

# - - - - - - - - - - - - - - - - - -


fenetre.mainloop()

...CORRECTION...

def extrairePile() :

    """

    On travaille à partir de la liste listeDesDonnees

    Il faudra stocker le résultat de l'extraction dans le StringVar lecture avec lecture.set()

    On pourra mettre l'affichage de la liste à jour à l'aide de la fonction afficherListe

    """

    elementLu = listeDesDonnees.pop()

    lecture.set(elementLu)

    afficherListe()


def rajouterPile() :

    """

    On travaille avec listeDesDonnees

    On récupère le contenu du StringVar nouvelleEntree avec nouvelleEntree.get()

    On pourra mettre l'affichage de la liste à jour à l'aide de la fonction afficherListe

    """

    rajout = nouvelleEntree.get()

    listeDesDonnees.append(rajout)

    afficherListe()


Bilan de la pile de données :

Le principe est donc de rajouter et d'extraire par la même extrémité de la liste.

J'ai pris comme technique de rajouter en fin de liste le nouvel élément et d'extraire depuis la fin de liste l'élément à lire.

maListe Entrée Sortie Elément 3 d Elément 2 a Elément 1 b Elément 0 c

# PILE : Rajoute et extraction pour le plus GRAND index

# Rajout

maListe.append("Nouvel élément")

# Extraction et lecture

extraction = maListe.pop()

Nous aurions néanmoins le même comportement en rajoutant le nouvel élément en début de liste (il va alors pousser les autres) et extraire du début de la liste l'élément à lire. Pour cela, il faut utiliser la méthode insert sous la forme maListe.insert(i,x) où x est l'élément à rajouter et i l'index dans la liste. De la même façon, on doit lire et extraire le premier élément et pas le dernier : on doit alors fournir 0 en argument à la méthode pop.

maListe Elément 2 a Elément 1 b Elément 0 c Entrée Sortie

# PILE : Rajoute et extraction pour le plus PETIT index

# Rajout

maListe.insert(0,"Nouvel élément")

# Extraction et lecture

extraction = maListe.pop(0)

Si j'utilise maListe.insert(0,"d") sur la liste précédente, j'obtiens :

maListe Elément 3 a Elément 2 b Elément 1 c Elément 0 d Entrée Sortie

Pour lire, il faudrait alors utiliser extraction = maListe.pop(0), et extraction contiendrait "d".

4 - La file (queue en anglais)

Principe de la file de données :

C'est l'inverse de la pile : on gère l'extraction et l'insertion par les deux extrémités opposées.

Le nom FILE correspond bien au principe d'une file d'attente : on place les éléments les uns derrière les autres : le plus récent est inséré en début de liste (indice 0) et il pousse les autres. Les indices des autres éléments augmentent donc de 1.

maListe Sortie Elément 2 a Elément 1 b Elément 0 c Entrée

Rajout de données dans une file :

Si on travaille avec des files de données, il faut insérer le nouvel élément à l'index 0. On pousse donc tous les autres éléments de 1.

:
maListe Sortie Elément 3 a Elément 2 b Elément 1 c Elément 0 d Entrée

Comment faire en Python ? On utilise la méthode insert en donnant 0 en argument d'index : maListe.insert(0,"D").

Lecture et extraction de données :

Si on travaille avec des files de données, il faut donc récupérer l'élément ayant le plus grand indice, l'élément en bout de file.

On utilise la méthode pop qui fait la lecture et l'extraction en même temps : lancer le commande lecture = maListe.pop() va stocker l'élément de plus haut indice dans la variable lecture ET va supprimer cet élement de la liste.

Exemple :

Si la liste contient initialement :

maListe Sortie Elément 3 a Elément 2 b Elément 1 c Elément 0 d Entrée

On lance lecture = maListe.pop(). La variable lecture contient alors  a  et dans la liste, il reste ceci :

maListe Sortie Elément 2 b Elément 1 c Elément 0 d Entrée

Avec un nouveau lecture = maListe.pop(), on obtient une variable lecture qui contient  b  et la liste suivante :

maListe Sortie Elément 1 c Elément 0 d Entrée

Moralité : on utilise la notion de file lorsqu'il vaut mieux traiter en premier la donnée la plus ancienne. C'est ce système qui sera utilisé dans la majorité des cas. Cela revient également à gérer les données de façon chronologique, comme avec notre Simili-Simon.

04° En utilisant les informations ci-dessus, transformer les fonctions du programme de gestion de PILE et FILE pour qu'il réagisse en gérer une FILE. Pour l'instant, les fonctions rajouterFile et rajouterFile sont de simples copies de celles de la pile.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


from tkinter import *


# - - - - - - - - - - - - - - - - - -

# Variables du programme

# - - - - - - - - - - - - - - - - - -


listeDesDonnees = ["A","B","C"]

grandePolice = ("Helvetica", 14)

petitePolice = ("Helvetica", 10)


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -


def extrairePile() :

    elementLu = listeDesDonnees.pop()

    lecture.set(elementLu)

    afficherListe()


def rajouterPile() :

    rajout = nouvelleEntree.get()

    listeDesDonnees.append(rajout)

    afficherListe()


def extraireFile() :

    elementLu = listeDesDonnees.pop()

    lecture.set(elementLu)

    afficherListe()


def rajouterFile() :

    rajout = nouvelleEntree.get()

    listeDesDonnees.append(rajout)

    afficherListe()


def afficherListe() :

    # NE PAS MODIFIER

    affichageListeH.set(str(listeDesDonnees))

    copieInverse = (listeDesDonnees.copy())[::-1]

    affichageVertical = ""

    for element in copieInverse :

        affichageVertical += element + '\n'

    affichageListeV.set(affichageVertical)


# - - - - - - - - - - - - - - - - - -

# Création de la fenêtre et des objets associés la fenêtre

# - - - - - - - - - - - - - - - - - -


fenetre = Tk()

fenetre.geometry("800x600")


# Création et initialisation des objets StringVar


nouvelleEntree = StringVar()

nouvelleEntree.set("")


lecture = StringVar()

lecture.set("")


affichageListeH = StringVar()

affichageListeH.set("")


affichageListeV = StringVar()

affichageListeV.set("")


afficherListe()


# Création et placement des 3 widgets principaux


Label(fenetre, text = "GESTION EN PILE", bg = '#FFAAFF', fg = "black", font = grandePolice).pack(fill = X, side = TOP)


zoneGauche = Frame(fenetre)

zoneGauche.pack(side = LEFT, fill = BOTH, expand = TRUE)


Label(fenetre, textvariable = affichageListeV, bg = "black", fg = "yellow", font = grandePolice, width = 20).pack(fill = Y, expand = TRUE)


# Création et placement des 3 zones dans le Frame zoneGauche


zoneEcriture = Frame(zoneGauche, bg = '#AAAAFF', padx = 10, pady = 10)

zoneEcriture.pack(fill = X, side = TOP)


zoneAffichage = Frame(zoneGauche, bg = '#555599', padx = 10, pady = 10)

zoneAffichage.pack(expand = TRUE, fill = BOTH, side = TOP)


zoneLecture = Frame(zoneGauche, bg = '#AAAAFF', padx = 10, pady = 10)

zoneLecture.pack(fill = X, side = BOTTOM)


# Création et placement des widgets dans le frame zoneEcriture


Button(zoneEcriture, text = "Rajouter dans la pile", bg = "#AA3333", fg = "ivory", font = petitePolice, command = rajouterPile).pack(side = LEFT, ipadx = 20, ipady = 10, padx = 5, pady = 5)

Button(zoneEcriture, text = "Rajouter dans la file", bg = "#33AA33", fg = "ivory", font = petitePolice, command = rajouterFile).pack(side = LEFT, ipadx = 20, ipady = 10, padx = 5, pady = 5)

Entry(zoneEcriture, textvariable = nouvelleEntree, bg = "black", fg = "yellow", font = grandePolice, insertbackground = "yellow").pack(fill = X, ipadx = 20, ipady = 20)


# Création et placement des widgets dans le frame zoneAffichage


Label(zoneAffichage, text = "Contenu de la liste", bg = "ivory", fg = "black", font = petitePolice).pack(ipadx = 20, ipady = 10)

Label(zoneAffichage, textvariable = affichageListeH, bg = "black", fg = "yellow", font = grandePolice).pack(fill = BOTH, ipadx = 20, ipady = 20, expand = TRUE)


# Création et placement des widgets dans le frame zoneLecture


Button(zoneLecture, text = "Extraire de la pile", bg = "#AA3333", fg = "ivory", font = petitePolice, command = extrairePile).pack(side = LEFT, ipadx = 20, ipady = 10, padx = 5, pady = 5)

Button(zoneLecture, text = "Extraire de la file", bg = "#33AA33", fg = "ivory", font = petitePolice, command = extraireFile).pack(side = LEFT, ipadx = 20, ipady = 10, padx = 5, pady = 5)

Label(zoneLecture, textvariable = lecture, bg = "black", fg = "yellow", font = grandePolice).pack(fill = X, ipadx = 20, ipady = 20, side = BOTTOM)


# - - - - - - - - - - - - - - - - - -

# Bouclage de la fenêtre fen_princ

# - - - - - - - - - - - - - - - - - -


fenetre.mainloop()

Et oui, une seule petite modification !

...CORRECTION...

def extraireFile() :

    elementLu = listeDesDonnees.pop()

    lecture.set(elementLu)

    afficherListe()


def rajouterFile() :

    rajout = nouvelleEntree.get()

    listeDesDonnees.insert(0,rajout)

    afficherListe()


Bilan de la file de données :

Le principe est donc de rajouter par une extrémité et d'extraire par l'autre extrémité de la liste.

J'ai pris comme technique de rajouter en début de liste le nouvel élément et d'extraire depuis la fin de liste l'élément à lire.

maListe Sortie Elément 3 d Elément 2 a Elément 1 b Elément 0 c Entrée

# FILE : Rajout en début de liste et extraction en fin de liste

# Rajout

maListe.insert(0,"Nouvel élément")

# Extraction et lecture

extraction = maListe.pop()

Nous aurions néanmoins le même comportement en rajoutant le nouvel élément en fin de liste et extraire du début de la liste l'élément à lire. Pour cela, on peut utiliser la méthode append. On doit lire et extraire le premier élément et pas le dernier : on doit alors fournir 0 en argument à la méthode pop.

maListe Entrée Elément 2 a Elément 1 b Elément 0 c Sortie

# FILE : Rajout en fin de liste et extraction en début de liste

# Rajout

maListe.append("Nouvel élément")

# Extraction et lecture

extraction = maListe.pop(0)

Si j'utilise maListe.append("d") sur la liste précédente, j'obtiens :

maListe Entrée Elément 3 d Elément 2 a Elément 1 b Elément 0 c Sortie

Pour lire, il faudrait alors utiliser extraction = maListe.pop(0), et extraction contiendrait "d".

5 - Mini-projet

Pour finir cette activité rapide, passons à un mini-projet rapide : vous allez devoir modifier le mini-Simon pour qu'il utilise un code d'enregistrement / extraction de données plus facile à comprendre que notre méthode.

Pour rappel, nous avions créer une liste listeDesActions qui contient les actions mémorisées sur les carrés colorés.

Les seuls endroits où on rajoute des choses à la liste se situe dans la fonction change et dans la fonction animer avec ces ligne :

        listeDesActions.append(chaineGeneree)

        listeDesActions.append('TOUTES ON')

        listeDesActions.append('TOUTES OFF')

Le seul endroit où on supprime des choses dans la liste des actions se situe dans la fonction animer avec la ligne contenant remove :

    # On place dans action l'action à faire, codée à l'aide d'un string : voir modifier_case

    action = listeDesActions[0]


    # On lance la modification à faire

    modifier_case(action)

    # On supprime l'action de la liste

    listeDesActions.remove(action)

05° A priori, nous n'avions créé ni une pile, ni une file. Analysez néanmoins le code et donnez la méthode de gestion (pile ou file) qui s'approche le plus de notre cas.

...CORRECTION...

On rajoute les éléments à la fin de la liste puisqu'on utilise la méthode append. L'entrée est donc en fin de liste.

Pour séparer les deux cas, il faut donc analyser comment on extrait les éléments. Le problème, c'est qu'on utilise pas la méthode pop mais la méthode remove. Regardons comment :

    # On place dans action l'action à faire, codée à l'aide d'un string : voir modifier_case

    action = listeDesActions[0]


    # On lance la modification à faire

    modifier_case(action)

    # On supprime l'action de la liste

    listeDesActions.remove(action)

Puisqu'on copie puis supprime, l'élément 0, on extrait depuis le début de la liste.

Nous avons donc affaire à une file : on lit et on enregistre à deux endroits différents.

06° Modifier le code (fourni ci-dessous) pour gérer plus proprement les données comme une file.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


"""

DESCRIPTION :

L'utilisateur peut faire du clic-gauche sur les carrés colorés pour mémoriser une séquence.

Lorsqu'on utilise un clic-droit sur l'un des carrés, on montre la séquence qui a été enregistrée, dans l'ordre chronologique.

"""


from tkinter import *

from PIL import Image as Img

from PIL import ImageTk


# - - - - - - - - - - - - - - - - - -

# Variables du programme

# - - - - - - - - - - - - - - - - - -


fen_princ = Tk()

fen_princ.geometry("500x500")


listeDesCases = []

"""

La variable listeDesCases contient la liste des Labels créés avec creation_widget_case.

"""


listeDesActions = ['TOUTES ON','TOUTES OFF']

"""

La variable listeDesActions est une liste qui contient l'ensemble des actions que l'utilisateur a réalisé sur les carrés colorés.

Valeurs possibles dans cette liste :

'TOUTES ON' permet d'activer les 4 zones Images (les rendre plus claires)

'TOUTES OFF' permet de remettre les 4 carrés d'origine (les rendre plus foncées)

De la même façon :

'A ON' et 'A OFF' permettent respectivement d'avoir le carré bleu en bleu clair et bleu foncé.

'B ON' et 'B OFF' permettent respectivement d'avoir le carré vert en vert clair et vert foncé.

'C ON' et 'C OFF' permettent respectivement d'avoir le carré rouge en rouge clair et rouge foncé.

'D ON' et 'D OFF' permettent respectivement d'avoir le carré jaune en jaune clair et jaune foncé.

Ainsi si listeDesActions = ['D ON','D OFF','A ON','A OFF'], cela veut dire qu'on a mémorisé d'abord un clic sur le carré jaune D puis un clic sur le carré bleu A.

"""


animation = False # Variables GLOBAL pour certaines fonctions

"""

Cette variable sert à savoir si c'est le programme d'animation ou l'utilisateur qui a la main.

Elle doit valoir True pendant la réstitution de la séquence, l'utilisateur n'a pas la main.

Elle doit valoir False pendant la mémorisation de la séquence créée par l'utilisateur via les clics gauche.

"""


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions de CREATION

# - - - - - - - - - - - - - - - - - -


def creation_image_tk(rouge = 0, vert = 0, bleu = 0) :

    """

    Permet de créer une image PhotoImage compatible avec le module tkinter.

    Les paramètres rouge, vert et bleu sont des valeurs comprises entre 0 et 255 de la couleur RGB voulue.

    Renvoie la référence de l'image PhotoImage. Devra être stockée dans une variable.

    """

    carrePIL = Img.new("RGB", (100,100), (rouge,vert,bleu))

    carreTk = ImageTk.PhotoImage(carrePIL)

    return(carreTk)


def creation_widget_case(rouge = 0, vert = 0, bleu = 0, lettre = "A", coordX = 100, coordY = 100) :

    """

    Permet de créer un widget Label affichant une image.

    Les paramètres ccord_x et coordY sont les coordonnées du widget dans la fenêtre.

    Les paramètres rouge,vert,bleu correspondent à la couleur initiale.

    Renvoie la référence du widget Label. Devra être stockée dans une variable.

    """


    # Création des deux images ON et OFF

    carreFonce = creation_image_tk(rouge = rouge, vert = vert, bleu = bleu)

    carreClair = creation_image_tk(rouge = rouge+50, vert = vert+50, bleu = bleu+50)


    # Création du Label

    widLabel = Label(fen_princ, image = carreFonce)

    widLabel.place(x = coordX, y = coordY)


    # Stockage de données dans le Label

    widLabel.image_OFF = carreFonce

    widLabel.image_ON = carreClair

    widLabel.lettreId = lettre


    # Associe un clic-gauche sur un carré à une fonction-événement

    widLabel.bind( '<Button-1>', lambda event : change(event,lettre,"ON") )

    # Associe le relachement du bouton-gauche à une fonction-événement

    widLabel.bind( '<ButtonRelease-1>', lambda event : change(event,lettre,"OFF") )

    return(widLabel)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions

# - - - - - - - - - - - - - - - - - -


def animer() :

    """

    Cette fonction lance l'animation qui montre la séquence stockée dans listeDesActions.

    Elle prend l'action stockée dans listeDesActions[0].

    Elle change l'affichage en fonction de l'action.

    Elle supprime ensuite le premier élement de listeDesActions.

    Exemple :

    listesDesActions = ['A ON','A OFF','B ON','B OFF'] va gérer l'action "A ON".

    On supprime 'A ON' et on obtient

    listesDesActions = ['A OFF','B ON','B OFF'] va gérer l'action 'A ON'.

    """

    global animation


    # On place dans action l'action à faire, codée à l'aide d'un string : voir modifier_case

    action = listeDesActions[0]


    # On lance la modification à faire

    modifier_case(action)

    # On supprime l'action de la liste

    listeDesActions.remove(action)


    # Le test suivant permet de voir s'il reste des actions enregistrées ou si on a fini

    if len(listeDesActions) > 0 :

        fen_princ.after(500,animer)

    else :

        listeDesActions.append('TOUTES ON')

        listeDesActions.append('TOUTES OFF')

        animation = False


def modifier_case(action) :

    """

    Cette fonction permet de modifier les widgets en fonctin du parametre action.

    "A ON" active la 1er case, "A OFF" la désactive

    "B ON" active la 2e case, "B OFF" la désactive

    "C ON" active la 3e case, "C OFF" la désactive

    "D ON" active la 4e case, "D OFF" la désactive

    "TOUTES ON" active les 4 cases, "TOUTES OFF" les désactive

    """

    codeCase, separateur, actionVoulue = action.partition(' ')

    for laCase in listeDesCases :

        if codeCase == "TOUTES" or codeCase == laCase.lettreId :

            if actionVoulue == "ON" :

                laCase.configure(image = laCase.image_ON)

            else :

                laCase.configure(image = laCase.image_OFF)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions EVENEMENTS

# - - - - - - - - - - - - - - - - - -


def change(event, case, etat ) :

    """

    Le paramètre case doit contenir le string "A","B","C" ou "D" par exemple.

    Le paramètre etat doit contenir le string "ON" ou "OFF" par exemple.

    """

    if animation == False :


        chaineGeneree = "{0} {1}".format(case,etat)

        # Cela revient à faire chaineGeneree = case+" "+etat

        modifier_case(chaineGeneree)

        listeDesActions.append(chaineGeneree)


def lancement(event) :

    """

    Fonction-événement qui lance l'animation de la séquence stockée.

    """

    global animation

    animation = True

    animer()


# - - - - - - - - - - - - - - - - - -

# Création de la fenêtre et des objets associés la fenêtre

# - - - - - - - - - - - - - - - - - -


# Création et placement des widgets Label


listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 200, coordX = 0, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 160, rouge = 40, coordX = 0, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 120, rouge = 80, coordX = 100, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 80, rouge = 120, coordX = 100, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 100, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 100, vert = 160, rouge = 40 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 0, vert = 120, rouge = 80 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 0, vert = 80, rouge = 120 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 200, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 300, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "nez", coordX = 200, coordY = 200, rouge = 50, vert = 50, bleu = 50 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 100, rouge = 80, vert = 50, bleu = 80 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 0, rouge = 80, vert = 80, bleu = 80 ))


# Gestion des événements


fen_princ.bind( '<ButtonRelease-3>', lancement )


# - - - - - - - - - - - - - - - - - -

# Bouclage de la fenêtre fen_princ

# - - - - - - - - - - - - - - - - - -


fen_princ.mainloop()

...CORRECTION ...

On simplifie le code puisqu'avec une seule ligne on extrait l'action et on la supprime de la liste.

Pourquoi avoir utiliser le remove avant ? Simplement parce que c'est pratique aussi dans certain projet. Il fallait bien présenter une méthode d'extraction avant l'autre.

Il faudra se souvenir qu'avec remove on supprime alors qu'avec pop on extrait et on supprime de la liste.


def animer() :

    """

    Cette fonction lance l'animation qui montre la séquence stockée dans listeDesActions.

    Elle prend l'action stockée dans listeDesActions[0].

    Elle change l'affichage en fonction de l'action.

    Elle supprime ensuite le premier élement de listeDesActions.

    Exemple :

    listesDesActions = ['A ON','A OFF','B ON','B OFF'] va gérer l'action "A ON".

    On supprime 'A ON' et on obtient

    listesDesActions = ['A OFF','B ON','B OFF'] va gérer l'action 'A ON'.

    """

    global animation


    # On extrait l'action de la liste

    action = listeDesActions.pop(0)


    # On lance la modification à faire

    modifier_case(action)


    # Le test suivant permet de voir s'il reste des actions enregistrées ou si on a fini

    if len(listeDesActions) > 0 :

        fen_princ.after(500,animer)

    else :

        listeDesActions.append('TOUTES ON')

        listeDesActions.append('TOUTES OFF')

        animation = False

...CORRECTION COMPLETE...

#!/usr/bin/env python

# -*- coding: utf-8 -*-


"""

DESCRIPTION :

L'utilisateur peut faire du clic-gauche sur les carrés colorés pour mémoriser une séquence.

Lorsqu'on utilise un clic-droit sur l'un des carrés, on montre la séquence qui a été enregistrée, dans l'ordre chronologique.

"""


from tkinter import *

from PIL import Image as Img

from PIL import ImageTk


# - - - - - - - - - - - - - - - - - -

# Variables du programme

# - - - - - - - - - - - - - - - - - -


fen_princ = Tk()

fen_princ.geometry("500x500")


listeDesCases = []

"""

La variable listeDesCases contient la liste des Labels créés avec creation_widget_case.

"""


listeDesActions = ['TOUTES ON','TOUTES OFF']

"""

La variable listeDesActions est une liste qui contient l'ensemble des actions que l'utilisateur a réalisé sur les carrés colorés.

Valeurs possibles dans cette liste :

'TOUTES ON' permet d'activer les 4 zones Images (les rendre plus claires)

'TOUTES OFF' permet de remettre les 4 carrés d'origine (les rendre plus foncées)

De la même façon :

'A ON' et 'A OFF' permettent respectivement d'avoir le carré bleu en bleu clair et bleu foncé.

'B ON' et 'B OFF' permettent respectivement d'avoir le carré vert en vert clair et vert foncé.

'C ON' et 'C OFF' permettent respectivement d'avoir le carré rouge en rouge clair et rouge foncé.

'D ON' et 'D OFF' permettent respectivement d'avoir le carré jaune en jaune clair et jaune foncé.

Ainsi si listeDesActions = ['D ON','D OFF','A ON','A OFF'], cela veut dire qu'on a mémorisé d'abord un clic sur le carré jaune D puis un clic sur le carré bleu A.

"""


animation = False # Variables GLOBAL pour certaines fonctions

"""

Cette variable sert à savoir si c'est le programme d'animation ou l'utilisateur qui a la main.

Elle doit valoir True pendant la réstitution de la séquence, l'utilisateur n'a pas la main.

Elle doit valoir False pendant la mémorisation de la séquence créée par l'utilisateur via les clics gauche.

"""


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions de CREATION

# - - - - - - - - - - - - - - - - - -


def creation_image_tk(rouge = 0, vert = 0, bleu = 0) :

    """

    Permet de créer une image PhotoImage compatible avec le module tkinter.

    Les paramètres rouge, vert et bleu sont des valeurs comprises entre 0 et 255 de la couleur RGB voulue.

    Renvoie la référence de l'image PhotoImage. Devra être stockée dans une variable.

    """

    carrePIL = Img.new("RGB", (100,100), (rouge,vert,bleu))

    carreTk = ImageTk.PhotoImage(carrePIL)

    return(carreTk)


def creation_widget_case(rouge = 0, vert = 0, bleu = 0, lettre = "A", coordX = 100, coordY = 100) :

    """

    Permet de créer un widget Label affichant une image.

    Les paramètres ccord_x et coordY sont les coordonnées du widget dans la fenêtre.

    Les paramètres rouge,vert,bleu correspondent à la couleur initiale.

    Renvoie la référence du widget Label. Devra être stockée dans une variable.

    """


    # Création des deux images ON et OFF

    carreFonce = creation_image_tk(rouge = rouge, vert = vert, bleu = bleu)

    carreClair = creation_image_tk(rouge = rouge+50, vert = vert+50, bleu = bleu+50)


    # Création du Label

    widLabel = Label(fen_princ, image = carreFonce)

    widLabel.place(x = coordX, y = coordY)


    # Stockage de données dans le Label

    widLabel.image_OFF = carreFonce

    widLabel.image_ON = carreClair

    widLabel.lettreId = lettre


    # Associe un clic-gauche sur un carré à une fonction-événement

    widLabel.bind( '<Button-1>', lambda event : change(event,lettre,"ON") )

    # Associe le relachement du bouton-gauche à une fonction-événement

    widLabel.bind( '<ButtonRelease-1>', lambda event : change(event,lettre,"OFF") )

    return(widLabel)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions

# - - - - - - - - - - - - - - - - - -


def animer() :

    """

    Cette fonction lance l'animation qui montre la séquence stockée dans listeDesActions.

    Elle prend l'action stockée dans listeDesActions[0].

    Elle change l'affichage en fonction de l'action.

    Elle supprime ensuite le premier élement de listeDesActions.

    Exemple :

    listesDesActions = ['A ON','A OFF','B ON','B OFF'] va gérer l'action "A ON".

    On supprime 'A ON' et on obtient

    listesDesActions = ['A OFF','B ON','B OFF'] va gérer l'action 'A ON'.

    """

    global animation


    # On extrait l'action de la liste

    action = listeDesActions.pop(0)


    # On lance la modification à faire

    modifier_case(action)


    # Le test suivant permet de voir s'il reste des actions enregistrées ou si on a fini

    if len(listeDesActions) > 0 :

        fen_princ.after(500,animer)

    else :

        listeDesActions.append('TOUTES ON')

        listeDesActions.append('TOUTES OFF')

        animation = False


def modifier_case(action) :

    """

    Cette fonction permet de modifier les widgets en fonctin du parametre action.

    "A ON" active la 1er case, "A OFF" la désactive

    "B ON" active la 2e case, "B OFF" la désactive

    "C ON" active la 3e case, "C OFF" la désactive

    "D ON" active la 4e case, "D OFF" la désactive

    "TOUTES ON" active les 4 cases, "TOUTES OFF" les désactive

    """

    codeCase, separateur, actionVoulue = action.partition(' ')

    for laCase in listeDesCases :

        if codeCase == "TOUTES" or codeCase == laCase.lettreId :

            if actionVoulue == "ON" :

                laCase.configure(image = laCase.image_ON)

            else :

                laCase.configure(image = laCase.image_OFF)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions EVENEMENTS

# - - - - - - - - - - - - - - - - - -


def change(event, case, etat ) :

    """

    Le paramètre case doit contenir le string "A","B","C" ou "D" par exemple.

    Le paramètre etat doit contenir le string "ON" ou "OFF" par exemple.

    """

    if animation == False :


        chaineGeneree = "{0} {1}".format(case,etat)

        # Cela revient à faire chaineGeneree = case+" "+etat

        modifier_case(chaineGeneree)

        listeDesActions.append(chaineGeneree)


def lancement(event) :

    """

    Fonction-événement qui lance l'animation de la séquence stockée.

    """

    global animation

    animation = True

    animer()


# - - - - - - - - - - - - - - - - - -

# Création de la fenêtre et des objets associés la fenêtre

# - - - - - - - - - - - - - - - - - -


# Création et placement des widgets Label


listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 200, coordX = 0, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 160, rouge = 40, coordX = 0, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 120, rouge = 80, coordX = 100, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 80, rouge = 120, coordX = 100, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 100, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 100, vert = 160, rouge = 40 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 0, vert = 120, rouge = 80 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 0, vert = 80, rouge = 120 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 200, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 300, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "nez", coordX = 200, coordY = 200, rouge = 50, vert = 50, bleu = 50 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 100, rouge = 80, vert = 50, bleu = 80 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 0, rouge = 80, vert = 80, bleu = 80 ))


# Gestion des événements


fen_princ.bind( '<ButtonRelease-3>', lancement )


# - - - - - - - - - - - - - - - - - -

# Bouclage de la fenêtre fen_princ

# - - - - - - - - - - - - - - - - - -


fen_princ.mainloop()

Comme vous avez pu le constater, on gére les rajouts à plusieurs endroits. Si on décide de changer de méthodes, il faudra donc changer à plusieurs endroits le code correspondant. Pas très sécurisant comme façon de faire...

07° Créer ensuite une fonction extraire et une fonction rajouter.qui vont gérer les rajouts et les extractions dans la liste des actions.

Voici la correction :

#!/usr/bin/env python

# -*- coding: utf-8 -*-


"""

DESCRIPTION :

L'utilisateur peut faire du clic-gauche sur les carrés colorés pour mémoriser une séquence.

Lorsqu'on utilise un clic-droit sur l'un des carrés, on montre la séquence qui a été enregistrée, dans l'ordre chronologique.

"""


from tkinter import *

from PIL import Image as Img

from PIL import ImageTk


# - - - - - - - - - - - - - - - - - -

# Variables du programme

# - - - - - - - - - - - - - - - - - -


fen_princ = Tk()

fen_princ.geometry("500x500")


listeDesCases = []

"""

La variable listeDesCases contient la liste des Labels créés avec creation_widget_case.

"""


listeDesActions = ['TOUTES ON','TOUTES OFF']

"""

La variable listeDesActions est une liste qui contient l'ensemble des actions que l'utilisateur a réalisé sur les carrés colorés.

Valeurs possibles dans cette liste :

'TOUTES ON' permet d'activer les 4 zones Images (les rendre plus claires)

'TOUTES OFF' permet de remettre les 4 carrés d'origine (les rendre plus foncées)

De la même façon :

'A ON' et 'A OFF' permettent respectivement d'avoir le carré bleu en bleu clair et bleu foncé.

'B ON' et 'B OFF' permettent respectivement d'avoir le carré vert en vert clair et vert foncé.

'C ON' et 'C OFF' permettent respectivement d'avoir le carré rouge en rouge clair et rouge foncé.

'D ON' et 'D OFF' permettent respectivement d'avoir le carré jaune en jaune clair et jaune foncé.

Ainsi si listeDesActions = ['D ON','D OFF','A ON','A OFF'], cela veut dire qu'on a mémorisé d'abord un clic sur le carré jaune D puis un clic sur le carré bleu A.

"""


animation = False # Variables GLOBAL pour certaines fonctions

"""

Cette variable sert à savoir si c'est le programme d'animation ou l'utilisateur qui a la main.

Elle doit valoir True pendant la réstitution de la séquence, l'utilisateur n'a pas la main.

Elle doit valoir False pendant la mémorisation de la séquence créée par l'utilisateur via les clics gauche.

"""


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions de CREATION

# - - - - - - - - - - - - - - - - - -


def creation_image_tk(rouge = 0, vert = 0, bleu = 0) :

    """

    Permet de créer une image PhotoImage compatible avec le module tkinter.

    Les paramètres rouge, vert et bleu sont des valeurs comprises entre 0 et 255 de la couleur RGB voulue.

    Renvoie la référence de l'image PhotoImage. Devra être stockée dans une variable.

    """

    carrePIL = Img.new("RGB", (100,100), (rouge,vert,bleu))

    carreTk = ImageTk.PhotoImage(carrePIL)

    return(carreTk)


def creation_widget_case(rouge = 0, vert = 0, bleu = 0, lettre = "A", coordX = 100, coordY = 100) :

    """

    Permet de créer un widget Label affichant une image.

    Les paramètres ccord_x et coordY sont les coordonnées du widget dans la fenêtre.

    Les paramètres rouge,vert,bleu correspondent à la couleur initiale.

    Renvoie la référence du widget Label. Devra être stockée dans une variable.

    """


    # Création des deux images ON et OFF

    carreFonce = creation_image_tk(rouge = rouge, vert = vert, bleu = bleu)

    carreClair = creation_image_tk(rouge = rouge+50, vert = vert+50, bleu = bleu+50)


    # Création du Label

    widLabel = Label(fen_princ, image = carreFonce)

    widLabel.place(x = coordX, y = coordY)


    # Stockage de données dans le Label

    widLabel.image_OFF = carreFonce

    widLabel.image_ON = carreClair

    widLabel.lettreId = lettre


    # Associe un clic-gauche sur un carré à une fonction-événement

    widLabel.bind( '<Button-1>', lambda event : change(event,lettre,"ON") )

    # Associe le relachement du bouton-gauche à une fonction-événement

    widLabel.bind( '<ButtonRelease-1>', lambda event : change(event,lettre,"OFF") )

    return(widLabel)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions

# - - - - - - - - - - - - - - - - - -


def extraireAction() :

    """

    Cette fonction centralise la gestion de l'extraction dans la liste.

    Dans ce code, elle gère le tout comme une FILE dont.

    - l'entrée est en fin de liste et

    - la sortie en début de liste.

    """

    action = listeDesActions.pop(0)

    return(action)


def rajouterAction(elementARajouter) :

    """

    Cette fonction centralise la gestion de l'extraction dans la liste.

    Dans ce code, elle gère le tout comme une FILE dont.

    - l'entrée est en fin de liste et

    - la sortie en début de liste.

    """

    listeDesActions.append(elementARajouter)


def animer() :

    """

    Cette fonction lance l'animation qui montre la séquence stockée dans listeDesActions.

    Elle prend l'action stockée dans listeDesActions[0].

    Elle change l'affichage en fonction de l'action.

    Elle supprime ensuite le premier élement de listeDesActions.

    Exemple :

    listesDesActions = ['A ON','A OFF','B ON','B OFF'] va gérer l'action "A ON".

    On supprime 'A ON' et on obtient

    listesDesActions = ['A OFF','B ON','B OFF'] va gérer l'action 'A ON'.

    """

    global animation


    # On place dans action l'action à faire, codée à l'aide d'un string : voir modifier_case

    action = extraireAction()


    # On lance la modification à faire

    modifier_case(action)


    # Le test suivant permet de voir s'il reste des actions enregistrées ou si on a fini

    if len(listeDesActions) > 0 :

        fen_princ.after(500,animer)

    else :

        rajouterAction('TOUTES ON')

        rajouterAction('TOUTES OFF')

        animation = False


def modifier_case(action) :

    """

    Cette fonction permet de modifier les widgets en fonctin du parametre action.

    "A ON" active la 1er case, "A OFF" la désactive

    "B ON" active la 2e case, "B OFF" la désactive

    "C ON" active la 3e case, "C OFF" la désactive

    "D ON" active la 4e case, "D OFF" la désactive

    "TOUTES ON" active les 4 cases, "TOUTES OFF" les désactive

    """

    codeCase, separateur, actionVoulue = action.partition(' ')

    for laCase in listeDesCases :

        if codeCase == "TOUTES" or codeCase == laCase.lettreId :

            if actionVoulue == "ON" :

                laCase.configure(image = laCase.image_ON)

            else :

                laCase.configure(image = laCase.image_OFF)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions EVENEMENTS

# - - - - - - - - - - - - - - - - - -


def change(event, case, etat ) :

    """

    Le paramètre case doit contenir le string "A","B","C" ou "D" par exemple.

    Le paramètre etat doit contenir le string "ON" ou "OFF" par exemple.

    """

    if animation == False :


        chaineGeneree = "{0} {1}".format(case,etat)

        # Cela revient à faire chaineGeneree = case+" "+etat

        modifier_case(chaineGeneree)

        rajouterAction(chaineGeneree)


def lancement(event) :

    """

    Fonction-événement qui lance l'animation de la séquence stockée.

    """

    global animation

    animation = True

    animer()


# - - - - - - - - - - - - - - - - - -

# Création de la fenêtre et des objets associés la fenêtre

# - - - - - - - - - - - - - - - - - -


# Création et placement des widgets Label


listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 200, coordX = 0, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 160, rouge = 40, coordX = 0, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 120, rouge = 80, coordX = 100, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 80, rouge = 120, coordX = 100, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 100, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 100, vert = 160, rouge = 40 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 0, vert = 120, rouge = 80 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 0, vert = 80, rouge = 120 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 200, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 300, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "nez", coordX = 200, coordY = 200, rouge = 50, vert = 50, bleu = 50 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 100, rouge = 80, vert = 50, bleu = 80 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 0, rouge = 80, vert = 80, bleu = 80 ))


# Gestion des événements


fen_princ.bind( '<ButtonRelease-3>', lancement )


# - - - - - - - - - - - - - - - - - -

# Bouclage de la fenêtre fen_princ

# - - - - - - - - - - - - - - - - - -


fen_princ.mainloop()

08° Modifier alors le contenu des fonctions pour qu'elles gèrent plutôt les données comme une pile. Lancer le code pour voir la différence avec la gestion en file.

Il suffit donc de modifier le contenu des deux fonctions gérant le flux de données.

Voici la correction :

#!/usr/bin/env python

# -*- coding: utf-8 -*-


"""

DESCRIPTION :

L'utilisateur peut faire du clic-gauche sur les carrés colorés pour mémoriser une séquence.

Lorsqu'on utilise un clic-droit sur l'un des carrés, on montre la séquence qui a été enregistrée, dans l'ordre chronologique.

"""


from tkinter import *

from PIL import Image as Img

from PIL import ImageTk


# - - - - - - - - - - - - - - - - - -

# Variables du programme

# - - - - - - - - - - - - - - - - - -


fen_princ = Tk()

fen_princ.geometry("500x500")


listeDesCases = []

"""

La variable listeDesCases contient la liste des Labels créés avec creation_widget_case.

"""


listeDesActions = ['TOUTES ON','TOUTES OFF']

"""

La variable listeDesActions est une liste qui contient l'ensemble des actions que l'utilisateur a réalisé sur les carrés colorés.

Valeurs possibles dans cette liste :

'TOUTES ON' permet d'activer les 4 zones Images (les rendre plus claires)

'TOUTES OFF' permet de remettre les 4 carrés d'origine (les rendre plus foncées)

De la même façon :

'A ON' et 'A OFF' permettent respectivement d'avoir le carré bleu en bleu clair et bleu foncé.

'B ON' et 'B OFF' permettent respectivement d'avoir le carré vert en vert clair et vert foncé.

'C ON' et 'C OFF' permettent respectivement d'avoir le carré rouge en rouge clair et rouge foncé.

'D ON' et 'D OFF' permettent respectivement d'avoir le carré jaune en jaune clair et jaune foncé.

Ainsi si listeDesActions = ['D ON','D OFF','A ON','A OFF'], cela veut dire qu'on a mémorisé d'abord un clic sur le carré jaune D puis un clic sur le carré bleu A.

"""


animation = False # Variables GLOBAL pour certaines fonctions

"""

Cette variable sert à savoir si c'est le programme d'animation ou l'utilisateur qui a la main.

Elle doit valoir True pendant la réstitution de la séquence, l'utilisateur n'a pas la main.

Elle doit valoir False pendant la mémorisation de la séquence créée par l'utilisateur via les clics gauche.

"""


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions de CREATION

# - - - - - - - - - - - - - - - - - -


def creation_image_tk(rouge = 0, vert = 0, bleu = 0) :

    """

    Permet de créer une image PhotoImage compatible avec le module tkinter.

    Les paramètres rouge, vert et bleu sont des valeurs comprises entre 0 et 255 de la couleur RGB voulue.

    Renvoie la référence de l'image PhotoImage. Devra être stockée dans une variable.

    """

    carrePIL = Img.new("RGB", (100,100), (rouge,vert,bleu))

    carreTk = ImageTk.PhotoImage(carrePIL)

    return(carreTk)


def creation_widget_case(rouge = 0, vert = 0, bleu = 0, lettre = "A", coordX = 100, coordY = 100) :

    """

    Permet de créer un widget Label affichant une image.

    Les paramètres ccord_x et coordY sont les coordonnées du widget dans la fenêtre.

    Les paramètres rouge,vert,bleu correspondent à la couleur initiale.

    Renvoie la référence du widget Label. Devra être stockée dans une variable.

    """


    # Création des deux images ON et OFF

    carreFonce = creation_image_tk(rouge = rouge, vert = vert, bleu = bleu)

    carreClair = creation_image_tk(rouge = rouge+50, vert = vert+50, bleu = bleu+50)


    # Création du Label

    widLabel = Label(fen_princ, image = carreFonce)

    widLabel.place(x = coordX, y = coordY)


    # Stockage de données dans le Label

    widLabel.image_OFF = carreFonce

    widLabel.image_ON = carreClair

    widLabel.lettreId = lettre


    # Associe un clic-gauche sur un carré à une fonction-événement

    widLabel.bind( '<Button-1>', lambda event : change(event,lettre,"ON") )

    # Associe le relachement du bouton-gauche à une fonction-événement

    widLabel.bind( '<ButtonRelease-1>', lambda event : change(event,lettre,"OFF") )

    return(widLabel)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions

# - - - - - - - - - - - - - - - - - -


def extraireAction() :

    """

    Cette fonction centralise la gestion de l'extraction dans la liste.

    Dans ce code, elle gère le tout comme une PILE dont

    l'entrée et la sortie sont en fin de liste

    """

    action = listeDesActions.pop()

    return(action)


def rajouterAction(elementARajouter) :

    """

    Cette fonction centralise la gestion de l'extraction dans la liste.

    Dans ce code, elle gère le tout comme une PILE dont

    l'entrée et la sortie sont en fin de liste

    """

    listeDesActions.append(elementARajouter)


def animer() :

    """

    Cette fonction lance l'animation qui montre la séquence stockée dans listeDesActions.

    Elle prend l'action stockée dans listeDesActions[0].

    Elle change l'affichage en fonction de l'action.

    Elle supprime ensuite le premier élement de listeDesActions.

    Exemple :

    listesDesActions = ['A ON','A OFF','B ON','B OFF'] va gérer l'action "A ON".

    On supprime 'A ON' et on obtient

    listesDesActions = ['A OFF','B ON','B OFF'] va gérer l'action 'A ON'.

    """

    global animation


    # On place dans action l'action à faire, codée à l'aide d'un string : voir modifier_case

    action = extraireAction()


    # On lance la modification à faire

    modifier_case(action)


    # Le test suivant permet de voir s'il reste des actions enregistrées ou si on a fini

    if len(listeDesActions) > 0 :

        fen_princ.after(500,animer)

    else :

        rajouterAction('TOUTES ON')

        rajouterAction('TOUTES OFF')

        animation = False


def modifier_case(action) :

    """

    Cette fonction permet de modifier les widgets en fonctin du parametre action.

    "A ON" active la 1er case, "A OFF" la désactive

    "B ON" active la 2e case, "B OFF" la désactive

    "C ON" active la 3e case, "C OFF" la désactive

    "D ON" active la 4e case, "D OFF" la désactive

    "TOUTES ON" active les 4 cases, "TOUTES OFF" les désactive

    """

    codeCase, separateur, actionVoulue = action.partition(' ')

    for laCase in listeDesCases :

        if codeCase == "TOUTES" or codeCase == laCase.lettreId :

            if actionVoulue == "ON" :

                laCase.configure(image = laCase.image_ON)

            else :

                laCase.configure(image = laCase.image_OFF)


# - - - - - - - - - - - - - - - - - -

# Déclaration des fonctions EVENEMENTS

# - - - - - - - - - - - - - - - - - -


def change(event, case, etat ) :

    """

    Le paramètre case doit contenir le string "A","B","C" ou "D" par exemple.

    Le paramètre etat doit contenir le string "ON" ou "OFF" par exemple.

    """

    if animation == False :


        chaineGeneree = "{0} {1}".format(case,etat)

        # Cela revient à faire chaineGeneree = case+" "+etat

        modifier_case(chaineGeneree)

        rajouterAction(chaineGeneree)


def lancement(event) :

    """

    Fonction-événement qui lance l'animation de la séquence stockée.

    """

    global animation

    animation = True

    animer()


# - - - - - - - - - - - - - - - - - -

# Création de la fenêtre et des objets associés la fenêtre

# - - - - - - - - - - - - - - - - - -


# Création et placement des widgets Label


listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 200, coordX = 0, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 160, rouge = 40, coordX = 0, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 120, rouge = 80, coordX = 100, coordY = 0 ))

listeDesCases.append(creation_widget_case( lettre = "oeilG", bleu = 80, rouge = 120, coordX = 100, coordY = 100 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 100, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 100, vert = 160, rouge = 40 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 300, coordY = 0, vert = 120, rouge = 80 ))

listeDesCases.append(creation_widget_case( lettre = "oeilD", coordX = 400, coordY = 0, vert = 80, rouge = 120 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 200, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "bouche", coordX = 300, coordY = 300, rouge = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 0, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 100, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 200, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 300, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 400, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 300, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "visage", coordX = 200, coordY = 400, rouge = 200, vert = 200 ))

listeDesCases.append(creation_widget_case( lettre = "nez", coordX = 200, coordY = 200, rouge = 50, vert = 50, bleu = 50 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 100, rouge = 80, vert = 50, bleu = 80 ))

listeDesCases.append(creation_widget_case( lettre = "front", coordX = 200, coordY = 0, rouge = 80, vert = 80, bleu = 80 ))


# Gestion des événements


fen_princ.bind( '<ButtonRelease-3>', lancement )


# - - - - - - - - - - - - - - - - - -

# Bouclage de la fenêtre fen_princ

# - - - - - - - - - - - - - - - - - -


fen_princ.mainloop()

Comme vous pouvez le constater, notre programme ne gère pas très bien le flux à l'envers : la succesion ON/OFF ne se fait plus dans le bon ordre ! Avec certains programmes, ça passe, ici non. Si nous avions voulu gérer les données dans les deux sens, il aura fallu réfléchir au préalable à une manière de stocker l'information qui supporte les deux sens. Ici, il faudra écrire les séquences dans l'ordre OFF/ON par exemple.

On retiendra de cette courte activité qu'il existe deux façons de gérer les flux de données.

La FILE / QUEUE où entrée et sortie sont aux deux extrémités.

La PILE / STACK ou l'entrée et la sortie sont du même côté souvent la fin de liste.

Autant gérer les rajouts et les extractions depuis des fonctions qui centralisent les actions à faire.

Il vaut mieux réfléchir à la façon de gérer les données AVANT que le programme ne soit trop avancé : il est parfois difficile de changer de technique.