Code source de lejeu.fenetre

# -*- 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 on_mouse_scroll(self, x, y, scroll_x, scroll_y): """Zoom centré sur la souris. :param x: Abscisse de la souris. :type x: int :param y: Ordonnée de la souris. :type y: int :param scroll_x: Scroll horizontal de la souris (rare). :type scroll_x: int :param scroll_y: Scroll vertical ('clics' de molette). :type scroll_y: int """ # nouvelles dimensions des cellules en pixels (au minimum 1px) new_cell_dim = (max(self.cell_dim[0] + (scroll_y/4), 0.5), max(self.cell_dim[1] + (scroll_y/4), 0.5)) # ancienne distance a la souris en unitee de cellule dist = ((self.offset[0] - x)/(self.cell_dim[0] * 1.2), (self.offset[1] - y)/(self.cell_dim[1] * 1.2)) # nouvelle distance a la souris en pixels self.offset = [x + dist[0]*(new_cell_dim[0] * 1.2), y + dist[1]*(new_cell_dim[1] * 1.2)] self.cell_dim = new_cell_dim 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)