Skip to content
Snippets Groups Projects
Commit 199e9d8f authored by Hudelot's avatar Hudelot
Browse files

update game of life

parent 3d223f83
Branches
No related tags found
No related merge requests found
......@@ -9,4 +9,5 @@ C'est une fonctionnalité très simple et nous vous laissons libre de choisir co
**N'oubliez pas de mettre à jour votre dépôt local et distant.**
Vous pouvez ensuite passer à la [**Fonctionnalité 6** : Simuler le jeu de la vie pour un univers donné sur un ensemble d'itérations fixé - la fonction `game_life_simulate`](./S2_simulate.md)
\ No newline at end of file
Vous pouvez ensuite passer à la [**Fonctionnalité 6** : Simuler le jeu de la vie pour un univers donné sur un ensemble d'itérations fixé - la fonction `game_life_simulate`](./S2_simulate.md)
# Montée en compétences : les interfaces graphiques en python
Il existe un certain nombre de frameworks pour le developpement d'interfaces graphiques en python. Une liste de ces différents outils est disponible [ici](https://wiki.python.org/moin/GuiProgramming).
En particulier, la bibliothèque standard de python possède un module appelé **Tkinter (Tk interface)** et qui permet de developper des interfaces graphiques. Nous nous contenterons d'utiliser ce module dans cette partie du projet qui ne nécessite pas d'installation.
Une documentation de ce module est disponible [ici](https://wiki.python.org/moin/TkInter).
Vous allez maintenant faire un rapide tutorial (inspiré du cours d'OpenClassRooms [Apprenez à programmer en python ](https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/234859-des-interfaces-graphiques-avec-tkinter) ainsi que de ce [tutorial](https://www.python-course.eu/python_tkinter.php)) pour nous familiariser avec ce module et cet outil.
Le [tutorial de Vincent Maillol](https://vincent.developpez.com/cours-tutoriels/python/tkinter/apprendre-creer-interface-graphique-tkinter-python-3/) est aussi fortement conseillé.
Une autre documentation bien faite de Tkinter en français est disponible [ici](http://tkinter.fdex.eu/index.html)
## Une première interface graphique en Tkinter
Ajouter un fichier `tuto_GUI.py` à votre projet et copier le code ci-dessous qui correspond à la création d'une fenêtre qui affiche le message *Hello World!*.
```PYTHON
from tkinter import *
# Window creation, root of the interface
window = Tk()
# Creation of a label (text line) that says Hello World ! and with as first parameter the previous window
label_field = Label(window,text="Hello World !")
# Display of the label
label_field.pack()
# Running of the Tkinter loop that ends when we close the windw
window.mainloop()
```
L'exécution de ce code devrait vous afficher la fenêtre suivante.
![Tkinter Hello World](./Images/hello.png)
La méthode `config()` permet de configurer des widgets. Par exemple, dans l'exemple précédent, les deux lignes suivantes
``` PYTHON
label_field = Label(window,text="Hello World !")
label_field.config(text="Hello World !")
```
sont équivalentes à
```
label_field = Label(window,text="Hello World !")
```
`pack` est un gestionnaire de géométrie qui gère la composition des widgets.
Le placement du widget se fait par rapport à son contenant (avec l’option de configuration `side`) en utilisant un placement cardinal (`Tkinter.TOP`, `Tkinter.LEFT`, `Tkinter.RIGHT`, `Tkinter.BOTTOM`). Par défaut, un widget est attaché en haut, ou au-dessous des widgets existants
Si l’on souhaite une occupation maximale de l’espace, utiliser l’option de configuration `expand=YES`. La direction de l’occupation maximale est précisée par `fill`. Elle peut être en largeur (`Tkinter.X`), en hauteur (`Tkinter.Y`) ou les deux (`Tkinter.BOTH`).
D'autres gestionnaires de position sont disponibles en Tkinter, le gestionnaire `grid` qui découpe le conteneur en grille et place le widget dans une cellule de cette grille et le gestionnaire `place` qui positionne le widget au pixel près mais qui est difficile à utiliser dans la pratique.
## Découverte des principaux widgets de Tkinter
Tkinter possède un certain nombre de widgets, des objets graphiques, comme des boutons, des champs de texte, des cases à cocher, des barres de progression…). Nous allons ici présenter les principaux.
### Label
Dans l'exemple précédent, nous avons vu le widget `Label` qui permet d'afficher du texte dans la fenêtre principale. Ce texte ne peut pas être modifié par l'utilisateur.
### Button
Les boutons (`Button`) sont des widgets sur lesquels on peut cliquer et qui peuvent déclencher des actions ou **commandes**.
Copier le code précédent dans votre fichier test et executer le. Que fait-il?
``` PYTHON
import tkinter as tk
def write_text():
print("Hello CentraleSupelec")
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame,
text="QUIT",
activebackground = "blue",
fg="red",
command=quit)
button.pack(side=tk.LEFT)
slogan = tk.Button(frame,
fg="blue",
text="Hello",
command=write_text)
slogan.pack(side=tk.LEFT)
root.mainloop()
```
Attention, pour les utilisateurs de MAC OSX, les couleurs peuvent ne pas être prises en compte. Des indications sont disponibles [ici](https://stackoverflow.com/questions/1529847/how-to-change-the-foreground-or-background-colour-of-a-tkinter-button-on-mac-os).
Cet exemple illustre comment initier un traitement depuis une interface graphique. Ici, la commande `quit` est associée au premier bouton pour quitter la fenêtre et la commande définie par la fonction `write_text()` est associée au deuxième bouton.
### Entry
Le widget `Entry` est un widget de saisie de données. Il sert à recueillir la saisie d'un utilisateur. Il prend en paramètre un objet de type `StringVar` qui va servir à récupérer le texte de la saisie. Si la `StringVar` est mise à jour, le champ de saisie est également modifié.
Tester le code ci-dessous
``` PYTHON
from tkinter import Tk, StringVar, Label, Entry, Button
from functools import partial
def update_label(label, stringvar):
"""
Met à jour le texte d'un label en utilisant une StringVar.
"""
text = stringvar.get()
label.config(text=text)
stringvar.set('merci')
root = Tk()
text = StringVar(root)
label = Label(root, text='Your name')
entry_name = Entry(root, textvariable=text)
button = Button(root, text='clic',
command=partial(update_label, label, text))
label.grid(column=0, row=0)
entry_name.grid(column=0, row=1)
button.grid(column=0, row=2)
root.mainloop()
```
Le widget `Button` prend en paramètre un texte à afficher et une fonction à exécuter.
La fonction à exécuter ne prend pas de paramètre et pour utiliser une foncion déjà ecrite il faut l'encapsuler avec le module [`partial`](https://docs.python.org/2/library/functools.html).
A l'aide du tutorial disponible [ici](https://www.python-course.eu/tkinter_radiobuttons.php), explorez les autres widgets de Tkinter comme par exemples les [`Radiobutton`](https://www.python-course.eu/tkinter_radiobuttons.php) ou les [`Checkbutton`](https://www.python-course.eu/tkinter_checkboxes.php).
## Les Conteneurs
Les conteneurs sont des widgets destinés à contenir d'autres widgets.
Dans nos exemples, nous avons déjà utilisé les fenêtres (Toplevel) via l'instruction `root = Tk()`.
Parmi les conteneurs, il y a aussi le widget `Frame`qui permet de regrouper plusieurs widgets entre eux. Cela peut simplifier leur placement par la suite comme dans l'exemple ci-dessous que vous devez executer.
```
from tkinter import Tk, Label, Frame
root = Tk()
f1 = Frame(root, bd=1, relief='solid')
Label(f1, text='je suis dans F1').grid(row=0, column=0)
Label(f1, text='moi aussi dans F1').grid(row=0, column=1)
f1.grid(row=0, column=0)
Label(root, text='je suis dans root').grid(row=1, column=0)
Label(root, text='moi aussi dans root').grid(row=2, column=0)
root.mainloop()
```
## Les évènements
Il est possible de récupérer des événements, comme la frappe d'une touche ou un clic de souris pour effectuer un traitement spécial. Pour qu'un widget puisse traiter les événements, il faut que celui-ci ait le focus. Généralement, un widget prend le focus lorsque l'on clique dessus. Les événements peuvent ensuite être traités par une fonction.
Le code ci-dessous que vous devez tester affiche *Hello* en fonction des clics ou de l'appui d'une des touches du clavier.
```PYTHON
from tkinter import *
from pprint import pformat
def print_bonjour(i):
label.config(text="Hello")
root = Tk()
frame = Frame(root, bg='white', height=100, width=400)
entry = Entry(root)
label = Label(root)
frame.grid(row=0, column=0)
entry.grid(row=1, column=0, sticky='ew')
label.grid(row=2, column=0)
frame.bind('<ButtonPress>', print_bonjour)
entry.bind('<KeyPress>', print_bonjour)
root.mainloop()
```
Prenez le temps de lire rapidement ce [très bon tutoriel](http://tkinter.fdex.eu/doc/event.html) concernant les évènements en Tkinter.
Vous pouvez maintenant vous atteler à votre 2048 avec interface graphique. N'hésitez pas à revenir vers les différents tutoriaux mentionnés pour approfondir votre connaissance de Tkinter.
Nous pouvons maintenant passer à notre [Fonctionnalité 8 : Affichage de la grille de jeu dans une fenêtre Tkinter](./2048_S6_affichagegrille.md).
......@@ -85,9 +85,17 @@ Ce concept de MVP a été introduit par Eric Ries, l'auteur de [The Lean Startup
### Objectif 2 : Un jeu de la vie avec une interface graphique (Amélioration du MVP) (JOUR 2)
+ **Sprint 5** : **Montée en compétences : les interfaces graphiques en python**
**<span style='color:red'>(A venir)</span>**
+ [**Fonctionnalité 11** : Premiers pas en Tkinter](S5_GUI_Tutorial.md)
+ **Sprint 6** : **Creation de l'interface pour l'univers**
+ [**Fonctionnalité 12**: Affichage de l'univers dans une fenêtre Tkinter](./univers.md)
+ [**Fonctionnalité 13** : Permettre la configuration du jeu via l'interface graphique](./config.md)
### Objectif 3 : Enrichissons le catalogue de manière automatique par analyse d'images (JOUR 3)
......
# Fonctionnalité 9 : Permettre la configuration du jeu via l'interface graphique
Dans cette fonctionnalité, nous allons ajouter un certain nombre de widgets à notre fenêtre principale (la fenêtre blanche laissée vide) pour permettre :
+ De choisir la taille de l'univers.
+ De choisir le pattern
+ Pour placer le pattern
+ ...
Si vous avez fini, vous pouvez maintenant ajouter d'autres fonctionnalités de votre choix à votre jeu.
## Pour finir
+ <span style='color:blue'>Faire un commit de vos derniers changements.</span>
+ <span style='color:blue'>Tagger ce dernier commit </span>
+ <span style='color:blue'>Pousser (Push) votre code vers votre dépôt distant sur GitLab.</span>
+ <span style='color:blue'>Faire un test de couverture de code de votre MVP et pousser le bilan obtenu vers votre dépôt distant sur GitLab.</span>
\ No newline at end of file
# Fonctionnalité 8 : Affichage de la grille de jeu dans une fenêtre Tkinter
Il s'agit ici de mettre en place votre interface Tkinter pour l'affichage du jeu de la vie. On travaillera dans un premier temps sur le module `display_game_of_life_gui`
## Etape 1 : Création de la structure Tkinter pour afficher l'univers de jeu.
L'étape 1 consiste en l'affichage de la grille de jeu initiale en Tkinter. Dans cette étape, on s'intéressera uniquement à l'affichage de la grille.
Pour cette étape, il est nécessaire de :
+ Definir avec quel type d'objets widgets notre grille sera représentée.
+ Definir un certain nombre de constantes qui serviront à l'affichage de la grille comme par exemple les couleurs des différentes cellules en fonction de leur valeur, les couleurs des valeurs elle-mêmes, la taille de la fenêtre principale.
### Choix de représentation de la grille de jeu en Tkinter
Pour l'affichage de notre grille de jeu avec Tkinter, nous allons utiliser le widget `Toplevel` qui permet de créer des fenêtres primaires, c'est-à-dire des fenêtres qui possèdent une existence indépendante pour le gestionnaire de fenêtre du système d’exploitation.
Une bonne explication de ce type de widget est disponible [ici](http://tkinter.fdex.eu/doc/toplww.html).
+ Créer une fenêtre principale avec Tkinter, intitulée gameoflife et créer un widget `Toplevel`, intitulé gameoflife que vous associerez à votre fenêtre principale.
+ Placer ce widget à l'aide de la fonction de placement `grid()` de Tkinter.
### Representation de l'univers de cellules
Nous allons maintenant créer et afficher l'univers lui-même.
+ Initialiser un univers de jeu.
+ Regarder le widget [`Canvas`](http://tkinter.fdex.eu/doc/caw.html) pour l'affichage de l'univers du jeu.
Nous avons donc à ce stade deux objets :
+ `grid_game` qui contient les données du jeu, donc les données à afficher (le **MODELE**).
+ `graphical_grid`, notre structure de données Tkinter qui contient la présentation de l'interface graphique (la **VUE**).
Cette manière de procéder est très classique en développement d'interfaces graphiques. Elle correspond à un motif d'architecture logicielle appelé [**Modèle-vue-contrôleur ou MVC**](https://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-contr%C3%B4leur) dédié aux interfaces graphiques, datant de 1978 et devenu très populaire. Ce motif est composé de trois types de modules ayant trois responsabilités différentes : les modèles, les vues et les contrôleurs :
+ Un **modèle (Model)** qui contient les données à afficher.
+ Une **vue (View)** qui contient la présentation de l'interface graphique.
+ Un **contrôleur (Controller)** qui contient la logique concernant les actions effectuées par l'utilisateur.
Nous avons donc pour l'instant respecté ce principe avec au moins les deux premiers modules. Il nous faut cependant encore connecter la vue au modèle et c'est ce que nous allons faire tout de suite.
### Affichage de la simulation.
Il s'agit maintenant d'afficher sur votre interface l'évolution de votre univers.
Pour cela, il faut configurer vos cellules en mettant à jour les paramètres de vos différents objets graphiqus avec les valeurs correspondantes dans la grille elle-même
Avant d'écrire votre code, il est nécéssaire de prendre un peu de temps pour réflechir à la conception. Le type de question qu'il faut vous poser est le nombre de fois que cette opération de mise à jour de la vue peut se produire dans le déroulé du jeu. Une fois ? Plusieurs fois ? A chaque tour de jeu ?
En fonction de votre réponse, il peut donc être utile de mettre le code correspondant à cette tâche dans une fonction, par exemple appelée `display_and_update_graphical_gameoflife()` que vous pourrez appeler dès que nécessaire.
## Etape 3 : Simuler !
A ce stade du projet, vous devriez déjà pouvoir simuer le jeu de la vie via votre interface graphique. Prenez le temps de le faire et n'oubliez pas de lister les fonctionnalités qui vous semblent manquantes.
Nous avons maintenant terminé cette fonctionnalité, il vous faut :
+ <span style='color:blue'>Faire un commit</span>
+ <span style='color:blue'>Pousser (Push) votre code vers votre dépôt distant sur GitLab.</span>
Nous pouvons maintenant ajouter des moyens graphiques de configurer le jeu. Il s'agit de la [Fonctionnalité 9 : Permettre la configuration du jeu via l'interface graphique](./2048_S6_configgrille.md)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment