pygo/widgets/gogame.py

267 lines
8.3 KiB
Python
Raw Normal View History

# This is a widget that handles a go game
# It needs a goban object, and handles it from beginning to end
import os
import gtk
import goban
class GoGame(gtk.HBox):
"""
This class draws a board and some information about the game (turn, captures, etc).
It also handles moves and other operations on the goban.
"""
img_res = None
def __init__(self, goban, network_mode=False, our_color=None):
super(GoGame, self).__init__()
self.goban = goban
if GoGame.img_res is None:
GoGame.img_res = _build_img_res()
self.init_widgets()
def init_widgets(self):
# All of this is to create the info box along the right side of the board
info_box = gtk.VBox()
info_rows = []
for i in range(3):
info_rows.append(gtk.HBox())
to_move_label = gtk.Label('To Move:')
black_cap_label = gtk.Label('Black Captures:')
white_cap_label = gtk.Label('White Captures:')
self.to_move_value = gtk.Label('None')
self.black_cap_value = gtk.Label('0')
self.white_cap_value = gtk.Label('0')
info_rows[0].pack_start(to_move_label, expand=False, padding=5)
info_rows[1].pack_start(black_cap_label, expand=False, padding=5)
info_rows[2].pack_start(white_cap_label, expand=False, padding=5)
info_rows[0].pack_end(self.to_move_value, expand=False, padding=5)
info_rows[1].pack_end(self.black_cap_value, expand=False, padding=5)
info_rows[2].pack_end(self.white_cap_value, expand=False, padding=5)
for row in info_rows:
info_box.pack_start(row, expand=False)
self.pass_button = gtk.Button('Pass')
self.resign_button = gtk.Button('Resign')
self.pass_button.connect('clicked', self.on_pass)
self.resign_button.connect('clicked', self.on_resign)
info_box.pack_start(self.pass_button, expand=False, padding=10)
info_box.pack_start(self.resign_button, expand=False, padding=10)
2012-04-17 22:07:04 +00:00
self.board_area = gtk.DrawingArea()
self.board_area.set_size_request(800, 800)
self.board_area.set_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.POINTER_MOTION_HINT_MASK |
gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.BUTTON_RELEASE_MASK)
self.board_area.connect('expose-event', self.do_board_expose)
self.board_area.connect('configure-event', self.do_board_configure)
self.board_area.connect('motion-notify-event', self.do_hover)
self.board_area.connect('button-press-event', self.do_play)
2012-04-17 22:07:04 +00:00
self.pack_start(self.board_area, expand=False)
self.pack_end(info_box, expand=False)
self.update_info()
def do_hover(self, widget, event):
x = event.x
y = event.y
width, height = widget.size_request()
size = min(width, height)
board_size = self.goban.board_size
inc = size / board_size
row = int(y / inc)
col = int(x / inc)
old_hover = self.goban.hover
if max(row,col) < board_size:
if self.goban._real_pos((row,col)) != self.goban.hover:
self.goban.set_hover((row,col))
self.update_board((old_hover, self.goban.hover))
else:
self.goban.clear_hover()
self.update_board([old_hover])
def do_play(self, widget, event):
x = event.x
y = event.y
width, height = widget.size_request()
size = min(width, height)
board_size = self.goban.board_size
inc = size / board_size
row = int(y / inc)
col = int(x / inc)
changed = self.goban.play_move((row,col))
self.update(changed)
def do_board_configure(self, widget, event):
2012-04-17 22:07:04 +00:00
gc = widget.get_style().fg_gc[gtk.STATE_NORMAL]
x,y,width,height = widget.get_allocation()
self.board_backbuf = gtk.gdk.Pixmap(widget.window, width, height, depth=-1)
self.board_backbuf.draw_rectangle(widget.get_style().white_gc, True, 0, 0, width, height)
2012-04-18 15:37:06 +00:00
self.update()
return True
def do_board_expose(self, widget, event):
x , y, width, height = event.area
widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],
self.board_backbuf, x, y, x, y, width, height)
return False
def update_board(self, to_update=None):
if to_update == None:
to_update = range(len(self.goban.board))
gc = self.board_area.get_style().fg_gc[gtk.STATE_NORMAL]
# this is a bit of a hack...
width, height = self.board_area.get_size_request()
2012-04-17 22:07:04 +00:00
size = min(width, height)
board_size = self.goban.board_size
inc = size / board_size
for i in to_update:
if i is None:
continue
2012-04-17 22:07:04 +00:00
# Get the right image and scale it
base_img = GoGame.img_res[self.goban.draw_code(i)]
img = base_img.scale_simple(inc, inc, gtk.gdk.INTERP_BILINEAR)
row = i / board_size
col = i % board_size
self.board_backbuf.draw_pixbuf(gc, img, 0, 0, inc * col, inc * row, inc, inc)
if i == self.goban.hover:
if self.goban.to_move == goban.Goban.BLACK:
code = 'bH'
elif self.goban.to_move == goban.Goban.WHITE:
code = 'wH'
base_img = GoGame.img_res[code]
img = base_img.scale_simple(inc, inc, gtk.gdk.INTERP_BILINEAR)
self.board_backbuf.draw_pixbuf(gc, img, 0, 0, inc * col, inc * row, inc, inc)
self.board_area.queue_draw()
def on_pass(self, widget):
changed = self.goban.pass_move()
self.update(changed)
def on_resign(self, widget):
changed = self.goban.resign()
self.update(changed)
# fixme: Add a widget to show the outcome
def update_info(self):
if self.goban.to_move == goban.Goban.BLACK:
move = 'Black'
elif self.goban.to_move == goban.Goban.WHITE:
move = 'White'
else:
move = 'None'
self.to_move_value.set_text(move)
self.black_cap_value.set_text(str(self.goban.black_captures))
self.white_cap_value.set_text(str(self.goban.white_captures))
2012-04-18 15:35:38 +00:00
def update(self, changed=None):
self.update_info()
self.update_board(changed)
def _magnitude(vector):
x,y = vector
return math.sqrt(x*x + y*y)
def _build_img_res():
ret = {}
triangle = _load_png('go_t.png')
circle = _load_png('go_c.png')
ret['wH'] = _load_png('go_wH.png')
ret['bH'] = _load_png('go_bH.png')
ret['wT'] = _load_png('go_w.png')
width = ret['wT'].get_width()
height = ret['wT'].get_height()
triangle.composite(ret['wT'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255)
ret['bT'] = _load_png('go_b.png')
width = ret['bT'].get_width()
height = ret['bT'].get_height()
triangle.composite(ret['bT'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255)
for d in ('u', 'd', 'l', 'r', 'm', 'dl', 'dr', 'ul', 'ur', 'h', 'w', 'b'):
ret[d] = _load_png('go_' + d + '.png')
ret[d + 'C'] = _load_png('go_' + d + '.png')
width = ret[d + 'C'].get_width()
height = ret[d + 'C'].get_height()
circle.composite(ret[d + 'C'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255)
return ret
def _load_png(name):
""" Load image and return image object"""
fullname = os.path.join('ui/res/', name)
image = gtk.gdk.pixbuf_new_from_file(fullname)
# if alpha is not None:
# image = _set_alpha(image, alpha)
return image
# _trans_png = None
# def _set_alpha(image, alpha):
# """
# change_opacity - changes the opacity of pixbuf by combining
# the pixbuf with a pixbuf derived from a transparent .png
# returns: a pixbuf of image made alpha more composite
# """
# global _trans_png
# if _trans_png == None:
# _trans_png = _load_png('transparent.png')
# width = image.get_width()
# height = image.get_height()
# trans= _trans_png.scale_simple(width,height,gtk.gdk.INTERP_NEAREST)
# trans.composite(image, 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, alpha)
# return trans