@@ -32,18 +32,34 @@ def __init__(self, canvas):
3232# Generate a unique tag for each widget
3333self .tag_name = self .__class__ .__name__ + self .id ;
3434
35+ # Initialize instance variables
36+ self .__disabled__ = False ;
37+
3538def set_clickable (self , clickable ):
3639if (clickable ):
3740self .canvas .tag_bind (self .tag_name , "<Button-1>" , self .on_click );
3841else :
3942self .canvas .tag_unbind (self .tag_name );
4043
4144def on_click (self , event ):
45+ if (self .__disabled__ ):
46+ return False ;
4247try :
4348self .command ();
49+ return True ;
4450except AttributeError :
4551print ("Error: " + self .__class__ .__name__ + " " + self .id + " does not have a command" );
4652raise ;
53+ return False ;
54+
55+ def disable (self ):
56+ self .__disabled__ = True ;
57+
58+ def enable (self ):
59+ self .__disabled__ = False ;
60+
61+ def is_enabled (self ):
62+ return self .__disabled__ ;
4763
4864def config (self , ** kwargs ):
4965return self .canvas .itemconfig (self .tag_name , ** kwargs );
@@ -121,6 +137,45 @@ def on_leave(self, event):
121137self .canvas .itemconfig (self .tag_name , fill = self .normal_background );
122138self .canvas .itemconfig ("text" + self .id , fill = self .normal_foreground );
123139
140+ class CanvasSquare (CanvasWidget ):
141+
142+ def __init__ (self , canvas , x , y , width , normal_background , hovered_background ,
143+ disabled_background ):
144+
145+ # Initialize super class
146+ CanvasWidget .__init__ (self , canvas );
147+
148+ # Set color scheme for different states
149+ self .normal_background = normal_background ;
150+ self .hovered_background = hovered_background ;
151+ self .disabled_background = disabled_background ;
152+
153+ # Create the circle background
154+ canvas .create_rectangle (x - width / 2 , y - width / 2 , x + width / 2 , y + width / 2 ,
155+ fill = self .normal_background , outline = "" , tags = (self .tag_name , "oval" + self .id ));
156+
157+ # Bind events
158+ canvas .tag_bind (self .tag_name , "<Enter>" , self .on_enter );
159+ canvas .tag_bind (self .tag_name , "<Leave>" , self .on_leave );
160+ self .set_clickable (True );
161+
162+ def on_enter (self , event ):
163+ if (self .__disabled__ ):
164+ return False ;
165+ self .canvas .itemconfig (self .tag_name , fill = self .hovered_background );
166+
167+ def on_leave (self , event ):
168+ if (self .__disabled__ ):
169+ return False ;
170+ self .canvas .itemconfig (self .tag_name , fill = self .normal_background );
171+
172+ def disable (self ):
173+ super ().disable ();
174+ self .canvas .itemconfig (self .tag_name , fill = self .disabled_background );
175+
176+ def enable (self ):
177+ super ().enable ();
178+ self .canvas .itemconfig (self .tag_name , fill = self .normal_background );
124179
125180# Define a subclass of Canvas as an abstract base scene class
126181class BaseScene (tkinter .Canvas ):
@@ -157,6 +212,13 @@ def create_button(self, x, y, button_text,
157212return CanvasButton (self , x , y , button_text ,
158213normal_background , hovered_background , normal_foreground , hovered_foreground );
159214
215+ def create_square (self , x , y , width ,
216+ normal_background = C_COLOR_BLUE , hovered_background = C_COLOR_BLUE_DARK ,
217+ disabled_background = C_COLOR_BLUE_LIGHT ):
218+
219+ return CanvasSquare (self , x , y , width ,
220+ normal_background , hovered_background , disabled_background );
221+
160222def create_clickable_label (self , x , y , button_text ,
161223normal_foreground = C_COLOR_BLUE_DARK , hovered_foreground = C_COLOR_BLUE_LIGHT ):
162224
@@ -273,14 +335,19 @@ class MainGameScene(BaseScene):
273335def __init__ (self , parent ):
274336BaseScene .__init__ (self , parent );
275337
338+ # Initialize instance variables
339+ self .board_grids_power = 3 ; # Make it a 3x3 grid board
340+
276341# Create a blue arch at the bottom of the canvas
277- self .create_arc ((- 128 , C_WINDOW_HEIGHT - 128 , C_WINDOW_WIDTH + 128 , C_WINDOW_HEIGHT + 368 ),
342+ self .create_arc ((- 128 , C_WINDOW_HEIGHT - 64 , C_WINDOW_WIDTH + 128 , C_WINDOW_HEIGHT + 368 ),
278343start = 0 , extent = 180 , fill = C_COLOR_BLUE , outline = "" );
279344
280345# Create the return button
281346return_btn = self .create_button (C_WINDOW_WIDTH - 128 , 32 , "Go back" );
282347return_btn .command = self .on_return_clicked ;
283348
349+ self .draw_board (256 );
350+
284351# Draw the player_self_text
285352player_self_text = self .create_text (96 , 128 , font = "Helvetica 16" , fill = C_COLOR_BLUE_DARK , tags = ("player_self_text" ));
286353# Draw the player_match_text
@@ -297,63 +364,38 @@ def pack(self):
297364# Start a new thread to deal with the client communication
298365threading .Thread (target = self .__start_client ).start ();
299366
300- def draw_board (self , board_width , board_grid_size = 3 , board_line_width = 4 ):
367+ def draw_board (self , board_width , board_line_width = 4 ):
301368"""Draws the board at the center of the screen, parameter board_width
302369determines the size of the board, e.g. 256 would mean the board is 256x256.
303- board_grid_size determines the grid size, the default value is 3x3,
304- board_line_width determines the line width."""
370+ board_line_width determines the border line width."""
371+
372+ # Create squares for the grid board
373+ self .squares = [None ] * self .board_grids_power * self .board_grids_power ;
374+ for i in range (0 , self .board_grids_power ):
375+ for j in range (0 , self .board_grids_power ):
376+ self .squares [i + j * 3 ] = self .create_square ((C_WINDOW_WIDTH - board_width )/ 2 +
377+ board_width / self .board_grids_power * i + board_width / self .board_grids_power / 2 ,
378+ (C_WINDOW_HEIGHT - board_width )/ 2 +
379+ board_width / self .board_grids_power * j + board_width / self .board_grids_power / 2 ,
380+ board_width / self .board_grids_power );
381+ # Disable those squares to make them unclickable
382+ self .squares [i + j * 3 ].disable ();
305383
306- # Draw the board at the center of the screeen
307- for i in range (1 , board_grid_size ):
384+ # Draw the border lines
385+ for i in range (1 , self . board_grids_power ):
308386# Draw horizontal lines
309387self .create_line ((C_WINDOW_WIDTH - board_width )/ 2 ,
310- (C_WINDOW_HEIGHT - board_width )/ 2 + board_width / board_grid_size * i ,
388+ (C_WINDOW_HEIGHT - board_width )/ 2 + board_width / self . board_grids_power * i ,
311389(C_WINDOW_WIDTH + board_width )/ 2 ,
312- (C_WINDOW_HEIGHT - board_width )/ 2 + board_width / board_grid_size * i ,
390+ (C_WINDOW_HEIGHT - board_width )/ 2 + board_width / self . board_grids_power * i ,
313391fill = C_COLOR_BLUE_DARK , width = board_line_width );
314392# Draw vertical lines
315- self .create_line ((C_WINDOW_WIDTH - board_width )/ 2 + board_width / board_grid_size * i ,
393+ self .create_line ((C_WINDOW_WIDTH - board_width )/ 2 + board_width / self . board_grids_power * i ,
316394(C_WINDOW_HEIGHT - board_width )/ 2 ,
317- (C_WINDOW_WIDTH - board_width )/ 2 + board_width / board_grid_size * i ,
395+ (C_WINDOW_WIDTH - board_width )/ 2 + board_width / self . board_grids_power * i ,
318396(C_WINDOW_HEIGHT + board_width )/ 2 ,
319397fill = C_COLOR_BLUE_DARK , width = board_line_width );
320398
321- # Create an invisible rectangle on each square for events handling
322- for i in range (0 , board_grid_size ):
323- for j in range (0 , board_grid_size ):
324- rect = self .create_rectangle ((C_WINDOW_WIDTH - board_width )/ 2 +
325- board_width / board_grid_size * i + board_line_width ,
326- (C_WINDOW_HEIGHT - board_width )/ 2 +
327- board_width / board_grid_size * j + board_line_width ,
328- (C_WINDOW_WIDTH - board_width )/ 2 +
329- board_width / board_grid_size * (i + 1 ) - board_line_width ,
330- (C_WINDOW_HEIGHT - board_width )/ 2 +
331- board_width / board_grid_size * (j + 1 ) - board_line_width , fill = "" , tags = ("squares" ));
332- self .tag_bind (rect , "<Enter>" , lambda event , index = i + j * 3 : self .__on_enter_squares (event , index ));
333-
334-
335-
336- # Bind events
337- #self.tag_bind("squares", "<Enter>", self.__on_enter_squares);
338-
339- def __on_enter_squares (self , event , index ):
340- self .delete ("the_O" );
341- j = int (index / 3 );
342- i = index % 3 ;
343- print (i , j );
344- board_width = 256 ;
345- board_grid_size = 3 ;
346- board_line_width = 4 ;
347- self .create_oval ((C_WINDOW_WIDTH - board_width )/ 2 +
348- board_width / board_grid_size * i + board_line_width ,
349- (C_WINDOW_HEIGHT - board_width )/ 2 +
350- board_width / board_grid_size * j + board_line_width ,
351- (C_WINDOW_WIDTH - board_width )/ 2 +
352- board_width / board_grid_size * (i + 1 ) - board_line_width ,
353- (C_WINDOW_HEIGHT - board_width )/ 2 +
354- board_width / board_grid_size * (j + 1 ) - board_line_width
355- , fill = "" , outline = C_COLOR_BLUE_DARK , width = 4 , tags = ("the_O" ));
356-
357399def __start_client (self ):
358400# Initialize the client object
359401self .client = TTTClientGameGUI ();
@@ -395,7 +437,35 @@ def __game_started__(self):
395437self .canvas .set_notif_text ("Game started. You are the \" " + self .role + "\" " );
396438self .canvas .itemconfig ("player_self_text" , text = "Player " + str (self .player_id ));
397439self .canvas .itemconfig ("player_match_text" , text = "Player " + str (self .match_id ));
398- self .canvas .draw_board (256 );
440+
441+ def __player_move__ (self ):
442+ """(Override) Lets the user to make a move and sends it back to the
443+ server. This function might be overridden by the GUI program."""
444+ for i in range (0 , self .canvas .board_grids_power *
445+ self .canvas .board_grids_power ):
446+ # Enable those squares to make them clickable
447+ self .canvas .squares [i ].enable ();
448+ # Bind their commands
449+ self .canvas .squares [i ].command = lambda self = self , i = i : self .__move_made (i );
450+
451+ while self .canvas .squares [0 ].is_enabled ():
452+ # Wait until the user has clicked on something
453+ pass ;
454+
455+ def __move_made (self , index ):
456+ print ("User chose " + str (index + 1 ));
457+ # Send the position back to the server
458+ self .s_send ("i" , str (index + 1 ));
459+
460+ for i in range (0 , self .canvas .board_grids_power *
461+ self .canvas .board_grids_power ):
462+ # Disable those squares to make them unclickable
463+ self .canvas .squares [i ].disable ();
464+ # Remove their commands
465+ self .canvas .squares [i ].command = None ;
466+
467+
468+
399469
400470
401471# Define the main program
0 commit comments