# -*- encoding: utf-8 -*-
"""Module gérant l'interface graphique
Ce module définie les classes utilisées pour toute l'interface graphique
(menus, édition, import de fichiers, évolution des cellules).
Il y a également une fonction :func:`main` qui démarre l'application.
"""
import os
import pyglet
from pyglet import gl
from pyglet.window import mouse, key
import cocos
from cocos.layer import Layer
from cocos.director import director
from cocos.menu import Menu, MenuItem, EntryMenuItem
from lejeu.dieu import SuperDieu
from lejeu.sauvegarde import importer_fichier, LifeError
[docs]class CoucheCellules(Layer):
"""Couche capable d'afficher des cellules.
Cette couche permet de se déplacer dans le monde en cliquant/glissant avec
le clic droit et de zommer avec la molette.
"""
is_event_handler = True
""":class:`CoucheCellules` gère les évenement de la fenêtre."""
def __init__(self, etat, cell_dim=(10., 10.),
theme=((255, 255, 255, 255), (0, 0, 0, 255))):
"""
:param etat: État à utiliser pour initialiser
:class:`lejeu.dieu.SuperDieu`.
:type etat: set
:param cell_dim: Hauteur et largeur d'une cellule (en pixels).
:type cell_dim: tuple
"""
super().__init__()
self.batch = pyglet.graphics.Batch()
self.vertexs = {}
self.redraw = True
# couleur des cellules mortes (arriere-plan)
background_color = [i/255 for i in theme[1]]
gl.glClearColor(*background_color)
# couleur des cellules vivantes
self.cell_color = theme[0]
self.cell_dim = list(cell_dim)
w_width, w_height = director.get_window_size()
self.center = (w_width/2, w_height/2)
self.offset = list(self.center) # doit pouvoir etre modifiable
self.dieu = SuperDieu(etat, mode="infini")
self.schedule(self.update)
[docs] def update(self, dt):
"""Met à jour la fenêtre si besoin.
:param dt: Durée depuis le dernier appel (environ 1/60sec).
:type dt: float
"""
if self.redraw:
self.batch = pyglet.graphics.Batch()
self.vertexs = {}
self.dessine_cellules(self.dieu.vivantes, set())
self.redraw = False
[docs] def draw(self):
"""Dessine les cellules vivantes sur la couche."""
gl.glPushMatrix()
self.transform()
self.batch.draw()
gl.glPopMatrix()
[docs] def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
"""Déplacer les cellules avec la souris.
:param x: Abscisse de la souris.
:type x: int
:param y: Ordonnée de la souris.
:type y: int
:param dx: Déplacement en abscisse de la souris.
:type dx: int
:param dy: Déplacement en ordonnée de la souris.
:type dy: int
:param buttons: Combinaison des codes des boutons de la souris
enfoncés.
:type buttons: int
"""
if buttons & mouse.RIGHT:
self.offset[0] += dx
self.offset[1] += dy
self.redraw = True
[docs] def vertexs_cellule(self, c):
"""Calcule les vertexs d'une cellule.
:param c: Coordonnées de la cellule.
:type c: tuple
:return: attributs formatées des vertexes (voir `vertex attributes
<http://www.pyglet.org/doc/programming_guide/vertex_attributes.html>`_)
:rtype: tuple
"""
c_px = (float(c[0] * (self.cell_dim[0] * 1.2) + self.offset[0]),
float(c[1] * (self.cell_dim[1] * 1.2) + self.offset[1]))
data = (('v2f', (c_px[0], c_px[1],
c_px[0]+self.cell_dim[0], c_px[1],
c_px[0]+self.cell_dim[0], c_px[1]+self.cell_dim[1],
c_px[0], c_px[1]+self.cell_dim[1])),
('c4B', self.cell_color*4))
return data
[docs] def dessine_cellules(self, nees, mortes):
"""Met à jour la liste de cellules à dessiner."""
for c in mortes:
self.vertexs[c].delete() # efface le vertex
del self.vertexs[c] # supprime la reference de self.vertexs
for c in nees:
c_px = (float(c[0] * self.cell_dim[0] + self.offset[0]),
float(c[1] * self.cell_dim[1] + self.offset[1]))
vertex_list = self.batch.add(4, gl.GL_QUADS, None,
*self.vertexs_cellule(c))
self.vertexs[c] = vertex_list
[docs]class CoucheEvolution(CoucheCellules):
"""Couche permettant l'évolution des cellules (scène principale)."""
def __init__(self, etat, vitesse_evo=0.25, **kwargs):
"""
:param etat: État à utiliser pour initialiser
:class:`lejeu.dieu.SuperDieu`.
:type etat: set de coordonnées des cellules vivantes
:param cell_dim: Hauteur et largeur d'une cellule (en pixels).
:type cell_dim: tuple d'entier
:param vitesse_evo: Nombre de secondes pour qu'une nouvelle génération
apparaisse.
:type vitesse_evo: int ou float
"""
super().__init__(etat, **kwargs)
self.last_gen = 0. # duree depuis la derniere evolution
self.vitesse_evo = vitesse_evo
self.en_pause = False
[docs] def update(self, dt):
"""Met à jour une nouvelle génération s'il le faut.
:param dt: Durée en seconde depuis le dernier appel.
:type dt: float
"""
super().update(dt)
self.last_gen += dt
if (self.last_gen >= self.vitesse_evo
and not self.redraw
and not self.en_pause):
self.last_gen = 0
nees, mortes = self.dieu.generation_suivante()
self.dessine_cellules(nees, mortes)
[docs] def on_key_press(self, symbol, modifiers):
"""Stoppe l'évolution avec la touche espace."""
if symbol == key.SPACE:
self.en_pause = not self.en_pause
# def debug(self):
# """Ecrit le nombre de voisin de chaque cellule en rouge (lent!)."""
# for lbl in self.labels:
# lbl.delete()
# self.labels = []
# for (x, y) in self.dieu.n_voisins:
# c = (float((x + 0.5) * self.cell_dim[0] + self.offset[0]),
# float((y + 0.5) * self.cell_dim[1] + self.offset[1]))
# self.labels.append(pyglet.text.Label(
# str(self.dieu.n_voisins[(x,y)]),
# font_name='Times New Roman',
# color=(255, 0, 0, 255),
# font_size=12,
# x=c[0], y=c[1],
# anchor_x='center', anchor_y='center',
# batch=self.batch,
# group=self.foreground))
[docs]class CoucheEdition(CoucheCellules):
"""Couche permettant l'édition d'un état initial."""
def __init__(self, **kwargs):
super().__init__(etat=set(), **kwargs)
[docs] def on_mouse_press(self, x, y, button, modifiers):
"""Dessine des cellules (efface avec <ctrl>).
:param x: Abscisse de la souris.
:type x: int
:param y: Ordonnée de la souris.
:type y: int
:param button: Code de la touche appuyée.
:type button: int
:param modifiers: Combinaison des codes des touches spéciales.
:type modifiers: int
"""
if button & mouse.LEFT:
c = (((x - self.offset[0])//(self.cell_dim[0] * 1.2)),
((y - self.offset[1])//(self.cell_dim[1] * 1.2)))
if not modifiers and not c in self.dieu.vivantes:
self.dieu.vivantes.add(c)
elif modifiers & key.MOD_CTRL and c in self.dieu.vivantes:
self.dieu.vivantes.remove(c)
self.redraw = True
[docs] def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
"""Dessine des cellules (efface avec <ctrl>).
:param x: Abscisse de la souris.
:type x: int
:param y: Ordonnée de la souris.
:type y: int
:param dx: Déplacement en abscisse de la souris.
:type dx: int
:param dy: Déplacement en ordonnée de la souris.
:type dy: int
:param button: Code de la touche appuyée.
:type button: int
:param modifiers: Combinaison des codes des touches spéciales.
:type modifiers: int
"""
super().on_mouse_drag(x, y, dx, dy, buttons, modifiers)
if buttons & mouse.LEFT:
c = (((x - self.offset[0])//(self.cell_dim[0] * 1.2)),
((y - self.offset[1])//(self.cell_dim[1] * 1.2)))
if not modifiers and not c in self.dieu.vivantes:
self.dieu.vivantes.add(c)
elif modifiers & key.MOD_CTRL and c in self.dieu.vivantes:
self.dieu.vivantes.remove(c)
self.redraw = True
[docs] def on_key_release(self, symbol, modifiers):
"""Stoppe/redémarre l'évolution en appuyant sur <space>."""
if symbol == key.RETURN:
scene = cocos.scene.Scene(CoucheEvolution(self.dieu.vivantes))
director.replace(scene)
[docs]class CoucheAide(Layer):
"""Couche de texte de l'aide."""
is_event_handler = True
def __init__(self):
super().__init__()
x, y = director.get_window_size()
aide = ("Pour se déplacer, cliquer/glisser droit.\n"
"Pour zoomer, utiliser la molette.\n"
"Dans le mode édition, cliquer/glisser gauche avec la souris pour "
"dessiner.\n"
"Pour effacer, idem en maintenant <ctrl>.\n"
"En mode évolution, appuyer sur <espace> pour mettre en pause.\n"
"Pour revenir qu menu principal, appuyez sur <ici>.")
text = cocos.text.Label(aide, (x/2, y/2), multiline=True, width=x,
anchor_x='center', anchor_y="center", align="center", font_size=20)
self.add(text)
[docs] def on_mouse_press(self, x, y, buttons, modifiers):
"""Revient au menu principal si on clique."""
director.replace(cocos.scene.Scene(MenuPrincipal()))
[docs]class CoucheCredits(Layer):
"""Couche de texte des crédits."""
is_event_handler = True
def __init__(self):
super().__init__()
x, y = director.get_window_size()
credits = ("Crédits\n"
"Musique: The Grid, Daft Punk (Tron Legacy, BO)\n"
"Developpement: lui, toi et moi.\n"
"Sur un concept original de John Conway.\n"
"Effet spéciaux: Sony Pictures Imageworks.\n"
"Merci à mon père, ma mère, mes frères-et mes soeurs "
"(wouhou c'était le bonheur).")
text = cocos.text.Label(credits, (x/2, y/2), multiline=True, width=x,
anchor_x='center', anchor_y="center", align="center", font_size=20)
self.add(text)
[docs] def on_mouse_press(self, x, y, buttons, modifiers):
"""Revient au menu principal si on clique."""
director.replace(cocos.scene.Scene(MenuPrincipal()))
[docs]def main(etat):
"""Démarre l'interface graphique"""
director.init(resizable=True, caption="The God Game")
scene = cocos.scene.Scene(MenuPrincipal())
#source = pyglet.media.load('data/the_grid.wav', streaming=False)
#player = pyglet.media.Player()
#player.queue(source)
#player.play()
director.run(scene)