Skip to content

Commit 4a58d3d

Browse files
author
CharmySoft
committed
Create CanvasSquare and get the board GUI working
1 parent 54f1d37 commit 4a58d3d

File tree

2 files changed

+123
-52
lines changed

2 files changed

+123
-52
lines changed

ttt_client.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,16 @@ def start_game(self):
140140
# the GUI program to interact with the user interface.
141141
self.__game_started__();
142142

143+
# Start the main loop
144+
self.__main_loop();
145+
143146
def __game_started__(self):
144147
"""(Private) This function is called when the game is getting started."""
145148
# This is a virtual function
146149
# The actual implementation is in the subclass (the GUI program)
147150
return;
148151

149-
def main_loop(self):
152+
def __main_loop(self):
150153
"""The main game loop."""
151154
while True:
152155
# Get the board content from the server
@@ -194,11 +197,11 @@ def __update_board__(self, command, board_string):
194197
# If it's this player's turn to move, print out the current
195198
# board with " " converted to the corresponding position number
196199
print("Current board:\n" + TTTClientGame.format_board(
197-
TTTClientGame.show_board_pos(board_content)));
200+
TTTClientGame.show_board_pos(board_string)));
198201
else:
199202
# Print out the current board
200203
print("Current board:\n" + TTTClientGame.format_board(
201-
board_content));
204+
board_string));
202205

203206
def __player_move__(self):
204207
"""(Private) Lets the user input the move and sends it back to the
@@ -278,8 +281,6 @@ def main():
278281
try:
279282
# Start the game
280283
client.start_game();
281-
# Start the main loop
282-
client.main_loop();
283284
except:
284285
print(("Game finished unexpectedly!"));
285286
raise;

ttt_client_gui.py

Lines changed: 117 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,34 @@ def __init__(self, canvas):
3232
# Generate a unique tag for each widget
3333
self.tag_name = self.__class__.__name__ + self.id;
3434

35+
# Initialize instance variables
36+
self.__disabled__ = False;
37+
3538
def set_clickable(self, clickable):
3639
if(clickable):
3740
self.canvas.tag_bind(self.tag_name, "<Button-1>", self.on_click);
3841
else:
3942
self.canvas.tag_unbind(self.tag_name);
4043

4144
def on_click(self, event):
45+
if(self.__disabled__):
46+
return False;
4247
try:
4348
self.command();
49+
return True;
4450
except AttributeError:
4551
print("Error: " + self.__class__.__name__ + " " + self.id + " does not have a command");
4652
raise;
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

4864
def config(self, **kwargs):
4965
return self.canvas.itemconfig(self.tag_name, **kwargs);
@@ -121,6 +137,45 @@ def on_leave(self, event):
121137
self.canvas.itemconfig(self.tag_name, fill=self.normal_background);
122138
self.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
126181
class BaseScene(tkinter.Canvas):
@@ -157,6 +212,13 @@ def create_button(self, x, y, button_text,
157212
return CanvasButton(self, x, y, button_text,
158213
normal_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+
160222
def create_clickable_label(self, x, y, button_text,
161223
normal_foreground=C_COLOR_BLUE_DARK, hovered_foreground=C_COLOR_BLUE_LIGHT):
162224

@@ -273,14 +335,19 @@ class MainGameScene(BaseScene):
273335
def __init__(self, parent):
274336
BaseScene.__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),
278343
start=0, extent=180, fill=C_COLOR_BLUE, outline="");
279344

280345
# Create the return button
281346
return_btn = self.create_button(C_WINDOW_WIDTH - 128, 32, "Go back");
282347
return_btn.command = self.on_return_clicked;
283348

349+
self.draw_board(256);
350+
284351
# Draw the player_self_text
285352
player_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
298365
threading.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
302369
determines 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
309387
self.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,
313391
fill=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,
319397
fill=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-
357399
def __start_client(self):
358400
# Initialize the client object
359401
self.client = TTTClientGameGUI();
@@ -395,7 +437,35 @@ def __game_started__(self):
395437
self.canvas.set_notif_text("Game started. You are the \"" + self.role + "\"");
396438
self.canvas.itemconfig("player_self_text", text="Player " + str(self.player_id));
397439
self.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

Comments
 (0)