Gestion d'un canvas

Tagged:

Bon j'ai vu sur ce site par l'intermédiaire de notre cher Tristant Nitot ... Truc de dingue ... Un jeu en 3D et en javascript ...

Bon perso j'y connais rien en 3D et c'est bien domage ... Je me dit qu'il faut absolument que je comprenne comment c'est fait, et pour ça rien de tel qu'un application en python avec un canvas et la reproduction parfaite de ce jeu aussi simple soit-il, pour apréhender le concept et éventuellement rebondir sur autre chose de plus sophistiqué !!!

Je vais y aller par étapes ainsi dans un premier temps :

Confection de l'interface graphique

La mon amis glade est toujours fidèle ... Pif paf pouf je prends une fenêtre j'y met un GtkDrawingArea dessus de 320 x 200 que j'appèle Canvas ... Je prend ma fidèle classe GLObject et hardi paulette ... Un fichier game.py qui ne fait que ça :

#!/usr/bin/env python
 
import gtk
 
from GLObject import GLObject
 
class winMain(GLObject):
 
	def __init__(self):
		GLObject.__init__(self,'winMain')
 
winMain()
gtk.main()

On y est... Un simple fenêtre grite ;) Voyons comment on s'en sert !!!

A la découverte du GTKDrawingArea

Quel titre majestueux n'est-il pas ? On va faire quelques pas en avant en se servant de la magnifique doc que nous file pyGTK.

tracer un rectangle

Bon je suis partit de la doc et j'ai pas réussi a tracer un malheureux rectangle ... Je suis donc partit de l'exemple finit et j'ai essayé d'adapter à glade ... J'ai galéré un moment mais j'y suis parvenu voila comment :

Je crée une classe Canvas qui sera désormais résponsable de me simplifier la vie ... au même titre que les autres classes que j'ai créé permettant de gérer des ListView et des ComboBox d'ailleur ;) Bon voici comment s'articule cette classe sachant que pour l'instant elle ne va me servir qu'a tracer un rectangle de 320px par 200px, noir qui plus est ! Donc rien de bien folichon !!!

import gtk
 
class Canvas:
    
    def __init__(self,canvas):
        self.canvas = canvas
        self.style = self.canvas.get_style()
        self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
        self.drawable = self.canvas.window
    
    def update(self):
        self.drawable.draw_rectangle(self.gc, True, 0, 0, 320, 200)

Voila on s'en servira avec glade de cette façon (dans le init de winMain):

		self.canvas = Canvas(self.widgets.get_widget('canvas'))

Il ne faut surtout pas oublier dans glade de connecter un évenement (il faut surtout y penser parce que de prime abord c'est pas le premier évenement que j'aurais connecté ...) qui va nous permettre d'appeller le update du Canvas ... cet évenement est l'évenement expose_event

Ainsi on rajoute la fonction :

	def on_canvas_expose_event(self,*args):
		self.canvas.update()

Et magique ... notre moche fenêtre affiche un ridicule rectangle en un temps considérable minime ...

Mettons-y un peu de couleur

Bon alors la j'ai planché un moment sur la myriade de solutions proposées par le web ... aucunes ne marchait vraiment mais bon la j'ai trouvé quelque chose ;) Si l'on veut tracer des rectangles, des cercles, des lignes d'une certaine couleur il faut indiquer que dorénavent, la couleur de foreground est différente ... Voici par exemple cette fonction :

    def setColor(self,red,green,blue,mult=1):
        color = self.drawable.get_colormap().alloc_color(red * mult, green * mult, blue * mult)
        self.gc.set_foreground(color)

Il suffit alors de faire

        self.setColor(0,65535,0)
        self.drawable.draw_rectangle(self.gc, True, 0, 0, -1, -1)

Ce qui est completement équivalent à :

        self.setColor(0,255,0,256)
        self.drawable.draw_rectangle(self.gc, True, 0, 0, -1, -1)

puisque les couleurs sont éxprimée avec 65536 niveau pour chacune d'entre elle. Dans le cas ou vous désirez réduire ce gap il vous suffit d'éxprimer un multiplicateur a la fonction setColor ce qui aura pour effet de réduire considérablement la palette de couleur utilisée !!! (ça la réduira d'autant que le multiplicateur est grand en toute logique !!!)

Creer un outil de dessin

L'idée c'est d'agrémenter notre classe Canvas de tout un tas de fonctionalitée pratiques ... Ce genre de chose que beaucoup de gens n'aiment pas faire et que moi j'adore ;) faire des classes qui font des trucs qui servent a rien si on ne s'en sert pas ;) bref je fait je vous file le résultat de mon terrible labeur ;)

class Coord:
    """
    Classe de base symbolisant une coordonnée
    """
    def __init__(self,x=0,y=0):
        self.x = x
        self.y = y
    def getX(self):
        return self.x
    def getY(self):
        return self.y
 
class Area:
    """
    Classe de base symbolisant une aire
    """
    def __init__(self,c1,c2):
        self.c1 = c1
        self.c2 = c2
    def getX(self):
        if self.c1.getX() > self.c2.getY():
            return self.c2.getX()
        return self.c1.getX()
    def getY(self):
        if self.c1.getX() > self.c2.getY():
            return self.c2.getX()
        return self.c1.getX()
    def getHeight(self):
        return abs(self.c1.getY() - self.c2.getY())
    def getWidth(self):
        return abs(self.c1.getX() - self.c2.getX())
 
class Color:
    """
    Classe de base symbolisant une couleur
    """
    def __init__(self,red,green,blue,precision=1):
        self.red = red * precision
        self.green = green * precision
        self.blue = blue * precision
 
class Canvas:
    """
    Classe de base symbolisant un canvas avec possibilité de tracer des rectangles
    """
    def __init__(self,canvas):
        self.canvas = canvas
        self.style = self.canvas.get_style()
        self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
        self.drawable = self.canvas.window
    
    def setColor(self,red,green,blue,mult=1):
        color = self.drawable.get_colormap().alloc_color(red * mult, green * mult, blue * mult)
        self.gc.set_foreground(color)
    
    def drawRectangle(self,area,color=None,fill=True):
        if not color is None:
            self.setColor(color.red,color.green,color.blue)
        self.drawable.draw_rectangle(self.gc, fill, area.getX(), area.getY(),area.getWidth(),area.getHeight())
        
    def update(self):
        pass #a redéfinir
 
class MyCanvas(Canvas):
    """
    Class perso qui définit un canvas dans lequel on déssine un carré des 95 de large en position 5,5
    """    
    def __init__(self,canvas):
        Canvas.__init__(self,canvas)
    
    def update(self):
        self.drawRectangle(Area(Coord(5,5),Coord(100,100)), Color(0,0,65000))

Il ne nous reste plus qu'a créer les fonctions pour tracer des lignes des cercles et compagnie !!! on peu étendre ça à l'infini !!!

Dans mes temps libre et pour le futur je vais préparer un billet qui explique comment j'arrive a recréer ce foutu jeu ... pour l'instant je n'ai pas commencé et je ne sais pas si je vais y arriver, cela dit, il n'y a pas de raisons hein ;)

Comments

ça pourrait le faire, tout le monde pourrait se rencontrer, et bosser par la suite sur de même projets...

Un programmateur c'est un truc dans une machine à laver !!!
Sinon si je viens sur paris, c'est plutot pour des vacances ;) mais si d'autres sont intéréssés pourquoi pas ;)

pas question que je mette les pieds à paris.