From: <umg...@us...> - 2006-09-20 10:56:56
|
Revision: 343 http://svn.sourceforge.net/pybridge/?rev=343&view=rev Author: umgangee Date: 2006-09-20 03:56:47 -0700 (Wed, 20 Sep 2006) Log Message: ----------- Rewritten CardArea widget for Cairo. Introduction of a "canvas" layer, which manages positioning and redrawing of graphics. Modifications to WindowBridgetable in line with CardArea. Modified Paths: -------------- trunk/pybridge/pybridge/ui/cardarea.py trunk/pybridge/pybridge/ui/window_bridgetable.py Added Paths: ----------- trunk/pybridge/pybridge/ui/canvas.py Added: trunk/pybridge/pybridge/ui/canvas.py =================================================================== --- trunk/pybridge/pybridge/ui/canvas.py (rev 0) +++ trunk/pybridge/pybridge/ui/canvas.py 2006-09-20 10:56:47 UTC (rev 343) @@ -0,0 +1,160 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2006 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +import gtk +import cairo + +from pybridge.environment import environment + + +class CairoCanvas(gtk.DrawingArea): + """Provides a simple canvas layer for . + + Overlapping items. + """ + + background_path = environment.find_pixmap('baize.png') + background = cairo.ImageSurface.create_from_png(background_path) + pattern = cairo.SurfacePattern(background) + pattern.set_extend(cairo.EXTEND_REPEAT) + + + def __init__(self): + super(CairoCanvas, self).__init__() # Initialise parent. + + self.items = {} + + # Set up gtk.Widget signals. + self.connect('configure_event', self.configure) + self.connect('expose_event', self.expose) + + + def clear(self): + """Clears all items from canvas.""" + self.items = {} # Remove all item references. + + # Redraw background pattern on backing. + width, height = self.window.get_size() + context = cairo.Context(self.backing) + context.rectangle(0, 0, width, height) + context.set_source(self.pattern) + context.paint() + self.window.invalidate_rect((0, 0, width, height), False) # Expose. + + + def add_item(self, id, source, xy, z_index): + """Places source item into items list. + + @param id: unique identifier for source. + @param source: ImageSurface. + @param xy: function providing (x, y) coords for source in backing. + @param z_index: integer. + """ + pos_x, pos_y = xy(*self.window.get_size()) + area = (pos_x, pos_y, source.get_width(), source.get_height()) + self.items[id] = {'source': source, 'area': area, + 'xy' : xy, 'z-index': z_index, } + self.redraw(*area) + + + def remove_item(self, id): + """Removes source item with identifier from items list. + + @param id: unique identifier for source. + """ + if self.items.get(id): + area = self.items[id]['area'] + del self.item[id] + self.redraw(*area) + + + def update_item(self, id, source=None, xy=None, z_index=0): + """ + + """ + # If optional parameters are not specified, use previous values. + source = source or self.items[id]['source'] + xy = xy or self.items[id]['xy'] + z_index = z_index or self.items[id]['z-index'] + + oldarea = self.items[id]['area'] # Current position of item. + pos_x, pos_y = xy(*self.window.get_size()) + area = (pos_x, pos_y, source.get_width(), source.get_height()) + if area != oldarea: # If position has changed, clear previous area. + del self.items[id] + self.redraw(*oldarea) + self.items[id] = {'source': source, 'area': area, + 'xy' : xy, 'z-index': z_index, } + self.redraw(*area) + + + def redraw(self, x, y, width, height): + """Redraws sources in area (x, y, width, height) to backing canvas. + + @param x: + @param y: + @param width: + @param height: + """ + context = cairo.Context(self.backing) + context.rectangle(x, y, width, height) + context.clip() # Set clip region. + + # Redraw background pattern in area. + context.set_source(self.pattern) + context.paint() + + # Build list of sources to redraw in area, in order of z-index. + # TODO: Find sources which intersect with area. + area = gtk.gdk.Rectangle(x, y, width, height) + items = self.items.values() + items.sort(lambda i, j : cmp(i['z-index'], j['z-index']), reverse=True) + + for item in items: + pos_x, pos_y = item['area'][0:2] + context.set_source_surface(item['source'], pos_x, pos_y) + context.paint() + + context.reset_clip() + self.window.invalidate_rect((x, y, width, height), False) # Expose. + + + + def configure(self, widget, event): + width, height = self.window.get_size() + self.backing = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + + # Recalculate position of all items. + for id, item in self.items.items(): + pos_x, pos_y = item['xy'](width, height) + area = (pos_x, pos_y, item['area'][2], item['area'][3]) + self.items[id]['area'] = area + + self.redraw(0, 0, width, height) # Full redraw required. + return True # Expected to return True. + + + def expose(self, widget, event): + context = widget.window.cairo_create() + context.rectangle(*event.area) + context.clip() # Only redraw the exposed area. + context.set_source_surface(self.backing, 0, 0) + context.paint() + context.reset_clip() + return False # Expected to return False. + Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2006-08-16 14:02:03 UTC (rev 342) +++ trunk/pybridge/pybridge/ui/cardarea.py 2006-09-20 10:56:47 UTC (rev 343) @@ -16,13 +16,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import gc, gtk +import gtk +import cairo from pybridge.environment import environment +from canvas import CairoCanvas -BACKGROUND_PATH = environment.find_pixmap("baize.png") -CARD_MASK_PATH = environment.find_pixmap("bonded.png") - from pybridge.bridge.card import Card, Rank, Suit from pybridge.bridge.deck import Seat @@ -35,73 +34,108 @@ # The red-black-red-black ordering convention. RED_BLACK = [Suit.Diamond, Suit.Club, Suit.Heart, Suit.Spade] -BORDER_X = BORDER_Y = 12 +class CardArea(CairoCanvas): + """This widget. -class CardArea(gtk.DrawingArea): + This widget uses Cairo and requires >= GTK 2.8. + """ - # TODO: when PyGTK 2.8 becomes widespread, adapt this module to Cairo. + # Load card mask. + card_mask_path = environment.find_pixmap('bonded.png') + card_mask = cairo.ImageSurface.create_from_png(card_mask_path) + + border_x = border_y = 10 + card_width = card_mask.get_width() / 13 + card_height = card_mask.get_height() / 5 + spacing_x = int(card_width * 0.4) + spacing_y = int(card_height * 0.2) - # Load table background and card pixbufs. - background = gtk.gdk.pixbuf_new_from_file(BACKGROUND_PATH).render_pixmap_and_mask()[0] - card_mask = gtk.gdk.pixbuf_new_from_file_at_size(CARD_MASK_PATH, 1028, 615) - def __init__(self): - gtk.DrawingArea.__init__(self) + super(CardArea, self).__init__() # Initialise parent. - self.backing = None # Placeholder for backing pixbuf. - self.hand_pixbufs = {} # Hand pixbufs, keyed by seat. - self.hand_coords = {} # Positions of hand pixbufs on backing. - self.card_coords = {} # Positions of cards on hand pixbufs. - self.trick_pixbuf = None # + # To receive card clicked events, override this with external method. + self.on_card_clicked = lambda card, seat: True - # Expect cards of unit size 13 x 5. - self.card_width = self.card_mask.get_width() / 13 - self.card_height = self.card_mask.get_height() / 5 - self.spacing_x = int(self.card_width * 0.4) - self.spacing_y = int(self.card_height * 0.2) + self.hands = {} + self.trick = None + self.set_seat_mapping(Seat.South) - # Method to call when a card is clicked. - self.on_card_clicked = None - - # Set up events. - self.connect('configure_event', self.configure) - self.connect('expose_event', self.expose) - self.connect('button_press_event', self.button_press) - self.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.connect('button_release_event', self.button_release) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK) - def draw_card(self, dest_pixbuf, pos_x, pos_y, card): - """Draws graphic of specified card to dest_pixbuf at (pos_x, pos_y).""" - if isinstance(card, Card): # Determine coordinates of graphic in card_mask. + def draw_card(self, context, pos_x, pos_y, card): + """Draws graphic of specified card to context at (pos_x, pos_y). + + @param context: a cairo.Context + @param pos_x: + @param pos_y: + @param card: the Card to draw. + """ + if isinstance(card, Card): # Determine coordinates of card graphic. src_x = CARD_MASK_RANKS.index(card.rank) * self.card_width src_y = CARD_MASK_SUITS.index(card.suit) * self.card_height else: # Draw a face-down card. src_x, src_y = self.card_width*2, self.card_height*4 - self.card_mask.copy_area(src_x, src_y, self.card_width, self.card_height, - dest_pixbuf, pos_x, pos_y) + context.rectangle(pos_x, pos_y, self.card_width, self.card_height) + context.clip() + context.set_source_surface(self.card_mask, pos_x-src_x, pos_y-src_y) + context.paint() + context.reset_clip() - def build_hand(self, seat, hand, facedown=False, omit=[]): - """Builds and saves a pixbuf of card images. + def set_hand(self, hand, seat, facedown=False, omit=[]): + """Sets the hand of player at seat. + Draws representation of cards in hand to context. - @param seat: the seat of player holding hand. - @param hand: a list of Card objects representing the hand. - @param facedown: if True, card elements of hand are drawn face down. - @param omit: a list of Card objects in hand not to draw. + The hand is buffered into an ImageSurface, since hands change + infrequently and multiple calls to draw_card() are expensive. + + @param hand: a list of Card objects. + @param seat: a member of Seat. + @param facedown: if True, cards are drawn face-down. + @param omit: a list of elements of hand not to draw. """ - # Filters out cards in hand that appear in omit. - ef = lambda hand: [(i, c) for i, c in enumerate(hand) if c not in omit] - - coords = [] + # TODO: coords should be dict (card : (pos_x, pos_y)), but this breaks when hashing. + def get_coords_for_hand(): + coords = [] + if seat in (self.TOP, self.BOTTOM): + pos_y = 0 + if facedown is True: # Draw cards in one continuous row. + for index, card in enumerate(hand): + pos_x = index * self.spacing_x + coords.append((card, pos_x, pos_y)) + else: # Insert a space between each suit. + spaces = sum([1 for suitcards in suits.values() if len(suitcards) > 0]) - 1 + for index, card in enumerate(hand): + # Insert a space for each suit in hand which appears before this card's suit. + insert = sum([1 for suit, suitcards in suits.items() if len(suitcards) > 0 + and RED_BLACK.index(card.suit) > RED_BLACK.index(suit)]) + pos_x = (index + insert) * self.spacing_x + coords.append((card, pos_x, pos_y)) + else: # LEFT or RIGHT. + if facedown is True: # Wrap cards to a 4x4 grid. + for index, card in enumerate(hand): + adjust = seat is self.RIGHT and index == 12 and 3 + pos_x = ((index % 4) + adjust) * self.spacing_x + pos_y = (index / 4) * self.spacing_y + coords.append((card, pos_x, pos_y)) + else: + longest = max([len(cards) for cards in suits.values()]) + for index, card in enumerate(hand): + adjust = seat is self.RIGHT and longest - len(suits[card.suit]) + pos_x = (suits[card.suit].index(card) + adjust) * self.spacing_x + pos_y = RED_BLACK.index(card.suit) * self.spacing_y + coords.append((card, pos_x, pos_y)) + return coords + if facedown is False: # Split hand into suits. - suits = {} - for suit in Suit: - suits[suit] = [] + suits = dict([(suit, []) for suit in Suit]) for card in hand: suits[card.suit].append(card) # Sort suits. @@ -111,178 +145,147 @@ hand = [] for suit in RED_BLACK: hand.extend(suits[suit]) + + saved = self.hands.get(seat) + if saved and saved['hand'] == hand: + # If hand has been set previously, do not recalculate coords. + coords = saved['coords'] + else: + coords = get_coords_for_hand() - if seat in (Seat.North, Seat.South): - height = self.card_height # Draw cards in one continuous row. - pos_y = 0 - - if facedown is True: - width = self.card_width + (self.spacing_x * 12) - for index, card in ef(hand): - pos_x = index * self.spacing_x - coords.append((card, pos_x, pos_y)) - - else: # Insert a space between each suit. - spaces = sum([1 for suitcards in suits.values() if len(suitcards) > 0]) - 1 - width = self.card_width + (self.spacing_x * (12 + spaces)) - for index, card in ef(hand): - # Insert a space for each suit in hand which appears before this card's suit. - insert = sum([1 for suit, suitcards in suits.items() if len(suitcards) > 0 - and RED_BLACK.index(card.suit) > RED_BLACK.index(suit)]) - pos_x = (index + insert) * self.spacing_x - coords.append((card, pos_x, pos_y)) + # Determine dimensions of hand. + width = max([x for card, x, y in coords]) + self.card_width + height = max([y for card, x, y in coords]) + self.card_height + # Create new ImageSurface for hand. + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + # Clear ImageSurface - in Cairo 1.2+, this is done automatically. + if cairo.version_info < (1, 2): + context.set_operator(cairo.OPERATOR_CLEAR) + context.paint() + context.set_operator(cairo.OPERATOR_OVER) # Restore. - else: # West or East. - if facedown is True: # Wrap cards to a 4x4 grid. - width = self.card_width + (self.spacing_x * 3) - height = self.card_height + (self.spacing_y * 3) - for index, card in ef(hand): - adjust = seat is Seat.East and index == 12 and 3 - pos_x = ((index % 4) + adjust) * self.spacing_x - pos_y = (index / 4) * self.spacing_y - coords.append((card, pos_x, pos_y)) - - else: - longest = max([len(cards) for cards in suits.values()]) - width = self.card_width + (self.spacing_x * (longest - 1)) - height = self.card_height + (self.spacing_y * (len(suits) - 1)) - for index, card in ef(hand): - adjust = seat is Seat.East and longest - len(suits[card.suit]) - pos_x = (suits[card.suit].index(card) + adjust) * self.spacing_x - pos_y = RED_BLACK.index(card.suit) * self.spacing_y - coords.append((card, pos_x, pos_y)) + # Draw cards to surface. + visible = [(i, card) for i, card in enumerate(hand) if card not in omit] + for i, card in visible: + pos_x, pos_y = coords[i][1:] + self.draw_card(context, pos_x, pos_y, card) - self.hand_pixbufs[seat] = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width, height) - self.hand_pixbufs[seat].fill(0x00000000) # Clear pixbuf. + # Save + self.hands[seat] = {'hand' : hand, 'visible' : visible, + 'surface' : surface, 'coords' : coords, } - # Draw cards to pixbuf. - for card, pos_x, pos_y in coords: - self.draw_card(self.hand_pixbufs[seat], pos_x, pos_y, card) - self.card_coords[seat] = coords - - - def draw_hand(self, seat): - """""" - x, y, width, height = self.get_allocation() + # + if seat is self.TOP: + xy = lambda w, h: ((w - width)/2, self.border_y) + elif seat is self.RIGHT: + xy = lambda w, h: ((w - width - self.border_x), (h - height)/2) + elif seat is self.BOTTOM: + xy = lambda w, h: ((w - width)/2, (h - height - self.border_y)) + elif seat is self.LEFT: + xy = lambda w, h: (self.border_x, (h - height)/2) - # Determine coordinates in backing pixmap to draw hand pixbuf. - hand_pixbuf = self.hand_pixbufs[seat] - hand_width = hand_pixbuf.get_width() - hand_height = hand_pixbuf.get_height() - - if seat is Seat.North: - pos_x = (width - hand_width) / 2 - pos_y = BORDER_Y - elif seat is Seat.East: - pos_x = width - hand_width - BORDER_X - pos_y = (height - hand_height) / 2 - elif seat is Seat.South: - pos_x = (width - hand_width) / 2 - pos_y = height - hand_height - BORDER_Y - elif seat is Seat.West: - pos_x = BORDER_X - pos_y = (height - hand_height) / 2 - - # Draw hand pixbuf and save hand coordinates. - self.backing.draw_rectangle(self.backingGC, True, pos_x, pos_y, hand_width, hand_height) - self.backing.draw_pixbuf(self.backingGC, hand_pixbuf, 0, 0, pos_x, pos_y) - self.hand_coords[seat] = (pos_x, pos_y, pos_x+hand_width, pos_y+hand_height) - - # Redraw modified area of backing pixbuf. - self.window.invalidate_rect((pos_x, pos_y, hand_width, hand_height), False) + id = 'hand-%s' % seat # Identifier for this item. + if id in self.items: + self.update_item(id, source=surface, xy=xy) + else: + self.add_item(id, surface, xy, 0) - def build_trick(self, trick): - """Builds and saves a pixbuf of trick.""" - width = self.card_width + self.spacing_x*2 - height = self.card_height + self.spacing_y*2 - self.trick_pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width, height) - self.trick_pixbuf.fill(0x00000000) # Clear pixbuf. - - # When called, returns (x, y) start point to draw card. - coords = {Seat.North : lambda: ((width-self.card_width)/2, 0), - Seat.East : lambda: (width-self.card_width, (height-self.card_height)/2), - Seat.South : lambda: ((width-self.card_width)/2, height-self.card_height), - Seat.West : lambda: (0, (height-self.card_height)/2), } + def set_seat_mapping(self, focus=Seat.South): + """Sets the mapping between seats at table and positions of hands. - # Draw cards in order of play, to permit overlapping of cards. - leader, cards = trick - seatorder = Seat[leader.index:] + Seat[:leader.index] - for seat in [s for s in seatorder if s in cards]: - pos_x, pos_y = coords[seat]() - self.draw_card(self.trick_pixbuf, pos_x, pos_y, cards[seat]) + @param focus: the Seat to be drawn "closest" to the observer. + """ + # Assumes Seat elements are ordered clockwise from North. + order = Seat[focus.index:] + Seat[:focus.index] + for seat, attr in zip(order, ('BOTTOM', 'LEFT', 'TOP', 'RIGHT')): + setattr(self, attr, seat) + # TODO: set seat labels. - def draw_trick(self): - """""" - x, y, width, height = self.get_allocation() + def set_trick(self, trick): + """Sets the current trick. + Draws representation of current trick to context. - trick_width = self.trick_pixbuf.get_width() - trick_height = self.trick_pixbuf.get_height() - pos_x = (width - trick_width) / 2 - pos_y = (height - trick_height) / 2 - self.backing.draw_rectangle(self.backingGC, True, pos_x, pos_y, trick_width, trick_height) - self.backing.draw_pixbuf(self.backingGC, self.trick_pixbuf, 0, 0, pos_x, pos_y) + @param trick: a (leader, cards_played) pair. + """ + width, height = 200, 200 + # (x, y) positions to fit within (width, height) bound box. + pos = {Seat.North : ((width - self.card_width)/2, 0 ), + Seat.East : ((width - self.card_width), (height - self.card_height)/2 ), + Seat.South : ((width - self.card_width)/2, (height - self.card_height) ), + Seat.West : (0, (height - self.card_height)/2 ), } - # Redraw modified area of backing pixbuf. - self.window.invalidate_rect((pos_x, pos_y, trick_width, trick_height), False) + # Create new ImageSurface for trick. + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + # Clear ImageSurface - in Cairo 1.2+, this is done automatically. + if cairo.version_info < (1, 2): + context.set_operator(cairo.OPERATOR_CLEAR) + context.paint() + context.set_operator(cairo.OPERATOR_OVER) # Restore. + + if trick: + leader, cards_played = trick + # The order of play is the leader, then clockwise around Seat. + for seat in Seat[leader.index:] + Seat[:leader.index]: + card = cards_played.get(seat) + if card: + pos_x, pos_y = pos[seat] + self.draw_card(context, pos_x, pos_y, card) + + id = 'trick' + if id in self.items: + self.update_item(id, source=surface) + else: + xy = lambda w, h: ((w - width)/2, (h - height)/2) + self.add_item(id, surface, xy, 0) - def clear(self): - """Clears backing pixmap and pixbufs.""" - self.hand_pixbufs = {} - self.trick_pixbuf = None - x, y, width, height = self.get_allocation() - self.backing.draw_rectangle(self.backingGC, True, 0, 0, width, height) - self.window.invalidate_rect((0, 0, width, height), False) - - def configure(self, widget, event): - """Creates backing pixmap of the appropriate size, containing pixbufs.""" - x, y, width, height = widget.get_allocation() - self.backing = gtk.gdk.Pixmap(widget.window, width, height) - self.backingGC = gtk.gdk.GC(self.backing, fill=gtk.gdk.TILED, tile=self.background) - self.backing.draw_rectangle(self.backingGC, True, 0, 0, width, height) + def set_turn(self, seat): + """ - for seat in self.hand_pixbufs: - self.draw_hand(seat) - if self.trick_pixbuf: - self.draw_trick() + @param seat: a member of Seat. + """ +# hand_surface = self.hands[seat]['surface'] +# x = self.hands[seat]['xy'] - 10 +# y = self.hands[seat]['xy'] - 10 +# width = surface.get_width() + 20 +# height = surface.get_height() + 20 +# args = ('turn', surface, pos_x, pos_y, -1) +# if self.items.get('turn'): +# self.update_item(*args) +# else: +# self.add_item(*args) - gc.collect() # Manual garbage collection. See PyGTK FAQ, section 8.4. - return True # Configure event is expected to return true. - - def expose(self, widget, event): - """Redraws card table widget from the backing pixmap.""" - x, y, width, height = event.area - gc = widget.get_style().bg_gc[gtk.STATE_NORMAL] - widget.window.draw_drawable(gc, self.backing, x, y, x, y, width, height) - return False # Expose event is expected to return false. - - - def button_press(self, widget, event): - """Determines which card was clicked, then calls external handler method.""" + def button_release(self, widget, event): + """Determines if a card was clicked: if so, calls card_selected.""" + if event.button == 1: + found_hand = False + + # Determine the hand which was clicked. + for seat in self.hands: + card_coords = self.hands[seat]['coords'] + surface = self.hands[seat]['surface'] + hand_x, hand_y = self.items['hand-%s' % seat]['area'][0:2] + if (hand_x <= event.x <= hand_x + surface.get_width()) and \ + (hand_y <= event.y <= hand_y + surface.get_height()): + found_hand = True + break + + if found_hand: + # Determine the card in hand which was clicked. + pos_x, pos_y = event.x - hand_x, event.y - hand_y + # Iterate through visible cards backwards. + for i, card in self.hands[seat]['visible'][::-1]: + x, y = card_coords[i][1:] + if (x <= pos_x <= x + self.card_width) and \ + (y <= pos_y <= y + self.card_height): + self.on_card_clicked(card, seat) + break - def get_hand_xy(): - for seat, hand in self.hand_coords.items(): - start_x, start_y, finish_x, finish_y = hand - if (start_x <= event.x <= finish_x) and (start_y <= event.y <= finish_y): - return seat, start_x, start_y - - def get_card_in_hand(seat, start_x, start_y): - pos_x = event.x - start_x - pos_y = event.y - start_y - for card, x, y in self.card_coords[seat][::-1]: # Iterate backwards. - if (x <= pos_x <= x+self.card_width) and (y <= pos_y <= y+self.card_height): - return card - - if event.button == 1 and self.on_card_clicked: - hand_xy = get_hand_xy() - if hand_xy: - card = get_card_in_hand(*hand_xy) - if isinstance(card, Card): - self.on_card_clicked(card, hand_xy[0]) # External handler. - - return True # Button press event is expected to return true. + return True # Expected to return True. Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2006-08-16 14:02:03 UTC (rev 342) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2006-09-20 10:56:47 UTC (rev 343) @@ -35,7 +35,7 @@ CALLTYPE_SYMBOLS = {Pass : _('pass'), Double : _('dbl'), Redouble : _('rdbl') } -LEVEL_SYMBOLS = {Level.One : _('1'), Level.Two : _('2'), Level.Three : _('3'), +LEVEL_SYMBOLS = {Level.One : _('1'), Level.Two : _('2'), Level.Three : _('3'), Level.Four : _('4'), Level.Five : _('5'), Level.Six : _('6'), Level.Seven : _('7'), } @@ -191,7 +191,7 @@ def resetGame(self): """Clears bidding history, contract, trick counts.""" - self.cardarea.clear() +# self.cardarea.clear() self.call_store.clear() # Reset bidding history. self.trick_store.clear() # Reset trick history. self.setContract(None) # Reset contract. @@ -263,28 +263,22 @@ if hand: # Own or known hand. if all is True: # Show all cards. played = [] - self.cardarea.build_hand(position, hand, omit=played) + self.cardarea.set_hand(hand, position, omit=played) else: # Unknown hand: draw cards face down, use integer placeholders. - cards = range(13) - played = range(len(played)) - self.cardarea.build_hand(position, cards, facedown=True, omit=played) - - self.cardarea.draw_hand(position) + cards, played = range(13), range(len(played)) + self.cardarea.set_hand(cards, position, facedown=True, omit=played) - def redrawTrick(self, trick=None): + def redrawTrick(self): """Redraws trick. @param table: @param trick: """ - # TODO: this cannot be called until playing in progress - # perhaps put a clear() method in cardarea? - if trick is None: + trick = None + if self.table.game.playing: trick = self.table.game.playing.getCurrentTrick() - - self.cardarea.build_trick(trick) - self.cardarea.draw_trick() + self.cardarea.set_trick(trick) def setTurnIndicator(self): @@ -391,6 +385,7 @@ self.setDealer(table.dealer) self.setVuln(table.game.vulnNS, table.game.vulnEW) + self.redrawTrick() for position in table.game.deal: self.redrawHand(position) if table.seated: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2006-09-20 15:59:46
|
Revision: 345 http://svn.sourceforge.net/pybridge/?rev=345&view=rev Author: umgangee Date: 2006-09-20 08:59:34 -0700 (Wed, 20 Sep 2006) Log Message: ----------- Added turn indicator to CardArea. Fixed a couple of bugs. Modified Paths: -------------- trunk/pybridge/pybridge/ui/canvas.py trunk/pybridge/pybridge/ui/cardarea.py trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/canvas.py =================================================================== --- trunk/pybridge/pybridge/ui/canvas.py 2006-09-20 11:00:26 UTC (rev 344) +++ trunk/pybridge/pybridge/ui/canvas.py 2006-09-20 15:59:34 UTC (rev 345) @@ -79,7 +79,7 @@ """ if self.items.get(id): area = self.items[id]['area'] - del self.item[id] + del self.items[id] self.redraw(*area) @@ -123,7 +123,7 @@ # TODO: Find sources which intersect with area. area = gtk.gdk.Rectangle(x, y, width, height) items = self.items.values() - items.sort(lambda i, j : cmp(i['z-index'], j['z-index']), reverse=True) + items.sort(lambda i, j : cmp(i['z-index'], j['z-index'])) for item in items: pos_x, pos_y = item['area'][0:2] Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2006-09-20 11:00:26 UTC (rev 344) +++ trunk/pybridge/pybridge/ui/cardarea.py 2006-09-20 15:59:34 UTC (rev 345) @@ -208,8 +208,14 @@ """Sets the current trick. Draws representation of current trick to context. - @param trick: a (leader, cards_played) pair. + @param trick: a (leader, cards_played) pair, or None. """ + id = 'trick' + self.trick = trick + if trick is None: + self.remove_item(id) + return + width, height = 200, 200 # (x, y) positions to fit within (width, height) bound box. pos = {Seat.North : ((width - self.card_width)/2, 0 ), @@ -235,7 +241,6 @@ pos_x, pos_y = pos[seat] self.draw_card(context, pos_x, pos_y, card) - id = 'trick' if id in self.items: self.update_item(id, source=surface) else: @@ -244,21 +249,36 @@ - def set_turn(self, seat): + def set_turn(self, turn): + """Sets the turn indicator. + + The turn indicator is displayed as a rounded rectangle around + the hand matching the specified seat. + + @param turn: a member of Seat, or None. """ + id = 'turn' + if turn is None: + self.remove_item(id) + return - @param seat: a member of Seat. - """ -# hand_surface = self.hands[seat]['surface'] -# x = self.hands[seat]['xy'] - 10 -# y = self.hands[seat]['xy'] - 10 -# width = surface.get_width() + 20 -# height = surface.get_height() + 20 -# args = ('turn', surface, pos_x, pos_y, -1) -# if self.items.get('turn'): -# self.update_item(*args) -# else: -# self.add_item(*args) + def xy(w, h): + x, y = self.items['hand-%s' % turn]['xy'](w, h) + return x-10, y-10 + + # TODO: select colours that don't clash with the background. + # TODO: one colour if user can click card, another if not. + width = self.hands[turn]['surface'].get_width() + 20 + height = self.hands[turn]['surface'].get_height() + 20 + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + context.set_source_rgb(0.3, 0.6, 0) # Green. + context.paint_with_alpha(0.5) + + if id in self.items: + self.update_item(id, source=surface, xy=xy) + else: + self.add_item(id, surface, xy, -1) def button_release(self, widget, event): Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2006-09-20 11:00:26 UTC (rev 344) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2006-09-20 15:59:34 UTC (rev 345) @@ -283,9 +283,12 @@ def setTurnIndicator(self): """Sets the statusbar text to indicate which player is on turn.""" - turn = self.table.game and not self.table.game.isComplete() \ - and self.table.game.whoseTurn() + turn = None + if self.table.game and not self.table.game.isComplete(): + turn = self.table.game.whoseTurn() + self.cardarea.set_turn(turn) + context = self.statusbar.get_context_id('turn') self.statusbar.pop(context) if turn: @@ -385,7 +388,7 @@ self.setDealer(table.dealer) self.setVuln(table.game.vulnNS, table.game.vulnEW) - self.redrawTrick() + self.redrawTrick() # Clear trick. for position in table.game.deal: self.redrawHand(position) if table.seated: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2006-10-06 16:26:04
|
Revision: 346 http://svn.sourceforge.net/pybridge/?rev=346&view=rev Author: umgangee Date: 2006-10-06 09:25:53 -0700 (Fri, 06 Oct 2006) Log Message: ----------- xy positioning of items in cardarea now uses (0..1, 0..1) values scaled by cardarea's width/height; replaces lambda positioning. Addition of set_player_name(). Modified Paths: -------------- trunk/pybridge/pybridge/ui/canvas.py trunk/pybridge/pybridge/ui/cardarea.py trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/canvas.py =================================================================== --- trunk/pybridge/pybridge/ui/canvas.py 2006-09-20 15:59:34 UTC (rev 345) +++ trunk/pybridge/pybridge/ui/canvas.py 2006-10-06 16:25:53 UTC (rev 346) @@ -54,7 +54,8 @@ context.rectangle(0, 0, width, height) context.set_source(self.pattern) context.paint() - self.window.invalidate_rect((0, 0, width, height), False) # Expose. + # Trigger a call to self.expose(). + self.window.invalidate_rect((0, 0, width, height), False) def add_item(self, id, source, xy, z_index): @@ -62,13 +63,13 @@ @param id: unique identifier for source. @param source: ImageSurface. - @param xy: function providing (x, y) coords for source in backing. + @param xy: tuple providing (x, y) coords for source in backing. @param z_index: integer. """ - pos_x, pos_y = xy(*self.window.get_size()) - area = (pos_x, pos_y, source.get_width(), source.get_height()) - self.items[id] = {'source': source, 'area': area, - 'xy' : xy, 'z-index': z_index, } + # Calculate and cache the on-screen area of the item. + area = self.get_area(source, xy) + self.items[id] = {'source': source, 'area': area, 'xy': xy, + 'z-index': z_index, } self.redraw(*area) @@ -87,29 +88,35 @@ """ """ - # If optional parameters are not specified, use previous values. - source = source or self.items[id]['source'] - xy = xy or self.items[id]['xy'] + # If optional parameters are not specified, use stored values. z_index = z_index or self.items[id]['z-index'] + if source or xy: + # If source or xy coords changed, recalculate on-screen area. + source = source or self.items[id]['source'] + xy = xy or self.items[id]['xy'] + area = self.get_area(source, xy) + # If area of item has changed, clear item from previous area. + oldarea = self.items[id]['area'] + if area != oldarea: + del self.items[id] + self.redraw(*oldarea) + else: + source = self.items[id]['source'] + xy = self.items[id]['xy'] + area = self.items[id]['area'] - oldarea = self.items[id]['area'] # Current position of item. - pos_x, pos_y = xy(*self.window.get_size()) - area = (pos_x, pos_y, source.get_width(), source.get_height()) - if area != oldarea: # If position has changed, clear previous area. - del self.items[id] - self.redraw(*oldarea) - self.items[id] = {'source': source, 'area': area, - 'xy' : xy, 'z-index': z_index, } + self.items[id] = {'source': source, 'area': area, 'xy' : xy, + 'z-index': z_index, } self.redraw(*area) def redraw(self, x, y, width, height): """Redraws sources in area (x, y, width, height) to backing canvas. - @param x: - @param y: - @param width: - @param height: + @param x: start x-coordinate of area to be redrawn. + @param y: start y-coordinate of area to be redrawn. + @param width: the width of area to be redrawn. + @param height: the height of area to be redrawn. """ context = cairo.Context(self.backing) context.rectangle(x, y, width, height) @@ -134,16 +141,55 @@ self.window.invalidate_rect((x, y, width, height), False) # Expose. + def get_area(self, source, xy): + """Calculates the on-screen area of the specified source centred at xy. + + @param source: + @param xy: + @return: a tuple (x, y, width, height) + """ + win_w, win_h = self.window.get_size() # Window width and height. + width, height = source.get_width(), source.get_height() + x = int((xy[0] * win_w) - width/2) # Round to integer. + y = int((xy[1] * win_h) - height/2) + # Ensure that source coordinates fit inside dimensions of backing. + if x < self.border_x: + x = self.border_x + elif x + width > win_w - self.border_x: + x = win_w - self.border_x - width + if y < self.border_y: + y = self.border_y + elif y + height > win_h - self.border_y: + y = win_h - self.border_y - height + return x, y, width, height + + def new_surface(self, width, height): + """Creates a new ImageSurface of dimensions (width, height) + and ensures that the ImageSurface is cleared. + + @param width: the expected width of the ImageSurface. + @param height: the expected height of the ImageSurface. + @return: tuple (surface, context) + """ + # Create new ImageSurface for hand. + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + # Clear ImageSurface - in Cairo 1.2+, this is done automatically. + if cairo.version_info < (1, 2): + context.set_operator(cairo.OPERATOR_CLEAR) + context.paint() + context.set_operator(cairo.OPERATOR_OVER) # Restore. + return surface, context + + def configure(self, widget, event): width, height = self.window.get_size() self.backing = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) # Recalculate position of all items. - for id, item in self.items.items(): - pos_x, pos_y = item['xy'](width, height) - area = (pos_x, pos_y, item['area'][2], item['area'][3]) - self.items[id]['area'] = area + for id, item in self.items.iteritems(): + self.items[id]['area'] = self.get_area(item['source'], item['xy']) self.redraw(0, 0, width, height) # Full redraw required. return True # Expected to return True. Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2006-09-20 15:59:34 UTC (rev 345) +++ trunk/pybridge/pybridge/ui/cardarea.py 2006-10-06 16:25:53 UTC (rev 346) @@ -18,6 +18,8 @@ import gtk import cairo +import pango +import pangocairo from pybridge.environment import environment from canvas import CairoCanvas @@ -45,6 +47,8 @@ card_mask_path = environment.find_pixmap('bonded.png') card_mask = cairo.ImageSurface.create_from_png(card_mask_path) + font_description = pango.FontDescription('Sans Bold 10') + border_x = border_y = 10 card_width = card_mask.get_width() / 13 card_height = card_mask.get_height() / 5 @@ -110,10 +114,10 @@ pos_x = index * self.spacing_x coords.append((card, pos_x, pos_y)) else: # Insert a space between each suit. - spaces = sum([1 for suitcards in suits.values() if len(suitcards) > 0]) - 1 + spaces = len([1 for suitcards in suits.values() if len(suitcards) > 0]) - 1 for index, card in enumerate(hand): # Insert a space for each suit in hand which appears before this card's suit. - insert = sum([1 for suit, suitcards in suits.items() if len(suitcards) > 0 + insert = len([1 for suit, suitcards in suits.items() if len(suitcards) > 0 and RED_BLACK.index(card.suit) > RED_BLACK.index(suit)]) pos_x = (index + insert) * self.spacing_x coords.append((card, pos_x, pos_y)) @@ -156,14 +160,7 @@ # Determine dimensions of hand. width = max([x for card, x, y in coords]) + self.card_width height = max([y for card, x, y in coords]) + self.card_height - # Create new ImageSurface for hand. - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - context = cairo.Context(surface) - # Clear ImageSurface - in Cairo 1.2+, this is done automatically. - if cairo.version_info < (1, 2): - context.set_operator(cairo.OPERATOR_CLEAR) - context.paint() - context.set_operator(cairo.OPERATOR_OVER) # Restore. + surface, context = self.new_surface(width, height) # Draw cards to surface. visible = [(i, card) for i, card in enumerate(hand) if card not in omit] @@ -175,23 +172,53 @@ self.hands[seat] = {'hand' : hand, 'visible' : visible, 'surface' : surface, 'coords' : coords, } - # - if seat is self.TOP: - xy = lambda w, h: ((w - width)/2, self.border_y) - elif seat is self.RIGHT: - xy = lambda w, h: ((w - width - self.border_x), (h - height)/2) - elif seat is self.BOTTOM: - xy = lambda w, h: ((w - width)/2, (h - height - self.border_y)) - elif seat is self.LEFT: - xy = lambda w, h: (self.border_x, (h - height)/2) - id = 'hand-%s' % seat # Identifier for this item. if id in self.items: - self.update_item(id, source=surface, xy=xy) - else: - self.add_item(id, surface, xy, 0) + self.update_item(id, source=surface) + else: + xy = {self.TOP : (0.5, 0.15), self.BOTTOM : (0.5, 0.85), + self.LEFT : (0.15, 0.5), self.RIGHT : (0.85, 0.5), } + self.add_item(id, surface, xy[seat], 0) + def set_player_name(self, seat, name=None): + """ + + @param name: the name of the player, or None. + """ + id = 'player-%s' % seat + if name is None or id in self.items: + self.remove_item(id) + return + + layout = pango.Layout(self.create_pango_context()) + layout.set_font_description(self.font_description) + layout.set_text(name) + # Create an ImageSurface respective to dimensions of text. + width, height = layout.get_pixel_size() + surface, context = self.new_surface(width, height) + context = pangocairo.CairoContext(context) + +# context.set_line_width(4) +# context.rectangle(0, 0, width, height) +# context.set_source_rgb(0, 0.5, 0) +# context.fill_preserve() +# context.set_source_rgb(0, 0.25, 0) +# context.stroke() +# context.move_to(4, 2) + context.set_source_rgb(1, 1, 1) + context.show_layout(layout) + + if id in self.items: + self.update_item(id, source=surface) + else: + xy = {self.TOP : (0.5, 0.05), self.BOTTOM : (0.5, 0.95), + self.LEFT : (0.15, 0.7), self.RIGHT : (0.85, 0.7), } +# xy = {self.TOP : (0.5, 0.3), self.BOTTOM : (0.5, 0.7), +# self.LEFT : (0.15, 0.7), self.RIGHT : (0.85, 0.7), } + self.add_item(id, surface, xy[seat], 2) + + def set_seat_mapping(self, focus=Seat.South): """Sets the mapping between seats at table and positions of hands. @@ -223,14 +250,7 @@ Seat.South : ((width - self.card_width)/2, (height - self.card_height) ), Seat.West : (0, (height - self.card_height)/2 ), } - # Create new ImageSurface for trick. - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - context = cairo.Context(surface) - # Clear ImageSurface - in Cairo 1.2+, this is done automatically. - if cairo.version_info < (1, 2): - context.set_operator(cairo.OPERATOR_CLEAR) - context.paint() - context.set_operator(cairo.OPERATOR_OVER) # Restore. + surface, context = self.new_surface(width, height) if trick: leader, cards_played = trick @@ -244,11 +264,10 @@ if id in self.items: self.update_item(id, source=surface) else: - xy = lambda w, h: ((w - width)/2, (h - height)/2) + xy = (0.5, 0.5) self.add_item(id, surface, xy, 0) - def set_turn(self, turn): """Sets the turn indicator. @@ -262,19 +281,16 @@ self.remove_item(id) return - def xy(w, h): - x, y = self.items['hand-%s' % turn]['xy'](w, h) - return x-10, y-10 - # TODO: select colours that don't clash with the background. - # TODO: one colour if user can click card, another if not. + # TODO: one colour if user can play card from hand, another if not. width = self.hands[turn]['surface'].get_width() + 20 height = self.hands[turn]['surface'].get_height() + 20 - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - context = cairo.Context(surface) + surface, context = self.new_surface(width, height) context.set_source_rgb(0.3, 0.6, 0) # Green. context.paint_with_alpha(0.5) + xy = self.items['hand-%s' % turn]['xy'] # Use same xy as hand. + if id in self.items: self.update_item(id, source=surface, xy=xy) else: Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2006-09-20 15:59:34 UTC (rev 345) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2006-10-06 16:25:53 UTC (rev 346) @@ -353,6 +353,7 @@ def event_playerAdded(self, table, player, position): if table == self.table: + self.cardarea.set_player_name(position, player) # Disable menu item corresponding to position. widget = self.takeseat_items[position] widget.set_property('sensitive', False) @@ -367,6 +368,7 @@ def event_playerRemoved(self, table, player, position): if table == self.table: + self.cardarea.set_player_name(position, None) # Enable menu item corresponding to position. widget = self.takeseat_items[position] widget.set_property('sensitive', True) @@ -384,13 +386,14 @@ self.children.close('dialog_gameresult') self.resetGame() + self.redrawTrick() # Clear trick. + for position in table.game.deal: + self.redrawHand(position) + self.setTurnIndicator() self.setDealer(table.dealer) self.setVuln(table.game.vulnNS, table.game.vulnEW) - self.redrawTrick() # Clear trick. - for position in table.game.deal: - self.redrawHand(position) if table.seated: self.children.open('window_bidbox', parent=self) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2006-10-08 15:02:05
|
Revision: 347 http://svn.sourceforge.net/pybridge/?rev=347&view=rev Author: umgangee Date: 2006-10-08 08:01:55 -0700 (Sun, 08 Oct 2006) Log Message: ----------- Enable border around player labels, draw individual cards in trick with canvas code, indicate turn by setting transparency on inactive hands. Modified Paths: -------------- trunk/pybridge/pybridge/ui/canvas.py trunk/pybridge/pybridge/ui/cardarea.py Modified: trunk/pybridge/pybridge/ui/canvas.py =================================================================== --- trunk/pybridge/pybridge/ui/canvas.py 2006-10-06 16:25:53 UTC (rev 346) +++ trunk/pybridge/pybridge/ui/canvas.py 2006-10-08 15:01:55 UTC (rev 347) @@ -58,18 +58,19 @@ self.window.invalidate_rect((0, 0, width, height), False) - def add_item(self, id, source, xy, z_index): + def add_item(self, id, source, xy, z_index, opacity=1): """Places source item into items list. @param id: unique identifier for source. @param source: ImageSurface. @param xy: tuple providing (x, y) coords for source in backing. @param z_index: integer. + @param opacity: integer in range 0 to 1. """ # Calculate and cache the on-screen area of the item. area = self.get_area(source, xy) self.items[id] = {'source': source, 'area': area, 'xy': xy, - 'z-index': z_index, } + 'z-index': z_index, 'opacity' : opacity, } self.redraw(*area) @@ -84,12 +85,17 @@ self.redraw(*area) - def update_item(self, id, source=None, xy=None, z_index=0): + def update_item(self, id, source=None, xy=None, z_index=0, opacity=0): """ - + @param id: unique identifier for source. + @param source: if specified, ImageSurface. + @param xy: if specified, tuple providing (x, y) coords for source in backing. + @param z_index: if specified, integer. + @param opacity: if specified, integer in range 0 to 1. """ # If optional parameters are not specified, use stored values. z_index = z_index or self.items[id]['z-index'] + opacity = opacity or self.items[id]['opacity'] if source or xy: # If source or xy coords changed, recalculate on-screen area. source = source or self.items[id]['source'] @@ -106,7 +112,7 @@ area = self.items[id]['area'] self.items[id] = {'source': source, 'area': area, 'xy' : xy, - 'z-index': z_index, } + 'z-index': z_index, 'opacity' : opacity, } self.redraw(*area) @@ -135,7 +141,8 @@ for item in items: pos_x, pos_y = item['area'][0:2] context.set_source_surface(item['source'], pos_x, pos_y) - context.paint() +# context.paint() + context.paint_with_alpha(item['opacity']) context.reset_clip() self.window.invalidate_rect((x, y, width, height), False) # Expose. Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2006-10-06 16:25:53 UTC (rev 346) +++ trunk/pybridge/pybridge/ui/cardarea.py 2006-10-08 15:01:55 UTC (rev 347) @@ -196,26 +196,26 @@ layout.set_text(name) # Create an ImageSurface respective to dimensions of text. width, height = layout.get_pixel_size() + width += 8; height += 4 surface, context = self.new_surface(width, height) context = pangocairo.CairoContext(context) -# context.set_line_width(4) -# context.rectangle(0, 0, width, height) -# context.set_source_rgb(0, 0.5, 0) -# context.fill_preserve() -# context.set_source_rgb(0, 0.25, 0) -# context.stroke() -# context.move_to(4, 2) + # Draw background box, text to ImageSurface. + context.set_line_width(4) + context.rectangle(0, 0, width, height) + context.set_source_rgb(0, 0.5, 0) + context.fill_preserve() + context.set_source_rgb(0, 0.25, 0) + context.stroke() + context.move_to(4, 2) context.set_source_rgb(1, 1, 1) context.show_layout(layout) if id in self.items: self.update_item(id, source=surface) else: - xy = {self.TOP : (0.5, 0.05), self.BOTTOM : (0.5, 0.95), - self.LEFT : (0.15, 0.7), self.RIGHT : (0.85, 0.7), } -# xy = {self.TOP : (0.5, 0.3), self.BOTTOM : (0.5, 0.7), -# self.LEFT : (0.15, 0.7), self.RIGHT : (0.85, 0.7), } + xy = {self.TOP : (0.5, 0.15), self.BOTTOM : (0.5, 0.85), + self.LEFT : (0.15, 0.6), self.RIGHT : (0.85, 0.6), } self.add_item(id, surface, xy[seat], 2) @@ -237,35 +237,34 @@ @param trick: a (leader, cards_played) pair, or None. """ - id = 'trick' - self.trick = trick - if trick is None: - self.remove_item(id) - return + xy = {self.TOP : (0.5, 0.425), self.BOTTOM : (0.5, 0.575), + self.LEFT : (0.425, 0.5), self.RIGHT : (0.575, 0.5), } - width, height = 200, 200 - # (x, y) positions to fit within (width, height) bound box. - pos = {Seat.North : ((width - self.card_width)/2, 0 ), - Seat.East : ((width - self.card_width), (height - self.card_height)/2 ), - Seat.South : ((width - self.card_width)/2, (height - self.card_height) ), - Seat.West : (0, (height - self.card_height)/2 ), } - - surface, context = self.new_surface(width, height) - if trick: - leader, cards_played = trick # The order of play is the leader, then clockwise around Seat. - for seat in Seat[leader.index:] + Seat[:leader.index]: - card = cards_played.get(seat) - if card: - pos_x, pos_y = pos[seat] - self.draw_card(context, pos_x, pos_y, card) + leader = trick[0] + order = Seat[leader.index:] + Seat[:leader.index] + for i, seat in enumerate(order): + id = 'trick-%s' % seat + old_card = self.trick and self.trick[1].get(seat) or None + new_card = trick[1].get(seat) + # If old card matches new card, take no action. + if old_card is None and new_card is not None: + surface, context = self.new_surface(self.card_width, self.card_height) + self.draw_card(context, 0, 0, new_card) + self.add_item(id, surface, xy[seat], z_index=i+1) + elif new_card is None and old_card is not None: + self.remove_item(id) + elif old_card != new_card: + surface, context = self.new_surface(self.card_width, self.card_height) + self.draw_card(context, 0, 0, new_card) + self.update_item(id, surface, z_index=i+1) - if id in self.items: - self.update_item(id, source=surface) - else: - xy = (0.5, 0.5) - self.add_item(id, surface, xy, 0) + elif self.trick: # Remove all cards from previous trick. + for seat in self.trick[1]: + self.remove_item('trick-%s' % seat) + + self.trick = trick # Save trick and return. def set_turn(self, turn): @@ -276,27 +275,29 @@ @param turn: a member of Seat, or None. """ - id = 'turn' if turn is None: - self.remove_item(id) return - # TODO: select colours that don't clash with the background. - # TODO: one colour if user can play card from hand, another if not. - width = self.hands[turn]['surface'].get_width() + 20 - height = self.hands[turn]['surface'].get_height() + 20 - surface, context = self.new_surface(width, height) - context.set_source_rgb(0.3, 0.6, 0) # Green. - context.paint_with_alpha(0.5) - - xy = self.items['hand-%s' % turn]['xy'] # Use same xy as hand. - - if id in self.items: - self.update_item(id, source=surface, xy=xy) - else: - self.add_item(id, surface, xy, -1) + for seat in Seat: + opacity = (seat is turn) and 1 or 0.5 + self.update_item('hand-%s' % seat, opacity=opacity) +# # TODO: select colours that don't clash with the background. +# # TODO: one colour if user can play card from hand, another if not. +# width = self.hands[turn]['surface'].get_width() + 20 +# height = self.hands[turn]['surface'].get_height() + 20 +# surface, context = self.new_surface(width, height) +# context.set_source_rgb(0.3, 0.6, 0) # Green. +# context.paint_with_alpha(0.5) +# +# xy = self.items['hand-%s' % turn]['xy'] # Use same xy as hand. +# +# if id in self.items: +# self.update_item(id, source=surface, xy=xy) +# else: +# self.add_item(id, surface, xy, -1) + def button_release(self, widget, event): """Determines if a card was clicked: if so, calls card_selected.""" if event.button == 1: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2006-10-09 17:45:29
|
Revision: 350 http://svn.sourceforge.net/pybridge/?rev=350&view=rev Author: umgangee Date: 2006-10-09 10:44:51 -0700 (Mon, 09 Oct 2006) Log Message: ----------- Add DialogPreferences window class. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_main.py Added Paths: ----------- trunk/pybridge/pybridge/ui/dialog_preferences.py Added: trunk/pybridge/pybridge/ui/dialog_preferences.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_preferences.py (rev 0) +++ trunk/pybridge/pybridge/ui/dialog_preferences.py 2006-10-09 17:44:51 UTC (rev 350) @@ -0,0 +1,62 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2006 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +import gtk +import os +from wrapper import GladeWrapper + +from pybridge.environment import environment +import utils + + +class DialogPreferences(GladeWrapper): + + glade_name = 'dialog_preferences' + + + def new(self): + model = gtk.ListStore(str) + self.carddeck.set_model(model) + cell = gtk.CellRendererText() + self.carddeck.pack_start(cell, True) + self.carddeck.add_attribute(cell, 'text', 0) + # Populate list of card decks. + path = environment.find_pixmap('') + for filename in os.listdir(path): + if filename.endswith('.png'): + iter = model.append((filename,)) + if filename == 'bonded.png': + self.carddeck.set_active_iter(iter) + + +# Signal handlers. + + + def on_carddeck_changed(self, widget, *args): + print "changed" + + + def on_cancelbutton_clicked(self, widget, *args): + print "cancel" + utils.windows.close(self.glade_name) + + + def on_okbutton_clicked(self, widget, *args): + print "ok" + utils.windows.close(self.glade_name) + Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2006-10-09 17:41:57 UTC (rev 349) +++ trunk/pybridge/pybridge/ui/window_main.py 2006-10-09 17:44:51 UTC (rev 350) @@ -172,9 +172,7 @@ self.frame_personinfo.set_property('sensitive', False) self.label_personname.set_text('') - - def on_window_main_delete_event(self, widget, *args): utils.quit() # return True @@ -200,6 +198,10 @@ utils.quit() + def on_preferences_activate(self, widget, *args): + utils.windows.open('dialog_preferences') + + def on_homepage_activate(self, widget, *args): webbrowser.open('http://pybridge.sourceforge.net/') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-01 17:42:58
|
Revision: 383 http://svn.sourceforge.net/pybridge/?rev=383&view=rev Author: umgangee Date: 2007-04-01 10:42:58 -0700 (Sun, 01 Apr 2007) Log Message: ----------- Fixed to support new table/game architecture. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_bidbox.py trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/window_bidbox.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bidbox.py 2007-04-01 17:41:14 UTC (rev 382) +++ trunk/pybridge/pybridge/ui/window_bidbox.py 2007-04-01 17:42:58 UTC (rev 383) @@ -19,7 +19,7 @@ import gtk from wrapper import GladeWrapper -from eventhandler import eventhandler +from eventhandler import SimpleEventHandler from pybridge.bridge.call import Bid, Pass, Double, Redouble from pybridge.bridge.call import Level, Strain @@ -36,7 +36,7 @@ 'nt' : Strain.NoTrump, } ALL_CALLS = [Pass(), Double(), Redouble()] + \ - [Bid(l, s) for l, s in zip(list(Level)*5, list(Strain)*7)] + [Bid(l, s) for l in Level for s in Strain] class WindowBidbox(GladeWrapper): @@ -49,19 +49,16 @@ glade_name = 'window_bidbox' - callbacks = ('gameCallMade',) - def new(self): - table = self.parent.table - self.set_available_calls(table.seated, table.game.bidding) - - eventhandler.registerCallbacksFor(self, self.callbacks) + self.eventHandler = SimpleEventHandler(self) + self.parent.table.game.attach(self.eventHandler) + self.set_available_calls(self.parent.position, self.parent.table.game.bidding) - def set_available_calls(self, seat, bidding): + def set_available_calls(self, position, bidding): """Enables buttons representing the given calls.""" - if bidding.whoseTurn() == seat: + if bidding.whoseTurn() == position: self.window.set_property('sensitive', True) for call in ALL_CALLS: button = self.get_button_from_call(call) @@ -73,9 +70,8 @@ # Registered event handlers. - def event_gameCallMade(self, table, call, position): - if table == self.parent.table: - self.set_available_calls(table.seated, table.game.bidding) + def event_makeCall(self, call, position): + self.set_available_calls(self.parent.position, self.parent.table.game.bidding) # Utility methods. @@ -108,11 +104,10 @@ def on_call_clicked(self, widget, *args): """Builds a call object and submits.""" - table = self.parent.table # Do not check validity of call: the server will do that. # If call is invalid, ignore the resultant errback. call = self.get_call_from_button(widget) - d = table.gameMakeCall(call) + d = self.parent.player.callRemote('makeCall', call) d.addErrback(lambda r: True) # Ignore any error. Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-01 17:41:14 UTC (rev 382) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-01 17:42:58 UTC (rev 383) @@ -20,169 +20,180 @@ from wrapper import GladeWrapper from cardarea import CardArea -from eventhandler import eventhandler +from eventhandler import SimpleEventHandler import utils - +from pybridge.network.error import GameError from pybridge.bridge.call import Bid, Pass, Double, Redouble -from pybridge.bridge.symbols import Level, Strain, Player, Rank +from pybridge.bridge.symbols import Direction, Level, Strain, Rank, Vulnerable # Translatable symbols for elements of bridge. CALLTYPE_SYMBOLS = {Pass : _('pass'), Double : _('dbl'), Redouble : _('rdbl') } +DIRECTION_SYMBOLS = {Direction.North : _('North'), Direction.East : _('East'), + Direction.South : _('South'), Direction.West : _('West') } + LEVEL_SYMBOLS = {Level.One : _('1'), Level.Two : _('2'), Level.Three : _('3'), Level.Four : _('4'), Level.Five : _('5'), Level.Six : _('6'), - Level.Seven : _('7'), } + Level.Seven : _('7') } RANK_SYMBOLS = {Rank.Two : _('2'), Rank.Three : _('3'), Rank.Four : _('4'), Rank.Five : _('5'), Rank.Six : _('6'), Rank.Seven : _('7'), Rank.Eight : _('8'), Rank.Nine : _('9'), Rank.Ten : _('10'), Rank.Jack : _('J'), Rank.Queen : _('Q'), Rank.King : _('K'), - Rank.Ace : _('A'), } + Rank.Ace : _('A') } STRAIN_SYMBOLS = {Strain.Club : u'\N{BLACK CLUB SUIT}', Strain.Diamond : u'\N{BLACK DIAMOND SUIT}', Strain.Heart : u'\N{BLACK HEART SUIT}', Strain.Spade : u'\N{BLACK SPADE SUIT}', - Strain.NoTrump : u'NT', } + Strain.NoTrump : u'NT' } -SEAT_SYMBOLS = {Player.North : _('North'), Player.East : _('East'), - Player.South : _('South'), Player.West : _('West') } +VULN_SYMBOLS = {Vulnerable.All : _('All'), Vulnerable.NorthSouth : _('N/S'), + Vulnerable.EastWest : _('E/W'), Vulnerable.None : _('None') } class WindowBridgetable(GladeWrapper): glade_name = 'window_bridgetable' - callbacks = ('observerAdded', 'observerRemoved', 'playerAdded', - 'playerRemoved', 'gameStarted', 'gameFinished', - 'gameCallMade', 'gameCardPlayed', 'gameHandRevealed', - 'messageReceived') - def new(self): self.children = utils.WindowManager() + self.eventHandler = SimpleEventHandler(self) + self.table = None # Table currently displayed in window. - + self.handler = None + self.player, self.position = None, None + # Set up "Take Seat" menu. - items = {} + self.takeseat_items = {} menu = gtk.Menu() - for player in Player: - items[player] = gtk.MenuItem(SEAT_SYMBOLS[player], True) - items[player].connect('activate', self.on_seat_activated, player) - items[player].show() - menu.append(items[player]) + for position in Direction: + item = gtk.MenuItem(DIRECTION_SYMBOLS[position], True) + item.connect('activate', self.on_seat_activated, position) + item.show() + menu.append(item) + self.takeseat_items[position] = item self.takeseat.set_menu(menu) - self.takeseat_items = items - + # Set up CardArea widget. self.cardarea = CardArea() self.cardarea.on_card_clicked = self.on_card_clicked self.cardarea.set_size_request(640, 480) self.scrolled_cardarea.add_with_viewport(self.cardarea) self.cardarea.show() - + renderer = gtk.CellRendererText() - + # Set up bidding history and column display. self.call_store = gtk.ListStore(str, str, str, str) self.biddingview.set_model(self.call_store) - for index, player in enumerate(Player): - title = SEAT_SYMBOLS[player] + for index, position in enumerate(Direction): + title = DIRECTION_SYMBOLS[position] column = gtk.TreeViewColumn(str(title), renderer, text=index) column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) column.set_fixed_width(50) self.biddingview.append_column(column) - + # Set up trick history and column display. self.trick_store = gtk.ListStore(str, str, str, str) self.trickview.set_model(self.trick_store) - for index, player in enumerate(Player): - title = SEAT_SYMBOLS[player] + for index, position in enumerate(Direction): + title = DIRECTION_SYMBOLS[position] column = gtk.TreeViewColumn(str(title), renderer, text=index) column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) column.set_fixed_width(50) self.trickview.append_column(column) - + # Set up score sheet and column display. self.score_store = gtk.ListStore(str, str, str, str) self.scoresheet.set_model(self.score_store) for index, title in enumerate([_('Contract'), _('Made'), _('N/S'), _('E/W')]): column = gtk.TreeViewColumn(title, renderer, text=index) self.scoresheet.append_column(column) - + # Set up observer listing. self.observer_store = gtk.ListStore(str) self.treeview_observers.set_model(self.observer_store) column = gtk.TreeViewColumn(None, renderer, text=0) self.observer_store.set_sort_column_id(0, gtk.SORT_ASCENDING) self.treeview_observers.append_column(column) - - eventhandler.registerCallbacksFor(self, self.callbacks) def cleanup(self): + print "Cleaning up" self.table = None # Dereference table. # Close all child windows. for windowname in self.children.keys(): self.children.close(windowname) + def errback(self, failure): + print "Error: %s" % failure.getErrorMessage() + + def setTable(self, table): """Changes display to match the table specified. @param table: the (now) focal table. """ self.table = table - self.window.set_title(_('Table %s') % table.id) + self.table.attach(self.eventHandler) + self.table.game.attach(self.eventHandler) + self.player, self.position = None, None + + self.window.set_title(_('Table %s (Bridge)') % self.table.id) self.resetGame() - - if table.game: - - # Redraw hands and, if playing in progress, redraw trick. - for position in table.game.deal: - self.redrawHand(position) - if table.game.playing: + + for position in Direction: + self.redrawHand(position) # Redraw all hands. + + if self.table.game.inProgress(): + # If trick play in progress, redraw trick. + if self.table.game.play: self.redrawTrick() self.setTurnIndicator() - - for call in table.game.bidding.calls: - player = table.game.bidding.whoCalled(call) - self.addCall(call, player) - - self.setDealer(table.dealer) - self.setVuln(table.game.vulnNS, table.game.vulnEW) - + + for call in self.table.game.bidding.calls: + position = self.table.game.bidding.whoCalled(call) + self.addCall(call, position) + + self.setDealer(self.table.game.board['dealer']) + self.setVuln(self.table.game.board['vuln']) + # If contract, set contract. - if table.game.bidding.isComplete(): - contract = table.game.bidding.getContract() + if self.table.game.bidding.isComplete(): + contract = self.table.game.bidding.getContract() self.setContract(contract) - + # If playing, set trick counts. - if table.game.playing: - for player, cards in table.game.playing.played.items(): + if self.table.game.play: + for position, cards in self.table.game.play.played.items(): for card in cards: - self.addCard(card, player) - self.setTrickCount(table.game.getTrickCount()) - + self.addCard(card, position) + self.setTrickCount(self.table.game.getTrickCount()) + # If user is a player and bidding in progress, open bidding box. - if table.seated and not table.game.bidding.isComplete(): + if self.player and not self.table.game.bidding.isComplete(): self.children.open('window_bidbox', parent=self) - + # Initialise seat menu and player labels. - for seat, player in table.players.items(): - available = player is None or seat == table.seated - self.takeseat_items[seat].set_property('sensitive', available) + for position in Direction: + player = self.table.players.get(position) # Player name or None. + + available = player is None or position == self.position + self.takeseat_items[position].set_property('sensitive', available) if player: - self.event_playerAdded(table, player, seat) - else: # Player vacant. - self.event_playerRemoved(table, None, seat) + self.event_joinGame(player, position) + else: # Position vacant. + self.event_leaveGame(None, position) # Initialise observer listing. self.observer_store.clear() - for observer in table.observers: - self.event_observerAdded(table, observer) + for observer in self.table.observers: + self.event_addObserver(observer) def resetGame(self): @@ -214,8 +225,9 @@ def addCard(self, card, position): """""" + position = self.table.game.play.whoPlayed(card) column = position.index - row = self.table.game.playing.played[position].index(card) + row = self.table.game.play.played[position].index(card) if self.trick_store.get_iter_first() == None: self.trick_store.append() @@ -234,8 +246,8 @@ def addScore(self, contract, made, score): textContract = self.getContractFormat(contract) textMade = '%s' % made - if contract['declarer'] in (Player.North, Player.South) and score > 0 \ - or contract['declarer'] in (Player.East, Player.West) and score < 0: + if contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ + or contract['declarer'] in (Direction.East, Direction.West) and score < 0: textNS, textEW = '%s' % abs(score), '' else: textNS, textEW = '', '%s' % abs(score) @@ -251,16 +263,15 @@ @param position: @param all: If True, do not filter out cards played. """ - hand = self.table.game.deal[position] - played = [] - if self.table.game.playing: - played = self.table.game.playing.played[position] - - if hand: # Own or known hand. - if all is True: # Show all cards. - played = [] + if all is True or self.table.game.play is None: + played = [] + else: + played = self.table.game.play.played[position] + + try: + hand = self.table.game.getHand(position) self.cardarea.set_hand(hand, position, omit=played) - else: # Unknown hand: draw cards face down, use integer placeholders. + except GameError: # Unknown hand: draw cards face down. cards, played = range(13), range(len(played)) self.cardarea.set_hand(cards, position, facedown=True, omit=played) @@ -272,24 +283,22 @@ @param trick: """ trick = None - if self.table.game.playing: - trick = self.table.game.playing.getCurrentTrick() + if self.table.game.play: + trick = self.table.game.play.getCurrentTrick() self.cardarea.set_trick(trick) def setTurnIndicator(self): """Sets the statusbar text to indicate which player is on turn.""" - turn = None - if self.table.game and not self.table.game.isComplete(): - turn = self.table.game.whoseTurn() - - self.cardarea.set_turn(turn) - context = self.statusbar.get_context_id('turn') self.statusbar.pop(context) - if turn: + try: + turn = self.table.game.getTurn() text = _("It is %s's turn") % str(turn) self.statusbar.push(context, text) + except GameError: # Game not in progress + turn = None + self.cardarea.set_turn(turn) def setContract(self, contract=None): @@ -300,7 +309,7 @@ def setDealer(self, dealer): - self.label_dealer.set_markup('<b>%s</b>' % SEAT_SYMBOLS[dealer]) + self.label_dealer.set_markup('<b>%s</b>' % DIRECTION_SYMBOLS[dealer]) def setTrickCount(self, count=None): @@ -320,143 +329,133 @@ self.label_defence.set_markup('<span size="x-large"><b>%s</b> (%s)</span>' % defence) - def setVuln(self, vulnNS, vulnEW): - vuln = {(True, True) : _('All'), - (True, False) : _('N/S'), - (False, True) : _('E/W'), - (False, False) : _('None'), } - self.label_vuln.set_markup('<b>%s</b>' % vuln[(vulnNS, vulnEW)]) + def setVuln(self, vulnerable): + self.label_vuln.set_markup('<b>%s</b>' % VULN_SYMBOLS[vulnerable]) # Registered event handlers. - def event_observerAdded(self, table, observer): - if table == self.table: - self.observer_store.append([observer]) + def event_addObserver(self, observer): + self.observer_store.append([observer]) - def event_observerRemoved(self, table, observer): - + def event_removeObserver(self, observer): + def func(model, path, iter, user_data): if model.get_value(iter, 0) in user_data: model.remove(iter) return True - - if table == self.table: - self.observer_store.foreach(func, observer) + self.observer_store.foreach(func, observer) - def event_playerAdded(self, table, player, position): - if table == self.table: - self.cardarea.set_player_name(position, player) - # Disable menu item corresponding to position. - widget = self.takeseat_items[position] - widget.set_property('sensitive', False) - # Set player label. - label = getattr(self, 'label_%s' % position.key.lower()) - label.set_markup('<b>%s</b>' % player) - - # If all seats occupied, disable Take Seat. - if len([p for p in table.players.values() if p is None]) == 0: - self.takeseat.set_property('sensitive', False) + def event_joinGame(self, player, position): + self.cardarea.set_player_name(position, player) + # Disable menu item corresponding to position. + widget = self.takeseat_items[position] + widget.set_property('sensitive', False) + # Set player label. + label = getattr(self, 'label_%s' % position.key.lower()) + label.set_markup('<b>%s</b>' % player) - def event_playerRemoved(self, table, player, position): - if table == self.table: - self.cardarea.set_player_name(position, None) - # Enable menu item corresponding to position. - widget = self.takeseat_items[position] - widget.set_property('sensitive', True) - # Reset player label. - label = getattr(self, 'label_%s' % position.key.lower()) - label.set_markup('<i>%s</i>' % _('Player vacant')) - - # If we are not seated, ensure Take Seat is enabled. - if not table.seated: - self.takeseat.set_property('sensitive', True) + # If all positions occupied, disable Take Seat. + if len(self.table.players.values()) == len(Direction): + self.takeseat.set_property('sensitive', False) - def event_gameStarted(self, table, dealer, vulnNS, vulnEW): - if table == self.table: - self.children.close('dialog_gameresult') - self.resetGame() - - self.redrawTrick() # Clear trick. - for position in table.game.deal: - self.redrawHand(position) - - self.setTurnIndicator() - self.setDealer(table.dealer) - self.setVuln(table.game.vulnNS, table.game.vulnEW) - - if table.seated: - self.children.open('window_bidbox', parent=self) + def event_leaveGame(self, player, position): + self.cardarea.set_player_name(position, None) + # Enable menu item corresponding to position. + widget = self.takeseat_items[position] + widget.set_property('sensitive', True) + # Reset player label. + label = getattr(self, 'label_%s' % position.key.lower()) + label.set_markup('<i>%s</i>' % _('Position vacant')) + # If we are not seated, ensure Take Seat is enabled. + if self.position is None: + self.takeseat.set_property('sensitive', True) - def event_gameCallMade(self, table, call, position): - if table == self.table: - self.addCall(call, position) - self.setTurnIndicator() - if table.game.bidding.isComplete(): - self.children.close('window_bidbox') # If playing. - contract = table.game.bidding.getContract() - self.setContract(contract) + def event_start(self, board): + #self.children.close('dialog_gameresult') + self.resetGame() - def event_gameCardPlayed(self, table, card, position): - if table == self.table: - self.addCard(card, position) - self.setTurnIndicator() - count = table.game.getTrickCount() - self.setTrickCount(count) - self.redrawTrick() + #self.redrawTrick() # Clear trick. + for position in Direction: self.redrawHand(position) + self.setTurnIndicator() + self.setDealer(board['dealer']) + self.setVuln(board['vuln']) - def event_gameFinished(self, table): - if table == self.table: - - for position in table.game.deal: - self.redrawHand(position, all=True) - self.setTurnIndicator() - - # Determine and display score in dialog box. - contract = table.game.bidding.getContract() - if contract: - trickCount = table.game.getTrickCount() - offset = trickCount['declarerWon'] - trickCount['required'] - score = table.game.score() - self.addScore(contract, trickCount['declarerWon'], score) - - textContract = _('Contract %s') % self.getContractFormat(contract) - textTrick = (offset > 0 and _('made by %s tricks') % offset) or \ - (offset < 0 and _('failed by %s tricks') % abs(offset)) or \ - _('made exactly') - scorer = (score >= 0 and _('declarer')) or _('defence') - textScore = _('Score %s points for %s') % (abs(score), scorer) - - message = '%s %s.\n\n%s.' % (textContract, textTrick, textScore) - else: - message = _('Bidding passed out.') - - dialog = self.children.open('dialog_gameresult', parent=self) - dialog.setup(message) + if self.player: + d = self.player.callRemote('getHand') + d.addCallbacks(self.table.game.revealHand, self.errback, + callbackKeywords={'position' : self.position}) + self.children.open('window_bidbox', parent=self) - def event_gameHandRevealed(self, table, hand, position): - if table == self.table: - self.redrawHand(position) + def event_makeCall(self, call, position): + self.addCall(call, position) + self.setTurnIndicator() + if self.table.game.bidding.isComplete(): + self.children.close('window_bidbox') # If playing. + contract = self.table.game.bidding.getContract() + self.setContract(contract) - def event_messageReceived(self, table, message, sender, recipients): - if table == self.table: - buffer = self.chat_messagehistory.get_buffer() - iter = buffer.get_end_iter() - buffer.insert(iter, '%s: %s\n' % (sender, message)) - self.chat_messagehistory.scroll_to_iter(iter, 0) + def event_playCard(self, card, position): + # Determine the position of the hand from which card was played. + playfrom = self.table.game.play.whoPlayed(card) + self.addCard(card, playfrom) + self.setTurnIndicator() + count = self.table.game.getTrickCount() + self.setTrickCount(count) + self.redrawTrick() + self.redrawHand(playfrom) + def event_gameFinished(self): + for position in self.table.game.deal: + self.redrawHand(position, all=True) + self.setTurnIndicator() + + # Determine and display score in dialog box. + contract = self.table.game.bidding.getContract() + if contract: + trickCount = self.table.game.getTrickCount() + offset = trickCount['declarerWon'] - trickCount['required'] + score = self.table.game.score() + self.addScore(contract, trickCount['declarerWon'], score) + + textContract = _('Contract %s') % self.getContractFormat(contract) + textTrick = (offset > 0 and _('made by %s tricks') % offset) or \ + (offset < 0 and _('failed by %s tricks') % abs(offset)) or \ + _('made exactly') + scorer = (score >= 0 and _('declarer')) or _('defence') + textScore = _('Score %s points for %s') % (abs(score), scorer) + + message = '%s %s.\n\n%s.' % (textContract, textTrick, textScore) + else: + message = _('Bidding passed out.') + + dialog = self.children.open('dialog_gameresult', parent=self) + dialog.setup(message) + + + def event_revealHand(self, hand, position): + self.redrawHand(position) + + + def event_messageReceived(self, message, sender, recipients): + buffer = self.chat_messagehistory.get_buffer() + iter = buffer.get_end_iter() + buffer.insert(iter, '%s: %s\n' % (sender, message)) + self.chat_messagehistory.scroll_to_iter(iter, 0) + + # Utility methods. @@ -481,41 +480,50 @@ def on_card_clicked(self, card, position): -# if self.table.playing and self.table.whoseTurn() == position: - d = self.table.gamePlayCard(card, position) - d.addErrback(lambda _: True) # Ignore any error. + if self.player: + d = self.player.callRemote('playCard', card) + d.addErrback(self.errback) + #d.addErrback(lambda _: True) # Ignore any error. - def on_seat_activated(self, widget, seat): + def on_seat_activated(self, widget, position): - def success(r): + def success(player): + self.player = player # RemoteReference to BridgePlayer object. + self.position = position + self.takeseat.set_property('sensitive', False) self.leaveseat.set_property('sensitive', True) # If game is running and bidding is active, open bidding box. - if self.table.game and not self.table.game.bidding.isComplete(): - self.children.open('window_bidbox', parent=self) + if self.table.game.inProgress(): + d = self.player.callRemote('getHand') + d.addCallbacks(self.table.game.revealHand, self.errback, + callbackKeywords={'position' : self.position}) + if not self.table.game.bidding.isComplete(): + self.children.open('window_bidbox', parent=self) - d = self.table.addPlayer(seat) # Take seat. - d.addCallback(success) + d = self.table.joinGame(position) + d.addCallbacks(success, self.errback) def on_takeseat_clicked(self, widget, *args): # TODO: match user up with preferred partner. - for seat, player in self.table.players.items(): - if player is None: # Vacant. - self.on_seat_activated(widget, seat) + for position in Direction: + if position not in self.table.players: # Position is vacant. + self.on_seat_activated(widget, position) # Take position. break def on_leaveseat_clicked(self, widget, *args): def success(r): + self.position = None self.takeseat.set_property('sensitive', True) self.leaveseat.set_property('sensitive', False) self.children.close('window_bidbox') # If open. - d = self.table.removePlayer() # Leave seat. - d.addCallback(success) + d = self.table.leaveGame(self.position) + d.addCallbacks(success, self.errback) def on_toggle_gameinfo_clicked(self, widget, *args): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-03 11:45:39
|
Revision: 393 http://svn.sourceforge.net/pybridge/?rev=393&view=rev Author: umgangee Date: 2007-04-03 04:45:40 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Move UI-wide Settings instance to pybridge.ui; remove utils. Modified Paths: -------------- trunk/pybridge/pybridge/ui/__init__.py Removed Paths: ------------- trunk/pybridge/pybridge/ui/utils.py Modified: trunk/pybridge/pybridge/ui/__init__.py =================================================================== --- trunk/pybridge/pybridge/ui/__init__.py 2007-04-03 11:42:56 UTC (rev 392) +++ trunk/pybridge/pybridge/ui/__init__.py 2007-04-03 11:45:40 UTC (rev 393) @@ -16,26 +16,34 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from twisted.internet import gtk2reactor +gtk2reactor.install() +import gtk + +from twisted.internet import reactor + +import pybridge.environment as env +from pybridge.settings import Settings + +import locale +import gettext +locale.setlocale(locale.LC_ALL, '') +gettext.bindtextdomain('pybridge', env.get_localedir()) +gettext.textdomain('pybridge') +gettext.install('pybridge') + +filename = env.find_config_client('client.cfg') +settings = Settings(filename, ['Connection', 'General']) + + def run(): - """""" - from twisted.internet import gtk2reactor - gtk2reactor.install() - - import gtk - from twisted.internet import reactor - - import locale - import gettext - import pybridge.environment as env - locale.setlocale(locale.LC_ALL, '') - gettext.bindtextdomain('pybridge', env.get_localedir()) - gettext.textdomain('pybridge') - gettext.install('pybridge') - - import utils - utils.windows.open('dialog_connection') - - # Start the program. + """Starts the PyBridge client UI.""" + + from manager import wm + from window_main import WindowMain + wm.open(WindowMain) + + # Start the event loop. reactor.run() gtk.main() Deleted: trunk/pybridge/pybridge/ui/utils.py =================================================================== --- trunk/pybridge/pybridge/ui/utils.py 2007-04-03 11:42:56 UTC (rev 392) +++ trunk/pybridge/pybridge/ui/utils.py 2007-04-03 11:45:40 UTC (rev 393) @@ -1,86 +0,0 @@ -# PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2007 PyBridge Project. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -# Set up client with UI event handler. -from pybridge.network.client import client -from eventhandler import eventhandler -client.setEventHandler(eventhandler) - -PORT = 5040 # Default port for PyBridge. - - -import pybridge.environment as env -from pybridge.settings import Settings - -file = env.find_config_client('client.cfg') -settings = Settings(file, ['Connection', 'General']) - - -import imp -from UserDict import UserDict - - -class WindowManager(UserDict): - - - def open(self, windowname, parent=None): - """Creates a new instance of a GladeWrapper window. - - @param windowname: the module name of the window. - @param parent: if specified, a parent window to set transient property. - """ - classname = ''.join([x.capitalize() for x in windowname.split('_')]) - exec("from %s import %s" % (windowname, classname)) - instance = eval(classname)(parent) - self[windowname] = instance - return instance - - - def close(self, windowname, instance=None): - """Closes an existing instance of a GladeWrapper window. - - @param windowname: the module name of the window. - @param instance: if provided, the specific window object to close. - """ - if instance or self.get(windowname): - if instance is None: - instance = self[windowname] - instance.window.destroy() - instance.cleanup() - if self.get(windowname) == instance: - del self[windowname] - return True - return False - - -windows = WindowManager() - - - - -import gtk -from twisted.internet import reactor - - -def quit(): - """Shutdown gracefully.""" - client.disconnect() - settings.save() # Save settings. - reactor.stop() - gtk.main_quit() - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-03 12:00:16
|
Revision: 396 http://svn.sourceforge.net/pybridge/?rev=396&view=rev Author: umgangee Date: 2007-04-03 05:00:17 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Change utils references to manager. Modified Paths: -------------- trunk/pybridge/pybridge/ui/dialog_connection.py trunk/pybridge/pybridge/ui/dialog_newtable.py trunk/pybridge/pybridge/ui/dialog_preferences.py Modified: trunk/pybridge/pybridge/ui/dialog_connection.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_connection.py 2007-04-03 11:59:11 UTC (rev 395) +++ trunk/pybridge/pybridge/ui/dialog_connection.py 2007-04-03 12:00:17 UTC (rev 396) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,7 +10,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -20,50 +20,47 @@ from wrapper import GladeWrapper from pybridge.network.client import client -import utils +from manager import wm +from pybridge.ui import settings +TCP_PORT = 5040 + class DialogConnection(GladeWrapper): glade_name = 'dialog_connection' - def new(self): + def setUp(self): # Read connection parameters from client settings. - hostname = utils.settings.connection.get('hostname', '') - portnum = utils.settings.connection.get('portnum', str(utils.PORT)) - username = utils.settings.connection.get('username', '') - password = utils.settings.connection.get('password', '') - - self.entry_hostname.set_text(hostname) - self.entry_portnum.set_text(portnum) - self.entry_username.set_text(username) - self.entry_password.set_text(password) - if password: - self.check_savepassword.set_active(True) + connection = settings.connection + if connection: + self.entry_hostname.set_text(connection['hostname']) + self.entry_portnum.set_text(connection['portnum']) + self.entry_username.set_text(connection['username']) + self.entry_password.set_text(connection['password']) + self.check_savepassword.set_active(connection['password'] != '') + else: + self.entry_portnum.set_text(str(TCP_PORT)) + def connectSuccess(self, avatar): """Actions to perform when connecting succeeds.""" - hostname = self.entry_hostname.get_text() - portnum = self.entry_portnum.get_text() - username = self.entry_username.get_text() + + # Save connection information. + settings.connection = {} + settings.connection['hostname'] = self.entry_hostname.get_text() + settings.connection['portnum'] = self.entry_portnum.get_text() + settings.connection['username'] = self.entry_username.get_text() if self.check_savepassword.get_active(): - password = self.entry_password.get_text() + settings.connection['password'] = self.entry_password.get_text() else: # Flush password. - password = '' - - # Save connection information. - utils.settings.connection['hostname'] = hostname - utils.settings.connection['portnum'] = portnum - utils.settings.connection['username'] = username - utils.settings.connection['password'] = password - - # Launch main window. - utils.windows.close('dialog_connection') - utils.windows.open('window_main') + settings.connection['password'] = '' + wm.close(self) + def connectFailure(self, failure): """Actions to perform when connecting fails.""" error = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, @@ -79,7 +76,7 @@ def on_dialog_connection_delete_event(self, widget, *args): - utils.quit() + wm.close(self) def on_field_changed(self, widget, *args): @@ -100,15 +97,15 @@ def on_connect_clicked(self, widget, *args): # Prevent repeat clicks. self.button_connect.set_property('sensitive', False) - + hostname = self.entry_hostname.get_text() port = int(self.entry_portnum.get_text()) client.connect(hostname, port) - + username = self.entry_username.get_text() password = self.entry_password.get_text() register = self.check_registeruser.get_active() == True - + if register: # Attempt login only after registration. # TODO: can defer.waitForDeferred() be used here? @@ -119,6 +116,6 @@ d.addCallbacks(self.connectSuccess, self.connectFailure) - def on_quit_clicked(self, widget, *args): - utils.quit() + def on_cancel_clicked(self, widget, *args): + wm.close(self) Modified: trunk/pybridge/pybridge/ui/dialog_newtable.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-04-03 11:59:11 UTC (rev 395) +++ trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-04-03 12:00:17 UTC (rev 396) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,7 +10,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -19,7 +19,7 @@ import gtk from wrapper import GladeWrapper -import utils +from manager import wm class DialogNewtable(GladeWrapper): @@ -31,31 +31,33 @@ pass + def createSuccess(self, table): + wm.close(self) + + + def createFailure(self, reason): + error = reason.getErrorMessage() + dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, + type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_OK) + dialog.set_markup(_('Could not create new table.')) + dialog.format_secondary_text(_('Reason: %s') % error) + + dialog.run() + dialog.destroy() + + # Signal handlers. def on_cancelbutton_clicked(self, widget, *args): - utils.windows.close('dialog_newtable') + wm.close(self) def on_okbutton_clicked(self, widget, *args): - - def success(table): - utils.windows.close('dialog_newtable') - - def failure(reason): - error = reason.getErrorMessage() - dialog = gtk.MessageDialog(parent=self.window, - flags=gtk.DIALOG_MODAL, - type=gtk.MESSAGE_ERROR, - buttons=gtk.BUTTONS_OK, - message_format=error) - dialog.run() - dialog.destroy() - tableid = self.entry_tablename.get_text() d = self.parent.joinTable(tableid, host=True) - d.addCallbacks(success, failure) + d.addCallbacks(self.createSuccess, self.createFailure) def on_tablename_changed(self, widget, *args): Modified: trunk/pybridge/pybridge/ui/dialog_preferences.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_preferences.py 2007-04-03 11:59:11 UTC (rev 395) +++ trunk/pybridge/pybridge/ui/dialog_preferences.py 2007-04-03 12:00:17 UTC (rev 396) @@ -21,7 +21,7 @@ from wrapper import GladeWrapper import pybridge.environment as env -import utils +from manager import wm class DialogPreferences(GladeWrapper): @@ -53,10 +53,10 @@ def on_cancelbutton_clicked(self, widget, *args): print "cancel" - utils.windows.close(self.glade_name) + wm.close(self) def on_okbutton_clicked(self, widget, *args): print "ok" - utils.windows.close(self.glade_name) + wm.close(self) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-03 13:26:32
|
Revision: 397 http://svn.sourceforge.net/pybridge/?rev=397&view=rev Author: umgangee Date: 2007-04-03 06:26:31 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Fix WindowBridgetable to open/close WindowBidbox; clean up WindowBidbox code. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_bidbox.py trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/window_bidbox.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bidbox.py 2007-04-03 12:00:17 UTC (rev 396) +++ trunk/pybridge/pybridge/ui/window_bidbox.py 2007-04-03 13:26:31 UTC (rev 397) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2005 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,7 +10,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -24,15 +24,14 @@ from pybridge.bridge.call import Bid, Pass, Double, Redouble from pybridge.bridge.call import Level, Strain -CALL_NAMES = {'bid' : Bid, 'pass' : Pass, - 'double' : Double, 'redouble' : Redouble, } +CALL_NAMES = {'bid': Bid, 'pass': Pass, 'double': Double, 'redouble': Redouble} -LEVEL_NAMES = {'1' : Level.One, '2' : Level.Two, '3' : Level.Three, - '4' : Level.Four, '5' : Level.Five, '6' : Level.Six, - '7' : Level.Seven, } +LEVEL_NAMES = {'1': Level.One, '2': Level.Two,'3': Level.Three, + '4': Level.Four, '5': Level.Five, '6': Level.Six, + '7': Level.Seven, } -STRAIN_NAMES = {'club' : Strain.Club, 'diamond' : Strain.Diamond, - 'heart' : Strain.Heart, 'spade' : Strain.Spade, +STRAIN_NAMES = {'club': Strain.Club, 'diamond': Strain.Diamond, + 'heart': Strain.Heart, 'spade': Strain.Spade, 'nt' : Strain.NoTrump, } ALL_CALLS = [Pass(), Double(), Redouble()] + \ @@ -50,34 +49,57 @@ glade_name = 'window_bidbox' - def new(self): + def setUp(self): + self.game = None self.eventHandler = SimpleEventHandler(self) - self.parent.table.game.attach(self.eventHandler) - self.set_available_calls(self.parent.position, self.parent.table.game.bidding) - def set_available_calls(self, position, bidding): - """Enables buttons representing the given calls.""" - if bidding.whoseTurn() == position: - self.window.set_property('sensitive', True) - for call in ALL_CALLS: - button = self.get_button_from_call(call) - button.set_property('sensitive', bidding.isValidCall(call)) - else: - self.window.set_property('sensitive', False) + def tearDown(self): + if self.game: + self.game.detach(self.eventHandler) -# Registered event handlers. + def monitor(self, game, position, callSelected): + """Monitor the state of bidding in game. + + @param game: the BridgeGame for which to observe bidding session. + @param position: if user is playing, their position in the game. + @param callSelected: a handler to invoke when user selects a call. + """ + if self.game: + self.game.detach(self.eventHandler) + self.game = game + self.position = position + self.callSelected = callSelected + self.game.attach(self.eventHandler) + self.enableCalls() + + +# Event handlers. + + def event_makeCall(self, call, position): - self.set_available_calls(self.parent.position, self.parent.table.game.bidding) + self.enableCalls() # Utility methods. - def get_button_from_call(self, call): + def enableCalls(self): + """Enables buttons representing the calls available to player.""" + if self.position == self.game.getTurn(): + self.window.set_property('sensitive', True) + for call in ALL_CALLS: + button = self.getButtonFromCall(call) + isvalid = self.game.bidding.isValidCall(call) + button.set_property('sensitive', isvalid) + else: + self.window.set_property('sensitive', False) + + + def getButtonFromCall(self, call): """Returns a pointer to GtkButton object representing given call.""" callname = [k for k,v in CALL_NAMES.items() if isinstance(call, v)][0] if isinstance(call, Bid): @@ -88,14 +110,14 @@ return getattr(self, 'button_%s' % callname) - def get_call_from_button(self, widget): + def getCallFromButton(self, widget): """Returns an instance of the call represented by given GtkButton.""" text = widget.get_name().split('_') # "button", calltype, level, strain calltype = CALL_NAMES[text[1]] - if calltype == Bid: + if calltype is Bid: level = LEVEL_NAMES[text[2]] strain = STRAIN_NAMES[text[3]] - return calltype(level, strain) + return Bid(level, strain) return calltype() @@ -103,12 +125,8 @@ def on_call_clicked(self, widget, *args): - """Builds a call object and submits.""" - # Do not check validity of call: the server will do that. - # If call is invalid, ignore the resultant errback. - call = self.get_call_from_button(widget) - d = self.parent.player.callRemote('makeCall', call) - d.addErrback(lambda r: True) # Ignore any error. + call = self.getCallFromButton(widget) + self.callSelected(call) # Invoke external handler. def on_window_bidbox_delete_event(self, widget, *args): Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-03 12:00:17 UTC (rev 396) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-03 13:26:31 UTC (rev 397) @@ -24,6 +24,8 @@ from eventhandler import SimpleEventHandler from manager import WindowManager +from window_bidbox import WindowBidbox + from pybridge.network.error import GameError from pybridge.bridge.call import Bid, Pass, Double, Redouble from pybridge.bridge.symbols import Direction, Level, Strain, Rank, Vulnerable @@ -124,13 +126,13 @@ def tearDown(self): - print "Cleaning up" - self.table = None # Dereference table. # Close all child windows. - for windowname in self.children.keys(): - self.children.close(windowname) + for window in self.children.values(): + self.children.close(window) + self.table = None # Dereference table. + def errback(self, failure): print "Error: %s" % failure.getErrorMessage() @@ -177,8 +179,10 @@ # If user is a player and bidding in progress, open bidding box. if self.player and not self.table.game.bidding.isComplete(): - self.children.open('window_bidbox', parent=self) + bidbox = self.children.open(WindowBidbox, parent=self) + bidbox.monitor(self.table.game, self.position, self.on_call_selected) + # Initialise seat menu and player labels. for position in Direction: player = self.table.players.get(position) # Player name or None. @@ -421,15 +425,17 @@ d = self.player.callRemote('getHand') d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) - self.children.open('window_bidbox', parent=self) + bidbox = self.children.open(WindowBidbox, parent=self) + bidbox.monitor(self.table.game, self.position, self.on_call_selected) def event_makeCall(self, call, position): self.addCall(call, position) self.setTurnIndicator() if self.table.game.bidding.isComplete(): - self.children.close('window_bidbox') # If playing. self.setContract() + if self.children.get(WindowBidbox): # If a player. + self.children.close(self.children[WindowBidbox]) def event_playCard(self, card, position): @@ -507,11 +513,16 @@ # Signal handlers. + def on_call_selected(self, call): + if self.player: + d = self.player.callRemote('makeCall', call) + d.addErrback(self.errback) + + def on_card_clicked(self, card, position): if self.player: d = self.player.callRemote('playCard', card) d.addErrback(self.errback) - #d.addErrback(lambda _: True) # Ignore any error. def on_seat_activated(self, widget, position): @@ -528,7 +539,8 @@ d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) if not self.table.game.bidding.isComplete(): - self.children.open('window_bidbox', parent=self) + bidbox = self.children.open(WindowBidbox, parent=self) + bidbox.monitor(self.table.game, self.position, self.on_call_selected) d = self.table.joinGame(position) d.addCallbacks(success, self.errback) @@ -549,7 +561,8 @@ self.position = None self.takeseat.set_property('sensitive', True) self.leaveseat.set_property('sensitive', False) - self.children.close('window_bidbox') # If open. + if self.children.get(WindowBidbox): + self.children.close(self.children[WindowBidbox]) d = self.table.leaveGame(self.position) d.addCallbacks(success, self.errback) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-06 17:59:18
|
Revision: 402 http://svn.sourceforge.net/pybridge/?rev=402&view=rev Author: umgangee Date: 2007-04-06 10:58:59 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Update references to roster classes, remove old event handler mechanism. Modified Paths: -------------- trunk/pybridge/pybridge/ui/eventhandler.py trunk/pybridge/pybridge/ui/window_main.py Modified: trunk/pybridge/pybridge/ui/eventhandler.py =================================================================== --- trunk/pybridge/pybridge/ui/eventhandler.py 2007-04-06 17:55:41 UTC (rev 401) +++ trunk/pybridge/pybridge/ui/eventhandler.py 2007-04-06 17:58:59 UTC (rev 402) @@ -38,111 +38,3 @@ if method: method(*args, **kwargs) - - - -class EventHandler: - """An implementation of ITableEvents.""" - - #implements(IServerEvents, ITableEvents, IBridgeTableEvents) - - - def __init__(self): - self.callbacks = {} - - -# Implementation of IServerEvents. - - - def connectionLost(self, connector, reason): - print "Lost connection:", reason.getErrorMessage() - self.runCallbacks('connectionLost', connector, reason) - - - def tableOpened(self, table): - self.runCallbacks('tableOpened', table) - - - def tableClosed(self, table): - self.runCallbacks('tableClosed', table) - - - def userLoggedIn(self, user): - self.runCallbacks('userLoggedIn', user) - - - def userLoggedOut(self, user): - self.runCallbacks('userLoggedOut', user) - - -# Implementation of ITableEvents. - - - def observerAdded(self, table, observer): - self.runCallbacks('observerAdded', table, observer) - - - def observerRemoved(self, table, observer): - self.runCallbacks('observerRemoved', table, observer) - - - def playerAdded(self, table, player, position): - self.runCallbacks('playerAdded', table, player, position) - - - def playerRemoved(self, table, player, position): - self.runCallbacks('playerRemoved', table, player, position) - - - def messageReceived(self, table, message, sender, recipients): - self.runCallbacks('messageReceived', table, message, sender, recipients) - - -# Implementation of IBridgeTableEvents. - - - def gameStarted(self, table, dealer, vulnNS, vulnEW): - self.runCallbacks('gameStarted', table, dealer, vulnNS, vulnEW) - - - def gameFinished(self, table): - self.runCallbacks('gameFinished', table) - - - def gameCallMade(self, table, call, position): - self.runCallbacks('gameCallMade', table, call, position) - - - def gameCardPlayed(self, table, card, position): - self.runCallbacks('gameCardPlayed', table, card, position) - - - def gameHandRevealed(self, table, hand, position): - self.runCallbacks('gameHandRevealed', table, hand, position) - - -# Methods to manipulate the callback lists. - - - def registerCallbacksFor(self, window, events): - """Places window object in callback list for each event.""" - for event in events: - if event not in self.callbacks: - self.callbacks[event] = [] - self.callbacks[event].append(window) - - - def unregister(self, window, events): - """Removes window object from callback list for each event.""" - for event in events: - self.callbacks[event].remove(window) - - - def runCallbacks(self, event, *args, **kwargs): - for window in self.callbacks.get(event, []): - callback = getattr(window, 'event_%s' % event) - callback(*args, **kwargs) - - -eventhandler = EventHandler() - Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-04-06 17:55:41 UTC (rev 401) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-04-06 17:58:59 UTC (rev 402) @@ -35,8 +35,6 @@ from dialog_preferences import DialogPreferences from window_bridgetable import WindowBridgetable -from eventhandler import SimpleEventHandler, eventhandler # TODO: remove - TABLE_ICON = env.find_pixmap("table.png") USER_ICON = env.find_pixmap("user.png") @@ -45,8 +43,6 @@ glade_name = 'window_main' - callbacks = ('tableOpened', 'tableClosed', 'userLoggedIn', 'userLoggedOut') - tableview_icon = gtk.gdk.pixbuf_new_from_file_at_size(TABLE_ICON, 48, 48) peopleview_icon = gtk.gdk.pixbuf_new_from_file_at_size(USER_ICON, 48, 48) @@ -70,18 +66,16 @@ self.peopleview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) self.peopleview.set_model(self.peopleview_model) - # Register event callbacks. + # Attach event handler to listen for events. self.eventHandler = SimpleEventHandler(self) client.attach(self.eventHandler) - client.setEventHandler(eventhandler) # REMOVE - eventhandler.registerCallbacksFor(self, self.callbacks) # REMOVE if not wm.get(DialogConnection): wm.open(DialogConnection, parent=self) def tearDown(self): - #eventhandler.unregister(self, self.callbacks) + # TODO: detach event handler from all attached subjects. # Close all windows. for window in wm.values(): @@ -123,36 +117,54 @@ return d -# Registered event handlers. +# Event handlers. - def event_connect(self): + def event_connectedAsUser(self, username): self.notebook.set_property('sensitive', True) self.menu_connect.set_property('visible', False) self.menu_disconnect.set_property('visible', True) + self.menu_newtable.set_property('sensitive', True) + self.newtable.set_property('sensitive', True) - def event_disconnect(self): + def event_connectionLost(self, reason): for table in self.tables.values(): self.tables.close(table) self.notebook.set_property('sensitive', False) self.menu_connect.set_property('visible', True) self.menu_disconnect.set_property('visible', False) + self.menu_newtable.set_property('sensitive', False) + self.newtable.set_property('sensitive', False) + self.tableview_model.clear() self.peopleview_model.clear() + def event_gotRoster(self, name, roster): + lookup = {'tables' : (self.tableview_model, self.tableview_icon), + 'users' : (self.peopleview_model, self.peopleview_icon)} + + try: + model, icon = lookup[name] + for id, info in roster.items(): + model.append([id, icon]) + roster.attach(self.eventHandler) + except KeyError: + pass # Ignore an unrecognised roster. + + def event_leaveTable(self, tableid): - self.tables.close(self.tables[tableid]) + self.tables.close(self.tables[tableid]) # Close window. - def event_tableOpened(self, tableid): + def event_openTable(self, tableid, info): """Adds a table to the table listing.""" self.tableview_model.append([tableid, self.tableview_icon]) - def event_tableClosed(self, tableid): + def event_closeTable(self, tableid): """Removes a table from the table listing.""" def func(model, path, iter, user_data): @@ -163,12 +175,12 @@ self.tableview_model.foreach(func, tableid) - def event_userLoggedIn(self, user): + def event_userLogin(self, username, info): """Adds a user to the people listing.""" - self.peopleview_model.append([user, self.peopleview_icon]) + self.peopleview_model.append([username, self.peopleview_icon]) - def event_userLoggedOut(self, user): + def event_userLogout(self, username): """Removes a user from the people listing.""" def func(model, path, iter, user_data): @@ -176,7 +188,7 @@ model.remove(iter) return True - self.peopleview_model.foreach(func, user) + self.peopleview_model.foreach(func, username) # Signal handlers. @@ -205,7 +217,7 @@ # Display information about table. self.frame_tableinfo.set_property('sensitive', True) self.label_tableid.set_text(tableid) - self.label_tabletype.set_text(client.tablesAvailable[tableid]['type']) + self.label_tabletype.set_text(client.tableRoster[tableid]['game']) else: self.frame_tableinfo.set_property('sensitive', False) self.label_tableid.set_text('') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-18 15:04:58
|
Revision: 412 http://svn.sourceforge.net/pybridge/?rev=412&view=rev Author: umgangee Date: 2007-04-18 08:04:50 -0700 (Wed, 18 Apr 2007) Log Message: ----------- Implement table rotation to CardArea, so user's hand is displayed at the bottom to give a "bird's eye view" of the table. In CardArea, store list of visible cards instead of list of cards to omit. Modified Paths: -------------- trunk/pybridge/pybridge/ui/cardarea.py trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2007-04-09 15:33:32 UTC (rev 411) +++ trunk/pybridge/pybridge/ui/cardarea.py 2007-04-18 15:04:50 UTC (rev 412) @@ -41,7 +41,7 @@ class CardArea(CairoCanvas): - """This widget displays a graphical view of card hands and tricks. + """This widget is a graphical display of tricks and hands of cards. Requirements: Cairo (>=1.0), PyGTK (>= 2.8). """ @@ -66,10 +66,11 @@ self.on_card_clicked = lambda card, position: True self.on_hand_clicked = lambda position: True + self.focus = Direction.South self.hands = {} self.trick = None self.players = {} - self.set_player_mapping(Direction.South) + self.set_player_mapping(Direction.South, redraw=False) self.connect('button_press_event', self.button_press) self.add_events(gtk.gdk.BUTTON_PRESS_MASK) @@ -96,21 +97,21 @@ context.reset_clip() - def set_hand(self, hand, player, facedown=False, omit=[]): - """Sets the hand of player. Draws cards in hand to context. + def set_hand(self, hand, position, facedown=False, visible=[]): + """Sets the hand of player at position. Draws cards in hand to context. The hand is buffered into an ImageSurface, since hands change infrequently and multiple calls to draw_card() are expensive. @param hand: a list of Card objects. - @param player: a member of Direction. + @param position: a member of Direction. @param facedown: if True, cards are drawn face-down. - @param omit: a list of elements of hand not to draw. + @param visible: a list of elements of hand to draw. """ def get_coords_for_hand(): coords = {} - if player in (self.TOP, self.BOTTOM): + if position in (self.TOP, self.BOTTOM): pos_y = 0 if facedown is True: # Draw cards in one continuous row. for index, card in enumerate(hand): @@ -127,14 +128,14 @@ else: # LEFT or RIGHT. if facedown is True: # Wrap cards to a 4x4 grid. for index, card in enumerate(hand): - adjust = player == self.RIGHT and index == 12 and 3 + adjust = position == self.RIGHT and index == 12 and 3 pos_x = ((index % 4) + adjust) * self.spacing_x pos_y = (index / 4) * self.spacing_y coords[card] = (pos_x, pos_y) else: longest = max([len(cards) for cards in suits.values()]) for index, card in enumerate(hand): - adjust = player == self.RIGHT and longest - len(suits[card.suit]) + adjust = position == self.RIGHT and longest - len(suits[card.suit]) pos_x = (suits[card.suit].index(card) + adjust) * self.spacing_x pos_y = RED_BLACK.index(card.suit) * self.spacing_y coords[card] = (pos_x, pos_y) @@ -153,7 +154,7 @@ for suit in RED_BLACK: hand.extend(suits[suit]) - saved = self.hands.get(player) + saved = self.hands.get(position) if saved and saved['hand'] == hand: # If hand has been set previously, do not recalculate coords. coords = saved['coords'] @@ -166,22 +167,24 @@ surface, context = self.new_surface(width, height) # Draw cards to surface. - visible = [(i, card) for i, card in enumerate(hand) if card not in omit] - for i, card in visible: - pos_x, pos_y = coords[card] - self.draw_card(context, pos_x, pos_y, card) + for i, card in enumerate(hand): + if card in visible: + pos_x, pos_y = coords[card] + self.draw_card(context, pos_x, pos_y, card) - # Save - self.hands[player] = {'hand' : hand, 'visible' : visible, - 'surface' : surface, 'coords' : coords, } + # Save. + self.hands[position] = {'hand' : hand, 'visible' : visible, + 'surface' : surface, 'coords' : coords, + 'facedown' : facedown} - id = 'hand-%s' % player # Identifier for this item. + id = 'hand-%s' % position # Identifier for this item. if id in self.items: self.update_item(id, source=surface) else: xy = {self.TOP : (0.5, 0.15), self.BOTTOM : (0.5, 0.85), self.LEFT : (0.15, 0.5), self.RIGHT : (0.85, 0.5), } - self.add_item(id, surface, xy[player], 0) + opacity = (self.players.get(position) is None) and 0.5 or 1 + self.add_item(id, surface, xy[position], 0, opacity=opacity) def set_player_name(self, position, name=None): @@ -190,11 +193,12 @@ @param position: the position of the player. @param name: the name of the player, or None. """ - self.players[position] = None + self.players[position] = name # If no name specified, show hand at position as translucent. - opacity = (name is None) and 0.5 or 1 - self.update_item('hand-%s' % position, opacity=opacity) + if ('hand-%s' % position) in self.items: + opacity = (name is None) and 0.5 or 1 + self.update_item('hand-%s' % position, opacity=opacity) id = 'player-%s' % position @@ -230,18 +234,37 @@ self.add_item(id, surface, xy[position], 2) - def set_player_mapping(self, focus=Direction.South): + def set_player_mapping(self, focus=Direction.South, redraw=True): """Sets the mapping between players at table and positions of hands. @param focus: the position to be drawn "closest" to the observer. + @param redraw: if True, redraw the card area display immediately. """ # Assumes Direction elements are ordered clockwise from North. order = Direction[focus.index:] + Direction[:focus.index] for player, attr in zip(order, ('BOTTOM', 'LEFT', 'TOP', 'RIGHT')): setattr(self, attr, player) - # TODO: set player labels. + # Only redraw if focus has changed. + if redraw and focus != self.focus: + self.focus = focus + self.clear() # Wipe all saved ImageSurface objects - not subtle! + # Use a copy of self.hands, since it will be changed by set_hand(). + hands = self.hands.copy() + self.hands.clear() + for position in Direction: + self.set_player_name(position, self.players.get(position)) + self.set_hand(hands[position]['hand'], position, + facedown=hands[position]['facedown'], + visible=hands[position]['visible']) + + trick = self.trick + self.trick = None + self.set_trick(trick) + + + def set_trick(self, trick): """Sets the current trick. Draws representation of current trick to context. @@ -252,18 +275,19 @@ self.LEFT : (0.425, 0.5), self.RIGHT : (0.575, 0.5), } if trick: + leader, cards = trick # The order of play is the leader, then clockwise around Direction. - leader = trick[0] order = Direction[leader.index:] + Direction[:leader.index] - for i, player in enumerate(order): - id = 'trick-%s' % player - old_card = self.trick and self.trick[1].get(player) or None - new_card = trick[1].get(player) + for i, position in enumerate(order): + id = 'trick-%s' % position + old_card = self.trick and self.trick[1].get(position) or None + new_card = cards.get(position) or None + # If old card matches new card, take no action. if old_card is None and new_card is not None: surface, context = self.new_surface(self.card_width, self.card_height) self.draw_card(context, 0, 0, new_card) - self.add_item(id, surface, xy[player], z_index=i+1) + self.add_item(id, surface, xy[position], z_index=i+1) elif new_card is None and old_card is not None: self.remove_item(id) elif old_card != new_card: @@ -275,7 +299,7 @@ for player in self.trick[1]: self.remove_item('trick-%s' % player) - self.trick = trick # Save trick and return. + self.trick = trick # Save trick. def button_press(self, widget, event): @@ -299,12 +323,13 @@ # Determine the card in hand which was clicked. pos_x, pos_y = event.x - hand_x, event.y - hand_y # Iterate through visible cards backwards. - for i, card in self.hands[position]['visible'][::-1]: - x, y = card_coords[card] - if (x <= pos_x <= x + self.card_width) and \ - (y <= pos_y <= y + self.card_height): - self.on_card_clicked(card, position) - break + for card in reversed(self.hands[position]['hand']): + if card in self.hands[position]['visible']: + x, y = card_coords[card] + if (x <= pos_x <= x + self.card_width) and \ + (y <= pos_y <= y + self.card_height): + self.on_card_clicked(card, position) + break return True # Expected to return True. Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-09 15:33:32 UTC (rev 411) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-18 15:04:50 UTC (rev 412) @@ -136,6 +136,7 @@ def errback(self, failure): print "Error: %s" % failure.getErrorMessage() + #print failure.getBriefTraceback() def setTable(self, table): @@ -306,17 +307,23 @@ @param position: @param all: If True, do not filter out cards played. """ + try: + hand = self.table.game.getHand(position) + facedown = False + except GameError: # Unknown hand. + hand = range(13) + facedown = True + if all is True or self.table.game.play is None: - played = [] + available = hand else: played = self.table.game.play.played[position] + if facedown: # Draw cards face down for unknown hand. + available = range(13 - len(played)) + else: + available = [card for card in hand if card not in played] - try: - hand = self.table.game.getHand(position) - self.cardarea.set_hand(hand, position, omit=played) - except GameError: # Unknown hand: draw cards face down. - cards, played = range(13), range(len(played)) - self.cardarea.set_hand(cards, position, facedown=True, omit=played) + self.cardarea.set_hand(hand, position, facedown, visible=available) def redrawTrick(self): @@ -557,11 +564,15 @@ self.takeseat.set_property('visible', False) self.leaveseat.set_property('visible', True) - # If game is running and bidding is active, open bidding box. + + self.cardarea.set_player_mapping(self.position) + if self.table.game.inProgress(): d = self.player.callRemote('getHand') d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) + + # If game is running and bidding is active, open bidding box. if not self.table.game.bidding.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.monitor(self.table.game, self.position, self.on_call_selected) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-06-18 11:56:50
|
Revision: 426 http://svn.sourceforge.net/pybridge/?rev=426&view=rev Author: umgangee Date: 2007-06-18 04:56:47 -0700 (Mon, 18 Jun 2007) Log Message: ----------- Switch to ConfigObj configuration management. Modified Paths: -------------- trunk/pybridge/pybridge/ui/__init__.py trunk/pybridge/pybridge/ui/dialog_connection.py Modified: trunk/pybridge/pybridge/ui/__init__.py =================================================================== --- trunk/pybridge/pybridge/ui/__init__.py 2007-06-18 11:56:20 UTC (rev 425) +++ trunk/pybridge/pybridge/ui/__init__.py 2007-06-18 11:56:47 UTC (rev 426) @@ -32,8 +32,8 @@ gettext.textdomain('pybridge') gettext.install('pybridge') -filename = env.find_config_client('client.cfg') -settings = Settings(filename, ['Connection', 'General']) +import config +config.load() def run(): @@ -47,3 +47,5 @@ reactor.run() gtk.main() + config.save() # Save config at exit. + Modified: trunk/pybridge/pybridge/ui/dialog_connection.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_connection.py 2007-06-18 11:56:20 UTC (rev 425) +++ trunk/pybridge/pybridge/ui/dialog_connection.py 2007-06-18 11:56:47 UTC (rev 426) @@ -20,8 +20,8 @@ from wrapper import GladeWrapper from pybridge.network.client import client +from config import config from manager import wm -from pybridge.ui import settings TCP_PORT = 5040 @@ -34,13 +34,14 @@ def setUp(self): # Read connection parameters from client settings. - connection = settings.connection + connection = config['Connection'] if connection: - self.entry_hostname.set_text(connection['hostname']) - self.entry_portnum.set_text(connection['portnum']) - self.entry_username.set_text(connection['username']) - self.entry_password.set_text(connection['password']) - self.check_savepassword.set_active(connection['password'] != '') + self.entry_hostname.set_text(connection.get('HostAddress', 'localhost')) + self.entry_portnum.set_text(str(connection.get('Port', TCP_PORT))) + self.entry_username.set_text(connection.get('Username', '')) + password = connection.get('Password', '').decode('rot13') + self.entry_password.set_text(password) + self.check_savepassword.set_active(password != '') else: self.entry_portnum.set_text(str(TCP_PORT)) @@ -49,14 +50,17 @@ """Actions to perform when connecting succeeds.""" # Save connection information. - settings.connection = {} - settings.connection['hostname'] = self.entry_hostname.get_text() - settings.connection['portnum'] = self.entry_portnum.get_text() - settings.connection['username'] = self.entry_username.get_text() + connection = config['Connection'] + connection['HostAddress'] = self.entry_hostname.get_text() + connection['PortNumber'] = int(self.entry_portnum.get_text()) + connection['Username'] = self.entry_username.get_text() if self.check_savepassword.get_active(): - settings.connection['password'] = self.entry_password.get_text() - else: # Flush password. - settings.connection['password'] = '' + # Encode password, to confuse password sniffer software. + # ROT13 encoding does *not* provide security! + password = self.entry_password.get_text().encode('rot13') + else: + password = '' # Flush password. + connection['Password'] = password wm.close(self) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-06-22 15:30:39
|
Revision: 439 http://svn.sourceforge.net/pybridge/?rev=439&view=rev Author: umgangee Date: 2007-06-22 08:30:41 -0700 (Fri, 22 Jun 2007) Log Message: ----------- Use cardstyle/background specified by user in preferences, relocate translatable bridge elements to vocabulary module. Modified Paths: -------------- trunk/pybridge/pybridge/ui/canvas.py trunk/pybridge/pybridge/ui/cardarea.py Modified: trunk/pybridge/pybridge/ui/canvas.py =================================================================== --- trunk/pybridge/pybridge/ui/canvas.py 2007-06-22 15:26:18 UTC (rev 438) +++ trunk/pybridge/pybridge/ui/canvas.py 2007-06-22 15:30:41 UTC (rev 439) @@ -20,15 +20,16 @@ import cairo import pybridge.environment as env +from config import config class CairoCanvas(gtk.DrawingArea): - """Provides a simple canvas layer for . - - Overlapping items. - """ + """Provides a simple canvas layer for the display of graphics.""" - background_path = env.find_pixmap('baize.png') + # TODO: enhance documentation. + + background_path = config['Appearance'].get('Background', + env.find_pixmap('baize.png')) background = cairo.ImageSurface.create_from_png(background_path) pattern = cairo.SurfacePattern(background) pattern.set_extend(cairo.EXTEND_REPEAT) @@ -89,7 +90,8 @@ """ @param id: unique identifier for source. @param source: if specified, ImageSurface. - @param xy: if specified, tuple providing (x, y) coords for source in backing. + @param xy: if specified, tuple providing (x, y) coords for source + in backing. @param z_index: if specified, integer. @param opacity: if specified, integer in range 0 to 1. """ Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2007-06-22 15:26:18 UTC (rev 438) +++ trunk/pybridge/pybridge/ui/cardarea.py 2007-06-22 15:30:41 UTC (rev 439) @@ -23,6 +23,8 @@ import pybridge.environment as env from canvas import CairoCanvas +from config import config +from vocabulary import * from pybridge.bridge.card import Card from pybridge.bridge.symbols import Direction, Rank, Suit @@ -36,10 +38,7 @@ # The red-black-red-black ordering convention. RED_BLACK = [Suit.Diamond, Suit.Club, Suit.Heart, Suit.Spade] -DIRECTION_SYMBOLS = {Direction.North : _('North'), Direction.East : _('East'), - Direction.South : _('South'), Direction.West : _('West') } - class CardArea(CairoCanvas): """This widget is a graphical display of tricks and hands of cards. @@ -47,7 +46,8 @@ """ # Load card mask. - card_mask_path = env.find_pixmap('bonded.png') + card_mask_file = config['Appearance'].get('CardStyle', 'bonded.png') + card_mask_path = env.find_pixmap(card_mask_file) card_mask = cairo.ImageSurface.create_from_png(card_mask_path) font_description = pango.FontDescription('Sans Bold 10') @@ -205,9 +205,9 @@ layout = pango.Layout(self.create_pango_context()) layout.set_font_description(self.font_description) if name is None: - layout.set_text('%s' % DIRECTION_SYMBOLS[position]) + layout.set_text('%s' % DIRECTION_NAMES[position]) else: - layout.set_text('%s: %s' % (DIRECTION_SYMBOLS[position], name)) + layout.set_text('%s: %s' % (DIRECTION_NAMES[position], name)) # Create an ImageSurface respective to dimensions of text. width, height = layout.get_pixel_size() @@ -264,7 +264,6 @@ self.set_trick(trick) - def set_trick(self, trick): """Sets the current trick. Draws representation of current trick to context. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-04 14:34:34
|
Revision: 459 http://svn.sourceforge.net/pybridge/?rev=459&view=rev Author: umgangee Date: 2007-07-04 07:34:35 -0700 (Wed, 04 Jul 2007) Log Message: ----------- Use sys.excepthook to provide a GtkDialog when an unhandled exception is raised, alerting user to the problem and prompting them to take action. Modified Paths: -------------- trunk/pybridge/pybridge/ui/__init__.py Added Paths: ----------- trunk/pybridge/pybridge/ui/excepthook.py Modified: trunk/pybridge/pybridge/ui/__init__.py =================================================================== --- trunk/pybridge/pybridge/ui/__init__.py 2007-06-25 16:36:32 UTC (rev 458) +++ trunk/pybridge/pybridge/ui/__init__.py 2007-07-04 14:34:35 UTC (rev 459) @@ -37,6 +37,11 @@ def run(): """Starts the PyBridge client UI.""" + # Set exception hook to display error dialog. + import sys + from excepthook import excepthook + sys.excepthook = excepthook + from manager import wm from window_main import WindowMain wm.open(WindowMain) Added: trunk/pybridge/pybridge/ui/excepthook.py =================================================================== --- trunk/pybridge/pybridge/ui/excepthook.py (rev 0) +++ trunk/pybridge/pybridge/ui/excepthook.py 2007-07-04 14:34:35 UTC (rev 459) @@ -0,0 +1,63 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2007 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +""" +Captures exceptions and displays them to user in a GTK dialog, +instead of the console which may not be visible. + +Source: http://faq.pygtk.org/index.py?req=show&file=faq20.010.htp +""" + +import gtk +import traceback +from StringIO import StringIO + + +def excepthook(type, value, tb): + dialog = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL, + buttons=gtk.BUTTONS_CLOSE, type=gtk.MESSAGE_WARNING) + dialog.set_title(_('Program error')) + dialog.set_markup(_('PyBridge detected an unexpected program error. You should close and restart PyBridge.')) + dialog.format_secondary_markup(_('If you continue to experience this error, please submit a bug report, attaching the following error trace.')) + + # Set up display of traceback. + textview = gtk.TextView(); textview.show() + textview.set_editable(False) + sw = gtk.ScrolledWindow(); sw.show() + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.add(textview) + frame = gtk.Frame(); + frame.set_shadow_type(gtk.SHADOW_IN) + frame.add(sw) + frame.set_border_width(6) + dialog.vbox.add(frame) + textbuffer = textview.get_buffer() + trace = StringIO() + traceback.print_exception(type, value, tb, None, trace) + textbuffer.set_text(trace.getvalue()) + textview.set_size_request(320, 240) + + dialog.details = frame + dialog.details.show() + + def dialog_response_cb(dialog, response_id): + dialog.destroy() + + dialog.connect('response', dialog_response_cb) + dialog.run() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-09 14:10:27
|
Revision: 463 http://svn.sourceforge.net/pybridge/?rev=463&view=rev Author: umgangee Date: 2007-07-09 07:10:29 -0700 (Mon, 09 Jul 2007) Log Message: ----------- Split bridgetable window into a generic table window, and a subclass bridge-specific table. Re-implement table widge ts and windows in PyGTK, instead of Glade, for flexibility and modularity. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_bidbox.py trunk/pybridge/pybridge/ui/window_bridgetable.py trunk/pybridge/pybridge/ui/window_main.py Added Paths: ----------- trunk/pybridge/pybridge/ui/window_gametable.py Modified: trunk/pybridge/pybridge/ui/window_bidbox.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bidbox.py 2007-07-09 14:00:02 UTC (rev 462) +++ trunk/pybridge/pybridge/ui/window_bidbox.py 2007-07-09 14:10:29 UTC (rev 463) @@ -13,19 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import gtk -from wrapper import GladeWrapper +from pybridge.ui.config import config +from pybridge.ui.eventhandler import SimpleEventHandler +from pybridge.ui.vocabulary import * + import pybridge.bridge.call as Call -from config import config -from eventhandler import SimpleEventHandler -from vocabulary import * - class WindowBidbox(object): """The bidding box is presented to a player, during bidding. Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-07-09 14:00:02 UTC (rev 462) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-07-09 14:10:29 UTC (rev 463) @@ -17,100 +17,238 @@ import gtk -from wrapper import GladeWrapper from pybridge.network.client import client from pybridge.network.error import GameError -from cardarea import CardArea -from config import config -from eventhandler import SimpleEventHandler -from manager import WindowManager -from vocabulary import * +from pybridge.ui.cardarea import CardArea +from pybridge.ui.config import config +from pybridge.ui.eventhandler import SimpleEventHandler +from pybridge.ui.manager import WindowManager +from pybridge.ui.vocabulary import * +from pybridge.ui.window_gametable import WindowGameTable from window_bidbox import WindowBidbox -class WindowBridgetable(GladeWrapper): +class BiddingView(gtk.TreeView): + """A view of all calls made in an auction.""" - glade_name = 'window_bridgetable' + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) - def setUp(self): - self.children = WindowManager() - self.eventHandler = SimpleEventHandler(self) - - self.table = None # Table currently displayed in window. - self.player, self.position = None, None - - # Set up "Take Seat" menu. - self.takeseat_items = {} - menu = gtk.Menu() - for position in Direction: - item = gtk.MenuItem(DIRECTION_NAMES[position], True) - item.connect('activate', self.on_seat_activated, position) - item.show() - menu.append(item) - self.takeseat_items[position] = item - self.takeseat.set_menu(menu) - - # Set up CardArea widget. - self.cardarea = CardArea() - self.cardarea.on_card_clicked = self.on_card_clicked - self.cardarea.on_hand_clicked = self.on_hand_clicked - self.cardarea.set_size_request(640, 480) - self.scrolled_cardarea.add_with_viewport(self.cardarea) - self.cardarea.show() - + self.store = gtk.ListStore(str, str, str, str) + self.set_model(self.store) + self.clear = self.store.clear renderer = gtk.CellRendererText() renderer.set_property('size-points', 12) renderer.set_property('xalign', 0.5) - # Set up bidding history and column display. - self.call_store = gtk.ListStore(str, str, str, str) - self.biddingview.set_model(self.call_store) + # Set up columns, each corresponding to a position. for index, position in enumerate(Direction): title = DIRECTION_NAMES[position] column = gtk.TreeViewColumn(title, renderer, markup=index) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - column.set_fixed_width(50) - self.biddingview.append_column(column) + self.append_column(column) - # Set up trick history and column display. - self.trick_store = gtk.ListStore(str, str, str, str) - self.trickview.set_model(self.trick_store) - for index, position in enumerate(Direction): - title = DIRECTION_NAMES[position] - column = gtk.TreeViewColumn(str(title), renderer, markup=index) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - column.set_fixed_width(50) - self.trickview.append_column(column) + def add_call(self, call, position): + """Adds call from specified position.""" + column = position.index + if column == 0 or self.store.get_iter_first() == None: + iter = self.store.append() + else: # Get bottom row. There must be a better way than this... + iter = self.store.get_iter_first() + while self.store.iter_next(iter) != None: + iter = self.store.iter_next(iter) + + format = render_call(call) + self.store.set(iter, column, format) + + + + +class ScoreView(gtk.TreeView): + """A display of contracts bid, their results and their scores.""" + + + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) + + self.store = gtk.ListStore(str, str, str, str) + self.set_model(self.store) + self.clear = self.store.clear renderer = gtk.CellRendererText() - # Set up score sheet and column display. - self.score_store = gtk.ListStore(str, str, str, str) - self.scoresheet.set_model(self.score_store) for index, title in enumerate([_('Contract'), _('Made'), _('N/S'), _('E/W')]): column = gtk.TreeViewColumn(title, renderer, markup=index) - self.scoresheet.append_column(column) + self.append_column(column) - # Set up observer listing. - self.observer_store = gtk.ListStore(str) - self.treeview_observers.set_model(self.observer_store) - column = gtk.TreeViewColumn(None, renderer, text=0) - self.observer_store.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.treeview_observers.append_column(column) + def add_score(self, game): + declarerWon, defenceWon = game.play.getTrickCount() + score = game.getScore() - def tearDown(self): - # Close all child windows. - for window in self.children.values(): - self.children.close(window) + textContract = render_contract(game.contract) + textMade = str(declarerWon) + if game.contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ + or game.contract['declarer'] in (Direction.East, Direction.West) and score < 0: + textNS, textEW = str(abs(score)), '' + else: + textNS, textEW = '', str(abs(score)) - self.table = None # Dereference table. + self.store.prepend([textContract, textMade, textNS, textEW]) + + +class BridgeDashboard(gtk.VBox): + """An at-a-glance display of the state of a bridge game.""" + + + def __init__(self): + gtk.VBox.__init__(self) + self.set_spacing(4) + + self.contract = gtk.Label() + self.pack_start(self.contract) + + hbox = gtk.HBox() + hbox.set_homogeneous(True) + hbox.set_spacing(6) + self.declarer_tricks = gtk.Label() + frame = gtk.Frame() + frame.set_label(_('Declarer')) + frame.set_label_align(0.5, 0.5) + frame.add(self.declarer_tricks) + hbox.pack_start(frame) + self.defence_tricks = gtk.Label() + frame = gtk.Frame() + frame.set_label(_('Defence')) + frame.set_label_align(0.5, 0.5) + frame.add(self.defence_tricks) + hbox.pack_start(frame) + self.pack_start(hbox) + + hbox = gtk.HBox() + hbox.set_homogeneous(True) + hbox.set_spacing(6) + # TODO: display board number? + self.dealer = gtk.Label() + self.dealer.set_alignment(0, 0.5) + hbox.pack_start(self.dealer) + self.vulnerability = gtk.Label() + self.vulnerability.set_alignment(0, 0.5) + hbox.pack_start(self.vulnerability) + self.pack_start(hbox) + + + def set_contract(self, game): + if game.contract: + text = render_contract(game.contract) + else: + text = _('No contract') + self.contract.set_markup("<span size='x-large'>%s</span>" % text) + + + def set_trickcount(self, game): + if game.play: + declarer, defence = game.play.getTrickCount() + required = game.contract['bid'].level.index + 7 + declarerNeeds = max(0, required - declarer) + defenceNeeds = max(0, 13 + 1 - required - defence) + else: + declarer, defence, declarerNeeds, defenceNeeds = 0, 0, 0, 0 + format = "<span size='x-large'><b>%s</b> (%s)</span>" + self.declarer_tricks.set_markup(format % (declarer, declarerNeeds)) + self.defence_tricks.set_markup(format % (defence, defenceNeeds)) + + + def set_dealer(self, game): + if game.inProgress(): + dealertext = "<b>%s</b>" % DIRECTION_NAMES[game.board['dealer']] + else: + dealertext = '' + self.dealer.set_markup(_('Dealer') + ': ' + dealertext) + + + def set_vulnerability(self, game): + if game.inProgress(): + vulntext = "<b>%s</b>" % VULN_SYMBOLS[game.board['vuln']] + else: + vulntext = '' + self.vulnerability.set_markup(_('Vuln') + ': ' + vulntext) + + + + +class WindowBridgeTable(WindowGameTable): + + gametype = _('Contract Bridge') + + stockdirections = [gtk.STOCK_GO_UP, gtk.STOCK_GO_FORWARD, + gtk.STOCK_GO_DOWN, gtk.STOCK_GO_BACK] + + + def setUp(self): + super(WindowBridgeTable, self).setUp() + + # Set up menu attached to 'Take Seat' toolbar button. + self.takeseat_menuitems = {} + menu = gtk.Menu() + for position, stock in zip(Direction, self.stockdirections): + item = gtk.ImageMenuItem(DIRECTION_NAMES[position], True) + item.set_image(gtk.image_new_from_stock(stock, gtk.ICON_SIZE_MENU)) + item.connect('activate', self.on_takeseat_clicked, position) + item.show() + menu.append(item) + self.takeseat_menuitems[position] = item + self.takeseat.set_menu(menu) + + # Set up CardArea widget. + self.cardarea = CardArea() + self.cardarea.on_card_clicked = self.on_card_clicked + self.cardarea.on_hand_clicked = self.on_hand_clicked + self.cardarea.set_size_request(640, 480) + self.gamearea.add(self.cardarea) + + # Set up sidebar. + self.dashboard = BridgeDashboard() + self.sidebar.pack_start(self.dashboard, expand=False) + + self.biddingview = BiddingView() + sw = gtk.ScrolledWindow() + sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + sw.add(self.biddingview) + frame = gtk.Frame() + frame.add(sw) + exp = gtk.Expander(_('Bidding')) + exp.set_expanded(True) + exp.add(frame) + self.sidebar.pack_start(exp) + +# self.lasttrick = CardArea() +# frame = gtk.Frame() +# frame.add(self.lasttrick) +# exp = gtk.Expander(_('Last Trick')) +# exp.set_expanded(True) +# exp.add(frame) +# self.sidebar.pack_start(exp) + + self.scoreview = ScoreView() + sw = gtk.ScrolledWindow() + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.add(self.scoreview) + frame = gtk.Frame() + frame.add(sw) + exp = gtk.Expander(_('Score Sheet')) + exp.set_expanded(False) + exp.add(frame) + self.sidebar.pack_start(exp) + + def errback(self, failure): print "Error: %s" % failure.getErrorMessage() #print failure.getBriefTraceback() @@ -121,12 +259,9 @@ @param table: the (now) focal table. """ - self.table = table - self.table.attach(self.eventHandler) - self.table.game.attach(self.eventHandler) - self.player, self.position = None, None + super(WindowBridgeTable, self).setTable(table) - self.window.set_title(_('Table %s (Bridge)') % self.table.id) + self.table.game.attach(self.eventHandler) self.resetGame() for position in Direction: @@ -140,21 +275,13 @@ for call in self.table.game.bidding.calls: position = self.table.game.bidding.whoCalled(call) - self.addCall(call, position) + self.biddingview.add_call(call, position) - self.setDealer() - self.setVulnerability() - - # If contract, set contract. - if self.table.game.bidding.isComplete(): - self.setContract() - # If playing, set trick counts. if self.table.game.play: for position, cards in self.table.game.play.played.items(): for card in cards: self.addCard(card, position) - self.setTrickCount() # If user is a player and bidding in progress, open bidding box. if self.player and not self.table.game.bidding.isComplete(): @@ -167,71 +294,41 @@ for position in Direction: player = self.table.players.get(position) # Player name or None. - available = player is None or position == self.position - self.takeseat_items[position].set_property('sensitive', available) - if player: - self.event_joinGame(player, position) - else: # Position vacant. - self.event_leaveGame(None, position) + avail = player is None or position == self.position + self.takeseat_menuitems[position].set_property('sensitive', avail) + # If player == None, this unsets player name. + self.cardarea.set_player_name(position, player) - # Initialise observer listing. - self.observer_store.clear() - for observer in self.table.observers: - self.event_addObserver(observer) - def resetGame(self): - """Clears bidding history, contract, trick counts.""" -# self.cardarea.clear() - self.call_store.clear() # Reset bidding history. - self.trick_store.clear() # Reset trick history. - self.setContract() # Reset contract. - self.setTrickCount() # Reset trick counts. + """Clear bidding history, contract, trick counts.""" + self.cardarea.clear() + self.biddingview.clear() # Reset bidding history. + self.dashboard.set_contract(self.table.game) + self.dashboard.set_trickcount(self.table.game) + self.dashboard.set_dealer(self.table.game) + self.dashboard.set_vulnerability(self.table.game) - def addCall(self, call, position): - """Adds call from specified player, to bidding tab.""" - column = position.index - if column == 0 or self.call_store.get_iter_first() == None: - iter = self.call_store.append() - else: # Get bottom row. There must be a better way than this... - iter = self.call_store.get_iter_first() - while self.call_store.iter_next(iter) != None: - iter = self.call_store.iter_next(iter) - format = render_call(call) - self.call_store.set(iter, column, format) - - def addCard(self, card, position): """""" - position = self.table.game.play.whoPlayed(card) - column = position.index - row = self.table.game.play.played[position].index(card) +# position = self.table.game.play.whoPlayed(card) +# column = position.index +# row = self.table.game.play.played[position].index(card) +# +# if self.trick_store.get_iter_first() == None: +# self.trick_store.append() +# iter = self.trick_store.get_iter_first() +# for i in range(row): +# iter = self.trick_store.iter_next(iter) +# if iter is None: +# iter = self.trick_store.append() +# +# format = render_card(card) +# self.trick_store.set(iter, column, format) - if self.trick_store.get_iter_first() == None: - self.trick_store.append() - iter = self.trick_store.get_iter_first() - for i in range(row): - iter = self.trick_store.iter_next(iter) - if iter is None: - iter = self.trick_store.append() - format = render_card(card) - self.trick_store.set(iter, column, format) - - - def addScore(self, contract, made, score): - textContract = render_contract(contract) - textMade = '%s' % made - if contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ - or contract['declarer'] in (Direction.East, Direction.West) and score < 0: - textNS, textEW = '%s' % abs(score), '' - else: - textNS, textEW = '', '%s' % abs(score) - self.score_store.prepend([textContract, textMade, textNS, textEW]) - - def gameComplete(self): # Display all previously revealed hands - the server will reveal the others. for position in self.table.game.visibleHands: @@ -242,13 +339,14 @@ dialog = gtk.MessageDialog(parent=self.window, type=gtk.MESSAGE_INFO) dialog.set_title(_('Game result')) - # Determine and display score in dialog box. + # Determine and display score in dialog box and score sheet. if self.table.game.contract: + self.scoreview.add_score(self.table.game) + declarerWon, defenceWon = self.table.game.play.getTrickCount() required = self.table.game.contract['bid'].level.index + 7 offset = declarerWon - required score = self.table.game.getScore() - self.addScore(self.table.game.contract, declarerWon, score) fields = {'contract': render_contract(self.table.game.contract), 'offset': abs(offset) } @@ -336,54 +434,6 @@ self.cardarea.set_trick(trick) -# Methods to set information displayed on side panel. - - - def setContract(self): - """Sets the contract label from contract.""" - format = "<span size=\"x-large\">%s</span>" - - if self.table.game.contract: - text = render_contract(self.table.game.contract) - self.label_contract.set_markup(format % text) - self.label_contract.set_property('sensitive', True) - else: - self.label_contract.set_markup(format % _('No contract')) - self.label_contract.set_property('sensitive', False) - - - def setDealer(self): - format = "<b>%s</b>" - - dealer = '' - if self.table.game.inProgress(): - dealer = DIRECTION_NAMES[self.table.game.board['dealer']] - - self.label_dealer.set_markup(format % dealer) - - - def setTrickCount(self): - """Sets the trick counter labels for declarer and defence.""" - format = "<span size=\"x-large\"><b>%s</b> (%s)</span>" - - if self.table.game.play: - declarer, defence = self.table.game.play.getTrickCount() - required = self.table.game.contract['bid'].level.index + 7 - declarerNeeds = max(0, required - declarer) - defenceNeeds = max(0, 13 + 1 - required - defence) - - self.label_declarer.set_markup(format % (declarer, declarerNeeds)) - self.label_defence.set_markup(format % (defence, defenceNeeds)) - self.frame_declarer.set_property('sensitive', True) - self.frame_defence.set_property('sensitive', True) - - else: # Reset trick counters. - self.label_declarer.set_markup(format % (0, 0)) - self.label_defence.set_markup(format % (0, 0)) - self.frame_declarer.set_property('sensitive', False) - self.frame_defence.set_property('sensitive', False) - - def setTurnIndicator(self): """Sets the statusbar text to indicate which player is on turn.""" context = self.statusbar.get_context_id('turn') @@ -412,38 +462,14 @@ self.statusbar.push(context, text) - def setVulnerability(self): - """Sets the vulnerability indicators.""" - format = "<b>%s</b>" - vulnerable = '' - if self.table.game.inProgress(): - vulnerable = VULN_SYMBOLS[self.table.game.board['vuln']] - - self.label_vuln.set_markup(format % vulnerable) - - # Registered event handlers. - def event_addObserver(self, observer): - self.observer_store.append([observer]) - - - def event_removeObserver(self, observer): - - def func(model, path, iter, user_data): - if model.get_value(iter, 0) in user_data: - model.remove(iter) - return True - - self.observer_store.foreach(func, observer) - - def event_joinGame(self, player, position): self.cardarea.set_player_name(position, player) # Disable menu item corresponding to position. - widget = self.takeseat_items[position] + widget = self.takeseat_menuitems[position] widget.set_property('sensitive', False) # If all positions occupied, disable Take Seat. @@ -458,7 +484,7 @@ def event_leaveGame(self, player, position): self.cardarea.set_player_name(position, None) # Enable menu item corresponding to position. - widget = self.takeseat_items[position] + widget = self.takeseat_menuitems[position] widget.set_property('sensitive', True) # If we are not seated, ensure Take Seat is enabled. @@ -467,34 +493,38 @@ def event_start(self, board): - #self.children.close('dialog_gameresult') self.resetGame() + # Re-initialise player labels. + # TODO: shouldn't need to do this. + for position in Direction: + player = self.table.players.get(position) # Player name or None. + self.cardarea.set_player_name(position, player) + self.redrawTrick() # Clear trick. for position in Direction: self.redrawHand(position) self.setTurnIndicator() - self.setDealer() - self.setVulnerability() + self.dashboard.set_dealer(self.table.game) + self.dashboard.set_vulnerability(self.table.game) if self.player: d = self.player.callRemote('getHand') - # When player's hand is returned by server, reveal it to client-side Game. - # TODO: is there a better way of synchronising hands? + # When user's hand is returned, reveal it to client-side Game. d.addCallbacks(self.table.game.revealHand, self.errback, - callbackKeywords={'position' : self.position}) + callbackKeywords={'position': self.position}) bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) def event_makeCall(self, call, position): - self.addCall(call, position) + self.biddingview.add_call(call, position) self.setTurnIndicator() if self.table.game.bidding.isComplete(): - self.setContract() + self.dashboard.set_contract(self.table.game) if self.children.get(WindowBidbox): # If a player. self.children.close(self.children[WindowBidbox]) @@ -507,7 +537,7 @@ playfrom = self.table.game.play.whoPlayed(card) self.addCard(card, playfrom) self.setTurnIndicator() - self.setTrickCount() + self.dashboard.set_trickcount(self.table.game) self.redrawTrick() self.redrawHand(playfrom) @@ -516,17 +546,10 @@ def event_revealHand(self, hand, position): - all = not self.table.game.inProgress() # Show all cards if game has finished. - self.redrawHand(position, all) + all = not self.table.game.inProgress() + self.redrawHand(position, all) # Show all cards if game has finished. - def event_sendMessage(self, message, sender, recipients): - buffer = self.chat_messagehistory.get_buffer() - iter = buffer.get_end_iter() - buffer.insert(iter, '\n' + _('%(sender)s: %(message)s' % {'sender': sender, 'message': message})) - self.chat_messagehistory.scroll_to_iter(iter, 0) - - # Signal handlers. @@ -539,7 +562,7 @@ def on_hand_clicked(self, position): if not self.player and not self.table.players.get(position): # Join game at position. - self.on_seat_activated(self.cardarea, position) + self.on_takeseat_clicked(self.cardarea, position) def on_card_clicked(self, card, position): @@ -549,110 +572,34 @@ d.addErrback(self.errback) - def on_seat_activated(self, widget, position): - - def success(player): - self.player = player # RemoteReference to BridgePlayer object. - self.position = position + def on_takeseat_clicked(self, widget, position=None): - self.takeseat.set_property('sensitive', False) - self.leaveseat.set_property('sensitive', True) - + def success(r): self.cardarea.set_player_mapping(self.position) - if self.table.game.inProgress(): d = self.player.callRemote('getHand') d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) - # If game is running and bidding is active, open bidding box. if not self.table.game.bidding.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) - d = self.table.joinGame(position) - d.addCallbacks(success, self.errback) - - - def on_takeseat_clicked(self, widget, *args): # TODO: match user up with preferred partner. - for position in Direction: - if position not in self.table.players: # Position is vacant. - self.on_seat_activated(widget, position) # Take position. - break + if position is None: + # No position specified by user: choose an arbitary position. + position = [p for p in Direction if p not in self.table.players][0] + d = super(WindowBridgeTable, self).on_takeseat_clicked(widget, position) + d.addCallback(success) def on_leaveseat_clicked(self, widget, *args): def success(r): - self.player = None - self.position = None - self.takeseat.set_property('sensitive', True) - self.leaveseat.set_property('sensitive', False) if self.children.get(WindowBidbox): self.children.close(self.children[WindowBidbox]) - d = self.table.leaveGame(self.position) - d.addCallbacks(success, self.errback) + d = super(WindowBridgeTable, self).on_leaveseat_clicked(widget, *args) + d.addCallback(success) - - def on_toggle_gameinfo_clicked(self, widget, *args): - visible = self.toggle_gameinfo.get_active() - self.gameinfo.set_property('visible', visible) - - - def on_toggle_chat_clicked(self, widget, *args): - visible = self.toggle_chat.get_active() - self.chatbox.set_property('visible', visible) - - - def on_toggle_fullscreen_clicked(self, widget, *args): - if self.toggle_fullscreen.get_active(): - self.window.fullscreen() - else: - self.window.unfullscreen() - - - def on_leavetable_clicked(self, widget, *args): - # If user is currently playing a game, request confirmation. - if self.player and self.table.game.inProgress(): - dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, - type=gtk.MESSAGE_QUESTION) - dialog.set_title(_('Leave table?')) - dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) - dialog.add_button(_('Leave Table'), gtk.RESPONSE_OK) - dialog.set_markup(_('Are you sure you wish to leave this table?')) - dialog.format_secondary_text(_('You are currently playing a game. Leaving may forfeit the game, or incur penalties.')) - - def dialog_response_cb(dialog, response_id): - dialog.destroy() - if response_id == gtk.RESPONSE_OK: - d = client.leaveTable(self.table.id) - d.addErrback(self.errback) - - dialog.connect('response', dialog_response_cb) - dialog.show() - - else: - d = client.leaveTable(self.table.id) - d.addErrback(self.errback) - - - def on_chat_message_changed(self, widget, *args): - sensitive = self.chat_message.get_text() != '' - self.chat_send.set_property('sensitive', sensitive) - - - def on_chat_send_clicked(self, widget, *args): - message = self.chat_message.get_text() - if message: # Don't send a null message. - self.chat_send.set_property('sensitive', False) - self.chat_message.set_text('') # Clear message. - self.table.sendMessage(message) - - - def on_window_delete_event(self, widget, *args): - self.on_leavetable_clicked(widget, *args) - return True # Stops window deletion taking place. - Added: trunk/pybridge/pybridge/ui/window_gametable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_gametable.py (rev 0) +++ trunk/pybridge/pybridge/ui/window_gametable.py 2007-07-09 14:10:29 UTC (rev 463) @@ -0,0 +1,215 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2007 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +import gtk + +from pybridge.network.client import client + +from eventhandler import SimpleEventHandler +from manager import WindowManager, wm +from wrapper import ICON_PATH + +from window_chat import WindowChat + + +class WindowGameTable(object): + """A generic table display window. + + This exposes core table functionality to the user, and may be subclassed to + support individual games. + """ + + gametype = _('Unnamed Game') + + + def __init__(self, parent=None): + self.window = gtk.Window() + if parent: + self.window.set_transient_for(parent.window) + self.window.connect('delete_event', self.on_window_delete_event) + self.window.set_icon_from_file(ICON_PATH) + self.window.set_title(_('Table')) + + self.setUp() + self.window.show_all() + + + def setUp(self): + self.children = WindowManager() + self.eventHandler = SimpleEventHandler(self) + + self.player = None + self.position = None + self.table = None # Table currently displayed in window. + + # Set up widget layout. + vbox = gtk.VBox() + self.toolbar = gtk.Toolbar() + self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ) + vbox.pack_start(self.toolbar, expand=False) + hbox = gtk.HBox() + self.gamearea = gtk.Viewport() # Use self.gamearea.add(...) + hbox.pack_start(self.gamearea) + self.sidebar = gtk.VBox(spacing=6) # Use self.sidebar.pack_start(...) + self.sidebar.set_border_width(6) + hbox.pack_start(self.sidebar, expand=False) + vbox.pack_start(hbox) + self.statusbar = gtk.Statusbar() + vbox.pack_start(self.statusbar, expand=False) + self.window.add(vbox) + + # Set up toolbar buttons. + self.takeseat = gtk.MenuToolButton(gtk.STOCK_MEDIA_PLAY) + self.takeseat.set_label(_('Take Seat')) + self.takeseat.set_is_important(True) + self.takeseat.connect('clicked', self.on_takeseat_clicked) + self.toolbar.insert(self.takeseat, -1) + + self.leaveseat = gtk.ToolButton(gtk.STOCK_MEDIA_STOP) + self.leaveseat.set_label(_('Leave Seat')) + self.leaveseat.connect('clicked', self.on_leaveseat_clicked) + self.leaveseat.set_property('sensitive', False) + self.toolbar.insert(self.leaveseat, -1) + + self.toolbar.insert(gtk.SeparatorToolItem(), -1) + + self.fullscreen = gtk.ToggleToolButton(gtk.STOCK_FULLSCREEN) + self.fullscreen.connect('clicked', self.on_fullscreen_clicked) + #self.fullscreen.set_tooltip() + self.toolbar.insert(self.fullscreen, -1) + + self.toolbar.insert(gtk.SeparatorToolItem(), -1) + + self.leavetable = gtk.ToolButton(gtk.STOCK_QUIT) + self.leavetable.set_label(_('Leave Table')) + self.leavetable.connect('clicked', self.on_leavetable_clicked) + self.toolbar.insert(self.leavetable, -1) + + + def tearDown(self): + # Close all child windows. + for window in self.children.values(): + self.children.close(window) + + # If table has chat session, remove it from chat window. + if self.table.chat: + chatwin = wm.get(WindowChat) + if chatwin: + chatwin.removeChat(self.table.chat) + + self.table = None # Dereference table. + + + def setTable(self, table): + """Set display to follow specified table object. + + @param table: the focal table. + """ +# if self.table: +# self.table.detach(self.eventHandler) + self.table = table + self.table.attach(self.eventHandler) + + fields = {'tablename': self.table.id, 'gametype': self.gametype} + title = _('%(tablename)s (%(gametype)s)') % fields + self.window.set_title(title) + + if self.table.chat: + chatwin = wm.get(WindowChat) or wm.open(WindowChat) + chatwin.addChat(self.table.chat, title=title) + + +# Registered event handlers. + + +# Signal handlers. + + + def on_takeseat_clicked(self, widget, position): + # Subclasses should override this method, and call it using super(...) + # with the position argument guaranteed to be specified. + assert position is not None + + self.takeseat.set_property('sensitive', False) + + def success(player): + self.leaveseat.set_property('sensitive', True) + self.player = player + self.position = position + + def failure(reason): + self.takeseat.set_property('sensitive', True) # Re-enable. + + d = self.table.joinGame(position) + d.addCallbacks(success, failure) + return d + + + def on_leaveseat_clicked(self, widget, *args): + self.leaveseat.set_property('sensitive', False) + + def success(r): + self.takeseat.set_property('sensitive', True) + self.player = None + self.position = None + + def failure(reason): + self.leaveseat.set_property('sensitive', True) # Re-enable. + + d = self.table.leaveGame(self.position) + d.addCallbacks(success, failure) + return d + + + def on_leavetable_clicked(self, widget, *args): + # If user is currently playing a game, request confirmation. + if self.player and self.table.game.inProgress(): + dialog = gtk.MessageDialog(parent=self.window, + flags=gtk.DIALOG_MODAL, + type=gtk.MESSAGE_QUESTION) + dialog.set_title(_('Leave table?')) + dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) + dialog.add_button(_('Leave Table'), gtk.RESPONSE_OK) + dialog.set_markup(_('Are you sure you wish to leave this table?')) + dialog.format_secondary_text(_('You are currently playing a game. Leaving may forfeit the game, or incur penalties.')) + + def dialog_response_cb(dialog, response_id): + dialog.destroy() + if response_id == gtk.RESPONSE_OK: + d = client.leaveTable(self.table.id) + #d.addErrback(self.errback) + + dialog.connect('response', dialog_response_cb) + dialog.show() + + else: + d = client.leaveTable(self.table.id) + #d.addErrback(self.errback) + + + def on_fullscreen_clicked(self, widget, *args): + if self.fullscreen.get_active(): + self.window.fullscreen() + else: + self.window.unfullscreen() + + + def on_window_delete_event(self, widget, *args): + self.on_leavetable_clicked(widget, *args) + return True # Stops window deletion taking place. + Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-07-09 14:00:02 UTC (rev 462) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-07-09 14:10:29 UTC (rev 463) @@ -32,8 +32,10 @@ from dialog_connection import DialogConnection from dialog_newtable import DialogNewtable from dialog_preferences import DialogPreferences -from window_bridgetable import WindowBridgetable +# TODO: import all Window*Table classes automatically. +from pybridge.bridge.ui.window_bridgetable import WindowBridgeTable + TABLE_ICON = env.find_pixmap("table.png") USER_ICON = env.find_pixmap("user.png") @@ -49,14 +51,14 @@ def setUp(self): # Use a private WindowManager for table window instances. self.tables = WindowManager() - + # Set up table model and icon view. self.tableview.set_text_column(0) self.tableview.set_pixbuf_column(1) self.tableview_model = gtk.ListStore(str, gtk.gdk.Pixbuf) self.tableview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) self.tableview.set_model(self.tableview_model) - + # Set up people model and icon view. # TODO: allow users to provide their own "avatar" icons. self.peopleview.set_text_column(0) @@ -64,7 +66,7 @@ self.peopleview_model = gtk.ListStore(str, gtk.gdk.Pixbuf) self.peopleview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) self.peopleview.set_model(self.peopleview_model) - + # Attach event handler to listen for events. self.eventHandler = SimpleEventHandler(self) client.attach(self.eventHandler) @@ -152,7 +154,7 @@ def event_joinTable(self, tableid, table): - window = self.tables.open(WindowBridgetable, id=tableid) + window = self.tables.open(WindowBridgeTable, id=tableid) window.setTable(table) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-23 20:09:00
|
Revision: 492 http://svn.sourceforge.net/pybridge/?rev=492&view=rev Author: umgangee Date: 2007-07-23 13:07:54 -0700 (Mon, 23 Jul 2007) Log Message: ----------- Open table windows in response to successful joinTable() requests, instead of waiting for event_tableJoined(), so internal errors are reported to sys.excepthook instead of the errback attached to joinTable() deferred by caller. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_gametable.py trunk/pybridge/pybridge/ui/window_main.py Modified: trunk/pybridge/pybridge/ui/window_gametable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_gametable.py 2007-07-23 11:55:00 UTC (rev 491) +++ trunk/pybridge/pybridge/ui/window_gametable.py 2007-07-23 20:07:54 UTC (rev 492) @@ -50,7 +50,7 @@ def setUp(self): - self.children = WindowManager() + self.children = WindowManager() # Private to this window. self.eventHandler = SimpleEventHandler(self) self.player = None @@ -192,14 +192,14 @@ dialog.destroy() if response_id == gtk.RESPONSE_OK: d = client.leaveTable(self.table.id) - #d.addErrback(self.errback) + d.addCallbacks(lambda _: wm.close(self), self.errback) dialog.connect('response', dialog_response_cb) dialog.show() else: d = client.leaveTable(self.table.id) - #d.addErrback(self.errback) + d.addCallbacks(lambda _: wm.close(self), self.errback) def on_fullscreen_clicked(self, widget, *args): Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-07-23 11:55:00 UTC (rev 491) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-07-23 20:07:54 UTC (rev 492) @@ -28,12 +28,13 @@ from pybridge.network.client import client from eventhandler import SimpleEventHandler -from manager import WindowManager, wm +from manager import wm from dialog_connection import DialogConnection from dialog_newtable import DialogNewtable from dialog_preferences import DialogPreferences from dialog_userinfo import DialogUserInfo +from window_gametable import WindowGameTable # TODO: import all Window*Table classes automatically. from pybridge.games.bridge.ui.window_bridgetable import WindowBridgeTable @@ -51,9 +52,6 @@ def setUp(self): - # Use a private WindowManager for table window instances. - self.tables = WindowManager() - # Set up table model and icon view. self.tableview.set_text_column(0) self.tableview.set_pixbuf_column(1) @@ -110,8 +108,9 @@ def event_loggedOut(self): - for table in self.tables.values(): - self.tables.close(table) + for window in wm.values(): + if isinstance(window, WindowGameTable): + wm.close(window) self.notebook.set_property('sensitive', False) self.menu_connect.set_property('visible', True) @@ -119,7 +118,6 @@ self.menu_newtable.set_property('sensitive', False) #self.newtable.set_property('sensitive', False) - print self.tableview.get_model() self.tableview.get_model().clear() self.userview.get_model().clear() @@ -157,15 +155,6 @@ pass # Ignore an unrecognised roster. - def event_joinTable(self, tableid, table): - window = self.tables.open(WindowBridgeTable, id=tableid) - window.setTable(table) - - - def event_leaveTable(self, tableid): - self.tables.close(self.tables[tableid]) # Close window. - - def event_openTable(self, tableid, info): """Adds a table to the table listing.""" # Only display table if it supported by client. @@ -210,12 +199,18 @@ def on_tableview_item_activated(self, iconview, path, *args): + + def joinedTable(table): + # TODO: select correct table window class. + window = wm.open(WindowBridgeTable, id=tableid) + window.setTable(table) + model = self.tableview.get_model() iter = model.get_iter(path) tableid = model.get_value(iter, 0) if tableid not in client.tables: d = client.joinTable(tableid) - d.addErrback(self.errback) + d.addCallbacks(joinedTable, self.errback) self.jointable.set_property('sensitive', False) @@ -285,7 +280,8 @@ def on_disconnect_activate(self, widget, *args): do_disconnect = True - if len([True for table in self.tables.values() if table.player]) > 0: + # TODO: avoid introspection of table windows. + if len([True for w in wm.values() if isinstance(w, WindowGameTable) and w.player]) > 0: dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_QUESTION) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |