Le patron de conception "Modèle - Vue - Contrôleur" est destiné à faciliter le développement d'interfaces graphiques.
Une Interface graphique est constituée essentiellement de deux modules :
Pour développer un tel programme, on le divise généralement en trois modules appelés respectivement:
Le modèle est la partie du programme qui manipule et met à jour les informations qui doivent être conservées d'une session à l'autre. Il s'agit de l'ensemble des variables et objets qui sont créés et mis à jour par l'utilisateur lorsqu'il interagit avec le programme.
La Vue est la partie du programme qui gère la mise en page, la disposition des informations, des boutons et des formulaires, l'organisation et la visibilité des différentes fenêtres du programme s'il y en a.
Le contrôleur est la partie du programme qui gère les actions de l'utilisateur. Chacune des actions proposées dans la vue est implémentée dans le contrôleur sous la forme d'une fonction.
Dans ce TD, nous développons une application permettant de gérer une petite animalerie. Le programme gère un cheptel de rongeurs (tamias, hamsters, etc..) qui vivent dans une cage.
L'état de notre animalerie est décrit à l'aide de deux fichiers json
:
Nous allons procéder par essai/erreur pour développer notre programme. Nous développerons, dans l'ordre:
Commençons par le Modèle. Le Modèle est la seule partir du programme autorisée à manipuler directement les données contenues dans les tables. Il permet essentiellement de réaliser des opérations de lecture et d'écriture dans les tableaux de données, qui doivent être mis à jour à chaque action de l'utilisateur.
Nous disposons pour l'instant uniquement de deux fichiers :
RACE
, leur TYPE
, leur ÉTAT
et leur LIEU
.Voici en clair l'état initial des animaux:
Animal | RACE | TYPE | ÉTAT | LIEU |
---|---|---|---|---|
Tic | tamia | rongeur | affamé | litière |
Tac | tamia | rongeur | affamé | litière |
Patrick | hamster | rongeur | affamé | litière |
Totoro | ili pika | rongeur | repus | mangeoire |
Pocahontas | opossum | marsupial | endormi | nid |
Voici en clair l'état initial de l'équipement:
Équipement | DISPONIBILITÉ |
---|---|
litière | libre |
mangeoire | occupé |
roue | libre |
nid | occupé |
Créez un nouveau projet.
Modèle.py
ainsi qu'un fichier de testorig
, pour que si les mises à jour ratent, vous puissiez repartir de zéro.
Créez dans Modèle.py
une fonction lit_état
qui:
animal.json
import json def lit_état(animal_id): . . .
"ETAT"
;Désolé, XXX n'est pas un animal connu
(où XXX
doit être remplacé par le nom de l'animal)None
(état nul)json
. Pour lire le contenu du fichier animal.json
, il suffit d'ouvrir le fichier avec la commande suivante :with open('animal.json', "r", encoding='utf-8') as f: animal = json.load(f)
on récupère ainsi un dictionnaire animal
indexé par les identifiants des différents animaux (ici Tic
, Tac
, Patrick
, Totoro
et Pocahontas
).
poca = animal['Pocahontas']
poca
est lui-même un dictionnaire. Pour lire une valeur spécifique, on utilise l'adressage par attributpoca_race = poca['RACE']
poca_race = animal['Pocahontas']['RACE']
La fonction lit_état
doit passer les tests suivants:
import Modèle def test_lit_etat(): assert Modèle.lit_état('Tac') == 'affamé' def test_lit_etat_nul(): assert Modèle.lit_état('Bob') == None
Tac
est "affamé"
.None
) est retourné lorsque le nom n'est pas dans le tableau
Créez dans le Modèle une fonction lit_lieu
analogue à lit_état
qui retourne le lieu où se trouve
l'animal.
def lit_lieu(animal_id): . . .
De manière analogue fonction devra passer les tests suivants:
def test_lit_lieu(): assert Modèle.lit_lieu('Tac') == 'litière' def test_lit_lieu_nul(): assert Modèle.lit_lieu('Bob') == None
Créer une fonction vérifie_disponibilité
retourne la disponibilité du lieu indiqué à partir de la table des équipements.
def vérifie_disponibilité(équipement_id): . . .
Cette fonction devra passer les tests suivants :
def test_vérifie_disponibilité(): assert Modèle.vérifie_disponibilité('litière') == 'libre' assert Modèle.vérifie_disponibilité('nid') == 'occupé'
Désolé, XXX n'est pas un équipement connu
(où XXX
doit être remplacé par le nom de l'équipement)None
(état nul)def test_vérifie_disponibilité_nul(): assert Modèle.vérifie_disponibilité('nintendo') == None
La fonction cherche_occupant
retourne la liste des occupants du lieu indiqué à partir de la table des animaux.
def cherche_occupant(lieu): . . .
Elle devra passer les tests suivants:
def test_cherche_occupant(): assert Modèle.cherche_occupant('nid') == ['Pocahontas'] assert 'Tac' in Modèle.cherche_occupant('litière') assert 'Tac' not in Modèle.cherche_occupant('mangeoire')
Désolé, XXX n'est pas un lieu connu
(où XXX
doit être remplacé par le nom du lieu)None
(état nul)def test_cherche_occupant_nul(): assert Modèle.cherche_occupant('casino') == None
Créez une fonction d'écriture change_état
qui modifie l'état de l'animal à partir de son nom et d'un nouvel état.
def change_état(id_animal, état): . . .
La fonction devra passer les tests suivants :
def test_change_état(): Modèle.change_état('Totoro', 'fatigué') assert Modèle.lit_état('Totoro') == 'fatigué' Modèle.change_état('Totoro', 'excité comme un pou') assert Modèle.lit_état('Totoro') == 'fatigué' Modèle.change_état('Bob', 'fatigué') assert Modèle.lit_état('Bob') == None
Autrement dit seuls les états affamé
, fatigué
, repus
, endormi
sont autorisés.
json.dump
. with open('animal.json', "w") as g: json.dump(animal, g)
json
plus joli, on peut utiliser le paramètre indent. Par exemple : json.dump(animal, open("animal.json", "w"), indent=4)
Créez une fonction change_lieu
permettant à un animal de changer de lieu.
def change_lieu(id_animal, lieu): . . .
libre
;occupé
(à l'exception de la litière qui est toujours libre
);LIEU
de l'animal.change_lieu
modifie à la fois le fichier animal.json
et le fichier équipement.json
.
La fonction devra passer les tests suivants:
def test_change_lieu(): Modèle.change_lieu('Totoro', 'roue') assert Modèle.lit_lieu('Totoro') == 'roue' assert Modèle.vérifie_disponibilité('litière') == 'libre' assert Modèle.vérifie_disponibilité('roue') == 'occupé'
Si le lieu est occupé, la fonction
Désolé, le lieu XXX est déjà occupé
.def test_change_lieu_occupé(): Modèle.change_lieu('Totoro', 'nid') assert Modèle.lit_lieu('Totoro') == 'roue'
Si le lieu n'existe pas, la fonction ne change rien:
def test_change_lieu_nul_1(): Modèle.change_lieu('Totoro', 'casino') assert Modèle.lit_lieu('Totoro') == 'roue'
Si l'animal n'existe pas, la fonction ne change rien non plus:
def test_change_lieu_nul_2(): Modèle.change_lieu('Bob', 'litière') assert Modèle.lit_lieu('Bob') == None
Le contrôleur est la partie du programme qui met en œuvre l'ensemble des actions possibles:
nourrir
divertir
coucher
réveiller
En vérifiant certaines contraintes d'intégrité:
Ajoutez à votre projet un nouveau module Contrôleur.py
qui implémentera les fonctions nourrir
, divertir
, coucher
et réveiller
. Le contrôleur fait appel au module Modèle
pour toutes les opérations de lecture et d'écriture.
Créez une fonction nourrir
qui prend en argument un identifiant d'animal.
Impossible, la mangeoire est actuellement occupée par XXX
.affamé
, la fonction affiche un message du type Désolé, XXX n'a pas faim!
.mangeoire
repus
occupé
La fonction nourrir
devra passer les tests suivants :
import Contrôleur def test_nourrir(): if Modèle.vérifie_disponibilité('mangeoire') == 'libre' and Modèle.lit_état('Tic') == 'affamé': Contrôleur.nourrir('Tic') assert Modèle.vérifie_disponibilité('mangeoire') == 'occupé' assert Modèle.lit_état('Tic') == 'repus' assert Modèle.lit_lieu('Tic') == 'mangeoire' Contrôleur.nourrir('Tac') assert Modèle.lit_état('Tac') == 'affamé' assert Modèle.lit_lieu('Tac') == 'litière' Contrôleur.nourrir('Pocahontas') assert Modèle.lit_état('Pocahontas') == 'endormi' assert Modèle.lit_lieu('Pocahontas') == 'nid' Contrôleur.nourrir('Bob') assert Modèle.lit_état('Bob') == None assert Modèle.lit_lieu('Bob') == None assert Modèle.vérifie_disponibilité('mangeoire') == 'occupé'
Désolé, la mangeoire est occupée par ['Tic']
Désolé, Pocahontas n'a pas faim
Désolé, Bob n'est pas un animal connu
Créez et testez une fonction divertir
qui prend en argument un identifiant d'animal.
Impossible, la roue est actuellement occupée par XXX
.repus
, la fonction affiche un message du type Désolé, XXX n'est pas en état de faire du sport!
.roue
fatigué
libre
Créez et testez une fonction coucher
qui prend en argument un identifiant d'animal.
Impossible, le nid est actuellement occupé par XXX
.fatigué
, la fonction affiche un message du type Désolé, XXX n'est pas fatigué!
.nid
endormi
libre
Créez et testez une fonction réveiller
qui prend en argument un identifiant d'animal.
endormi
, la fonction affiche un message du type Désolé, XXX ne dort pas!
.litière
affamé
libre
Cette partie sera vue dans un prochain TD!