IX. wxReversi.py▲
Vous pouvez visualiser le contenu du fichier en cliquant sur le lien wxReversi.py.
Ce fichier contient deux classes wxPlate et wxReversi. La première permet de gérer uniquement le plateau de jeu, la deuxième le reste de l'interface graphique. Vous aurez avec ce fichier un bel exemple d'utilisation de la bibliothèque wxPython.
IX-1. La classe wxPlate▲
wxPlate gère le plateau de jeu à travers un wx.BufferedDC et wx.BufferedPaintDC. Il existe plusieurs façons de dessiner à l'écran sans que celui-ci soit saccadé. Ici, ce couple est adapté, car on se contente de dessiner à l'écran l'état du plateau sans qu'il y ait d'interaction avec l'utilisateur. Pour mettre cette classe en place, je me suis servi d'un des exemples du livre wxPython in Action que je recommande fortement si vous souhaitez rapidement progresser avec cette bibliothèque. Le tout consiste à dessiner la totalité du plateau de jeu dans un buffer de type wx.BufferedDC puis de l'afficher à l'écran avec un wx.BufferedPaintDC.
Les attributs de la classe wxPlate :
- parent : parent de type wxReversi ;
- btmplate : Bitmap du plateau de jeu vide ;
- btmwhite : Bitmap d'un pion blanc ;
- btmblack : Bitmap d'un pion noir ;
- reversi : pointeur sur une instance de Reversi.Reversi pour avoir un accès complet au jeu.
Les méthodes de la classe wxPlate :
- __init__(self, parent, reversi) : constructeur initialisant les paramètres et affichant le plateau de jeu initial ;
- OnPaint(self, event = None) : dessine à l'écran le buffer du plateau de jeu lorsqu'un rafraîchissement de l'écran est demandé ;
- Redraw(self) : permet de forcer le rafraîchissement du plateau à l'écran ;
- InitBuffer(self) : crée en mémoire le buffer du plateau de jeu avant le dessiner dedans ;
- DoDrawing(self, dc) : remplit le buffer de la fenêtre du plateau de jeu.
Finalement, le code de cette classe est relativement court et très facile à lire. Dans la fonction InitBuffer, on crée tout d'abord un bitmap vide de la taille du plateau de jeu à l'écran. On va ensuite gérer ce bitmap à travers un wx.BufferedDC dans lequel on dessine le plateau de jeu.
On affiche tout d'abord la grille du plateau ( dc.DrawBitmap(self.btmplate, 0, 0, False) ). On le remplit ensuite avec les différents pions présents sur le plateau.
class
wxPlate
(
wx.Window):
""" Created: 2007.10.25 - Updated: 2008.01.08 """
def
__init__
(
self, parent, reversi):
""" Created: 2007.10.25 - Updated: 2008.01.08 """
wx.Window.__init__
(
self, parent, -
1
, size =
(
338
, 338
))
self.parent =
parent
self.btmplate =
wx.Image
(
'.'
+
os.sep +
'images'
+
os.sep +
"plate.png"
, wx.BITMAP_TYPE_ANY).ConvertToBitmap
(
)
self.btmwhite =
wx.Image
(
'.'
+
os.sep +
'images'
+
os.sep +
"white.png"
, wx.BITMAP_TYPE_ANY).ConvertToBitmap
(
)
self.btmblack =
wx.Image
(
'.'
+
os.sep +
'images'
+
os.sep +
"black.png"
, wx.BITMAP_TYPE_ANY).ConvertToBitmap
(
)
self.reversi =
reversi
self.InitBuffer
(
)
self.Bind
(
wx.EVT_PAINT, self.OnPaint)
def
OnPaint
(
self, event =
None
):
""" Created: 2007.10.25 - Updated: 2008.01.08 """
dc =
wx.BufferedPaintDC
(
self, self.buffer)
def
Redraw
(
self):
""" Created: 2007.10.25 - Updated: 2008.01.08 """
self.InitBuffer
(
)
def
InitBuffer
(
self):
""" Created: 2007.10.25 - Updated: 2008.01.08 """
self.buffer =
wx.EmptyBitmap
(
338
, 338
)
dc =
wx.BufferedDC
(
wx.ClientDC
(
self), self.buffer)
self.DoDrawing
(
dc)
def
DoDrawing
(
self, dc):
""" Created: 2007.10.25 - Updated: 2008.01.08 """
dc.Clear
(
)
dc.DrawBitmap
(
self.btmplate, 0
, 0
, False
)
for
i in
range(
1
, 9
):
for
j in
range(
1
, 9
):
if
self.reversi.plate[i*
10
+
j] ==
1
: dc.DrawBitmap
(
self.btmblack, 2
+
42
*
(
j-
1
), 2
+
42
*
(
i-
1
), False
)
elif
self.reversi.plate[i*
10
+
j] ==
2
: dc.DrawBitmap
(
self.btmwhite, 2
+
42
*
(
j-
1
), 2
+
42
*
(
i-
1
), False
)
IX-2. La classe wxReversi▲
Comme je ne suis pas fan d'éditeur spécialisé aux IHM, toute l'interface graphique a été créée à la main. Mais cela se fait vraiment très bien une fois qu'on a un peu l'habitude. La classe wxReversi possède une barre de menu ( wx.MenuBar, wx.Menu, wx.MenuItem) et des widgets classiques (wx.StaticText, wx.Gauge, wx.ListBox, wx.Timer). L'agencement des widgets est réalisé à travers des wx.BoxSizer, wx.StaticBox et wx.StaticBoxSizer.
Il vous suffira de lire linéairement la fonction __init__(self, reversi) pour comprendre comment l'interface est construite.
Cette classe possède de nombreuses méthodes qui correspondent toutes à des évènements depuis la barre de menu sauf les deux fonctions pnlPlateLDown(self, event) et pnlPlateMove(self, r, score)). Là encore, vous devriez tout comprendre après une simple lecture du code. Nous allons simplement nous attarder sur ces deux dernières méthodes qui gèrent la pose d'un pion par un joueur humain ( pnlPlateLDown ) ou par un ordinateur (pnlPlateMove)
IX-2-1. pnlPlateLDown(self, event)▲
Un joueur humain joue son tour de jeu en cliquant avec la souris sur la case sur laquelle il veut poser un pion. On vérifie ainsi tout d'abord qu'un clic correspond bien à un joueur humain qui a la main. On récupère ensuite la case cliquée (X = event.X // 42 + 1 et Y = event.Y // 42 + 1). Si la case cliquée est une case jouable ( TestLeClic(self.reversi.plate, Y*10+ X, self.reversi.playermain.joueur) ), on pose le pion et on change le tour de jeu ( self.reversi.ChangePlayerMain() )
def
pnlPlateLDown
(
self, event):
if
(
self.reversi.playermain.joueur ==
1
and
isinstance(
self.reversi.player1, Player.Player)) \
or
(
self.reversi.playermain.joueur ==
2
and
isinstance(
self.reversi.player2, Player.Player)):
X =
event.X //
42
+
1
Y =
event.Y //
42
+
1
if
TestLeClic
(
self.reversi.plate, Y*
10
+
X, self.reversi.playermain.joueur):
self.timerWhite.Stop
(
)
self.timerBlack.Stop
(
)
Reverse
(
self.reversi.plate, Y*
10
+
X, self.reversi.playermain.joueur)
b, w =
Score
(
self.reversi.plate)
self.lbMove.InsertItems
(
[str(
b+
w) +
'. '
+
self.reversi.playermain.couleur.upper
(
)[0
] +
" - "
+
chr(
64
+
X) +
" "
+
str(
9
-
Y) +
" 0"
], 0
)
self.pnlPlate.Redraw
(
)
self.reversi.lastMove =
Y *
10
+
X
glovar['freemove'
].remove
(
Y *
10
+
X)
glovar['platemove'
].append
(
Y *
10
+
X)
self.reversi.ChangePlayerMain
(
)
event.Skip
(
)
IX-2-2. pnlPlateMove(self, r, score)▲
Un ordinateur effectue son coup en appelant la fonction pnlPlateMove. Le premier test (TestLeClic(self.reversi.plate, r, self.reversi.playermain.joueur)) n'est utile que pour éviter des effets de bords quand on relance une partie. Le reste de la fonction est identique à ce qu'on peut avoir pour un joueur humain.
def
pnlPlateMove
(
self, r, score):
if
TestLeClic
(
self.reversi.plate, r, self.reversi.playermain.joueur):
self.timerWhite.Stop
(
)
self.timerBlack.Stop
(
)
Reverse
(
self.reversi.plate, r, self.reversi.playermain.joueur)
b, w =
Score
(
self.reversi.plate)
Y, X =
divmod(
r, 10
)
self.reversi.lastMove =
r
glovar['freemove'
].remove
(
r)
glovar['platemove'
].append
(
r)
self.lbMove.InsertItems
(
[str(
b+
w) +
'. '
+
self.reversi.playermain.couleur.upper
(
)[0
] +
" - "
+
chr(
64
+
X) +
" "
+
str(
9
-
Y) +
" "
+
str(
score)], 0
)
self.pnlPlate.Redraw
(
)
self.reversi.ChangePlayerMain
(
)