You can subscribe to this list here.
2006 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(8) |
Sep
(3) |
Oct
(5) |
Nov
|
Dec
(1) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
|
Feb
(7) |
Mar
(17) |
Apr
(37) |
May
|
Jun
(46) |
Jul
(40) |
Aug
(2) |
Sep
(4) |
Oct
(2) |
Nov
|
Dec
|
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <umg...@us...> - 2007-04-07 20:56:12
|
Revision: 407 http://svn.sourceforge.net/pybridge/?rev=407&view=rev Author: umgangee Date: 2007-04-07 13:56:08 -0700 (Sat, 07 Apr 2007) Log Message: ----------- Show position title in player name labels, change "is" to "==" for comparison with elements of Direction. Modified Paths: -------------- trunk/pybridge/pybridge/ui/cardarea.py Modified: trunk/pybridge/pybridge/ui/cardarea.py =================================================================== --- trunk/pybridge/pybridge/ui/cardarea.py 2007-04-06 20:07:59 UTC (rev 406) +++ trunk/pybridge/pybridge/ui/cardarea.py 2007-04-07 20:56:08 UTC (rev 407) @@ -36,19 +36,22 @@ # 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. - - This widget uses Cairo and requires >= GTK 2.8. + """This widget displays a graphical view of card hands and tricks. + + Requirements: Cairo (>=1.0), PyGTK (>= 2.8). """ # Load card mask. card_mask_path = env.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 @@ -58,14 +61,14 @@ def __init__(self): super(CardArea, self).__init__() # Initialise parent. - + # To receive card clicked events, override this with external method. self.on_card_clicked = lambda card, player: True - + self.hands = {} self.trick = None self.set_player_mapping(Direction.South) - + self.connect('button_press_event', self.button_press) self.add_events(gtk.gdk.BUTTON_PRESS_MASK) @@ -83,7 +86,7 @@ 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 - + 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) @@ -102,7 +105,7 @@ @param facedown: if True, cards are drawn face-down. @param omit: a list of elements of hand not to draw. """ - + # TODO: coords should be dict (card : (pos_x, pos_y)), but this breaks when hashing. def get_coords_for_hand(): coords = [] @@ -123,19 +126,19 @@ else: # LEFT or RIGHT. if facedown is True: # Wrap cards to a 4x4 grid. for index, card in enumerate(hand): - adjust = player is self.RIGHT and index == 12 and 3 + adjust = player == 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 = player is self.RIGHT and longest - len(suits[card.suit]) + adjust = player == 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 = dict([(suit, []) for suit in Suit]) @@ -148,29 +151,29 @@ hand = [] for suit in RED_BLACK: hand.extend(suits[suit]) - + saved = self.hands.get(player) 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() - + # 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 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[i][1:] self.draw_card(context, pos_x, pos_y, card) - + # Save self.hands[player] = {'hand' : hand, 'visible' : visible, 'surface' : surface, 'coords' : coords, } - + id = 'hand-%s' % player # Identifier for this item. if id in self.items: self.update_item(id, source=surface) @@ -180,25 +183,26 @@ self.add_item(id, surface, xy[player], 0) - def set_player_name(self, player, name=None): + def set_player_name(self, position, name=None): """ + @param position: the position of the player. @param name: the name of the player, or None. """ - id = 'player-%s' % player + id = 'player-%s' % position 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) + layout.set_text('%s: %s' % (DIRECTION_SYMBOLS[position], 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) - + # Draw background box, text to ImageSurface. context.set_line_width(4) context.rectangle(0, 0, width, height) @@ -209,19 +213,19 @@ 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.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[player], 2) + xy = {self.TOP : (0.5, 0.2), self.BOTTOM : (0.5, 0.9), + self.LEFT : (0.125, 0.625), self.RIGHT : (0.875, 0.625), } + self.add_item(id, surface, xy[position], 2) def set_player_mapping(self, focus=Direction.South): """Sets the mapping between players at table and positions of hands. - @param focus: the Direction to be drawn "closest" to the observer. + @param focus: the position to be drawn "closest" to the observer. """ # Assumes Direction elements are ordered clockwise from North. order = Direction[focus.index:] + Direction[:focus.index] @@ -238,7 +242,7 @@ """ 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), } - + if trick: # The order of play is the leader, then clockwise around Direction. leader = trick[0] @@ -258,11 +262,11 @@ 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) - + elif self.trick: # Remove all cards from previous trick. for player in self.trick[1]: self.remove_item('trick-%s' % player) - + self.trick = trick # Save trick and return. @@ -271,13 +275,13 @@ The hand of the player on turn is drawn opaque; the other hands are drawn translucent. - + @param turn: the position of the turn indicator. @type turn: Direction or None """ if turn is None: return - + for position in Direction: opacity = (position is turn) and 1 or 0.5 self.update_item('hand-%s' % position, opacity=opacity) @@ -287,7 +291,7 @@ """Determines if a card was clicked: if so, calls card_selected.""" if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: found_hand = False - + # Determine the hand which was clicked. for player in self.hands: card_coords = self.hands[player]['coords'] @@ -297,7 +301,7 @@ (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 @@ -308,6 +312,6 @@ (y <= pos_y <= y + self.card_height): self.on_card_clicked(card, player) break - + return True # Expected to return True. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-06 20:07:58
|
Revision: 406 http://svn.sourceforge.net/pybridge/?rev=406&view=rev Author: umgangee Date: 2007-04-06 13:07:59 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Fix call to client.joinTable(), improve "Could not create table" error message. Modified Paths: -------------- trunk/pybridge/pybridge/ui/dialog_newtable.py Modified: trunk/pybridge/pybridge/ui/dialog_newtable.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-04-06 20:07:13 UTC (rev 405) +++ trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-04-06 20:07:59 UTC (rev 406) @@ -19,6 +19,7 @@ import gtk from wrapper import GladeWrapper +from pybridge.network.client import client from manager import wm @@ -38,9 +39,9 @@ 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.')) + type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK) + dialog.set_title(_('Could not create table')) + dialog.set_markup(_('The table could not be created.')) dialog.format_secondary_text(_('Reason: %s') % error) dialog.run() @@ -56,11 +57,12 @@ def on_okbutton_clicked(self, widget, *args): tableid = self.entry_tablename.get_text() - d = self.parent.joinTable(tableid, host=True) + d = client.joinTable(tableid, host=True) d.addCallbacks(self.createSuccess, self.createFailure) def on_tablename_changed(self, widget, *args): + # Disable the OK button if the table name field is empty. sensitive = self.entry_tablename.get_text() != "" self.okbutton.set_property('sensitive', sensitive) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-06 20:07:11
|
Revision: 405 http://svn.sourceforge.net/pybridge/?rev=405&view=rev Author: umgangee Date: 2007-04-06 13:07:13 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Remove joinTable and leaveTable methods (use the ones in NetworkClient instead). Listen for 'joinTable' and 'leaveTable' events: when received, act accordingly. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_main.py Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-04-06 18:04:49 UTC (rev 404) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-04-06 20:07:13 UTC (rev 405) @@ -96,27 +96,6 @@ print "Error: %s" % failure.getErrorMessage() - def joinTable(self, tableid, host=False): - - def success(table): - window = self.tables.open(WindowBridgetable, id=tableid) - window.setTable(table) - - d = client.joinTable(tableid, host) - d.addCallback(success) - return d - - - def leaveTable(self, tableid): - - def success(r): - del self.tables[tableid] - - d = client.leaveTable(tableid) - d.addCallback(success) - return d - - # Event handlers. @@ -155,6 +134,11 @@ 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. @@ -202,7 +186,8 @@ iter = self.tableview_model.get_iter(path) tableid = self.tableview_model.get_value(iter, 0) if tableid not in client.tables: - self.joinTable(tableid) + d = client.joinTable(tableid) + d.addErrback(self.errback) self.jointable.set_property('sensitive', False) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-06 18:05:03
|
Revision: 404 http://svn.sourceforge.net/pybridge/?rev=404&view=rev Author: umgangee Date: 2007-04-06 11:04:49 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Whoops! One-line fix for when an invalid trump suit is given at object instantiation. Modified Paths: -------------- trunk/pybridge/pybridge/bridge/playing.py Modified: trunk/pybridge/pybridge/bridge/playing.py =================================================================== --- trunk/pybridge/pybridge/bridge/playing.py 2007-04-06 18:02:26 UTC (rev 403) +++ trunk/pybridge/pybridge/bridge/playing.py 2007-04-06 18:04:49 UTC (rev 404) @@ -40,7 +40,7 @@ if declarer not in Direction: raise TypeError, "Expected Direction, got %s" % type(declarer) if trumpSuit not in Suit and trumpSuit is not None: # None => No Trumps - raise TypeError, "Expected Suit, got %s" % type(suit) + raise TypeError, "Expected Suit, got %s" % type(trumpSuit) self.trumps = trumpSuit self.declarer = declarer This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-06 18:02:34
|
Revision: 403 http://svn.sourceforge.net/pybridge/?rev=403&view=rev Author: umgangee Date: 2007-04-06 11:02:26 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Tweaks to window_main Glade file, remove unused dialog_gameresult. Modified Paths: -------------- trunk/pybridge/glade/pybridge.glade Removed Paths: ------------- trunk/pybridge/pybridge/ui/dialog_gameresult.py Modified: trunk/pybridge/glade/pybridge.glade =================================================================== --- trunk/pybridge/glade/pybridge.glade 2007-04-06 17:58:59 UTC (rev 402) +++ trunk/pybridge/glade/pybridge.glade 2007-04-06 18:02:26 UTC (rev 403) @@ -15,13 +15,49 @@ <child> <widget class="GtkMenuItem" id="menu_server"> <property name="visible">True</property> - <property name="label" translatable="yes">_File</property> + <property name="label" translatable="yes">_Server</property> <property name="use_underline">True</property> <child> <widget class="GtkMenu" id="menu_server_menu"> <child> + <widget class="GtkImageMenuItem" id="menu_connect"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Connect to Server...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_connect_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image342"> + <property name="visible">True</property> + <property name="stock">gtk-connect</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="menu_disconnect"> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">_Disconnect</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_disconnect_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="menu-item-image6"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="stock">gtk-disconnect</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="menu_separator"> + <property name="visible">True</property> + </widget> + </child> + <child> <widget class="GtkImageMenuItem" id="menu_newtable"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="label" translatable="yes">_New Table...</property> <property name="use_underline">True</property> <signal name="activate" handler="on_newtable_clicked"/> @@ -56,21 +92,6 @@ </widget> </child> <child> - <widget class="GtkImageMenuItem" id="menu_disconnect"> - <property name="visible">True</property> - <property name="label" translatable="yes">_Disconnect</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_disconnect_activate"/> - <child internal-child="image"> - <widget class="GtkImage" id="image342"> - <property name="visible">True</property> - <property name="stock">gtk-disconnect</property> - <property name="icon_size">1</property> - </widget> - </child> - </widget> - </child> - <child> <widget class="GtkImageMenuItem" id="menu_quit"> <property name="visible">True</property> <property name="label">gtk-quit</property> @@ -159,6 +180,7 @@ <child> <widget class="GtkNotebook" id="notebook"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_focus">True</property> <signal name="switch_page" handler="on_notebook_switch_page"/> <child> @@ -193,6 +215,7 @@ <child> <widget class="GtkButton" id="newtable"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_focus">True</property> <signal name="clicked" handler="on_newtable_clicked"/> <child> @@ -1418,10 +1441,10 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="can_default">True</property> - <property name="label">gtk-quit</property> + <property name="label">gtk-cancel</property> <property name="use_stock">True</property> <property name="response_id">-6</property> - <signal name="clicked" handler="on_quit_clicked"/> + <signal name="clicked" handler="on_cancel_clicked"/> </widget> </child> <child> @@ -1545,148 +1568,6 @@ </widget> </child> </widget> - <widget class="GtkDialog" id="dialog_gameresult"> - <property name="visible">True</property> - <property name="title" translatable="yes">Game Result</property> - <property name="resizable">False</property> - <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> - <property name="skip_taskbar_hint">True</property> - <signal name="delete_event" handler="on_dialog_gameresult_delete_event"/> - <child internal-child="vbox"> - <widget class="GtkVBox" id="dialog-vbox4"> - <property name="visible">True</property> - <child> - <widget class="GtkHBox" id="hbox16"> - <property name="visible">True</property> - <child> - <widget class="GtkImage" id="image_information"> - <property name="visible">True</property> - <property name="stock">gtk-dialog-info</property> - <property name="icon_size">6</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="padding">12</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_result"> - <property name="visible">True</property> - </widget> - <packing> - <property name="position">1</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">12</property> - <property name="pack_type">GTK_PACK_END</property> - <property name="position">2</property> - </packing> - </child> - <child internal-child="action_area"> - <widget class="GtkHButtonBox" id="dialog-action_area4"> - <property name="visible">True</property> - <property name="layout_style">GTK_BUTTONBOX_END</property> - <child> - <widget class="GtkButton" id="button_leavetable"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <signal name="clicked" handler="on_leavetable_clicked"/> - <child> - <widget class="GtkAlignment" id="alignment17"> - <property name="visible">True</property> - <property name="xscale">0</property> - <property name="yscale">0</property> - <child> - <widget class="GtkHBox" id="hbox14"> - <property name="visible">True</property> - <property name="spacing">2</property> - <child> - <widget class="GtkImage" id="image166"> - <property name="visible">True</property> - <property name="stock">gtk-quit</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label21"> - <property name="visible">True</property> - <property name="label" translatable="yes">Leave Table</property> - <property name="use_underline">True</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - </widget> - </child> - <child> - <widget class="GtkButton" id="button_nextdeal"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <signal name="clicked" handler="on_nextdeal_clicked"/> - <child> - <widget class="GtkAlignment" id="alignment18"> - <property name="visible">True</property> - <property name="xscale">0</property> - <property name="yscale">0</property> - <child> - <widget class="GtkHBox" id="hbox15"> - <property name="visible">True</property> - <property name="spacing">2</property> - <child> - <widget class="GtkImage" id="image167"> - <property name="visible">True</property> - <property name="stock">gtk-ok</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label22"> - <property name="visible">True</property> - <property name="label" translatable="yes">Next Deal</property> - <property name="use_underline">True</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - </widget> - <packing> - <property name="position">1</property> - </packing> - </child> - </widget> - <packing> - <property name="expand">False</property> - <property name="pack_type">GTK_PACK_END</property> - </packing> - </child> - </widget> - </child> - </widget> <widget class="GtkWindow" id="window_bridgetable"> <property name="visible">True</property> <property name="title" translatable="yes">Table</property> @@ -1706,6 +1587,10 @@ <property name="stock_id">gtk-media-play</property> <signal name="clicked" handler="on_takeseat_clicked"/> </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> </child> <child> <widget class="GtkToolButton" id="leaveseat"> @@ -1716,11 +1601,18 @@ <property name="stock_id">gtk-media-stop</property> <signal name="clicked" handler="on_leaveseat_clicked"/> </widget> + <packing> + <property name="expand">False</property> + </packing> </child> <child> <widget class="GtkSeparatorToolItem" id="separatortoolitem2"> <property name="visible">True</property> </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> </child> <child> <widget class="GtkToggleToolButton" id="toggle_gameinfo"> @@ -1731,6 +1623,9 @@ <property name="active">True</property> <signal name="clicked" handler="on_toggle_gameinfo_clicked"/> </widget> + <packing> + <property name="expand">False</property> + </packing> </child> <child> <widget class="GtkToggleToolButton" id="toggle_chat"> @@ -1741,6 +1636,9 @@ <property name="active">True</property> <signal name="clicked" handler="on_toggle_chat_clicked"/> </widget> + <packing> + <property name="expand">False</property> + </packing> </child> <child> <widget class="GtkToggleToolButton" id="toggle_fullscreen"> @@ -1748,11 +1646,18 @@ <property name="stock_id">gtk-fullscreen</property> <signal name="clicked" handler="on_toggle_fullscreen_clicked"/> </widget> + <packing> + <property name="expand">False</property> + </packing> </child> <child> <widget class="GtkSeparatorToolItem" id="separatortoolitem1"> <property name="visible">True</property> </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> </child> <child> <widget class="GtkToolButton" id="leavetable"> @@ -1762,6 +1667,9 @@ <property name="stock_id">gtk-quit</property> <signal name="clicked" handler="on_leavetable_clicked"/> </widget> + <packing> + <property name="expand">False</property> + </packing> </child> </widget> <packing> Deleted: trunk/pybridge/pybridge/ui/dialog_gameresult.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_gameresult.py 2007-04-06 17:58:59 UTC (rev 402) +++ trunk/pybridge/pybridge/ui/dialog_gameresult.py 2007-04-06 18:02:26 UTC (rev 403) @@ -1,49 +0,0 @@ -# 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 -from wrapper import GladeWrapper - - -class DialogGameresult(GladeWrapper): - - glade_name = 'dialog_gameresult' - - - def new(self): - pass - - - def setup(self, message): - self.label_result.set_text(message) - - -# Signal handlers. - - - def on_leavetable_clicked(self, widget, *args): - self.parent.on_leavetable_clicked(widget, *args) - - - def on_nextdeal_clicked(self, widget, *args): - self.parent.children.close(self.glade_name) - - - def on_dialog_gameresult_delete_event(self, widget, *args): - return True # Stops window deletion taking place. - 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-06 17:56:09
|
Revision: 401 http://svn.sourceforge.net/pybridge/?rev=401&view=rev Author: umgangee Date: 2007-04-06 10:55:41 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Update references to roster classes. Modified Paths: -------------- trunk/pybridge/pybridge/server/server.py trunk/pybridge/pybridge/server/user.py Modified: trunk/pybridge/pybridge/server/server.py =================================================================== --- trunk/pybridge/pybridge/server/server.py 2007-04-06 17:54:58 UTC (rev 400) +++ trunk/pybridge/pybridge/server/server.py 2007-04-06 17:55:41 UTC (rev 401) @@ -23,34 +23,36 @@ from pybridge import __version__ from pybridge.network.error import DeniedRequest, IllegalRequest +from pybridge.network.localtable import LocalTable from pybridge.network.tablemanager import LocalTableManager from pybridge.network.usermanager import LocalUserManager -from pybridge.network.localtable import LocalTable from pybridge.bridge.game import BridgeGame -class Server: +class Server(object): def __init__(self): + # Set up rosters. self.tables = LocalTableManager() self.users = LocalUserManager() + self.version = __version__ self.supported = ['bridge'] def userConnects(self, user): """""" - self.users.userLoggedIn(user) + log.msg("User %s connected" % user.name) + self.users.userLogin(user) db.UserAccount.byUsername(user.name).set(lastLogin=datetime.now()) - log.msg("User %s connected" % user.name) def userDisconnects(self, user): """""" - self.users.userLoggedOut(user) log.msg("User %s disconnected" % user.name) + self.users.userLogout(user) # Methods invoked by user perspectives. @@ -85,4 +87,5 @@ table.id = tableid table.server = self self.tables.openTable(table) + #self.tables[tableid] = table Modified: trunk/pybridge/pybridge/server/user.py =================================================================== --- trunk/pybridge/pybridge/server/user.py 2007-04-06 17:54:58 UTC (rev 400) +++ trunk/pybridge/pybridge/server/user.py 2007-04-06 17:55:41 UTC (rev 401) @@ -26,7 +26,9 @@ class User(pb.Avatar): + info = property(lambda self: {}) + def __init__(self, name): self.name = name # User name. self.server = None # Set by Realm. @@ -62,16 +64,16 @@ return info - def perspective_getTables(self): - """Provides RemoteTableManager to the client.""" - return self.server.tables + def perspective_getRoster(self, name): + """Provides roster requested by client.""" + if name == 'tables': + return self.server.tables + elif name == 'users': + return self.server.users + else: + raise DeniedRequest, "Unknown roster name \'%s\'" % name - def perspective_getUsers(self): - """Provides RemoteUserManager to the client.""" - return self.server.users - - def perspective_hostTable(self, tableid, tabletype): """Creates a new table.""" if not isinstance(tableid, str): 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:55:22
|
Revision: 400 http://svn.sourceforge.net/pybridge/?rev=400&view=rev Author: umgangee Date: 2007-04-06 10:54:58 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Update references to roster classes, remove old event handler mechanism. Modified Paths: -------------- trunk/pybridge/pybridge/network/client.py Modified: trunk/pybridge/pybridge/network/client.py =================================================================== --- trunk/pybridge/pybridge/network/client.py 2007-04-06 17:52:49 UTC (rev 399) +++ trunk/pybridge/pybridge/network/client.py 2007-04-06 17:54:58 UTC (rev 400) @@ -29,12 +29,12 @@ pb.setUnjellyableForClass(LocalTable, RemoteTable) from pybridge.network.tablemanager import LocalTableManager, RemoteTableManager +from pybridge.network.usermanager import LocalUserManager, RemoteUserManager pb.setUnjellyableForClass(LocalTableManager, RemoteTableManager) - -from pybridge.network.usermanager import LocalUserManager, RemoteUserManager pb.setUnjellyableForClass(LocalUserManager, RemoteUserManager) + class NetworkClient(pb.Referenceable): """Provides the glue between the client code and the server.""" @@ -50,15 +50,25 @@ self.username = None self.tables = {} # Tables observed. - self.tablesAvailable = None - self.usersOnline = None + self.tableRoster = None + self.userRoster = None def connectionLost(self, connector, reason): - print "Connection lost:", reason.getErrorMessage() - self.notify('connectionLost', message=reason.getErrorMessage()) + # Reset invalidated remote references. + self.avatar = None + self.tables.clear() + self.tableRoster.clear() + self.userRoster.clear() + print "Lost connection: %s" % reason.getErrorMessage() + self.notify('connectionLost', reason=reason.getErrorMessage()) + + def errback(self, failure): + print "Error: %s" % failure.getErrorMessage() + + # Implementation of ISubject. @@ -78,11 +88,6 @@ # Methods - def setEventHandler(self, handler): - print "REMOVE THIS" - self.eventHandler = handler - - def connect(self, hostname, port): """Connect to server. @@ -95,9 +100,6 @@ def disconnect(self): """Drops connection to server.""" self.factory.disconnect() - self.avatar = None - self.username = None - self.notify('disconnect') def login(self, username, password): @@ -109,49 +111,48 @@ The SHA-1 hash of the password string is transmitted, protecting the user's password from eavesdroppers. """ - - def gotTables(tables): - self.tablesAvailable = tables - tables.setEventHandler(self.eventHandler) - for table in self.tablesAvailable.keys(): - self.eventHandler.tableOpened(table) - - def gotUsers(users): - self.usersOnline = users - users.setEventHandler(self.eventHandler) - for user in self.usersOnline.keys(): - self.eventHandler.userLoggedIn(user) - + + def gotRoster(roster, name): + if name == 'tables': + self.tableRoster = roster + elif name == 'users': + self.userRoster = roster + self.notify('gotRoster', name=name, roster=roster) + def connectedAsUser(avatar): """Actions to perform when connection succeeds.""" self.avatar = avatar self.username = username - self.notify('connect') - avatar.callRemote('getTables').addCallback(gotTables) - avatar.callRemote('getUsers').addCallback(gotUsers) - + self.notify('connectedAsUser', username=username) + + # Request services from server. + for rostername in ['tables', 'users']: + d = avatar.callRemote('getRoster', rostername) + d.addCallbacks(gotRoster, self.errback, callbackArgs=[rostername]) + hash = sha.new(password).hexdigest() creds = credentials.UsernamePassword(username, hash) d = self.factory.login(creds, client=self) - d.addCallback(connectedAsUser) - + d.addCallbacks(connectedAsUser, self.errback) + # for rostername... + return d def register(self, username, password): """Register user account on connected server.""" - + def connectedAsAnonymousUser(avatar): """Register user account on server.""" hash = sha.new(password).hexdigest() d = avatar.callRemote('register', username, hash) - print "TODO: after registration, need to disconnect from server" + # TODO: after registration, need to disconnect from server? return d - + anon = credentials.UsernamePassword('', '') d = self.factory.login(anon, client=None) d.addCallback(connectedAsAnonymousUser) - + return d @@ -159,13 +160,13 @@ def joinTable(self, tableid, host=False): - + def success((table, remote)): table.master = remote # Set RemoteReference for RemoteBridgeTable. self.tables[tableid] = table self.notify('joinTable', tableid=tableid, table=table) return table - + if host: d = self.avatar.callRemote('hostTable', tableid=tableid, tabletype='bridge') @@ -176,11 +177,11 @@ def leaveTable(self, tableid): - + def success(r): del self.tables[tableid] self.notify('leaveTable', tableid=tableid) - + d = self.avatar.callRemote('leaveTable', tableid=tableid) d.addCallback(success) return d 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:52:54
|
Revision: 399 http://svn.sourceforge.net/pybridge/?rev=399&view=rev Author: umgangee Date: 2007-04-06 10:52:49 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Move common code from table and user rosters into LocalRoster and RemoteRoster. Modified Paths: -------------- trunk/pybridge/pybridge/network/tablemanager.py trunk/pybridge/pybridge/network/usermanager.py Added Paths: ----------- trunk/pybridge/pybridge/network/roster.py Added: trunk/pybridge/pybridge/network/roster.py =================================================================== --- trunk/pybridge/pybridge/network/roster.py (rev 0) +++ trunk/pybridge/pybridge/network/roster.py 2007-04-06 17:52:49 UTC (rev 399) @@ -0,0 +1,101 @@ +# 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. + + +from UserDict import IterableUserDict +from twisted.internet import reactor +from twisted.spread import pb +from zope.interface import implements + +from pybridge.interfaces.observer import ISubject + + +class Roster(IterableUserDict): + """A dictionary-like object, which combines a set of available items with + information associated with each item. + + This class implements the ISubject interface to provide notifications + when an entry in the roster is added, removed or changed. + """ + + implements(ISubject) + + + def __init__(self): + IterableUserDict.__init__(self) + self.listeners = [] + + + def attach(self, listener): + self.listeners.append(listener) + + + def detach(self, listener): + self.listeners.remove(listener) + + + def notify(self, event, *args, **kwargs): + for listener in self.listeners: + listener.update(event, *args, **kwargs) + + + + +class LocalRoster(Roster, pb.Cacheable): + """A server-side 'master copy' of a Roster. + + Changes to the LocalRoster are relayed to registered RemoteRoster objects + as well as to all local listeners. + """ + + + def __init__(self): + Roster.__init__(self) + self.observers = [] + + + def getStateToCacheAndObserveFor(self, perspective, observer): + self.observers.append(observer) + # Assumes that each item has an 'info' attribute. + return dict([(id, item.info) for id, item in self.items()]) + + + def stoppedObserving(self, perspective, observer): + self.observers.remove(observer) + + + def notify(self, event, *args, **kwargs): + # Override to provide event notification for remote observers. + Roster.notify(self, event, *args, **kwargs) + + for observer in self.observers: + # Event handlers are called on the next iteration of the reactor, + # to allow the caller of this method to return a result. + reactor.callLater(0, observer.callRemote, event, *args, **kwargs) + + + + +class RemoteRoster(Roster, pb.RemoteCache): + """A client-side Roster, which mirrors a server-side LocalRoster object + by tracking changes. + """ + + + def setCopyableState(self, state): + self.update(state) + Modified: trunk/pybridge/pybridge/network/tablemanager.py =================================================================== --- trunk/pybridge/pybridge/network/tablemanager.py 2007-04-06 17:48:09 UTC (rev 398) +++ trunk/pybridge/pybridge/network/tablemanager.py 2007-04-06 17:52:49 UTC (rev 399) @@ -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,87 +10,40 @@ # 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. -from UserDict import UserDict -from twisted.spread import pb +from roster import LocalRoster, RemoteRoster -class LocalTableManager(UserDict, pb.Cacheable): - """Information that the server provides to all connected clients. - - """ +class LocalTableManager(LocalRoster): - def __init__(self): - UserDict.__init__(self) - self.observers = [] - - - def getStateToCacheAndObserveFor(self, perspective, observer): - self.observers.append(observer) - - # TODO: iterate through tables and pull out information. - state = {} - for tableid in self.keys(): - state[tableid] = {'type' : 'bridge'} - - return state - - - def stoppedObserving(self, perspective, observer): - self.observers.remove(observer) - - def openTable(self, table): + # TODO: don't notify clients which don't recognise game type. self[table.id] = table - state = {'type' : 'bridge'} - self.updateObservers('tableOpened', tableid=table.id, info=state) + self.notify('openTable', tableid=table.id, info=table.info) def closeTable(self, table): del self[table.id] - self.updateObservers('tableClosed', tableid=table.id) + self.notify('closeTable', tableid=table.id) -# Utility methods. - def updateObservers(self, event, **kwargs): - """For each observer, calls event handler with provided kwargs.""" - for observer in self.observers: - observer.callRemote(event, **kwargs) +class RemoteTableManager(RemoteRoster): - - -class RemoteTableManager(UserDict, pb.RemoteCache): - """Maintains a cache of a server-side LocalTableManager object. - - """ - - - def setCopyableState(self, state): - self.update(state) - - - def setEventHandler(self, handler): - self.eventHandler = handler - - -# Remote update methods. - - - def observe_tableOpened(self, tableid, info): + def observe_openTable(self, tableid, info): self[tableid] = info - self.eventHandler.tableOpened(tableid) + self.notify('openTable', tableid=tableid, info=info) - def observe_tableClosed(self, tableid): + def observe_closeTable(self, tableid): del self[tableid] - self.eventHandler.tableClosed(tableid) + self.notify('closeTable', tableid=tableid) Modified: trunk/pybridge/pybridge/network/usermanager.py =================================================================== --- trunk/pybridge/pybridge/network/usermanager.py 2007-04-06 17:48:09 UTC (rev 398) +++ trunk/pybridge/pybridge/network/usermanager.py 2007-04-06 17:52:49 UTC (rev 399) @@ -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,88 +10,38 @@ # 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. -from UserDict import UserDict -from twisted.spread import pb +from roster import LocalRoster, RemoteRoster -class LocalUserManager(UserDict, pb.Cacheable): - """Information that the server provides to all connected clients. - - Subclassing dict. - """ +class LocalUserManager(LocalRoster): - def __init__(self): - UserDict.__init__(self) - self.observers = [] - - - def getStateToCacheAndObserveFor(self, perspective, observer): - self.observers.append(observer) - - # TODO: iterate through users and pull out information. - state = {} - for username in self.keys(): - state[username] = {} # For now, just provide a blank dict. - - return state - - - def stoppedObserving(self, perspective, observer): - self.observers.remove(observer) - - - def userLoggedIn(self, user): + def userLogin(self, user): self[user.name] = user - state = {} - self.updateObservers('userLoggedIn', username=user.name, info=state) + self.notify('userLogin', username=user.name, info=user.info) - def userLoggedOut(self, user): + def userLogout(self, user): del self[user.name] - self.updateObservers('userLoggedOut', username=user.name) - + self.notify('userLogout', username=user.name) -# Utility methods. - def updateObservers(self, event, **kwargs): - """For each observer, calls event handler with provided kwargs.""" - for observer in self.observers: - observer.callRemote(event, **kwargs) +class RemoteUserManager(RemoteRoster): - - -class RemoteUserManager(UserDict, pb.RemoteCache): - """Maintains a cache of a remote LocalUserManager. - - """ - - - def setCopyableState(self, state): - self.update(state) - - - def setEventHandler(self, handler): - self.eventHandler = handler - - -# Remote update methods. - - - def observe_userLoggedIn(self, username, info): + def observe_userLogin(self, username, info): self[username] = info - self.eventHandler.userLoggedIn(username) + self.notify('userLogin', username=username, info=info) - def observe_userLoggedOut(self, username): + def observe_userLogout(self, username): del self[username] - self.eventHandler.userLoggedOut(username) + self.notify('userLogout', username=username) 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:48:15
|
Revision: 398 http://svn.sourceforge.net/pybridge/?rev=398&view=rev Author: umgangee Date: 2007-04-06 10:48:09 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Addition of an 'info' attribute to tables. Modified Paths: -------------- trunk/pybridge/pybridge/network/localtable.py trunk/pybridge/pybridge/network/remotetable.py Modified: trunk/pybridge/pybridge/network/localtable.py =================================================================== --- trunk/pybridge/pybridge/network/localtable.py 2007-04-03 13:26:31 UTC (rev 397) +++ trunk/pybridge/pybridge/network/localtable.py 2007-04-06 17:48:09 UTC (rev 398) @@ -35,7 +35,9 @@ implements(ITable, ISubject, IListener) + info = property(lambda self: {'game': self.gametype.__name__}) + def __init__(self, id, gametype, config={}): self.listeners = [] Modified: trunk/pybridge/pybridge/network/remotetable.py =================================================================== --- trunk/pybridge/pybridge/network/remotetable.py 2007-04-03 13:26:31 UTC (rev 397) +++ trunk/pybridge/pybridge/network/remotetable.py 2007-04-06 17:48:09 UTC (rev 398) @@ -40,7 +40,9 @@ implements(ITable, ISubject) + info = property(lambda self: {'game': self.gametype.__name__}) + def __init__(self): self.master = None # Server-side ITable object. self.listeners = [] @@ -68,10 +70,6 @@ # Implementation of ITable. - def setEventHandler(self, e): - print "called event handler - remove this!" - - def joinGame(self, position, user=None): d = self.master.callRemote('joinGame', position) return d 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-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 11:59:11
|
Revision: 395 http://svn.sourceforge.net/pybridge/?rev=395&view=rev Author: umgangee Date: 2007-04-03 04:59:11 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Make DialogConnection a child of WindowMain by adding appropriate logic to WindowMain; change utils references to manager. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_main.py Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-04-03 11:57:29 UTC (rev 394) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-04-03 11:59:11 UTC (rev 395) @@ -19,15 +19,24 @@ import gtk from wrapper import GladeWrapper +from twisted.internet import reactor import webbrowser -from pybridge import __version__ as version +from pybridge import __version__ as PYBRIDGE_VERSION import pybridge.environment as env from pybridge.network.client import client -from eventhandler import eventhandler -import utils +from eventhandler import SimpleEventHandler +from manager import WindowManager, wm +from pybridge.ui import settings +from dialog_connection import DialogConnection +from dialog_newtable import DialogNewtable +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") @@ -42,8 +51,9 @@ peopleview_icon = gtk.gdk.pixbuf_new_from_file_at_size(USER_ICON, 48, 48) - def new(self): - self.tables = {} # For each observed table, reference to window. + 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) @@ -61,20 +71,41 @@ self.peopleview.set_model(self.peopleview_model) # Register event callbacks. - eventhandler.registerCallbacksFor(self, self.callbacks) + 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 cleanup(self): - eventhandler.unregister(self, self.callbacks) - for instance in self.tables.values(): - utils.windows.close('window_bridgetable', instance) + def tearDown(self): + #eventhandler.unregister(self, self.callbacks) + # Close all windows. + for window in wm.values(): + wm.close(window) + client.disconnect() + + settings.save() # Save configuration. + + + def quit(self): + """Shut down gracefully.""" + wm.close(self) + reactor.stop() + gtk.main_quit() + + + def errback(self, failure): + print "Error: %s" % failure.getErrorMessage() + + def joinTable(self, tableid, host=False): def success(table): - window = utils.windows.open('window_bridgetable', parent=self) - self.tables[table.id] = window + window = self.tables.open(WindowBridgetable, id=tableid) window.setTable(table) d = client.joinTable(tableid, host) @@ -95,6 +126,27 @@ # Registered event handlers. + def event_connect(self): + self.notebook.set_property('sensitive', True) + self.menu_connect.set_property('visible', False) + self.menu_disconnect.set_property('visible', True) + + + def event_disconnect(self): + 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.tableview_model.clear() + self.peopleview_model.clear() + + + def event_leaveTable(self, tableid): + self.tables.close(self.tables[tableid]) + + def event_tableOpened(self, tableid): """Adds a table to the table listing.""" self.tableview_model.append([tableid, self.tableview_icon]) @@ -174,13 +226,12 @@ def on_window_main_delete_event(self, widget, *args): - utils.quit() -# return True + self.quit() def on_newtable_clicked(self, widget, *args): - if not utils.windows.get('dialog_newtable'): - utils.windows.open('dialog_newtable', parent=self) + if not wm.get(DialogNewtable): + wm.open(DialogNewtable) def on_jointable_clicked(self, widget, *args): @@ -188,18 +239,41 @@ self.on_tableview_item_activated(self.tableview, path) + def on_connect_activate(self, widget, *args): + if not wm.get(DialogConnection): + wm.open(DialogConnection) + + def on_disconnect_activate(self, widget, *args): - client.disconnect() - utils.windows.close(self.glade_name) - utils.windows.open('dialog_connection') + do_disconnect = True + #if len([True for table in self.tables if table.player]) > 0: + if self.tables: + dialog = gtk.MessageDialog(parent=self.window, + flags=gtk.DIALOG_MODAL, + type=gtk.MESSAGE_QUESTION) + dialog.set_title(_('Disconnect from Server')) + dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) + dialog.add_button(gtk.STOCK_DISCONNECT, gtk.RESPONSE_OK) + dialog.set_markup(_('Are you sure you wish to disconnect?')) + dialog.format_secondary_text(_('You are playing a game. Disconnecting may forfeit the game, or incur penalties.')) + do_disconnect = (dialog.run() == gtk.RESPONSE_OK) + dialog.destroy() + + if do_disconnect: + # Close all table windows, triggers stoppedObserving() on all tables. + # TODO: should do this on_disconnected + client.disconnect() + + def on_quit_activate(self, widget, *args): - utils.quit() + self.quit() def on_preferences_activate(self, widget, *args): - utils.windows.open('dialog_preferences') + if not wm.get(DialogPreferences): + wm.open(DialogPreferences) def on_homepage_activate(self, widget, *args): @@ -209,7 +283,7 @@ def on_about_activate(self, widget, *args): about = gtk.AboutDialog() about.set_name('PyBridge') - about.set_version(version) + about.set_version(PYBRIDGE_VERSION) about.set_copyright('Copyright (C) 2004-2007 Michael Banks') about.set_comments(_('A free online bridge game.')) about.set_website('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-03 11:57:28
|
Revision: 394 http://svn.sourceforge.net/pybridge/?rev=394&view=rev Author: umgangee Date: 2007-04-03 04:57:29 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Remove calls to WindowMain when closing WindowBridgeTable (WindowMain now acts upon leaveTable notification); switch from utils to manager for WindowManager. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-03 11:45:40 UTC (rev 393) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-03 11:57:29 UTC (rev 394) @@ -20,8 +20,10 @@ from wrapper import GladeWrapper from cardarea import CardArea +from pybridge.network.client import client from eventhandler import SimpleEventHandler -import utils +from manager import WindowManager + 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 @@ -59,12 +61,11 @@ glade_name = 'window_bridgetable' - def new(self): - self.children = utils.WindowManager() + def setUp(self): + self.children = 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. @@ -122,7 +123,7 @@ self.treeview_observers.append_column(column) - def cleanup(self): + def tearDown(self): print "Cleaning up" self.table = None # Dereference table. # Close all child windows. @@ -572,8 +573,8 @@ def on_leavetable_clicked(self, widget, *args): - d = self.parent.leaveTable(self.table.id) - d.addCallback(lambda _: utils.windows.close(self.glade_name, instance=self)) + d = client.leaveTable(self.table.id) + d.addErrback(self.errback) def on_chat_message_changed(self, widget, *args): @@ -590,8 +591,6 @@ def on_window_delete_event(self, widget, *args): - # TODO: if playing, "are you sure" dialog? - d = self.parent.leaveTable(self.table.id) - d.addCallback(lambda _: utils.windows.close(self.glade_name, instance=self)) + self.on_leavetable_clicked(widget, *args) return True # Stops window deletion taking place. 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 11:43:02
|
Revision: 392 http://svn.sourceforge.net/pybridge/?rev=392&view=rev Author: umgangee Date: 2007-04-03 04:42:56 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Rename new() and cleanup() to setUp() and tearDown(), respectively. Modified Paths: -------------- trunk/pybridge/pybridge/ui/wrapper.py Modified: trunk/pybridge/pybridge/ui/wrapper.py =================================================================== --- trunk/pybridge/pybridge/ui/wrapper.py 2007-04-03 11:39:46 UTC (rev 391) +++ trunk/pybridge/pybridge/ui/wrapper.py 2007-04-03 11:42:56 UTC (rev 392) @@ -30,9 +30,9 @@ ICON_PATH = env.find_pixmap("pybridge.png") -class GladeWrapper: +class GladeWrapper(object): """A superclass for Glade-based application windows. - + Modified from: http://www.pixelbeat.org/libs/libglade.py """ @@ -44,20 +44,19 @@ self.widgets = gtk.glade.XML(GLADE_PATH, self.glade_name, gettext.textdomain()) self.window = self.widgets.get_widget(self.glade_name) - + instance_attributes = {} for attribute in dir(self.__class__): instance_attributes[attribute] = getattr(self, attribute) self.widgets.signal_autoconnect(instance_attributes) - + self.window.set_icon_from_file(ICON_PATH) if parent is not None: self.window.set_transient_for(parent.window) - self.parent = parent - - self.new() + self.setUp() + def __getattr__(self, attribute): """Allows referencing of Glade widgets as window attributes.""" widget = self.widgets.get_widget(attribute) @@ -67,12 +66,12 @@ return widget - def new(self): - """Called when this window is opened.""" + def setUp(self): + """Override this method to run code when this window is created.""" pass - def cleanup(self): - """Called when this window is closed.""" + def tearDown(self): + """Override this method to run code when this window is destroyed.""" pass 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:39:45
|
Revision: 391 http://svn.sourceforge.net/pybridge/?rev=391&view=rev Author: umgangee Date: 2007-04-03 04:39:46 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Add ISubject (observer) facility to NetworkClient. Modified Paths: -------------- trunk/pybridge/pybridge/network/client.py Modified: trunk/pybridge/pybridge/network/client.py =================================================================== --- trunk/pybridge/pybridge/network/client.py 2007-04-03 11:36:47 UTC (rev 390) +++ trunk/pybridge/pybridge/network/client.py 2007-04-03 11:39:46 UTC (rev 391) @@ -20,11 +20,14 @@ from twisted.cred import credentials from twisted.internet import reactor from twisted.spread import pb +from zope.interface import implements -from pybridge.network.localbridge import LocalBridgeTable -from pybridge.network.remotebridge import RemoteBridgeTable -pb.setUnjellyableForClass(LocalBridgeTable, RemoteBridgeTable) +from pybridge.interfaces.observer import ISubject +from pybridge.network.localtable import LocalTable +from pybridge.network.remotetable import RemoteTable +pb.setUnjellyableForClass(LocalTable, RemoteTable) + from pybridge.network.tablemanager import LocalTableManager, RemoteTableManager pb.setUnjellyableForClass(LocalTableManager, RemoteTableManager) @@ -35,21 +38,49 @@ class NetworkClient(pb.Referenceable): """Provides the glue between the client code and the server.""" + implements(ISubject) + def __init__(self): + self.listeners = [] + self.avatar = None # Remote avatar reference. self.factory = pb.PBClientFactory() - self.eventHandler = None - + self.factory.clientConnectionLost = self.connectionLost + self.username = None self.tables = {} # Tables observed. self.tablesAvailable = None self.usersOnline = None + def connectionLost(self, connector, reason): + print "Connection lost:", reason.getErrorMessage() + self.notify('connectionLost', message=reason.getErrorMessage()) + + +# Implementation of ISubject. + + + def attach(self, listener): + self.listeners.append(listener) + + + def detach(self, listener): + self.listeners.remove(listener) + + + def notify(self, event, *args, **kwargs): + for listener in self.listeners: + listener.update(event, *args, **kwargs) + + +# Methods + + def setEventHandler(self, handler): + print "REMOVE THIS" self.eventHandler = handler - self.factory.clientConnectionLost = handler.connectionLost def connect(self, hostname, port): @@ -66,6 +97,7 @@ self.factory.disconnect() self.avatar = None self.username = None + self.notify('disconnect') def login(self, username, password): @@ -94,6 +126,7 @@ """Actions to perform when connection succeeds.""" self.avatar = avatar self.username = username + self.notify('connect') avatar.callRemote('getTables').addCallback(gotTables) avatar.callRemote('getUsers').addCallback(gotUsers) @@ -112,6 +145,7 @@ """Register user account on server.""" hash = sha.new(password).hexdigest() d = avatar.callRemote('register', username, hash) + print "TODO: after registration, need to disconnect from server" return d anon = credentials.UsernamePassword('', '') @@ -126,11 +160,10 @@ def joinTable(self, tableid, host=False): - def success(args): - table, remote = args # RemoteBridgeTable, RemoteReference. - table.master = remote # Set RemoteReference. - table.setEventHandler(self.eventHandler) + def success((table, remote)): + table.master = remote # Set RemoteReference for RemoteBridgeTable. self.tables[tableid] = table + self.notify('joinTable', tableid=tableid, table=table) return table if host: @@ -146,6 +179,7 @@ def success(r): del self.tables[tableid] + self.notify('leaveTable', tableid=tableid) d = self.avatar.callRemote('leaveTable', tableid=tableid) d.addCallback(success) 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:36:53
|
Revision: 390 http://svn.sourceforge.net/pybridge/?rev=390&view=rev Author: umgangee Date: 2007-04-03 04:36:47 -0700 (Tue, 03 Apr 2007) Log Message: ----------- Move the WindowManager into a module of its own; accept GladeWrapper class objects for window creation instead of dynamically import. Added Paths: ----------- trunk/pybridge/pybridge/ui/manager.py Added: trunk/pybridge/pybridge/ui/manager.py =================================================================== --- trunk/pybridge/pybridge/ui/manager.py (rev 0) +++ trunk/pybridge/pybridge/ui/manager.py 2007-04-03 11:36:47 UTC (rev 390) @@ -0,0 +1,71 @@ +# 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. + + +from wrapper import GladeWrapper + + +class WindowManager(dict): + """A dictionary with features for managing GladeWrapper window instances.""" + + + def open(self, windowclass, id=None, parent=None): + """Creates a new instance of a GladeWrapper window. + + @param windowclass: a subclass of GladeWrapper. + @type windowclass: classobj + @param id: if specified, an identifier for the window instance. + @type id: str or None + @param parent: if specified, a parent window to set as transient. + @type parent: GladeWrapper instance or None + @return: the instance variable of the created window. + @rtype: GladeWrapper instance + """ + id = id or windowclass + if self.get(id): + raise KeyError, "Identifier \'%s\' already registered" % id + + instance = windowclass(parent) + self[id] = instance + return instance + + + def close(self, instance): + """Closes an existing instance of a GladeWrapper window. + + @param id: the window instance. + @type id: instance + """ + if instance not in self.values(): + raise ValueError, "Window instance not registered" + + # Identify the window instance. + for id, inst in self.items(): + if inst == instance: + break + + # Since a window may close itself, it is necessary to remove the + # reference before invoking tearDown(), to prevent an infinite loop. + del self[id] + + instance.tearDown() + instance.window.destroy() + + +# An instance of WindowManager to be shared by all windows. +wm = WindowManager() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-02 13:24:10
|
Revision: 389 http://svn.sourceforge.net/pybridge/?rev=389&view=rev Author: umgangee Date: 2007-04-02 06:24:11 -0700 (Mon, 02 Apr 2007) Log Message: ----------- Cleaned up the set*() method code. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-02 13:23:15 UTC (rev 388) +++ trunk/pybridge/pybridge/ui/window_bridgetable.py 2007-04-02 13:24:11 UTC (rev 389) @@ -160,20 +160,19 @@ 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']) + self.setDealer() + self.setVulnerability() # If contract, set contract. if self.table.game.bidding.isComplete(): - contract = self.table.game.bidding.getContract() - self.setContract(contract) + 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(self.table.game.getTrickCount()) + self.setTrickCount() # If user is a player and bidding in progress, open bidding box. if self.player and not self.table.game.bidding.isComplete(): @@ -201,8 +200,8 @@ # self.cardarea.clear() self.call_store.clear() # Reset bidding history. self.trick_store.clear() # Reset trick history. - self.setContract(None) # Reset contract. - self.setTrickCount(None) # Reset trick counts. + self.setContract() # Reset contract. + self.setTrickCount() # Reset trick counts. def addCall(self, call, position): @@ -244,7 +243,7 @@ def addScore(self, contract, made, score): - textContract = self.getContractFormat(contract) + textContract = self.formatContract(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: @@ -288,6 +287,54 @@ 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 = self.formatContract(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_SYMBOLS[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') @@ -301,38 +348,17 @@ self.cardarea.set_turn(turn) - def setContract(self, contract=None): - """Sets the contract label from contract.""" - format = (contract and self.getContractFormat(contract)) or _('No contract') - self.label_contract.set_property('sensitive', contract!=None) - self.label_contract.set_markup('<span size="x-large">%s</span>' % format) + 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']] - def setDealer(self, dealer): - self.label_dealer.set_markup('<b>%s</b>' % DIRECTION_SYMBOLS[dealer]) + self.label_vuln.set_markup(format % vulnerable) - def setTrickCount(self, count=None): - """Sets the trick counter labels for declarer and defence. - - @param count: - """ - if count: - declarer = count['declarerWon'], count['declarerNeeds'] - defence = count['defenceWon'], count['defenceNeeds'] - else: - declarer = defence = (0, 0) - - self.frame_declarer.set_property('sensitive', count!=None) - self.frame_defence.set_property('sensitive', count!=None) - self.label_declarer.set_markup('<span size="x-large"><b>%s</b> (%s)</span>' % declarer) - self.label_defence.set_markup('<span size="x-large"><b>%s</b> (%s)</span>' % defence) - - - def setVuln(self, vulnerable): - self.label_vuln.set_markup('<b>%s</b>' % VULN_SYMBOLS[vulnerable]) - - # Registered event handlers. @@ -387,8 +413,8 @@ self.redrawHand(position) self.setTurnIndicator() - self.setDealer(board['dealer']) - self.setVuln(board['vuln']) + self.setDealer() + self.setVulnerability() if self.player: d = self.player.callRemote('getHand') @@ -402,8 +428,7 @@ self.setTurnIndicator() if self.table.game.bidding.isComplete(): self.children.close('window_bidbox') # If playing. - contract = self.table.game.bidding.getContract() - self.setContract(contract) + self.setContract() def event_playCard(self, card, position): @@ -411,8 +436,7 @@ playfrom = self.table.game.play.whoPlayed(card) self.addCard(card, playfrom) self.setTurnIndicator() - count = self.table.game.getTrickCount() - self.setTrickCount(count) + self.setTrickCount() self.redrawTrick() self.redrawHand(playfrom) @@ -430,7 +454,7 @@ score = self.table.game.score() self.addScore(contract, trickCount['declarerWon'], score) - textContract = _('Contract %s') % self.getContractFormat(contract) + textContract = _('Contract %s') % self.formatContract(contract) textTrick = (offset > 0 and _('made by %s tricks') % offset) or \ (offset < 0 and _('failed by %s tricks') % abs(offset)) or \ _('made exactly') @@ -459,23 +483,26 @@ # Utility methods. - def getContractFormat(self, contract): - """Returns a format string representing the contract. + def formatContract(self, contract): + """Produce a format string representing the contract. - @param contract: a dict from bidding.getContract(). + @param contract: a contract object. + @type contract: dict + @return: a format string representing the contract. + @rtype: str """ bidlevel = LEVEL_SYMBOLS[contract['bid'].level] bidstrain = STRAIN_SYMBOLS[contract['bid'].strain] - double = '' + doubled = '' if contract['redoubleBy']: - double = CALLTYPE_SYMBOLS[Redouble] + doubled = ' (%s)' % CALLTYPE_SYMBOLS[Redouble] elif contract['doubleBy']: - double = CALLTYPE_SYMBOLS[Double] + doubled = ' (%s)' % CALLTYPE_SYMBOLS[Double] declarer = contract['declarer'] - - return _('%s%s%s by %s') % (bidlevel, bidstrain, double, declarer) + return _('%s%s%s by %s') % (bidlevel, bidstrain, doubled, declarer) + # Signal handlers. @@ -517,6 +544,7 @@ 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) @@ -562,6 +590,7 @@ def on_window_delete_event(self, widget, *args): + # TODO: if playing, "are you sure" dialog? d = self.parent.leaveTable(self.table.id) d.addCallback(lambda _: utils.windows.close(self.glade_name, instance=self)) return True # Stops window deletion taking place. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-02 13:23:14
|
Revision: 388 http://svn.sourceforge.net/pybridge/?rev=388&view=rev Author: umgangee Date: 2007-04-02 06:23:15 -0700 (Mon, 02 Apr 2007) Log Message: ----------- Fix a bug which allowed BridgePlayer objects to continue playing after removePlayer() had been called. makeCall() and playCard() now accept either a player argument or a position. Invert players dict (so it maps BridgePlayer -> Direction) Modified Paths: -------------- trunk/pybridge/pybridge/bridge/game.py Modified: trunk/pybridge/pybridge/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/bridge/game.py 2007-04-02 13:18:43 UTC (rev 387) +++ trunk/pybridge/pybridge/bridge/game.py 2007-04-02 13:23:15 UTC (rev 388) @@ -65,13 +65,16 @@ self.boardQueue = [] # Boards for successive games. self.visibleHands = {} # A subset of deal, containing revealed hands. - self.players = {} # One-to-one mapping from Direction to BridgePlayer. + self.players = {} # One-to-one mapping from BridgePlayer to Direction. # Implementation of ICardGame. def start(self, board=None): + if self.inProgress(): + raise GameError, "Game in progress" + if board: # Use specified board. self.board = board elif self.board: # Advance to next deal. @@ -129,31 +132,34 @@ for call in state.get('calls', []): turn = self.getTurn() - self.makeCall(call, turn) + self.makeCall(call, position=turn) for card in state.get('played', []): turn = self.getTurn() # TODO: remove this hack if turn == self.play.dummy: turn = self.play.declarer - self.playCard(card, turn) + self.playCard(card, position=turn) def updateState(self, event, *args, **kwargs): allowed = ['start', 'makeCall', 'playCard', 'revealHand'] if event in allowed: - handler = getattr(self, event) - handler(*args, **kwargs) + try: + handler = getattr(self, event) + handler(*args, **kwargs) + except GameError, e: + print "Unexpected error when updating game state:", e def addPlayer(self, position): if position not in Direction: raise TypeError, "Expected Direction, got %s" % type(position) - if position in self.players: + if position in self.players.values(): raise GameError, "Position %s is taken" % position - player = BridgePlayer(self, position) - self.players[position] = player + player = BridgePlayer(self) + self.players[player] = position self.notify('addPlayer', position=position) return player @@ -162,11 +168,14 @@ def removePlayer(self, position): if position not in Direction: raise TypeError, "Expected Direction, got %s" % type(position) - if position not in self.players: + if position not in self.players.values(): raise GameError, "Position %s is vacant" % position - player = self.players[position] - del self.players[position] + for player, pos in self.players.items(): + if pos == position: + del self.players[player] + break + self.notify('removePlayer', position=position) @@ -189,22 +198,31 @@ # Bridge-specific methods. - def makeCall(self, call, position): + def makeCall(self, call, player=None, position=None): """Make a call in the current bidding session. + This method expects to receive either a player argument or a position. + If both are given, the position argument is disregarded. + @param call: a Call object. @type call: Bid or Pass or Double or Redouble - @param position: the position of the player making the call. - @type position: Direction + @param player: if specified, a player object. + @type player: BridgePlayer or None + @param position: if specified, the position of the player making call. + @type position: Direction or None """ if not isinstance(call, (Bid, Pass, Double, Redouble)): raise TypeError, "Expected Call, got %s" % type(call) + if player: + if player not in self.players: + raise GameError, "Player unknown to this game" + position = self.players[player] if position not in Direction: raise TypeError, "Expected Direction, got %s" % type(position) # Validate call according to game state. if not self.bidding or self.bidding.isComplete(): - raise GameError, "Game not running or bidding complete" + raise GameError, "No game in progress, or bidding complete" if self.getTurn() != position: raise GameError, "Call made out of turn" if not self.bidding.isValidCall(call, position): @@ -227,24 +245,33 @@ pass # TODO - def playCard(self, card, position): + def playCard(self, card, player=None, position=None): """Play a card in the current play session. - The position specified is that of the player of the card: in particular, - declarer plays cards from dummy's hand when it is dummy's turn. + This method expects to receive either a player argument or a position. + If both are given, the position argument is disregarded. + If position is specified, it must be that of the player of the card: + declarer plays cards from dummy's hand when it is dummy's turn. + @param card: a Card object. @type card: Card - @param position: the position of the player playing the card. - @type position: Direction + @param player: if specified, a player object. + @type player: BridgePlayer or None + @param position: if specified, the position of the player of the card. + @type position: Direction or None """ if not isinstance(card, Card): raise TypeError, "Expected Card, got %s" % type(card) + if player: + if player not in self.players: + raise GameError, "Invalid player reference" + position = self.players[player] if position not in Direction: raise TypeError, "Expected Direction, got %s" % type(position) if not self.play or self.play.isComplete(): - raise GameError, "Game not running or play complete" + raise GameError, "No game in progress, or play complete" playfrom = position @@ -278,7 +305,7 @@ @param hand: a hand of Card objects. @type hand: list - @param position: the position of the player with hand. + @param position: the position of the hand. @type position: Direction """ if position not in Direction: @@ -318,45 +345,6 @@ raise GameError, "No game in progress" - def getTrickCount(self): - """Returns various - - @return: a dictionary of result information. - @rtype: dict - - - ['declarerWon']: number of tricks won by declarer/dummy. - @return['defenceWon']: number of tricks won by defenders. - @return['declarerNeeds']: number of extra tricks required by declarer - to make contract. - @return['defenceNeeds']: number of extra tricks required by defenders - to break contract. - @return['required']: number of tricks required from contract level. - - """ - if self.play is None: - raise GameError, "Not in play" - - count = dict.fromkeys(('declarerWon', 'declarerNeeds', - 'defenceWon', 'defenceNeeds'), 0) - - for index in range(len(self.play.winners)): - trick = self.play.getTrick(index) - winner = self.play.whoPlayed(self.play.winningCard(trick)) - if winner in (self.play.declarer, self.play.dummy): - count['declarerWon'] += 1 - else: # Trick won by defenders. - count ['defenceWon'] += 1 - - contract = self.bidding.getContract() - # Get index value of bid level, increment, add 6. - count['required'] = contract['bid'].level.index + 7 - count['declarerNeeds'] = max(0, count['required'] - count['declarerWon']) - count['defenceNeeds'] = max(0, 13 - count['required'] - count['defenceWon'] + 1) - - return count - - def getScore(self): """Returns the integer score value for declarer/dummy if: @@ -395,22 +383,22 @@ """Actor representing a player's view of a BridgeGame object.""" - def __init__(self, game, position): + def __init__(self, game): self.__game = game # Provide access to game only through this object. - self.__position = position def getHand(self): - return self.__game.getHand(self.__position) + position = self.__game.players[self] + return self.__game.getHand(position) def makeCall(self, call): - return self.__game.makeCall(call, self.__position) + return self.__game.makeCall(call, player=self) def playCard(self, card): # TODO: need try/except block on each. - return self.__game.playCard(card, self.__position) + return self.__game.playCard(card, player=self) def nextGame(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-02 13:18:42
|
Revision: 387 http://svn.sourceforge.net/pybridge/?rev=387&view=rev Author: umgangee Date: 2007-04-02 06:18:43 -0700 (Mon, 02 Apr 2007) Log Message: ----------- Fix references to Direction. Modified Paths: -------------- trunk/pybridge/tests/test_bidding.py trunk/pybridge/tests/test_playing.py Modified: trunk/pybridge/tests/test_bidding.py =================================================================== --- trunk/pybridge/tests/test_bidding.py 2007-04-02 13:17:54 UTC (rev 386) +++ trunk/pybridge/tests/test_bidding.py 2007-04-02 13:18:43 UTC (rev 387) @@ -3,7 +3,7 @@ from pybridge.bridge.bidding import Bidding from pybridge.bridge.call import Bid, Pass, Double, Redouble -from pybridge.bridge.symbols import Level, Player, Strain +from pybridge.bridge.symbols import Direction, Level, Strain class TestBidding(unittest.TestCase): @@ -14,7 +14,7 @@ def setUp(self): - dealer = random.choice(Player) + dealer = random.choice(Direction) self.bidding = Bidding(dealer) @@ -44,11 +44,11 @@ def testWhoseTurn(self): """whoseTurn""" # Tests whoseTurn() before and after making calls. - turn = Player[self.bidding.dealer.index] + turn = Direction[self.bidding.dealer.index] for call in self.bids: self.assertEqual(self.bidding.whoseTurn(), turn) self.bidding.makeCall(call) - turn = Player[(turn.index + 1) % 4] + turn = Direction[(turn.index + 1) % 4] self.assertEqual(self.bidding.whoseTurn(), turn) Modified: trunk/pybridge/tests/test_playing.py =================================================================== --- trunk/pybridge/tests/test_playing.py 2007-04-02 13:17:54 UTC (rev 386) +++ trunk/pybridge/tests/test_playing.py 2007-04-02 13:18:43 UTC (rev 387) @@ -4,14 +4,14 @@ from pybridge.bridge.card import Card from pybridge.bridge.deck import Deck from pybridge.bridge.playing import Playing -from pybridge.bridge.symbols import Player, Rank, Suit +from pybridge.bridge.symbols import Direction, Rank, Suit class TestPlaying(unittest.TestCase): def setUp(self): - declarer = random.choice(Player) + declarer = random.choice(Direction) trumps = random.choice(list(Suit) + [None]) self.deal = Deck().randomDeal() self.playing = Playing(declarer, trumps) @@ -31,7 +31,7 @@ if len(cards) == 4: turn = self.playing.whoPlayed(self.playing.winningCard(trick)) else: - turn = Player[(leader.index + len(cards)) % 4] + turn = Direction[(leader.index + len(cards)) % 4] # Check that whoseTurn() works. self.assertEqual(self.playing.whoseTurn(), turn) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-02 13:17:53
|
Revision: 386 http://svn.sourceforge.net/pybridge/?rev=386&view=rev Author: umgangee Date: 2007-04-02 06:17:54 -0700 (Mon, 02 Apr 2007) Log Message: ----------- Two test cases for BridgeGame: one to test basic functionality, the other to play through a simple game. Added Paths: ----------- trunk/pybridge/tests/test_game.py Added: trunk/pybridge/tests/test_game.py =================================================================== --- trunk/pybridge/tests/test_game.py (rev 0) +++ trunk/pybridge/tests/test_game.py 2007-04-02 13:17:54 UTC (rev 386) @@ -0,0 +1,125 @@ +import unittest + +from pybridge.bridge.board import Board +from pybridge.bridge.call import Bid, Pass, Double, Redouble +from pybridge.bridge.card import Card +from pybridge.bridge.game import BridgeGame +from pybridge.bridge.symbols import Direction, Level, Strain, Rank, Suit, Vulnerable +from pybridge.network.error import GameError + + +# A sample board. +#from pybridge.bridge.deck import Deck +#d = Deck() +#hands = d.randomDeal() +hands = dict(zip(Direction, [[Card(r, s) for r in Rank] for s in Suit])) +board = Board(deal=hands, dealer=Direction.North, vuln=Vulnerable.All) + + +class TestGame(unittest.TestCase): + + + def setUp(self): + self.game = BridgeGame() + + + def tearDown(self): + self.game = None + + + def testStart(self): + """Starting game should reinitialise state""" + self.assertEqual(self.game.inProgress(), False) + self.assertRaises(GameError, self.game.getTurn) + self.game.start() + self.assertEqual(self.game.inProgress(), True) + self.assertEqual(self.game.getTurn(), self.game.board['dealer']) + + + def testStartWithBoard(self): + self.game.start(board) + self.assertEqual(board, self.game.board) + self.assertEqual(self.game.getTurn(), board['dealer']) + + + def testPlayers(self): + players = {} + for position in Direction: + # Associate position with player. + players[position] = self.game.addPlayer(position) + # Cannot add another player to the same position. + self.assertRaises(GameError, self.game.addPlayer, position) + for position in Direction: + self.game.removePlayer(position) + + + def testGetState(self): + self.game.start(board) + + + + +class TestGameRuns(unittest.TestCase): + + + def setUp(self): + self.game = BridgeGame() + self.players = {} + for position in list(Direction): + self.players[position] = self.game.addPlayer(position) + + + def tearDown(self): + self.game = None + self.players = {} + + + def testBiddingPassedOut(self): + """All players pass, game should finish without reaching play""" + self.game.start(board) + + turn = board['dealer'] # Avoid calling getTurn. + for i in range(len(Direction)): # Iterate for each player. + self.players[turn].makeCall( Pass() ) # Each player passes. + turn = Direction[(turn.index + 1) % len(Direction)] + self.assertEqual(turn, board['dealer']) # Sanity check. + + # Bidding is passed out - game is over. + self.assertEqual(self.game.inProgress(), False) + # Should not be able to make calls or play cards. + self.assertRaises(GameError, self.players[turn].makeCall, Bid(Level.One, Strain.Club)) + self.assertRaises(GameError, self.players[turn].playCard, board['deal'][turn][0]) + + + def testSampleGame(self): + """Play through a sample game. + + This does not attempt to test the integrity of Bidding and Play. + """ + calls = [Bid(l, s) for l in Level for s in Strain] + [Pass()]*3 + self.game.start(board) + + for call in calls: + turn = self.game.getTurn() + self.players[turn].makeCall(call) + + while not self.game.play.isComplete(): + turn = self.game.getTurn() + # Find a valid card. + for card in board['deal'][turn]: + if self.game.play.isValidPlay(card, turn, hand=board['deal'][turn]): + if turn == self.game.play.dummy: + turn = self.game.play.declarer + self.players[turn].playCard(card) + break + + self.assertEqual(self.game.inProgress(), False) # Game complete. + #self.game.getState() + + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestGame) +unittest.TextTestRunner(verbosity=2).run(suite) +suite = unittest.TestLoader().loadTestsFromTestCase(TestGameRuns) +unittest.TextTestRunner(verbosity=2).run(suite) + 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:48:47
|
Revision: 385 http://svn.sourceforge.net/pybridge/?rev=385&view=rev Author: umgangee Date: 2007-04-01 10:48:48 -0700 (Sun, 01 Apr 2007) Log Message: ----------- Replace "gumby" with an "angry monkey" test client, which attempts to play without regard to game rules. Added Paths: ----------- trunk/pybridge/tests/monkey.py Removed Paths: ------------- trunk/pybridge/tests/gumby.py Deleted: trunk/pybridge/tests/gumby.py =================================================================== --- trunk/pybridge/tests/gumby.py 2007-04-01 17:44:25 UTC (rev 384) +++ trunk/pybridge/tests/gumby.py 2007-04-01 17:48:48 UTC (rev 385) @@ -1,228 +0,0 @@ -#!/usr/bin/env python - -# 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 os -import sys - - -# Determine the base directory. -currentdir = os.path.dirname(os.path.abspath(sys.argv[0])) -basedir = os.path.abspath(os.path.join(currentdir, '..')) - -# Find the Python module path, relative to the base directory. -if os.path.exists(os.path.join(basedir, 'lib')): - pythonver = 'python%d.%d' % sys.version_info[:2] - pythonpath = os.path.join(basedir, 'lib', pythonver, 'site-packages') -else: - pythonpath = basedir - -sys.path.insert(0, pythonpath) - - -import random -from twisted.internet import reactor -from zope.interface import implements - -from pybridge.interfaces.table import ITableEvents -from pybridge.interfaces.bridgetable import IBridgeTableEvents -from pybridge.interfaces.serverstate import IServerEvents - -from pybridge.network.client import client - -from pybridge.bridge.call import Bid, Double, Redouble, Pass -from pybridge.bridge.symbols import Level, Strain - - -# Give the bot some "personality". -QUOTES = ["Hello!", "Doctor? Doctor? DOCTOR!", "My brain hurts!", - "Are you the brain specialist?", - "No. No, I am not the brain specialist. No I am not. Yes! Yes I am!", - "Well, I think cement is more interesting than people think!", - "I believe in peace... and bashing two bricks together.", - "I would put a tax on all people who stand in water... Oh!", ] - -SLOTH = 0.1 # Time between game event and response. - - -class BotEventHandler: - - implements(IServerEvents, ITableEvents, IBridgeTableEvents) - - - def __init__(self): - self.table = None # Convenient reference to observed table. - - - def errback(self, failure): - print "ERROR:", failure.getErrorMessage() - - -# Selection of calls and cards. - - - def chooseCall(self): - - def makeCall(): - print "My turn: calling %s" % call - d = self.table.gameMakeCall(call) - d.addErrback(self.errback) - - current = self.table.game.bidding.getCurrentCall(Bid) - if current: - if current.level < Level.Three: - strain = Strain[(current.strain.index + 1) % 5] - level = Level[current.level.index + int(strain.index==0)] - call = Bid(level, strain) - else: - call = Pass() - else: # Start the bidding. - call = Bid(Level.One, Strain.Club) - reactor.callLater(SLOTH, makeCall) - - - def chooseCard(self, position): - - def playCard(): - print "My turn: playing %s from %s" % (str(selected), position) - d = self.table.gamePlayCard(selected, position) - d.addErrback(self.errback) - - hand = self.table.game.deal[position] - if hand: - valid = [c for c in hand if self.table.game.playing.isValidPlay(c, position, hand)] - selected = random.choice(valid) - reactor.callLater(SLOTH, playCard) - else: # Hand not revealed just yet, try again a little later. - reactor.callLater(0.1, self.chooseCard, position) - - - def tableOpened(self, table): - - def joinedTable(table): - self.table = table # Set table reference. - print "I joined table %s" % table.id - table.sendMessage(random.choice(QUOTES)) - - # Find a vacant seat. - for seat, player in table.players.items(): - if player is None: - table.addPlayer(seat) # Take seat. - break - - print "Table %s opened" % table - if len(client.tables) == 0: # Not observing any tables. - d = client.joinTable(table).addCallback(joinedTable) - - - def tableClosed(self, table): - pass - - - def userLoggedIn(self, user): - pass - - - def userLoggedOut(self, user): - pass - - -# Implementation of ITableEvents. - - - def observerAdded(self, table, observer): - pass - - - def observerRemoved(self, table, observer): - pass - - - def playerAdded(self, table, player, position): - if player == client.username: - print "I take seat %s" % position - else: - print "Player %s takes seat %s" % (player, position) - - - def playerRemoved(self, table, player, position): - print "Player %s leaves seat %s" % (player, position) - - - def messageReceived(self, table, message, sender, recipients): - if sender != client.username: - print "%s says: %s" % (sender, message) - - -# Implementation of IBridgeTableEvents. - - - def gameStarted(self, table, dealer, vulnNS, vulnEW): - print "Game started: dealer is %s" % dealer - if self.table.game.whoseTurn() == self.table.getPositionOfPlayer(client.username): - self.chooseCall() - - - def gameFinished(self, table): - print "Game finished" - - - def gameCallMade(self, table, call, position): - print "Call %s made by %s" % (call, position) - if self.table.game.whoseTurn() == self.table.getPositionOfPlayer(client.username): - if not self.table.game.bidding.isComplete(): - self.chooseCall() - else: - myseat = self.table.getPositionOfPlayer(client.username) - self.chooseCard(myseat) - - - def gameCardPlayed(self, table, card, position): - print "Card %s played by %s" % (card, position) - - if not self.table.game.isComplete(): - myseat = self.table.getPositionOfPlayer(client.username) - turn = self.table.game.whoseTurn() - if turn == self.table.game.playing.dummy and myseat == self.table.game.playing.declarer: - myseat = self.table.game.playing.dummy # Declarer controls dummy. - if turn == myseat: - self.chooseCard(myseat) - - - def gameHandRevealed(self, table, hand, position): - print "I can see %s's hand" % position - - - - -def lostConnection(connector, reason): - print "Connection lost: %s" % reason.getErrorMessage() - reactor.stop() - - -if __name__ == '__main__': - username, password = sys.argv[1], sys.argv[2] - - client.factory.clientConnectionLost = lostConnection - client.eventHandler = BotEventHandler() - client.connect(hostname="localhost", port=5040) - client.login(username, password) - - reactor.run() - Added: trunk/pybridge/tests/monkey.py =================================================================== --- trunk/pybridge/tests/monkey.py (rev 0) +++ trunk/pybridge/tests/monkey.py 2007-04-01 17:48:48 UTC (rev 385) @@ -0,0 +1,274 @@ +#!/usr/bin/env python + +# 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 os +import sys + +# Determine the base directory. +currentdir = os.path.dirname(os.path.abspath(sys.argv[0])) +basedir = os.path.abspath(os.path.join(currentdir, '..')) + +# Find the Python module path, relative to the base directory. +if os.path.exists(os.path.join(basedir, 'lib')): + pythonver = 'python%d.%d' % sys.version_info[:2] + pythonpath = os.path.join(basedir, 'lib', pythonver, 'site-packages') +else: + pythonpath = basedir + +sys.path.insert(0, pythonpath) + + +import random +import time +from twisted.internet import reactor +from zope.interface import implements + +from pybridge.interfaces.observer import IListener +from pybridge.interfaces.serverstate import IServerEvents + +from pybridge.network.client import NetworkClient +from pybridge.network.error import GameError + +from pybridge.bridge.call import Bid, Double, Redouble, Pass +from pybridge.bridge.card import Card +from pybridge.bridge.symbols import Level, Strain, Rank, Suit + + +# Give the bot some "personality". +QUOTES = ["Hello!", "Doctor? Doctor? DOCTOR!", "My brain hurts!", + "Are you the brain specialist?", + "No. No, I am not the brain specialist. No I am not. Yes! Yes I am!", + "Well, I think cement is more interesting than people think!", + "I believe in peace... and bashing two bricks together.", + "I would put a tax on all people who stand in water... Oh!", ] + +SLOTH = 0.1 # Time between game event and response. + + +class SimpleEventHandler: + + implements(IListener) + + def __init__(self, target): + self.target = target + + def update(self, event, *args, **kwargs): + method = getattr(self.target, "event_%s" % event, None) + if method: + method(*args, **kwargs) + + +class Monkey: + """Simulation of a monkey playing bridge. + + The monkey will attempt to make calls and play cards at random, disregarding + the rules of bridge (playing in turn, following suit, etc). Only valid + calls and cards should be accepted by the Game object. + + For inspiration, see: + http://folklore.org/StoryView.py?project=Macintosh&story=Monkey_Lives.txt + """ + + implements(IServerEvents) + + + def __init__(self, client): + self.client = client + self.table = None # RemoteTable. + self.player = None # RemoteReference to remote BridgePlayer object. + self.position = None + + + def success(self, r): + pass + + + def errback(self, failure): + if failure.type == str(GameError): + pass # Suppress GameErrors. + # TODO: command-line option + else: + print "ERROR:", failure.getErrorMessage() + + + def connected(self, r): + print "Logged in to server" + reactor.callLater(0.2, self.joinTable) + + + def joinTable(self): + print "Looking for a table to join..." + if self.client.tablesAvailable.get('Monkey'): + d = self.client.joinTable('Monkey') + else: + d = self.client.joinTable('Monkey', host=True) + d.addCallbacks(self.joinedTable, self.errback) + + + def joinedTable(self, table): + self.table = table # Set table reference. + self.table.attach(SimpleEventHandler(self)) + self.table.game.attach(SimpleEventHandler(self)) + + print "I joined table %s" % table.id + table.sendMessage(random.choice(QUOTES)) + + # Find a vacant place at the table, if any. + for position in table.game.positions: + if table.players.get(position) is None: + d = table.joinGame(position) # Take seat. + d.addCallbacks(self.joinedGame, self.errback, + callbackArgs=[position]) + break + + + def joinedGame(self, player, position): + self.player = player + self.position = position + + +# Selection of calls and cards. + + + calls = [Bid(random.choice(Level), random.choice(Strain)), + Pass(), Double(), Redouble()] + + def chooseCall(self): + call = random.choice(self.calls) + d = self.player.callRemote('makeCall', call) # TODO + d.addCallbacks(self.success, self.errback) + + + #cards = [Card(r, s) for r in Rank for s in Suit] + + def chooseCard(self): + turn = self.table.game.getTurn() + try: + hand = self.table.game.getHand(turn) + card = random.choice(hand) + d = self.player.callRemote('playCard', card) # TODO + d.addCallbacks(self.success, self.errback) + except GameError: + pass + + +# IServerEvents + + + def tableOpened(self, table): + print "Table %s opened" % table + + + def tableClosed(self, table): + print "Table %s closed" % table + + + def userLoggedIn(self, user): + print "User %s logged in" % user + + + def userLoggedOut(self, user): + print "User %s logged out" % user + + +# Table events. + + + def event_joinGame(self, player, position): + if player == self.client.username: + print "I take seat %s" % position + else: + print "Player %s takes seat %s" % (player, position) + + + def event_leaveGame(self, player, position): + print "Player %s leaves seat %s" % (player, position) + + + def event_sendMessage(self, message, sender, recipients): + if sender == self.client.username: + print "I say: %s" % message + else: + print "%s says: %s" % (sender, message) + + +# Game events. + + + def event_start(self, board): + + def gotHand(hand): + print "My hand is", [str(c) for c in hand] + self.table.game.revealHand(hand, self.position) + + def loop(): + if self.table.game.inProgress(): + if not self.table.game.bidding.isComplete(): + self.chooseCall() + else: + self.chooseCard() + reactor.callLater(SLOTH, loop) + else: + print "Game terminated" + + print "Game started: dealer is %s" % board['dealer'] + if self.player: + d = self.player.callRemote('getHand') + d.addCallbacks(gotHand, self.errback) + loop() + + + def event_makeCall(self, call, position): + print "%s made by %s" % (call, position) + + + def event_playCard(self, card, position): + print "%s played by %s" % (card, position) + + + def event_revealHand(self, hand, position): + print "Hand of %s revealed" % position + + +client = NetworkClient() + + +def loginFailed(reason): + print "Login failed: %s" % reason.getErrorMessage() + client.disconnect() + + +def lostConnection(connector, reason): + print "Connection lost: %s" % reason.getErrorMessage() + reactor.stop() + + +if __name__ == '__main__': + username, password = sys.argv[1], sys.argv[2] + + client.factory.clientConnectionLost = lostConnection + client.eventHandler = Monkey(client) + + client.connect(hostname="localhost", port=5040) + d = client.login(username, password) + d.addCallbacks(client.eventHandler.connected, loginFailed) + + reactor.run() + Property changes on: trunk/pybridge/tests/monkey.py ___________________________________________________________________ Name: svn:executable + * 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:44:23
|
Revision: 384 http://svn.sourceforge.net/pybridge/?rev=384&view=rev Author: umgangee Date: 2007-04-01 10:44:25 -0700 (Sun, 01 Apr 2007) Log Message: ----------- A couple of fixes. Modified Paths: -------------- trunk/pybridge/pybridge/bridge/board.py Modified: trunk/pybridge/pybridge/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/bridge/board.py 2007-04-01 17:42:58 UTC (rev 383) +++ trunk/pybridge/pybridge/bridge/board.py 2007-04-01 17:44:25 UTC (rev 384) @@ -57,13 +57,14 @@ self['deal'] = deck.randomDeal() self['num'] = self.get('num', 0) + 1 - self['time'] = time.localtime() + self['time'] = tuple(time.localtime()) if self.get('dealer'): # Rotate dealer. - self['dealer'] = Direction((self['dealer'].index+1) % len(Direction)) + self['dealer'] = Direction[(self['dealer'].index + 1) % 4] else: # Select any player as the dealer. self['dealer'] = random.choice(Direction) if result: self['vuln'] = Vulnerable.All # TODO - + else: + self['vuln'] = Vulnerable.None # The default value. 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. |