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...> - 2008-05-10 09:51:33
|
Revision: 507 http://pybridge.svn.sourceforge.net/pybridge/?rev=507&view=rev Author: umgangee Date: 2008-05-10 02:51:38 -0700 (Sat, 10 May 2008) Log Message: ----------- A variety of changes, that, for some reason, weren't committed last year. Modified Paths: -------------- trunk/pybridge/INSTALL trunk/pybridge/NEWS trunk/pybridge/README trunk/pybridge/locale/src/Makefile trunk/pybridge/pybridge/__init__.py trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py trunk/pybridge/pybridge/network/remotetable.py trunk/pybridge/pybridge/server/database.py trunk/pybridge/pybridge/ui/dialog_connection.py trunk/pybridge/pybridge/ui/window_chat.py trunk/pybridge/pybridge/ui/window_gametable.py trunk/pybridge/setup.py trunk/pybridge/tests/bridge/test_deal.py trunk/pybridge/tests/monkey.py Modified: trunk/pybridge/INSTALL =================================================================== --- trunk/pybridge/INSTALL 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/INSTALL 2008-05-10 09:51:38 UTC (rev 507) @@ -38,7 +38,7 @@ - PyGTK (>= 2.8) - http://www.pygtk.org/ - Cairo (>= 1.0) - http://cairographics.org/ - PyCairo (>= 1.0) - http://cairographics.org/pycairo/ - - ConfigObj (>= 4.0) -http://www.voidspace.org.uk/python/configobj.html + - ConfigObj (>= 4.0) - http://www.voidspace.org.uk/python/configobj.html To run the standalone PyBridge server, the following software is also required: Modified: trunk/pybridge/NEWS =================================================================== --- trunk/pybridge/NEWS 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/NEWS 2008-05-10 09:51:38 UTC (rev 507) @@ -1,3 +1,39 @@ +========== +0.4.0 (??) +========== + +New features +------------ + + - Architecture: + + - Full support for other games. + + + - User interface: + + - Separation of bridge table UI from generic table window. Tables for particular games inherit. + + - Enhanced chat support. + + +Bug fixes +--------- + + - Fixed https://bugs.launchpad.net/pybridge/+bug/127974 + + +Translations +------------ + + - French: Aldo Reset <al...@pl...> + + - Polish: + + Please help with translating PyBridge into your native language! + See https://translations.launchpad.net/pybridge/ to get started. + + ===================== 0.3.0 (25 June 2007) ===================== Modified: trunk/pybridge/README =================================================================== --- trunk/pybridge/README 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/README 2008-05-10 09:51:38 UTC (rev 507) @@ -1,6 +1,7 @@ -PyBridge 0.3.0 - a free online bridge game +PyBridge 0.4.0 - a free online bridge game http://pybridge.sourceforge.net/ http://sourceforge.net/projects/pybridge/ +https://launchpad.net/pybridge About PyBridge @@ -31,12 +32,12 @@ Your feedback, bug reports and ideas for new features are especially welcome. - - Forums: http://sourceforge.net/forum/?group_id=114287 + - Bug reports: https://bugs.launchpad.net/pybridge/ - - Bug reports: http://sourceforge.net/tracker/?group_id=114287&atid=667822 + - Feature ideas: https://blueprints.launchpad.net/pybridge/ + + - Translations: https://translations.launchpad.net/pybridge/ - - Feature ideas: http://sourceforge.net/tracker/?group_id=114287&atid=667825 - If you are a competent (or aspiring!) Python programmer and would like to contribute code to PyBridge, then please get in touch with the developers! Modified: trunk/pybridge/locale/src/Makefile =================================================================== --- trunk/pybridge/locale/src/Makefile 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/locale/src/Makefile 2008-05-10 09:51:38 UTC (rev 507) @@ -18,12 +18,12 @@ msgmerge -UN $@ $< #Generic rule for POT files -%.pot: ../../glade/pybridge.glade ../../pybridge/ui/*.py +%.pot: ../../glade/pybridge.glade ############ #CREATING $@ ############ intltool-extract --type=gettext/glade ../../glade/pybridge.glade - xgettext -k_ -kN_ --from-code=UTF-8 -o $@ ../../glade/pybridge.glade.h ../../pybridge/ui/*.py + xgettext -k_ -kN_ --from-code=UTF-8 -o $@ ../../glade/pybridge.glade.h ../../pybridge/ui/*.py ../../pybridge/games/bridge/ui/*.py update: $(shell ls *.po) Modified: trunk/pybridge/pybridge/__init__.py =================================================================== --- trunk/pybridge/pybridge/__init__.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/__init__.py 2008-05-10 09:51:38 UTC (rev 507) @@ -2,7 +2,7 @@ PyBridge - a free online bridge game. """ -__version__ = '0.3.0' +__version__ = '0.4.0' __author__ = 'Michael Banks <mi...@ba...>' __license__ = 'GNU General Public License, Version 2 or later' Modified: trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2008-05-10 09:51:38 UTC (rev 507) @@ -124,10 +124,15 @@ self.window.set_title(_('Score Sheet')) #self.window.connect('delete_event', self.on_delete_event) + self.sw = gtk.ScrolledWindow() + self.sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + self.sw.set_size_request(-1, 150) + self.window.add(self.sw) + self.eventHandler = SimpleEventHandler(self) self.table = None - self.window.show() + self.window.show_all() def tearDown(self): @@ -150,7 +155,7 @@ for result in self.table.game.results: self.scoresheet.add_result(result) - self.window.add(self.scoresheet) + self.sw.add(self.scoresheet) self.scoresheet.show() @@ -164,7 +169,6 @@ self.scoresheet.add_result(result) - def event_makeCall(self, call, position): self.update() Modified: trunk/pybridge/pybridge/network/remotetable.py =================================================================== --- trunk/pybridge/pybridge/network/remotetable.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/network/remotetable.py 2008-05-10 09:51:38 UTC (rev 507) @@ -49,6 +49,8 @@ if state['gamename'] in SUPPORTED_GAMES: gameclass = SUPPORTED_GAMES[state['gamename']] self.game = gameclass() + # TODO: encapsulate within a try/except block, so errors are not + # propagated back to server, and client drops table gracefully. self.game.setState(state['gamestate']) else: raise NameError, "Unsupported game class %s" % state['gamename'] Modified: trunk/pybridge/pybridge/server/database.py =================================================================== --- trunk/pybridge/pybridge/server/database.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/server/database.py 2008-05-10 09:51:38 UTC (rev 507) @@ -99,6 +99,7 @@ # Don't split name field - see http://people.w3.org/rishida/blog/?p=100 realname = UnicodeCol(default=None, length=40) profile = UnicodeCol(default=None) + #country = StringCol(length=2, default=None) # ISO 3166 country code. created = DateTimeCol(default=datetime.now) lastLogin = DateTimeCol(default=None) # friends = MultipleJoin('UserFriend', joinColumn='from_user') Modified: trunk/pybridge/pybridge/ui/dialog_connection.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_connection.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/ui/dialog_connection.py 2008-05-10 09:51:38 UTC (rev 507) @@ -41,7 +41,7 @@ self.entry_username.set_text(connection.get('Username', '')) password = connection.get('Password', '').decode('rot13') self.entry_password.set_text(password) - self.check_savepassword.set_active(password != '') + self.check_savepassword.set_active(bool(password)) else: self.entry_portnum.set_text(str(TCP_PORT)) Modified: trunk/pybridge/pybridge/ui/window_chat.py =================================================================== --- trunk/pybridge/pybridge/ui/window_chat.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/ui/window_chat.py 2008-05-10 09:51:38 UTC (rev 507) @@ -115,12 +115,13 @@ # People list display. self.people = PeopleBox() hpaned.pack2(self.people, resize=False, shrink=True) - self.pack1(hpaned, resize=True, shrink=False) + self.pack1(hpaned, resize=True, shrink=True) self.textentry = gtk.TextView() self.textentry.set_editable(True) self.textentry.set_property('sensitive', False) - self.conversation.set_wrap_mode(gtk.WRAP_WORD) + self.textentry.set_wrap_mode(gtk.WRAP_WORD) + #self.textentry.set_size_request(30, 30) self.textentry.connect('key_press_event', self.on_textentry_key_pressed) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) @@ -129,7 +130,7 @@ frame.set_shadow_type(gtk.SHADOW_IN) frame.add(sw) frame.set_border_width(6) - self.pack2(frame, resize=False, shrink=False) + self.pack2(frame, resize=True, shrink=True) # Populate conversation textview with text tags. tagtable = self.conversation.get_buffer().get_tag_table() @@ -251,6 +252,7 @@ self.chatboxes = {} # Maps Chat objects to their ChatBox instances. self.notebook = gtk.Notebook() + self.notebook.set_scrollable(True) self.notebook.connect('switch-page', self.on_switch_page) self.window.add(self.notebook) self.window.set_border_width(4) Modified: trunk/pybridge/pybridge/ui/window_gametable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_gametable.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/pybridge/ui/window_gametable.py 2008-05-10 09:51:38 UTC (rev 507) @@ -89,18 +89,18 @@ self.toolbar.insert(gtk.SeparatorToolItem(), -1) self.fullscreen = gtk.ToggleToolButton(gtk.STOCK_FULLSCREEN) + self.fullscreen.set_label(_('Full Screen')) self.fullscreen.connect('clicked', self.on_fullscreen_clicked) - #self.fullscreen.set_tooltip() self.toolbar.insert(self.fullscreen, -1) - self.toolbar.insert(gtk.SeparatorToolItem(), -1) - self.leavetable = gtk.ToolButton(gtk.STOCK_QUIT) self.leavetable.set_label(_('Leave Table')) self.leavetable.connect('clicked', self.on_leavetable_clicked) self.toolbar.insert(self.leavetable, -1) + self.toolbar.insert(gtk.SeparatorToolItem(), -1) + def tearDown(self): # Close all child windows. for window in self.children.values(): @@ -115,6 +115,12 @@ self.table = None # Dereference table. + def errback(self, failure): + # TODO: display error in window. + print "Error: %s" % failure.getErrorMessage() + print failure.getBriefTraceback() + + def setTable(self, table): """Set display to follow specified table object. Modified: trunk/pybridge/setup.py =================================================================== --- trunk/pybridge/setup.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/setup.py 2008-05-10 09:51:38 UTC (rev 507) @@ -21,7 +21,7 @@ url = 'http://sourceforge.net/projects/pybridge/', description = 'A free online bridge game.', download_url = 'http://sourceforge.net/project/showfiles.php?group_id=114287', - packages = ['pybridge', 'pybridge.bridge', 'pybridge.interfaces', 'pybridge.network', 'pybridge.server', 'pybridge.ui'], + packages = ['pybridge', 'pybridge.games', 'pybridge.interfaces', 'pybridge.network', 'pybridge.server', 'pybridge.ui'], scripts = ['bin/pybridge', 'bin/pybridge-server'], data_files = [('share/applications', ['bin/pybridge.desktop']), ('share/doc/pybridge', ['AUTHORS', 'COPYING', 'INSTALL', 'NEWS', 'README']), Modified: trunk/pybridge/tests/bridge/test_deal.py =================================================================== --- trunk/pybridge/tests/bridge/test_deal.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/tests/bridge/test_deal.py 2008-05-10 09:51:38 UTC (rev 507) @@ -73,6 +73,12 @@ self.assertEqual(Deal.fromIndex(index), deal) +# def test_toString(self): +# """Testing toString method over a set of known deals""" +# for deal in self.samples.values(): +# self.assertEqual(Deal.fromString(deal.toString())) + + def main(): suite = unittest.makeSuite(TestDeck) unittest.TextTestRunner(verbosity=2).run(suite) Modified: trunk/pybridge/tests/monkey.py =================================================================== --- trunk/pybridge/tests/monkey.py 2007-10-04 16:40:59 UTC (rev 506) +++ trunk/pybridge/tests/monkey.py 2008-05-10 09:51:38 UTC (rev 507) @@ -150,7 +150,7 @@ # Selection of calls and cards. - calls = [lambda : Bid(random.choice(Level), random.choice(Strain)), + calls = [lambda : Bid(Level.One, random.choice(Strain)), Pass, Double, Redouble] def chooseCall(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-10-04 16:40:59
|
Revision: 506 http://pybridge.svn.sourceforge.net/pybridge/?rev=506&view=rev Author: umgangee Date: 2007-10-04 09:40:59 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Pop-up score sheet in own window, remove expanding frame for bidding view. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-10-04 16:37:16 UTC (rev 505) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-10-04 16:40:59 UTC (rev 506) @@ -29,7 +29,7 @@ from pybridge.ui.window_gametable import WindowGameTable from window_bidbox import WindowBidbox -from window_scoresheet import ScoreSheet +from window_scoresheet import WindowScoreSheet class BiddingView(gtk.TreeView): @@ -187,6 +187,12 @@ self.takeseat_menuitems[position] = item self.takeseat.set_menu(menu) + # Set up bridge-specific toolbar buttons. + self.showscores = gtk.ToggleToolButton(gtk.STOCK_EDIT) + self.showscores.set_label(_('Show Scoresheet')) + self.showscores.connect('clicked', self.on_showscores_clicked) + self.toolbar.insert(self.showscores, -1) + # Set up CardArea widget. self.cardarea = CardArea(positions=Direction) @@ -205,10 +211,7 @@ sw.add(self.biddingview) frame = gtk.Frame() frame.add(sw) - exp = gtk.Expander(_('Bidding')) - exp.set_expanded(True) - exp.add(frame) - self.sidebar.pack_start(exp, expand=True) + self.sidebar.pack_start(frame, expand=True) self.trickarea = TrickArea(positions=Direction) self.trickarea.set_size_request(-1, 180) @@ -219,16 +222,16 @@ exp.add(frame) self.sidebar.pack_start(exp, expand=False) - self.scoresheet = ScoreSheet() - sw = gtk.ScrolledWindow() - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - sw.add(self.scoresheet) - frame = gtk.Frame() - frame.add(sw) - exp = gtk.Expander(_('Score Sheet')) - exp.set_expanded(False) - exp.add(frame) - self.sidebar.pack_start(exp, expand=False) +# self.scoresheet = ScoreSheet() +# sw = gtk.ScrolledWindow() +# sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) +# sw.add(self.scoresheet) +# frame = gtk.Frame() +# frame.add(sw) +# exp = gtk.Expander(_('Score Sheet')) +# exp.set_expanded(False) +# exp.add(frame) +# self.sidebar.pack_start(exp, expand=False) def setTable(self, table): @@ -263,7 +266,6 @@ bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) - # Initialise seat menu and player labels. for position in Direction: player = self.table.players.get(position) # Player name or None. @@ -298,7 +300,7 @@ # Determine and display score in dialog box and score sheet. if self.table.game.contract: - self.scoresheet.add_result(self.table.game.result) + #self.scoresheet.add_result(self.table.game.result) tricksMade = self.table.game.result.tricksMade tricksRequired = self.table.game.contract.bid.level.index + 7 @@ -565,3 +567,15 @@ d = super(WindowBridgeTable, self).on_leaveseat_clicked(widget, *args) d.addCallback(success) + + def on_showscores_clicked(self, widget, *args): + if self.showscores.get_active(): + w = self.children.open(WindowScoreSheet) + # This re-invokes on_showscores_clicked if user closes w. + delete_event = lambda w, e: self.showscores.set_active(False) + w.window.connect('delete_event', delete_event) + w.setTable(self.table) + + else: + self.children.close(self.children[WindowScoreSheet]) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-10-04 16:37:17
|
Revision: 505 http://pybridge.svn.sourceforge.net/pybridge/?rev=505&view=rev Author: umgangee Date: 2007-10-04 09:37:16 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Make Contract and Result objects copyable, send results over-the-wire for display on clients. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/auction.py trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py Modified: trunk/pybridge/pybridge/games/bridge/auction.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/auction.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/auction.py 2007-10-04 16:37:16 UTC (rev 505) @@ -16,11 +16,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from twisted.spread import pb + from call import Bid, Pass, Double, Redouble from symbols import Direction -class Contract(object): +class Contract(object, pb.Copyable, pb.RemoteCopy): """Represents the result of an auction.""" @@ -54,8 +56,19 @@ self.redoubleBy = auction.whoCalled(auction.currentRedouble) + def getStateToCopy(self): + return self.bid, self.declarer, self.doubleBy, self.redoubleBy + def setCopyableState(self, state): + self.bid, self.declarer, self.doubleBy, self.redoubleBy = state + + +pb.setUnjellyableForClass(Contract, Contract) + + + + class Auction(list): """The auction (bidding phase) of a game of bridge.""" Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-10-04 16:37:16 UTC (rev 505) @@ -87,6 +87,8 @@ if board: # Use specified board. self.board = board + elif self.boardQueue: # Use pre-specified board. + self.board = self.boardQueue.pop(0) elif self.board: # Advance to next round. self.board = self.board.next() else: # Create an initial board. @@ -136,6 +138,9 @@ def getState(self): state = {} + state['options'] = self.options + state['results'] = self.results + if self.inProgress(): # Remove hidden hands from deal. visibleBoard = self.board.copy() @@ -151,6 +156,8 @@ def setState(self, state): + self.options = state.get('options', {}) + if state.get('board'): self.start(state['board']) @@ -172,7 +179,9 @@ else: self.playCard(card, position=turn) + self.results = state.get('results', []) # Overwrites current game result. + def updateState(self, event, *args, **kwargs): allowed = ['start', 'makeCall', 'playCard', 'revealHand'] if event in allowed: Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-10-04 16:37:16 UTC (rev 505) @@ -16,6 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from twisted.spread import pb + from symbols import Direction, Strain, Vulnerable @@ -40,6 +42,7 @@ self.contract = contract self.tricksMade = tricksMade + self.isVulnerable = None if self.contract: vuln = self.board.get('vuln', Vulnerable.None) self.isVulnerable = self.contract.declarer in self.__vulnMap[vuln] @@ -47,6 +50,16 @@ self.score = self._getScore() + def getStateToCopy(self): + return (self.board.copy(), self.contract, self.tricksMade, + self.isVulnerable, self.score) + + + def setCopyableState(self, state): + self.board, self.contract, self.tricksMade, self.isVulnerable, self.score = state + # assert self.score == self._getScore() + + def _getScoreComponents(self): """Compute the component values which contribute to the score. Note that particular scoring schemes may ignore some of the components. @@ -178,7 +191,7 @@ -class DuplicateResult(GameResult): +class DuplicateResult(GameResult, pb.Copyable, pb.RemoteCopy): """Represents the result of a completed round of duplicate bridge.""" @@ -196,9 +209,12 @@ return score +pb.setUnjellyableForClass(DuplicateResult, DuplicateResult) -class RubberResult(GameResult): + + +class RubberResult(GameResult, pb.Copyable, pb.RemoteCopy): """Represents the result of a completed round of rubber bridge.""" @@ -219,8 +235,11 @@ return above, below +pb.setUnjellyableForClass(RubberResult, RubberResult) + + class Rubber(list): """A rubber set, in which pairs compete to make two consecutive games. Modified: trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-10-04 16:37:16 UTC (rev 505) @@ -52,7 +52,7 @@ score = result.score if result.contract is None: # Bidding passed out. - row = (result.board['num'], _('Passed out'), '-', '-', '', '') + row = (result.board['num'], _('Passed out'), '-', '', '') else: if result.contract.declarer in (Direction.North, Direction.South) and score > 0 \ @@ -122,19 +122,53 @@ if parent: self.window.set_transient_for(parent.window) self.window.set_title(_('Score Sheet')) - self.window.connect('delete_event', self.on_delete_event) - #self.window.set_resizable(False) + #self.window.connect('delete_event', self.on_delete_event) self.eventHandler = SimpleEventHandler(self) self.table = None + self.window.show() + def tearDown(self): - pass + self.table.game.detach(self.eventHandler) + self.table = None # Dereference table. + def setTable(self, table): + self.table = table + self.table.game.attach(self.eventHandler) - def on_delete_event(self, widget, *args): - # TODO: call wm.close(self) - return True # Stops window deletion taking place. + if hasattr(self.table.game, 'rubbers'): + self.scoresheet = RubberScoreSheet() + if self.table.game.rubbers: + rubber = self.table.game.rubbers[-1] + self.scoresheet.set_rubber(rubber) + else: # Duplicate-style list of results. + self.scoresheet = ScoreSheet() + for result in self.table.game.results: + self.scoresheet.add_result(result) + + self.window.add(self.scoresheet) + self.scoresheet.show() + + + def update(self): + if self.table.game.results and not self.table.game.inProgress(): + if isinstance(self.scoresheet, RubberScoreSheet): + rubber = self.table.game.rubbers[-1] + self.scoresheet.set_rubber(rubber) + else: + result = self.table.game.results[-1] + self.scoresheet.add_result(result) + + + + def event_makeCall(self, call, position): + self.update() + + + def event_playCard(self, card, position): + self.update() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-09-26 13:02:20
|
Revision: 504 http://pybridge.svn.sourceforge.net/pybridge/?rev=504&view=rev Author: umgangee Date: 2007-09-26 06:02:19 -0700 (Wed, 26 Sep 2007) Log Message: ----------- Polish translation of PyBridge UI, thanks to Monsz. Update French PO and MO files. Modified Paths: -------------- trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.mo trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po Added Paths: ----------- trunk/pybridge/locale/pl/ trunk/pybridge/locale/pl/LC_MESSAGES/ trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.mo trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.po Modified: trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.mo =================================================================== (Binary files differ) Modified: trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po =================================================================== --- trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po 2007-09-26 12:51:30 UTC (rev 503) +++ trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po 2007-09-26 13:02:19 UTC (rev 504) @@ -3,20 +3,32 @@ # This file is distributed under the same license as the pybridge package. # FIRST AUTHOR <EMAIL@ADDRESS>, 2007. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: pybridge\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2007-08-26 18:09+0100\n" +"POT-Creation-Date: 2007-09-22 12:25+0100\n" "PO-Revision-Date: 2007-09-02 13:13+0000\n" "Last-Translator: Aldo Reset <al...@pl...>\n" "Language-Team: French <fr...@li...>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Rosetta-Export-Date: 2007-09-02 14:18:13+0000\n" +"X-Launchpad-Export-Date: 2007-09-26 12:48+0000\n" +"X-Generator: Launchpad (build Unknown)\n" +#~ msgid "gtk-cancel" +#~ msgstr "gtk-cancel" + +#~ msgid "gtk-close" +#~ msgstr "gtk-close" + +#~ msgid "gtk-ok" +#~ msgstr "gtk-ok" + +#~ msgid "gtk-preferences" +#~ msgstr "gtk-preferences" + #: ../../glade/pybridge.glade.h:1 msgid "<b>Server Address</b>" msgstr "<b>Adresse du Serveur</b>" @@ -76,109 +88,101 @@ msgstr "Nom du Poste" #: ../../glade/pybridge.glade.h:15 +msgid "Join Table" +msgstr "" + +#: ../../glade/pybridge.glade.h:16 +msgid "New Table..." +msgstr "" + +#: ../../glade/pybridge.glade.h:17 msgid "Open Tables" msgstr "Tables Ouvertes" -#: ../../glade/pybridge.glade.h:16 +#: ../../glade/pybridge.glade.h:18 msgid "Password:" msgstr "Mot de Passe" -#: ../../glade/pybridge.glade.h:17 +#: ../../glade/pybridge.glade.h:19 msgid "Port:" msgstr "Port:" -#: ../../glade/pybridge.glade.h:18 +#: ../../glade/pybridge.glade.h:20 msgid "PyBridge" msgstr "PyBridge" -#: ../../glade/pybridge.glade.h:19 +#: ../../glade/pybridge.glade.h:21 msgid "PyBridge Preferences" msgstr "PyBridge Prfrences" -#: ../../glade/pybridge.glade.h:20 +#: ../../glade/pybridge.glade.h:22 msgid "PyBridge _Home Page" msgstr "PyBridge _Page d'accueil" -#: ../../glade/pybridge.glade.h:21 +#: ../../glade/pybridge.glade.h:23 msgid "Register as New User" msgstr "Inscrire Nouveau Joueur" -#: ../../glade/pybridge.glade.h:22 ../../pybridge/ui/vocabulary.py:53 +#: ../../glade/pybridge.glade.h:24 ../../pybridge/ui/vocabulary.py:53 msgid "S" msgstr "S" -#: ../../glade/pybridge.glade.h:23 +#: ../../glade/pybridge.glade.h:25 msgid "Save Password" msgstr "Sauvegarder Mot de Passe" -#: ../../glade/pybridge.glade.h:24 +#: ../../glade/pybridge.glade.h:26 msgid "Suit Colours:" msgstr "" -#: ../../glade/pybridge.glade.h:25 +#: ../../glade/pybridge.glade.h:27 msgid "Table Name:" msgstr "Nom de la Table" -#: ../../glade/pybridge.glade.h:26 +#: ../../glade/pybridge.glade.h:28 msgid "User Information" msgstr "Information Joueur" -#: ../../glade/pybridge.glade.h:27 +#: ../../glade/pybridge.glade.h:29 msgid "User Name:" msgstr "Nom du Joueur" -#: ../../glade/pybridge.glade.h:28 +#: ../../glade/pybridge.glade.h:30 msgid "Users Online" msgstr "Joueurs en Ligne" -#: ../../glade/pybridge.glade.h:29 +#: ../../glade/pybridge.glade.h:31 msgid "_About PyBridge" msgstr "_Au sujet de Pybridge" -#: ../../glade/pybridge.glade.h:30 +#: ../../glade/pybridge.glade.h:32 msgid "_Connect to Server..." msgstr "_Connexion au Serveur" -#: ../../glade/pybridge.glade.h:31 +#: ../../glade/pybridge.glade.h:33 msgid "_Connection" msgstr "_Connexion" -#: ../../glade/pybridge.glade.h:32 +#: ../../glade/pybridge.glade.h:34 msgid "_Disconnect" msgstr "_Dconnexion" -#: ../../glade/pybridge.glade.h:33 +#: ../../glade/pybridge.glade.h:35 msgid "_Help" msgstr "_Aide" -#: ../../glade/pybridge.glade.h:34 +#: ../../glade/pybridge.glade.h:36 msgid "_New Table..." msgstr "_Nouvelle Table" -#: ../../glade/pybridge.glade.h:35 +#: ../../glade/pybridge.glade.h:37 msgid "_Open Game..." msgstr "_Ouvrir Jeu..." -#: ../../glade/pybridge.glade.h:36 +#: ../../glade/pybridge.glade.h:38 msgid "_Options" msgstr "_Options" -#: ../../glade/pybridge.glade.h:37 -msgid "gtk-cancel" -msgstr "gtk-cancel" - -#: ../../glade/pybridge.glade.h:38 -msgid "gtk-close" -msgstr "gtk-close" - -#: ../../glade/pybridge.glade.h:39 -msgid "gtk-ok" -msgstr "gtk-ok" - -#: ../../glade/pybridge.glade.h:40 -msgid "gtk-preferences" -msgstr "gtk-preferences" - #: ../../pybridge/ui/dialog_connection.py:74 msgid "Connection failed" msgstr "Connexion choue" @@ -382,14 +386,14 @@ msgstr "Tout" #: ../../pybridge/ui/vocabulary.py:148 -#: ../../pybridge/games/bridge/ui/scoresheet.py:42 -#: ../../pybridge/games/bridge/ui/scoresheet.py:87 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:87 msgid "N/S" msgstr "N/S" #: ../../pybridge/ui/vocabulary.py:149 -#: ../../pybridge/games/bridge/ui/scoresheet.py:42 -#: ../../pybridge/games/bridge/ui/scoresheet.py:87 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:87 msgid "E/W" msgstr "E/O" @@ -453,7 +457,7 @@ msgstr "Prendre Place" #: ../../pybridge/ui/window_gametable.py:84 -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:336 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:342 msgid "Leave Seat" msgstr "Quitter Place" @@ -520,27 +524,6 @@ msgid "A free online bridge game." msgstr "Un jeu libre de brige en ligne" -#: ../../pybridge/games/bridge/ui/scoresheet.py:42 -msgid "Board" -msgstr "" - -#: ../../pybridge/games/bridge/ui/scoresheet.py:42 -msgid "Contract" -msgstr "Contrat" - -#: ../../pybridge/games/bridge/ui/scoresheet.py:42 -msgid "Made" -msgstr "" - -#: ../../pybridge/games/bridge/ui/scoresheet.py:55 -msgid "Passed out" -msgstr "" - -#: ../../pybridge/games/bridge/ui/scoresheet.py:124 -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:228 -msgid "Score Sheet" -msgstr "Scores" - #: ../../pybridge/games/bridge/ui/window_bidbox.py:41 msgid "Bidding Box" msgstr "Enchre" @@ -569,90 +552,114 @@ msgid "Contract Bridge" msgstr "Contrat" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:208 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:192 +msgid "Show Scoresheet" +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:214 msgid "Bidding" msgstr "Ouverture" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:217 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:223 msgid "Previous Trick" msgstr "Leve prcdente" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:297 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:303 msgid "Game result" msgstr "Resultat de la Partie" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:311 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:317 #, python-format msgid "Contract %(contract)s made by 1 trick." msgstr "Le Contrat %(contract)s raliser d'une leve." -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:313 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:319 #, python-format msgid "Contract %(contract)s made by %(offset)s tricks." msgstr "Le Contrat %(contract)s raliser avec %(offset)s leves." -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:316 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:322 #, python-format msgid "Contract %(contract)s failed by 1 trick." msgstr "Le contrat %(contract)s chute d'une leve" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:318 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:324 #, python-format msgid "Contract %(contract)s failed by %(offset)s tricks." msgstr "Le contrat %(contract)s chute de %(offset)s leves" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:320 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:326 #, python-format msgid "Contract %(contract)s made exactly." msgstr "Le contrat %(contract)s russit" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:326 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:332 msgid "declarer" msgstr "dclare" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:326 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:332 msgid "defence" msgstr "Intervention" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:327 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:333 #, python-format msgid "Score %(points)s points for %(pair)s." msgstr "Score %(points)s points pour %(pair)s." -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:332 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:338 msgid "Bidding passed out." msgstr "Enchere passe" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:333 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:339 msgid "No score." msgstr "Pas de point" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:337 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:343 msgid "Click OK to start next game." msgstr "Cliquer Ok pour dmarrer la prochaine partie" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:408 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:414 msgid "Play a card from your hand." msgstr "Jouer une carte de votre main" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:410 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:416 msgid "Play a card from dummy's hand." msgstr "Jouer une carte de la main du mort" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:412 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:418 #, python-format msgid "It is %s's turn to play a card." msgstr "Ce n'est pas %s's de jouer une carte" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:416 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:422 msgid "Make a call from the bidding box." msgstr "Faire un appel de l'enchre" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:418 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:424 #, python-format msgid "It is %s's turn to make a call." msgstr "Ce n'est pas le tour de %s's de faire un appel" -#: ../../pybridge/games/bridge/ui/window_bridgetable.py:421 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:427 msgid "Waiting for next game to start." -msgstr "Attender que la prochaine partie commence" \ No newline at end of file +msgstr "Attender que la prochaine partie commence" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +msgid "Board" +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +msgid "Contract" +msgstr "Contrat" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +msgid "Made" +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:55 +msgid "Passed out" +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:124 +msgid "Score Sheet" +msgstr "Scores" Added: trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.mo =================================================================== (Binary files differ) Property changes on: trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.mo ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.po =================================================================== --- trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.po (rev 0) +++ trunk/pybridge/locale/pl/LC_MESSAGES/pybridge.po 2007-09-26 13:02:19 UTC (rev 504) @@ -0,0 +1,677 @@ +# Polish translation for pybridge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the pybridge package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: pybridge\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2007-09-22 12:25+0100\n" +"PO-Revision-Date: 2007-09-20 15:49+0000\n" +"Last-Translator: Piotr Kwilński <eu...@ho...>\n" +"Language-Team: Polish <pl...@li...>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2007-09-26 12:48+0000\n" +"X-Generator: Launchpad (build Unknown)\n" + +#~ msgid "gtk-cancel" +#~ msgstr "gtk-anuluj" + +#~ msgid "gtk-close" +#~ msgstr "gtk-zamknij" + +#~ msgid "gtk-ok" +#~ msgstr "gtk-ok" + +#~ msgid "gtk-preferences" +#~ msgstr "gtk-właściwości" + +#: ../../glade/pybridge.glade.h:1 +msgid "<b>Server Address</b>" +msgstr "<b>Adres serwera</b>" + +#: ../../glade/pybridge.glade.h:2 +msgid "<b>User Account</b>" +msgstr "<b>Konto użytkownika</b>" + +#: ../../glade/pybridge.glade.h:3 +msgid "" +"<i>Changes to these settings are effective after restarting PyBridge.</i>" +msgstr "" +"<i>Zmiany ustawień zostaną uwzględnione przy ponownym uruchomieniu " +"PyBridge.</i>" + +#: ../../glade/pybridge.glade.h:4 +msgid "Background:" +msgstr "Tło:" + +#: ../../glade/pybridge.glade.h:5 +msgid "C" +msgstr "C" + +#: ../../glade/pybridge.glade.h:6 +msgid "Card Style:" +msgstr "Styl kart:" + +#: ../../glade/pybridge.glade.h:7 +msgid "Connect to Server" +msgstr "Połącz z serwerem" + +#: ../../glade/pybridge.glade.h:8 +msgid "Create a New Table" +msgstr "Utwórz nowy stół" + +#: ../../glade/pybridge.glade.h:9 +msgid "D" +msgstr "D" + +#: ../../glade/pybridge.glade.h:10 +msgid "Display suits as symbols?" +msgstr "Wyświetlać kolory kart jako symbole?" + +#: ../../glade/pybridge.glade.h:11 +#, fuzzy +msgid "Game Display" +msgstr "Wyświetlanie gry" + +#: ../../glade/pybridge.glade.h:12 +msgid "Game at Table:" +msgstr "Gra na stole:" + +#: ../../glade/pybridge.glade.h:13 +msgid "H" +msgstr "H" + +#: ../../glade/pybridge.glade.h:14 +msgid "Host Name:" +msgstr "Nazwa hosta:" + +#: ../../glade/pybridge.glade.h:15 +msgid "Join Table" +msgstr "" + +#: ../../glade/pybridge.glade.h:16 +msgid "New Table..." +msgstr "" + +#: ../../glade/pybridge.glade.h:17 +#, fuzzy +msgid "Open Tables" +msgstr "Stoły otwarte" + +#: ../../glade/pybridge.glade.h:18 +msgid "Password:" +msgstr "Hasło:" + +#: ../../glade/pybridge.glade.h:19 +msgid "Port:" +msgstr "Port:" + +#: ../../glade/pybridge.glade.h:20 +msgid "PyBridge" +msgstr "PyBridge" + +#: ../../glade/pybridge.glade.h:21 +msgid "PyBridge Preferences" +msgstr "Ustawienia PyBridge" + +#: ../../glade/pybridge.glade.h:22 +msgid "PyBridge _Home Page" +msgstr "Strona _domowa PyBridge" + +#: ../../glade/pybridge.glade.h:23 +msgid "Register as New User" +msgstr "Zarejestruj jako nowyego użytkownika" + +#: ../../glade/pybridge.glade.h:24 ../../pybridge/ui/vocabulary.py:53 +msgid "S" +msgstr "S" + +#: ../../glade/pybridge.glade.h:25 +msgid "Save Password" +msgstr "Zapisz hasło" + +#: ../../glade/pybridge.glade.h:26 +msgid "Suit Colours:" +msgstr "Kolory kart:" + +#: ../../glade/pybridge.glade.h:27 +msgid "Table Name:" +msgstr "Nazwa stołu:" + +#: ../../glade/pybridge.glade.h:28 +msgid "User Information" +msgstr "Informacje użytkownika" + +#: ../../glade/pybridge.glade.h:29 +msgid "User Name:" +msgstr "Nazwa uzytkownika:" + +#: ../../glade/pybridge.glade.h:30 +msgid "Users Online" +msgstr "Użytkowników On-line" + +#: ../../glade/pybridge.glade.h:31 +msgid "_About PyBridge" +msgstr "_O programie PYBridge" + +#: ../../glade/pybridge.glade.h:32 +msgid "_Connect to Server..." +msgstr "_Połącz z serwerem..." + +#: ../../glade/pybridge.glade.h:33 +msgid "_Connection" +msgstr "P_ołączenie" + +#: ../../glade/pybridge.glade.h:34 +msgid "_Disconnect" +msgstr "_Rozłącz" + +#: ../../glade/pybridge.glade.h:35 +msgid "_Help" +msgstr "Po_moc" + +#: ../../glade/pybridge.glade.h:36 +msgid "_New Table..." +msgstr "_Nowy stół..." + +#: ../../glade/pybridge.glade.h:37 +msgid "_Open Game..." +msgstr "_Otwórz grę..." + +#: ../../glade/pybridge.glade.h:38 +msgid "_Options" +msgstr "_Opcje" + +#: ../../pybridge/ui/dialog_connection.py:74 +msgid "Connection failed" +msgstr "Nie udało się połączyć" + +#: ../../pybridge/ui/dialog_connection.py:75 +msgid "Could not connect to server." +msgstr "Nie można połączyć z serwerem." + +#: ../../pybridge/ui/dialog_connection.py:76 +#: ../../pybridge/ui/dialog_newtable.py:62 +#, python-format +msgid "Reason: %s" +msgstr "Przyczyna: %s" + +#: ../../pybridge/ui/dialog_newtable.py:60 +msgid "Could not create table" +msgstr "Nie można utworzyć stołu" + +#: ../../pybridge/ui/dialog_newtable.py:61 +msgid "The table was not created by the server." +msgstr "Stół nie został utworzony przez serwer." + +#: ../../pybridge/ui/dialog_preferences.py:43 +msgid "PNG images" +msgstr "Obrazy PNG" + +#: ../../pybridge/ui/dialog_preferences.py:98 +#, python-format +msgid "Select colour for %s symbol" +msgstr "Wybierz kolor dla symbolu %s" + +#: ../../pybridge/ui/dialog_userinfo.py:29 +msgid "Real Name" +msgstr "Prawdziwa nazwa" + +#: ../../pybridge/ui/dialog_userinfo.py:30 +msgid "Email Address" +msgstr "Adres e-mail" + +#: ../../pybridge/ui/dialog_userinfo.py:32 +msgid "Profile" +msgstr "Profil" + +#: ../../pybridge/ui/dialog_userinfo.py:54 +#, python-format +msgid "Information for %(user)s" +msgstr "Informacja dla %(user)s" + +#: ../../pybridge/ui/dialog_userinfo.py:68 +msgid "not specified" +msgstr "nie określony" + +#: ../../pybridge/ui/excepthook.py:34 +msgid "Program error" +msgstr "Błąd programu" + +#: ../../pybridge/ui/excepthook.py:35 +msgid "" +"PyBridge detected an unexpected program error. You should close and restart " +"PyBridge." +msgstr "" +"PyBridge wykrył nieoczekiwany błąd programu. Zamknij i ponownie uruchom " +"PyBridge." + +#: ../../pybridge/ui/excepthook.py:36 +msgid "" +"If you continue to experience this error, please submit a bug report, " +"attaching the following error trace." +msgstr "" +"Jeśli błąd będzie nadal występował zgłoś go nam proszę załączając " +"następujący opis błędu." + +#: ../../pybridge/ui/vocabulary.py:32 +msgid "Pass" +msgstr "Pas" + +#: ../../pybridge/ui/vocabulary.py:33 +#, fuzzy +msgid "Double" +msgstr "Kontra" + +#: ../../pybridge/ui/vocabulary.py:34 +#, fuzzy +msgid "Redouble" +msgstr "Rekontra" + +#: ../../pybridge/ui/vocabulary.py:44 +msgid "North" +msgstr "Północ" + +#: ../../pybridge/ui/vocabulary.py:45 +msgid "East" +msgstr "Wschód" + +#: ../../pybridge/ui/vocabulary.py:46 +msgid "South" +msgstr "Południe" + +#: ../../pybridge/ui/vocabulary.py:47 +msgid "West" +msgstr "Zachód" + +#: ../../pybridge/ui/vocabulary.py:51 +msgid "N" +msgstr "N" + +#: ../../pybridge/ui/vocabulary.py:52 +msgid "E" +msgstr "E" + +#: ../../pybridge/ui/vocabulary.py:54 +msgid "W" +msgstr "W" + +#: ../../pybridge/ui/vocabulary.py:68 +msgid "Two" +msgstr "Dwa" + +#: ../../pybridge/ui/vocabulary.py:69 +msgid "Three" +msgstr "Trzy" + +#: ../../pybridge/ui/vocabulary.py:70 +msgid "Four" +msgstr "Cztery" + +#: ../../pybridge/ui/vocabulary.py:71 +msgid "Five" +msgstr "Pięć" + +#: ../../pybridge/ui/vocabulary.py:72 +msgid "Six" +msgstr "Sześć" + +#: ../../pybridge/ui/vocabulary.py:73 +msgid "Seven" +msgstr "Siedem" + +#: ../../pybridge/ui/vocabulary.py:74 +msgid "Eight" +msgstr "Osiem" + +#: ../../pybridge/ui/vocabulary.py:75 +msgid "Nine" +msgstr "Dziewięć" + +#: ../../pybridge/ui/vocabulary.py:76 +msgid "Ten" +msgstr "Dziesięć" + +#: ../../pybridge/ui/vocabulary.py:77 +msgid "Jack" +msgstr "Walet" + +#: ../../pybridge/ui/vocabulary.py:78 +msgid "Queen" +msgstr "Dama" + +#: ../../pybridge/ui/vocabulary.py:79 +msgid "King" +msgstr "Król" + +#: ../../pybridge/ui/vocabulary.py:80 +msgid "Ace" +msgstr "As" + +#: ../../pybridge/ui/vocabulary.py:100 +msgid "Clubs" +msgstr "Trefle" + +#: ../../pybridge/ui/vocabulary.py:101 +msgid "Diamonds" +msgstr "Kara" + +#: ../../pybridge/ui/vocabulary.py:102 +msgid "Hearts" +msgstr "Kiery" + +#: ../../pybridge/ui/vocabulary.py:103 +msgid "Spades" +msgstr "Piki" + +#: ../../pybridge/ui/vocabulary.py:122 +msgid "Club" +msgstr "Trefl" + +#: ../../pybridge/ui/vocabulary.py:123 +msgid "Diamond" +msgstr "Karo" + +#: ../../pybridge/ui/vocabulary.py:124 +msgid "Heart" +msgstr "Kier" + +#: ../../pybridge/ui/vocabulary.py:125 +msgid "Spade" +msgstr "Pik" + +#: ../../pybridge/ui/vocabulary.py:126 +msgid "No Trump" +msgstr "Bez atu" + +#: ../../pybridge/ui/vocabulary.py:147 +msgid "All" +msgstr "Wszystko" + +#: ../../pybridge/ui/vocabulary.py:148 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:87 +msgid "N/S" +msgstr "N/S" + +#: ../../pybridge/ui/vocabulary.py:149 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:87 +msgid "E/W" +msgstr "E/W" + +#: ../../pybridge/ui/vocabulary.py:150 +msgid "None" +msgstr "Żaden" + +#: ../../pybridge/ui/vocabulary.py:169 +#, python-format +msgid "%(level)s %(strain)s" +msgstr "%(level)s %(strain)s" + +#: ../../pybridge/ui/vocabulary.py:183 +#, python-format +msgid "%(rank)s of %(suit)s" +msgstr "%(rank)s z %(suit)s" + +#: ../../pybridge/ui/vocabulary.py:201 +#, python-format +msgid "%(bid)s %(doubled)s by %(declarer)s" +msgstr "%(bid)s %(doubled)s przez %(declarer)s" + +#: ../../pybridge/ui/vocabulary.py:203 +#, python-format +msgid "%(bid)s by %(declarer)s" +msgstr "%(bid)s przez %(declarer)s" + +#: ../../pybridge/ui/window_chat.py:73 +msgid "1 person in room" +msgstr "1 osoba w pokoju" + +#: ../../pybridge/ui/window_chat.py:75 +#, python-format +msgid "%(num)s people in room" +msgstr "%(num)s osób w pokoju" + +#: ../../pybridge/ui/window_chat.py:194 +#, python-format +msgid "%(user)s has joined the conversation" +msgstr "%(user)s dołączył do konwersacji" + +#: ../../pybridge/ui/window_chat.py:196 +#, python-format +msgid "%(user)s has left the conversation" +msgstr "%(user)s opuścił konwersację" + +#: ../../pybridge/ui/window_chat.py:290 +msgid "Chat" +msgstr "Chat" + +#: ../../pybridge/ui/window_gametable.py:37 +msgid "Unnamed Game" +msgstr "Gra bez nazwy" + +#: ../../pybridge/ui/window_gametable.py:46 +msgid "Table" +msgstr "Stół" + +#: ../../pybridge/ui/window_gametable.py:78 +msgid "Take Seat" +msgstr "Zajmij miejsce" + +#: ../../pybridge/ui/window_gametable.py:84 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:342 +msgid "Leave Seat" +msgstr "Opuść miejsce" + +#: ../../pybridge/ui/window_gametable.py:99 +#: ../../pybridge/ui/window_gametable.py:193 +msgid "Leave Table" +msgstr "Opuść stół" + +#: ../../pybridge/ui/window_gametable.py:135 +#, python-format +msgid "%(tablename)s (%(gametype)s)" +msgstr "%(tablename)s (%(gametype)s)" + +#: ../../pybridge/ui/window_gametable.py:191 +msgid "Leave table?" +msgstr "Opuścić stół?" + +#: ../../pybridge/ui/window_gametable.py:194 +msgid "Are you sure you wish to leave this table?" +msgstr "Czy jesteś pewien że chcesz opuścić ten stół?" + +#: ../../pybridge/ui/window_gametable.py:195 +msgid "" +"You are currently playing a game. Leaving may forfeit the game, or incur " +"penalties." +msgstr "" +"Aktualnie grasz. Rozłączenie może spowodowac utratę gry lub wywołać kary." + +#: ../../pybridge/ui/window_main.py:125 +msgid "Connection to server lost" +msgstr "Utracono połączenie z serwerem" + +#: ../../pybridge/ui/window_main.py:126 +#, python-format +msgid "The connection to %s was lost unexpectedly." +msgstr "Nieoczekiwanie utracono połączenie z %s." + +#: ../../pybridge/ui/window_main.py:127 +msgid "" +"Please check your computer's network connection status before reconnecting. " +"If you cannot reconnect, the server may be offline." +msgstr "" +"Sprawdź stan swojego połączenie przed ponownym podłączeniem. Jeśli nie " +"możesz ponownie połączyć serwer mże być off-line." + +#: ../../pybridge/ui/window_main.py:272 +msgid "Disconnect from server" +msgstr "Odłącz od serwera" + +#: ../../pybridge/ui/window_main.py:275 +msgid "Are you sure you wish to disconnect?" +msgstr "Czy jesteś pewien że chcesz odłączyć?" + +#: ../../pybridge/ui/window_main.py:276 +msgid "" +"You are playing at a table. Disconnecting may forfeit the game, or incur " +"penalties." +msgstr "" +"Grasz na stole. Rozłączenie może spowodowac utratę gry lub wywołać kary." + +#: ../../pybridge/ui/window_main.py:305 +msgid "A free online bridge game." +msgstr "Bezpłatna gra w brydża on-line." + +#: ../../pybridge/games/bridge/ui/window_bidbox.py:41 +msgid "Bidding Box" +msgstr "Bidding Box" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:103 +#, fuzzy +msgid "Declarer" +msgstr "Deklarujący" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:109 +msgid "Defence" +msgstr "Obrona" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:132 +msgid "No contract" +msgstr "Bez kontraktu" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:154 +#, fuzzy +msgid "Dealer" +msgstr "Rozdający" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:162 +#, fuzzy +msgid "Vuln" +msgstr "Założenia" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:169 +#, fuzzy +msgid "Contract Bridge" +msgstr "Kontrakt brydż" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:192 +msgid "Show Scoresheet" +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:214 +msgid "Bidding" +msgstr "Licytowanie" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:223 +msgid "Previous Trick" +msgstr "Poprzednia lewa" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:303 +msgid "Game result" +msgstr "Wynik gry" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:317 +#, python-format +msgid "Contract %(contract)s made by 1 trick." +msgstr "Kontrakt %(contract)s wykonano z 1 dadatkową lewą." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:319 +#, python-format +msgid "Contract %(contract)s made by %(offset)s tricks." +msgstr "Kontrakt %(contract)s wykonano z dodatkowymi %(offset)s lewami." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:322 +#, python-format +msgid "Contract %(contract)s failed by 1 trick." +msgstr "Kontraktu %(contract)s nie wykonano o 1 lewę." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:324 +#, python-format +msgid "Contract %(contract)s failed by %(offset)s tricks." +msgstr "Kontraktu %(contract)s nie wykonano o %(offset)s lew." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:326 +#, python-format +msgid "Contract %(contract)s made exactly." +msgstr "Kontrakt %(contract)s wykonany dokładnie." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:332 +#, fuzzy +msgid "declarer" +msgstr "deklarujący" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:332 +msgid "defence" +msgstr "obrona" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:333 +#, python-format +msgid "Score %(points)s points for %(pair)s." +msgstr "Punkty %(points)s punkty dla %(pair)s." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:338 +msgid "Bidding passed out." +msgstr "Licytowanie spasowane." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:339 +msgid "No score." +msgstr "Brak punktów." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:343 +msgid "Click OK to start next game." +msgstr "Kliknij OK by rozpocząć nową grę." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:414 +msgid "Play a card from your hand." +msgstr "Graj kartą z ręki." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:416 +#, fuzzy +msgid "Play a card from dummy's hand." +msgstr "Graj kartą ze stołu." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:418 +#, python-format +msgid "It is %s's turn to play a card." +msgstr "Kolej %s na rozegranie karty." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:422 +msgid "Make a call from the bidding box." +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:424 +#, fuzzy +msgid "It is %s's turn to make a call." +msgstr "Kolej %s's na odzywkę." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:427 +msgid "Waiting for next game to start." +msgstr "Oczekiwanie na rozpoczęcie nowej gry." + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +#, fuzzy +msgid "Board" +msgstr "Plansza" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +msgid "Contract" +msgstr "Kontrakt" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:42 +msgid "Made" +msgstr "Wykonano" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:55 +msgid "Passed out" +msgstr "Pas" + +#: ../../pybridge/games/bridge/ui/window_scoresheet.py:124 +msgid "Score Sheet" +msgstr "Lista wyników" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-09-26 12:51:34
|
Revision: 503 http://pybridge.svn.sourceforge.net/pybridge/?rev=503&view=rev Author: umgangee Date: 2007-09-26 05:51:30 -0700 (Wed, 26 Sep 2007) Log Message: ----------- Set "translatable" property on a couple strings, unset for "gtk-ok", "gtk-cancel" etc. Modified Paths: -------------- trunk/pybridge/glade/pybridge.glade Modified: trunk/pybridge/glade/pybridge.glade =================================================================== --- trunk/pybridge/glade/pybridge.glade 2007-09-02 14:26:54 UTC (rev 502) +++ trunk/pybridge/glade/pybridge.glade 2007-09-26 12:51:30 UTC (rev 503) @@ -115,7 +115,7 @@ <child> <widget class="GtkImageMenuItem" id="menu_preferences"> <property name="visible">True</property> - <property name="label" translatable="yes">gtk-preferences</property> + <property name="label">gtk-preferences</property> <property name="use_underline">True</property> <property name="use_stock">True</property> <signal name="activate" handler="on_preferences_activate"/> @@ -217,7 +217,7 @@ <widget class="GtkToolButton" id="newtable"> <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="label">New Table...</property> + <property name="label" translatable="yes">New Table...</property> <property name="stock_id">gtk-new</property> <signal name="clicked" handler="on_newtable_clicked"/> </widget> @@ -229,7 +229,7 @@ <widget class="GtkToolButton" id="jointable"> <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="label">Join Table</property> + <property name="label" translatable="yes">Join Table</property> <property name="stock_id">gtk-jump-to</property> <signal name="clicked" handler="on_jointable_clicked"/> </widget> @@ -244,9 +244,6 @@ </packing> </child> </widget> - <packing> - <property name="tab_expand">False</property> - </packing> </child> <child> <widget class="GtkLabel" id="label_tabletab"> @@ -255,7 +252,6 @@ </widget> <packing> <property name="type">tab</property> - <property name="tab_expand">False</property> <property name="tab_fill">False</property> </packing> </child> @@ -309,7 +305,6 @@ </widget> <packing> <property name="position">1</property> - <property name="tab_expand">False</property> </packing> </child> <child> @@ -320,7 +315,6 @@ <packing> <property name="type">tab</property> <property name="position">1</property> - <property name="tab_expand">False</property> <property name="tab_fill">False</property> </packing> </child> @@ -523,6 +517,7 @@ <property name="can_focus">True</property> <property name="label" translatable="yes">Save Password</property> <property name="use_underline">True</property> + <property name="response_id">0</property> <property name="draw_indicator">True</property> </widget> <packing> @@ -537,6 +532,7 @@ <property name="can_focus">True</property> <property name="label" translatable="yes">Register as New User</property> <property name="use_underline">True</property> + <property name="response_id">0</property> <property name="draw_indicator">True</property> </widget> <packing> @@ -843,6 +839,7 @@ <property name="can_focus">True</property> <property name="receives_default">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="response_id">0</property> <signal name="clicked" handler="on_suitcolour_clicked"/> <child> <widget class="GtkLabel" id="label_clubcolour"> @@ -863,6 +860,7 @@ <property name="can_focus">True</property> <property name="receives_default">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="response_id">0</property> <signal name="clicked" handler="on_suitcolour_clicked"/> <child> <widget class="GtkLabel" id="label_diamondcolour"> @@ -884,6 +882,7 @@ <property name="can_focus">True</property> <property name="receives_default">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="response_id">0</property> <signal name="clicked" handler="on_suitcolour_clicked"/> <child> <widget class="GtkLabel" id="label_heartcolour"> @@ -905,6 +904,7 @@ <property name="can_focus">True</property> <property name="receives_default">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="response_id">0</property> <signal name="clicked" handler="on_suitcolour_clicked"/> <child> <widget class="GtkLabel" id="label_spadecolour"> @@ -939,6 +939,7 @@ <property name="can_focus">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="label" translatable="yes">Display suits as symbols?</property> + <property name="response_id">0</property> <property name="draw_indicator">True</property> </widget> <packing> @@ -958,9 +959,6 @@ </packing> </child> </widget> - <packing> - <property name="tab_expand">False</property> - </packing> </child> <child> <widget class="GtkLabel" id="label_gamedisplay"> @@ -969,7 +967,6 @@ </widget> <packing> <property name="type">tab</property> - <property name="tab_expand">False</property> <property name="tab_fill">False</property> </packing> </child> @@ -986,16 +983,18 @@ <child> <widget class="GtkButton" id="cancelbutton"> <property name="visible">True</property> - <property name="label" translatable="yes">gtk-cancel</property> + <property name="label">gtk-cancel</property> <property name="use_stock">True</property> + <property name="response_id">0</property> <signal name="clicked" handler="on_cancelbutton_clicked"/> </widget> </child> <child> <widget class="GtkButton" id="okbutton"> <property name="visible">True</property> - <property name="label" translatable="yes">gtk-ok</property> + <property name="label">gtk-ok</property> <property name="use_stock">True</property> + <property name="response_id">0</property> <signal name="clicked" handler="on_okbutton_clicked"/> </widget> <packing> @@ -1088,8 +1087,9 @@ <property name="can_focus">True</property> <property name="receives_default">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="label" translatable="yes">gtk-close</property> + <property name="label">gtk-close</property> <property name="use_stock">True</property> + <property name="response_id">0</property> <signal name="clicked" handler="on_closebutton_clicked"/> </widget> </child> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-09-02 14:26:52
|
Revision: 502 http://pybridge.svn.sourceforge.net/pybridge/?rev=502&view=rev Author: umgangee Date: 2007-09-02 07:26:54 -0700 (Sun, 02 Sep 2007) Log Message: ----------- French translation for PyBridge UI, thanks to Aldo Reset <al...@pl...>. Added Paths: ----------- trunk/pybridge/locale/fr/ trunk/pybridge/locale/fr/LC_MESSAGES/ trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.mo trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po Added: trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.mo =================================================================== (Binary files differ) Property changes on: trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.mo ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po =================================================================== --- trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po (rev 0) +++ trunk/pybridge/locale/fr/LC_MESSAGES/pybridge.po 2007-09-02 14:26:54 UTC (rev 502) @@ -0,0 +1,658 @@ +# French translation for pybridge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the pybridge package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pybridge\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2007-08-26 18:09+0100\n" +"PO-Revision-Date: 2007-09-02 13:13+0000\n" +"Last-Translator: Aldo Reset <al...@pl...>\n" +"Language-Team: French <fr...@li...>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-09-02 14:18:13+0000\n" + +#: ../../glade/pybridge.glade.h:1 +msgid "<b>Server Address</b>" +msgstr "<b>Adresse du Serveur</b>" + +#: ../../glade/pybridge.glade.h:2 +msgid "<b>User Account</b>" +msgstr "<b>Compte Joueur</b>" + +#: ../../glade/pybridge.glade.h:3 +msgid "" +"<i>Changes to these settings are effective after restarting PyBridge.</i>" +msgstr "" +"<i>Les modifications seront effectives au redemmarage de PyBridge.</i>" + +#: ../../glade/pybridge.glade.h:4 +msgid "Background:" +msgstr "Arrire Plan" + +#: ../../glade/pybridge.glade.h:5 +msgid "C" +msgstr "C" + +#: ../../glade/pybridge.glade.h:6 +msgid "Card Style:" +msgstr "Modle Carte" + +#: ../../glade/pybridge.glade.h:7 +msgid "Connect to Server" +msgstr "Connexion au Serveur" + +#: ../../glade/pybridge.glade.h:8 +msgid "Create a New Table" +msgstr "Crer une nouvelle Table" + +#: ../../glade/pybridge.glade.h:9 +msgid "D" +msgstr "D" + +#: ../../glade/pybridge.glade.h:10 +msgid "Display suits as symbols?" +msgstr "Reprsenter les couleurs par des symboles ?" + +#: ../../glade/pybridge.glade.h:11 +msgid "Game Display" +msgstr "Affichage du Jeu" + +#: ../../glade/pybridge.glade.h:12 +msgid "Game at Table:" +msgstr "Jeu la table:" + +#: ../../glade/pybridge.glade.h:13 +msgid "H" +msgstr "H" + +#: ../../glade/pybridge.glade.h:14 +msgid "Host Name:" +msgstr "Nom du Poste" + +#: ../../glade/pybridge.glade.h:15 +msgid "Open Tables" +msgstr "Tables Ouvertes" + +#: ../../glade/pybridge.glade.h:16 +msgid "Password:" +msgstr "Mot de Passe" + +#: ../../glade/pybridge.glade.h:17 +msgid "Port:" +msgstr "Port:" + +#: ../../glade/pybridge.glade.h:18 +msgid "PyBridge" +msgstr "PyBridge" + +#: ../../glade/pybridge.glade.h:19 +msgid "PyBridge Preferences" +msgstr "PyBridge Prfrences" + +#: ../../glade/pybridge.glade.h:20 +msgid "PyBridge _Home Page" +msgstr "PyBridge _Page d'accueil" + +#: ../../glade/pybridge.glade.h:21 +msgid "Register as New User" +msgstr "Inscrire Nouveau Joueur" + +#: ../../glade/pybridge.glade.h:22 ../../pybridge/ui/vocabulary.py:53 +msgid "S" +msgstr "S" + +#: ../../glade/pybridge.glade.h:23 +msgid "Save Password" +msgstr "Sauvegarder Mot de Passe" + +#: ../../glade/pybridge.glade.h:24 +msgid "Suit Colours:" +msgstr "" + +#: ../../glade/pybridge.glade.h:25 +msgid "Table Name:" +msgstr "Nom de la Table" + +#: ../../glade/pybridge.glade.h:26 +msgid "User Information" +msgstr "Information Joueur" + +#: ../../glade/pybridge.glade.h:27 +msgid "User Name:" +msgstr "Nom du Joueur" + +#: ../../glade/pybridge.glade.h:28 +msgid "Users Online" +msgstr "Joueurs en Ligne" + +#: ../../glade/pybridge.glade.h:29 +msgid "_About PyBridge" +msgstr "_Au sujet de Pybridge" + +#: ../../glade/pybridge.glade.h:30 +msgid "_Connect to Server..." +msgstr "_Connexion au Serveur" + +#: ../../glade/pybridge.glade.h:31 +msgid "_Connection" +msgstr "_Connexion" + +#: ../../glade/pybridge.glade.h:32 +msgid "_Disconnect" +msgstr "_Dconnexion" + +#: ../../glade/pybridge.glade.h:33 +msgid "_Help" +msgstr "_Aide" + +#: ../../glade/pybridge.glade.h:34 +msgid "_New Table..." +msgstr "_Nouvelle Table" + +#: ../../glade/pybridge.glade.h:35 +msgid "_Open Game..." +msgstr "_Ouvrir Jeu..." + +#: ../../glade/pybridge.glade.h:36 +msgid "_Options" +msgstr "_Options" + +#: ../../glade/pybridge.glade.h:37 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: ../../glade/pybridge.glade.h:38 +msgid "gtk-close" +msgstr "gtk-close" + +#: ../../glade/pybridge.glade.h:39 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: ../../glade/pybridge.glade.h:40 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: ../../pybridge/ui/dialog_connection.py:74 +msgid "Connection failed" +msgstr "Connexion choue" + +#: ../../pybridge/ui/dialog_connection.py:75 +msgid "Could not connect to server." +msgstr "Impossible de se connecter au serveur" + +#: ../../pybridge/ui/dialog_connection.py:76 +#: ../../pybridge/ui/dialog_newtable.py:62 +#, python-format +msgid "Reason: %s" +msgstr "Raison: %s" + +#: ../../pybridge/ui/dialog_newtable.py:60 +msgid "Could not create table" +msgstr "Impossible de crer la table" + +#: ../../pybridge/ui/dialog_newtable.py:61 +msgid "The table was not created by the server." +msgstr "La table n'a pas t cre par le serveur" + +#: ../../pybridge/ui/dialog_preferences.py:43 +msgid "PNG images" +msgstr "Images PNG" + +#: ../../pybridge/ui/dialog_preferences.py:98 +#, python-format +msgid "Select colour for %s symbol" +msgstr "Choisir la couleur comem symbole %s" + +#: ../../pybridge/ui/dialog_userinfo.py:29 +msgid "Real Name" +msgstr "Nom rel" + +#: ../../pybridge/ui/dialog_userinfo.py:30 +msgid "Email Address" +msgstr "Adresse courriel" + +#: ../../pybridge/ui/dialog_userinfo.py:32 +msgid "Profile" +msgstr "Profil" + +#: ../../pybridge/ui/dialog_userinfo.py:54 +#, python-format +msgid "Information for %(user)s" +msgstr "Information pour %(user)s" + +#: ../../pybridge/ui/dialog_userinfo.py:68 +msgid "not specified" +msgstr "Non prcis" + +#: ../../pybridge/ui/excepthook.py:34 +msgid "Program error" +msgstr "Erreur de programme" + +#: ../../pybridge/ui/excepthook.py:35 +msgid "" +"PyBridge detected an unexpected program error. You should close and restart " +"PyBridge." +msgstr "Pybridge a dtect une erreur. vosu devez fermer et relancer Pybridge." + +#: ../../pybridge/ui/excepthook.py:36 +msgid "" +"If you continue to experience this error, please submit a bug report, " +"attaching the following error trace." +msgstr "" +"Si cette erreur est frquente, svp informer nous du bug, en attachant les " +"informations suivantes." + +#: ../../pybridge/ui/vocabulary.py:32 +msgid "Pass" +msgstr "Passe" + +#: ../../pybridge/ui/vocabulary.py:33 +msgid "Double" +msgstr "Contre" + +#: ../../pybridge/ui/vocabulary.py:34 +msgid "Redouble" +msgstr "Surcintre" + +#: ../../pybridge/ui/vocabulary.py:44 +msgid "North" +msgstr "Nord" + +#: ../../pybridge/ui/vocabulary.py:45 +msgid "East" +msgstr "Est" + +#: ../../pybridge/ui/vocabulary.py:46 +msgid "South" +msgstr "Sud" + +#: ../../pybridge/ui/vocabulary.py:47 +msgid "West" +msgstr "Ouest" + +#: ../../pybridge/ui/vocabulary.py:51 +msgid "N" +msgstr "N" + +#: ../../pybridge/ui/vocabulary.py:52 +msgid "E" +msgstr "E" + +#: ../../pybridge/ui/vocabulary.py:54 +msgid "W" +msgstr "O" + +#: ../../pybridge/ui/vocabulary.py:68 +msgid "Two" +msgstr "Deux" + +#: ../../pybridge/ui/vocabulary.py:69 +msgid "Three" +msgstr "Trois" + +#: ../../pybridge/ui/vocabulary.py:70 +msgid "Four" +msgstr "Quatre" + +#: ../../pybridge/ui/vocabulary.py:71 +msgid "Five" +msgstr "Cinq" + +#: ../../pybridge/ui/vocabulary.py:72 +msgid "Six" +msgstr "Six" + +#: ../../pybridge/ui/vocabulary.py:73 +msgid "Seven" +msgstr "Sept" + +#: ../../pybridge/ui/vocabulary.py:74 +msgid "Eight" +msgstr "Huit" + +#: ../../pybridge/ui/vocabulary.py:75 +msgid "Nine" +msgstr "Neuf" + +#: ../../pybridge/ui/vocabulary.py:76 +msgid "Ten" +msgstr "Dix" + +#: ../../pybridge/ui/vocabulary.py:77 +msgid "Jack" +msgstr "Valet" + +#: ../../pybridge/ui/vocabulary.py:78 +msgid "Queen" +msgstr "Dame" + +#: ../../pybridge/ui/vocabulary.py:79 +msgid "King" +msgstr "Roi" + +#: ../../pybridge/ui/vocabulary.py:80 +msgid "Ace" +msgstr "As" + +#: ../../pybridge/ui/vocabulary.py:100 +msgid "Clubs" +msgstr "Trefles" + +#: ../../pybridge/ui/vocabulary.py:101 +msgid "Diamonds" +msgstr "Carreaux" + +#: ../../pybridge/ui/vocabulary.py:102 +msgid "Hearts" +msgstr "Coeurs" + +#: ../../pybridge/ui/vocabulary.py:103 +msgid "Spades" +msgstr "Piques" + +#: ../../pybridge/ui/vocabulary.py:122 +msgid "Club" +msgstr "Trefle" + +#: ../../pybridge/ui/vocabulary.py:123 +msgid "Diamond" +msgstr "Carreau" + +#: ../../pybridge/ui/vocabulary.py:124 +msgid "Heart" +msgstr "Coeur" + +#: ../../pybridge/ui/vocabulary.py:125 +msgid "Spade" +msgstr "Pique" + +#: ../../pybridge/ui/vocabulary.py:126 +msgid "No Trump" +msgstr "Sans Atout" + +#: ../../pybridge/ui/vocabulary.py:147 +msgid "All" +msgstr "Tout" + +#: ../../pybridge/ui/vocabulary.py:148 +#: ../../pybridge/games/bridge/ui/scoresheet.py:42 +#: ../../pybridge/games/bridge/ui/scoresheet.py:87 +msgid "N/S" +msgstr "N/S" + +#: ../../pybridge/ui/vocabulary.py:149 +#: ../../pybridge/games/bridge/ui/scoresheet.py:42 +#: ../../pybridge/games/bridge/ui/scoresheet.py:87 +msgid "E/W" +msgstr "E/O" + +#: ../../pybridge/ui/vocabulary.py:150 +msgid "None" +msgstr "Aucun" + +#: ../../pybridge/ui/vocabulary.py:169 +#, python-format +msgid "%(level)s %(strain)s" +msgstr "" + +#: ../../pybridge/ui/vocabulary.py:183 +#, python-format +msgid "%(rank)s of %(suit)s" +msgstr "" + +#: ../../pybridge/ui/vocabulary.py:201 +#, python-format +msgid "%(bid)s %(doubled)s by %(declarer)s" +msgstr "%(bid)s %(doubled)s par %(declarer)s" + +#: ../../pybridge/ui/vocabulary.py:203 +#, python-format +msgid "%(bid)s by %(declarer)s" +msgstr "%(bid)s par %(declarer)s" + +#: ../../pybridge/ui/window_chat.py:73 +msgid "1 person in room" +msgstr "1 personne dans la pice" + +#: ../../pybridge/ui/window_chat.py:75 +#, python-format +msgid "%(num)s people in room" +msgstr "%(num)s personne dans la pice" + +#: ../../pybridge/ui/window_chat.py:194 +#, python-format +msgid "%(user)s has joined the conversation" +msgstr "%(user)s a rejoint la conversation" + +#: ../../pybridge/ui/window_chat.py:196 +#, python-format +msgid "%(user)s has left the conversation" +msgstr "%(user)s a quitt la conversation" + +#: ../../pybridge/ui/window_chat.py:290 +msgid "Chat" +msgstr "Chat" + +#: ../../pybridge/ui/window_gametable.py:37 +msgid "Unnamed Game" +msgstr "Jeu sans Nom" + +#: ../../pybridge/ui/window_gametable.py:46 +msgid "Table" +msgstr "Table" + +#: ../../pybridge/ui/window_gametable.py:78 +msgid "Take Seat" +msgstr "Prendre Place" + +#: ../../pybridge/ui/window_gametable.py:84 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:336 +msgid "Leave Seat" +msgstr "Quitter Place" + +#: ../../pybridge/ui/window_gametable.py:99 +#: ../../pybridge/ui/window_gametable.py:193 +msgid "Leave Table" +msgstr "Quitter la Table" + +#: ../../pybridge/ui/window_gametable.py:135 +#, python-format +msgid "%(tablename)s (%(gametype)s)" +msgstr "%(tablename)s (%(gametype)s)" + +#: ../../pybridge/ui/window_gametable.py:191 +msgid "Leave table?" +msgstr "Quitter la table ?" + +#: ../../pybridge/ui/window_gametable.py:194 +msgid "Are you sure you wish to leave this table?" +msgstr "Etes vous sure de vouloir quitter cette table?" + +#: ../../pybridge/ui/window_gametable.py:195 +msgid "" +"You are currently playing a game. Leaving may forfeit the game, or incur " +"penalties." +msgstr "" +"Vous etes en train de jouer. Quiter peut faire forfait le jeu, ou provoquer " +"de pnalits" + +#: ../../pybridge/ui/window_main.py:125 +msgid "Connection to server lost" +msgstr "Connexion au serveur rompue" + +#: ../../pybridge/ui/window_main.py:126 +#, python-format +msgid "The connection to %s was lost unexpectedly." +msgstr "La connexion a %s a t perdue." + +#: ../../pybridge/ui/window_main.py:127 +msgid "" +"Please check your computer's network connection status before reconnecting. " +"If you cannot reconnect, the server may be offline." +msgstr "" +"Verifier votre connexion avant tenter une reconnexion.Si vous ne vous " +"reconnectez pas, le serveur est peut tre hors ligne." + +#: ../../pybridge/ui/window_main.py:272 +msgid "Disconnect from server" +msgstr "Deconnexion du serveur" + +#: ../../pybridge/ui/window_main.py:275 +msgid "Are you sure you wish to disconnect?" +msgstr "Etes vous sur de vouloir vous deconnecter?" + +#: ../../pybridge/ui/window_main.py:276 +msgid "" +"You are playing at a table. Disconnecting may forfeit the game, or incur " +"penalties." +msgstr "" +"Vous etes en train de jouer. Quiter peut faire forfait le jeu, ou provoquer " +"de pnalits" + +#: ../../pybridge/ui/window_main.py:305 +msgid "A free online bridge game." +msgstr "Un jeu libre de brige en ligne" + +#: ../../pybridge/games/bridge/ui/scoresheet.py:42 +msgid "Board" +msgstr "" + +#: ../../pybridge/games/bridge/ui/scoresheet.py:42 +msgid "Contract" +msgstr "Contrat" + +#: ../../pybridge/games/bridge/ui/scoresheet.py:42 +msgid "Made" +msgstr "" + +#: ../../pybridge/games/bridge/ui/scoresheet.py:55 +msgid "Passed out" +msgstr "" + +#: ../../pybridge/games/bridge/ui/scoresheet.py:124 +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:228 +msgid "Score Sheet" +msgstr "Scores" + +#: ../../pybridge/games/bridge/ui/window_bidbox.py:41 +msgid "Bidding Box" +msgstr "Enchre" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:103 +msgid "Declarer" +msgstr "Dclarant" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:109 +msgid "Defence" +msgstr "" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:132 +msgid "No contract" +msgstr "Pas de contrat" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:154 +msgid "Dealer" +msgstr "Ouvreur" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:162 +msgid "Vuln" +msgstr "Vulnrable" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:169 +msgid "Contract Bridge" +msgstr "Contrat" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:208 +msgid "Bidding" +msgstr "Ouverture" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:217 +msgid "Previous Trick" +msgstr "Leve prcdente" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:297 +msgid "Game result" +msgstr "Resultat de la Partie" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:311 +#, python-format +msgid "Contract %(contract)s made by 1 trick." +msgstr "Le Contrat %(contract)s raliser d'une leve." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:313 +#, python-format +msgid "Contract %(contract)s made by %(offset)s tricks." +msgstr "Le Contrat %(contract)s raliser avec %(offset)s leves." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:316 +#, python-format +msgid "Contract %(contract)s failed by 1 trick." +msgstr "Le contrat %(contract)s chute d'une leve" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:318 +#, python-format +msgid "Contract %(contract)s failed by %(offset)s tricks." +msgstr "Le contrat %(contract)s chute de %(offset)s leves" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:320 +#, python-format +msgid "Contract %(contract)s made exactly." +msgstr "Le contrat %(contract)s russit" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:326 +msgid "declarer" +msgstr "dclare" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:326 +msgid "defence" +msgstr "Intervention" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:327 +#, python-format +msgid "Score %(points)s points for %(pair)s." +msgstr "Score %(points)s points pour %(pair)s." + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:332 +msgid "Bidding passed out." +msgstr "Enchere passe" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:333 +msgid "No score." +msgstr "Pas de point" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:337 +msgid "Click OK to start next game." +msgstr "Cliquer Ok pour dmarrer la prochaine partie" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:408 +msgid "Play a card from your hand." +msgstr "Jouer une carte de votre main" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:410 +msgid "Play a card from dummy's hand." +msgstr "Jouer une carte de la main du mort" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:412 +#, python-format +msgid "It is %s's turn to play a card." +msgstr "Ce n'est pas %s's de jouer une carte" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:416 +msgid "Make a call from the bidding box." +msgstr "Faire un appel de l'enchre" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:418 +#, python-format +msgid "It is %s's turn to make a call." +msgstr "Ce n'est pas le tour de %s's de faire un appel" + +#: ../../pybridge/games/bridge/ui/window_bridgetable.py:421 +msgid "Waiting for next game to start." +msgstr "Attender que la prochaine partie commence" \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-09-02 11:41:33
|
Revision: 501 http://pybridge.svn.sourceforge.net/pybridge/?rev=501&view=rev Author: umgangee Date: 2007-09-02 04:41:30 -0700 (Sun, 02 Sep 2007) Log Message: ----------- Register translation paths for Glade. Modified Paths: -------------- trunk/pybridge/pybridge/ui/__init__.py Modified: trunk/pybridge/pybridge/ui/__init__.py =================================================================== --- trunk/pybridge/pybridge/ui/__init__.py 2007-08-31 13:45:20 UTC (rev 500) +++ trunk/pybridge/pybridge/ui/__init__.py 2007-09-02 11:41:30 UTC (rev 501) @@ -18,18 +18,23 @@ from twisted.internet import gtk2reactor gtk2reactor.install() -import gtk -from twisted.internet import reactor +# Default settings based on the user's environment. import locale -import gettext locale.setlocale(locale.LC_ALL, '') + +import gettext, gtk.glade as glade import pybridge.environment as env -gettext.bindtextdomain('pybridge', env.get_localedir()) -gettext.textdomain('pybridge') -gettext.install('pybridge') +for module in gettext, glade: + module.bindtextdomain('pybridge', env.get_localedir()) + module.textdomain('pybridge') + +# Register the gettext function for the whole interpreter as "_" +gettext.install('pybridge', env.get_localedir()) + + import config config.load() @@ -47,7 +52,9 @@ wm.open(WindowMain) # Start the event loop. + from twisted.internet import reactor reactor.run() + import gtk gtk.main() config.save() # Save config at exit. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-08-31 13:45:22
|
Revision: 500 http://pybridge.svn.sourceforge.net/pybridge/?rev=500&view=rev Author: umgangee Date: 2007-08-31 06:45:20 -0700 (Fri, 31 Aug 2007) Log Message: ----------- Refactor rubber scoring, to facilitate rubber scoresheet display. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-08-03 18:42:14 UTC (rev 499) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-08-31 13:45:20 UTC (rev 500) @@ -97,14 +97,14 @@ if len(self.rubbers) == 0 or self.rubbers[-1].winner: self.board['vuln'] = Vulnerable.None # First round, new rubber. else: - games = self.rubbers[-1].games - if len(games[(Direction.North, Direction.South)]) > 0: - if len(games[(Direction.East, Direction.West)]) > 0: + pairs = [pair for game, pair in self.rubbers[-1].games] + if pairs.count((Direction.North, Direction.South)) > 0: + if pairs.count((Direction.East, Direction.West)) > 0: self.board['vuln'] = Vulnerable.All else: self.board['vuln'] = Vulnerable.NorthSouth else: - if len(games[(Direction.East, Direction.West)]) > 0: + if pairs.count((Direction.East, Direction.West)) > 0: self.board['vuln'] = Vulnerable.EastWest else: self.board['vuln'] = Vulnerable.None Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-08-03 18:42:14 UTC (rev 499) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-08-31 13:45:20 UTC (rev 500) @@ -233,40 +233,41 @@ def _getGames(self): - """Returns, for each pair, a list of completed 'games' won by the pair - in this rubber. + """Returns a list of completed (ie. won) 'games' in this rubber, in the + order of their completion. - A game is represented as the list of consecutive results in this rubber, - with below-the-line scores that count towards the game. + A game is represented as a list of consecutive results from this rubber, + coupled with the identifier of the scoring pair. """ - gamesNS, gamesEW = [], [] + games = [] - game = [] - belowNS, belowEW = 0, 0 # Cumulative totals for results. + thisgame = [] + belowNS, belowEW = 0, 0 # Cumulative totals for results in this game. + for result in self: - game.append(result) - + thisgame.append(result) if result.contract.declarer in (Direction.North, Direction.South): belowNS += result.score[1] if belowNS >= 100: - gamesNS.append(game) + games.append((thisgame, (Direction.North, Direction.South))) else: belowEW += result.score[1] if belowEW >= 100: - gamesEW.append(game) + games.append((thisgame, (Direction.East, Direction.West))) - # If either accumulated total exceeds 100, proceed to next game. - if belowNS >= 100 or belowEW >= 100: - game = [] - belowNS, belowEW = 0, 0 + # If either total for this game exceeds 100, proceed to next game. + if belowNS >= 100 or belowEW >= 100: + thisgame = [] + belowNS, belowEW = 0, 0 # Reset accumulators. - return {(Direction.North, Direction.South): gamesNS, - (Direction.East, Direction.West): gamesEW} + return games def _getWinner(self): """The rubber is won by the pair which have completed two games.""" - for pair, games in self.games.items(): - if len(games) >= 2: + pairs = [pair for game, pair in self.games] + + for pair in ((Direction.North, Direction.South), (Direction.East, Direction.West)): + if pairs.count(pair) >= 2: return pair Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-08-03 18:42:14 UTC (rev 499) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-08-31 13:45:20 UTC (rev 500) @@ -29,6 +29,7 @@ from pybridge.ui.window_gametable import WindowGameTable from window_bidbox import WindowBidbox +from window_scoresheet import ScoreSheet class BiddingView(gtk.TreeView): @@ -83,41 +84,6 @@ -class ScoreView(gtk.TreeView): - """A display of contracts bid, their results and their scores.""" - - - def __init__(self): - gtk.TreeView.__init__(self) - self.set_rules_hint(True) - - self.store = gtk.ListStore(str, str, str, str) - self.set_model(self.store) - self.clear = self.store.clear - renderer = gtk.CellRendererText() - - for index, title in enumerate([_('Contract'), _('Made'), _('N/S'), _('E/W')]): - column = gtk.TreeViewColumn(title, renderer, markup=index) - self.append_column(column) - - - def add_score(self, game): - declarerWon, defenceWon = game.play.wonTrickCount() - score = game.result.score - - textContract = render_contract(game.contract) - textMade = str(declarerWon) - if game.contract.declarer in (Direction.North, Direction.South) and score > 0 \ - or game.contract.declarer in (Direction.East, Direction.West) and score < 0: - textNS, textEW = str(abs(score)), '' - else: - textNS, textEW = '', str(abs(score)) - - self.store.prepend([textContract, textMade, textNS, textEW]) - - - - class BridgeDashboard(gtk.VBox): """An at-a-glance display of the state of a bridge game.""" @@ -242,7 +208,7 @@ exp = gtk.Expander(_('Bidding')) exp.set_expanded(True) exp.add(frame) - self.sidebar.pack_start(exp) + self.sidebar.pack_start(exp, expand=True) self.trickarea = TrickArea(positions=Direction) self.trickarea.set_size_request(-1, 180) @@ -253,10 +219,10 @@ exp.add(frame) self.sidebar.pack_start(exp, expand=False) - self.scoreview = ScoreView() + self.scoresheet = ScoreSheet() sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - sw.add(self.scoreview) + sw.add(self.scoresheet) frame = gtk.Frame() frame.add(sw) exp = gtk.Expander(_('Score Sheet')) @@ -265,11 +231,6 @@ self.sidebar.pack_start(exp, expand=False) - def errback(self, failure): - print "Error: %s" % failure.getErrorMessage() - print failure.getBriefTraceback() - - def setTable(self, table): """Changes display to match the table specified. @@ -337,12 +298,11 @@ # Determine and display score in dialog box and score sheet. if self.table.game.contract: - self.scoreview.add_score(self.table.game) + self.scoresheet.add_result(self.table.game.result) tricksMade = self.table.game.result.tricksMade tricksRequired = self.table.game.contract.bid.level.index + 7 offset = tricksMade - tricksRequired - score = self.table.game.result.score fields = {'contract': render_contract(self.table.game.contract), 'offset': abs(offset)} @@ -359,6 +319,10 @@ else: resultText = _('Contract %(contract)s made exactly.') % fields + score = self.table.game.result.score + if isinstance(score, tuple): # Rubber scoring. + score = sum(score) # TODO: display above, below separately. + pair = (score >= 0 and _('declarer')) or _('defence') scoreText = _('Score %(points)s points for %(pair)s.') % {'points': abs(score), 'pair': pair} Added: trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-08-31 13:45:20 UTC (rev 500) @@ -0,0 +1,140 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2007 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +import gtk + +from pybridge.games.bridge.result import DuplicateResult, RubberResult +from pybridge.games.bridge.symbols import Direction + +from pybridge.ui.eventhandler import SimpleEventHandler +import pybridge.ui.vocabulary as voc + + +class ScoreSheet(gtk.TreeView): + """A score sheet widget, which presents GameResult information.""" + + # TODO: display total scores for N/S and E/W. + + + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) + + self.store = gtk.ListStore(int, str, str, str, str) + self.set_model(self.store) + + renderer = gtk.CellRendererText() + for index, title in enumerate([_('Board'), _('Contract'), _('Made'), _('N/S'), _('E/W')]): + column = gtk.TreeViewColumn(title, renderer, markup=index) + self.append_column(column) + + + def add_result(self, result): + if isinstance(result, RubberResult): + # Rubber results are split into 'above' and 'below' scores. + score = sum(result.score) + else: + score = result.score + + if result.contract is None: # Bidding passed out. + row = (result.board['num'], _('Passed out'), '-', '-', '', '') + + else: + if result.contract.declarer in (Direction.North, Direction.South) and score > 0 \ + or result.contract.declarer in (Direction.East, Direction.West) and score < 0: + scoreNS, scoreEW = str(abs(score)), '' + else: + scoreNS, scoreEW = '', str(abs(score)) + + row = (result.board['num'], voc.render_contract(result.contract), + #voc.DIRECTION_NAMES[result.contract.declarer], + str(result.tricksMade), scoreNS, scoreEW) + + self.store.append(row) + + + + +class RubberScoreSheet(gtk.TreeView): + """A score sheet widget, which presents a Rubber object.""" + + + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) + self.set_row_separator_func(self._row_separator) + + # Set bool field in a row to display as separator. + self.store = gtk.ListStore(str, str, bool) + self.set_model(self.store) + + renderer = gtk.CellRendererText() + for index, title in enumerate([_('N/S'), _('E/W')]): + column = gtk.TreeViewColumn(title, renderer, markup=index) + self.append_column(column) + + + def set_rubber(self, rubber): + self.store.clear() + self.store.append(('', '', True)) # The initial dividing line. + + for game, pair in rubber.games: + #aboveiters, belowiters = [], [] + for result in rubber: + above, below = result.score + if result.contract.declarer in (Direction.North, Direction.South) and below > 0 \ + or result.contract.declarer in (Direction.East, Direction.West) and score < 0: + self.store.prepend((str(above), '', False)) + self.store.append((str(below), '', False)) + else: + self.store.prepend(('', str(above), False)) + self.store.append(('', str(below), False)) + + + def _row_separator(self, model, iter, data): + print model, iter, data + return True + + + + +class WindowScoreSheet(object): + """""" + + + def __init__(self, parent=None): + self.window = gtk.Window() + if parent: + self.window.set_transient_for(parent.window) + self.window.set_title(_('Score Sheet')) + self.window.connect('delete_event', self.on_delete_event) + #self.window.set_resizable(False) + + self.eventHandler = SimpleEventHandler(self) + self.table = None + + + def tearDown(self): + pass + + + + def on_delete_event(self, widget, *args): + # TODO: call wm.close(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-08-03 18:42:16
|
Revision: 499 http://pybridge.svn.sourceforge.net/pybridge/?rev=499&view=rev Author: umgangee Date: 2007-08-03 11:42:14 -0700 (Fri, 03 Aug 2007) Log Message: ----------- New Rubber class (for rubber bridge), move some logic from Board to BridgeGame. I think this implements rubber bridge scoring/vulnerability rules accurately, but some testing is still required. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-27 19:05:07 UTC (rev 498) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-08-03 18:42:14 UTC (rev 499) @@ -48,7 +48,7 @@ @classmethod def first(cls, deal=None): - """Build an initial board. + """Builds an initial board. @deal: if provided, the deal to be wrapped by board. Otherwise, a randomly-generated deal is wrapped. @@ -58,27 +58,22 @@ board['num'] = 1 board['time'] = tuple(time.localtime()) - board['dealer'] = Direction.North # Arbitary. + # Convention for duplicate bridge. + board['dealer'] = Direction.North board['vuln'] = Vulnerable.None return board - def next(self, results, deal=None): - """Given the results for this board (and all previous boards), - builds the next board. + def next(self, deal=None): + """Builds and returns a successor board to this board. - The dealer and vulnerability of the next board are calculated - from the results provided. + The dealer and vulnerability of the successor board are determined from + the board number, according to the rotation scheme for duplicate bridge. - @param result: a list of all previous results, ordered from earliest - to most recent, ie. this board's result is last in list. @param deal: if provided, the deal to be wrapped by next board. Otherwise, a randomly-generated deal is wrapped. """ - boardresult = results[-1] - assert boardresult.board == self - board = Board(self.copy()) # copy() returns a dict. board['deal'] = deal or Deal.fromRandom() board['num'] = board.get('num', 0) + 1 @@ -87,33 +82,10 @@ # Dealer rotates clockwise. board['dealer'] = Direction[(board['dealer'].index + 1) % 4] - if isinstance(boardresult, DuplicateResult): - # See http://www.d21acbl.com/References/Laws/node5.html#law2 - i = (board['num'] - 1) % 16 - # Map from duplicate board index range 1..16 to vulnerability. - board['vuln'] = Vulnerable[(i%4 + i/4)%4] + # Map from duplicate board index range 1..16 to vulnerability. + # See http://www.d21acbl.com/References/Laws/node5.html#law2 + i = (board['num'] - 1) % 16 + board['vuln'] = Vulnerable[(i%4 + i/4)%4] - elif isinstance(boardresult, RubberResult): - belowNS, belowEW = 0, 0 # Running totals of below-the-line scores. - board['vuln'] = Vulnerable.None - # Only consider rounds which score below-the-line. - for result in (r for r in results if r.score.below > 0): - if result.contract.declarer in (Direction.North, Direction.South): - belowNS += result.score.below - pair = Vulnerable.NorthSouth - else: - belowEW += result.score.below - pair = Vulnerable.EastWest - # If either score exceeds 100, pair has made game. - if belowNS >= 100 or belowEW >= 100: - belowNS, belowEW = 0, 0 # Reset totals for next game. - # Vulnerability transitions. - if board['vuln'] == Vulnerable.None: - board['vuln'] = pair - elif board['vuln'] in (pair, Vulnerable.All): - board['vuln'] = Vulnerable.None - else: # Pair was not vulnerable, but other pair was. - board['vuln'] = Vulnerable.All - return board Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-27 19:05:07 UTC (rev 498) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-08-03 18:42:14 UTC (rev 499) @@ -25,8 +25,8 @@ from auction import Auction from board import Board -from play import Trick, TrickPlay -from result import DuplicateResult, RubberResult +from play import TrickPlay +from result import DuplicateResult, Rubber, RubberResult from call import Bid, Pass, Double, Redouble from card import Card @@ -56,7 +56,7 @@ Strain.NoTrump: None} - def __init__(self): + def __init__(self, **options): self.listeners = [] self.board = None @@ -68,6 +68,11 @@ self.visibleHands = {} # A subset of deal, containing revealed hands. self.players = {} # One-to-one mapping from BridgePlayer to Direction. + self.options = options + if self.options.get('RubberScoring'): # Use rubber scoring? + self.rubbers = [] # Group results into Rubber objects. + + contract = property(lambda self: self.auction and self.auction.contract or None) trumpSuit = property(lambda self: self.play and self.play.trumpSuit or None) result = property(lambda self: not self.inProgress() and self.results[-1]) @@ -83,10 +88,27 @@ if board: # Use specified board. self.board = board elif self.board: # Advance to next round. - self.board = self.board.next(self.results) - else: # Create a board. + self.board = self.board.next() + else: # Create an initial board. self.board = Board.first() + if self.options.get('RubberScoring'): + # Vulnerability determined by number of games won by each pair. + if len(self.rubbers) == 0 or self.rubbers[-1].winner: + self.board['vuln'] = Vulnerable.None # First round, new rubber. + else: + games = self.rubbers[-1].games + if len(games[(Direction.North, Direction.South)]) > 0: + if len(games[(Direction.East, Direction.West)]) > 0: + self.board['vuln'] = Vulnerable.All + else: + self.board['vuln'] = Vulnerable.NorthSouth + else: + if len(games[(Direction.East, Direction.West)]) > 0: + self.board['vuln'] = Vulnerable.EastWest + else: + self.board['vuln'] = Vulnerable.None + self.auction = Auction(self.board['dealer']) # Start auction. self.play = None self.visibleHands.clear() @@ -246,7 +268,7 @@ # If bidding is passed out, game is complete. if not self.inProgress() and self.board['deal']: - self.results.append(DuplicateResult(self.board, contract=None)) + self._addResult(self.board, contract=None) self.notify('makeCall', call=call, position=position) @@ -318,8 +340,7 @@ # If play is complete, game is complete. if not self.inProgress() and self.board['deal']: tricksMade, _ = self.play.wonTrickCount() - result = DuplicateResult(self.board, self.contract, tricksMade) - self.results.append(result) + self._addResult(self.board, self.contract, tricksMade) self.notify('playCard', card=card, position=position) @@ -329,9 +350,22 @@ hand = self.board['deal'].get(position) if hand and position not in self.visibleHands: self.revealHand(hand, position) - + def _addResult(self, board, contract=None, tricksMade=None): + if self.options.get('RubberScoring'): + result = RubberResult(board, contract, tricksMade) + if len(self.rubbers) > 0 and self.rubbers[-1].winner is None: + rubber = self.rubbers[-1] + else: # Instantiate new rubber. + rubber = Rubber() + self.rubbers.append(rubber) + rubber.append(result) + else: + result = DuplicateResult(board, contract, tricksMade) + self.results.append(result) + + def revealHand(self, hand, position): """Reveal hand to all observers. Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-07-27 19:05:07 UTC (rev 498) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-08-03 18:42:14 UTC (rev 499) @@ -24,10 +24,10 @@ _getScore = NotImplemented # Expected to be implemented by subclasses. - __vulnMapping = {Vulnerable.None: (), - Vulnerable.NorthSouth: (Direction.North, Direction.South), - Vulnerable.EastWest: (Direction.East, Direction.West), - Vulnerable.All: tuple(Direction)} + __vulnMap = {Vulnerable.None: (), + Vulnerable.NorthSouth: (Direction.North, Direction.South), + Vulnerable.EastWest: (Direction.East, Direction.West), + Vulnerable.All: tuple(Direction)} def __init__(self, board, contract, tricksMade=None): @@ -42,7 +42,7 @@ if self.contract: vuln = self.board.get('vuln', Vulnerable.None) - self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] + self.isVulnerable = self.contract.declarer in self.__vulnMap[vuln] self.score = self._getScore() @@ -218,3 +218,55 @@ below += value return above, below + + + +class Rubber(list): + """A rubber set, in which pairs compete to make two consecutive games. + + A game is made by accumulation of 100+ points from below-the-line scores + without interruption from an opponent's game. + """ + + games = property(lambda self: self._getGames()) + winner = property(lambda self: self._getWinner()) + + + def _getGames(self): + """Returns, for each pair, a list of completed 'games' won by the pair + in this rubber. + + A game is represented as the list of consecutive results in this rubber, + with below-the-line scores that count towards the game. + """ + gamesNS, gamesEW = [], [] + + game = [] + belowNS, belowEW = 0, 0 # Cumulative totals for results. + for result in self: + game.append(result) + + if result.contract.declarer in (Direction.North, Direction.South): + belowNS += result.score[1] + if belowNS >= 100: + gamesNS.append(game) + else: + belowEW += result.score[1] + if belowEW >= 100: + gamesEW.append(game) + + # If either accumulated total exceeds 100, proceed to next game. + if belowNS >= 100 or belowEW >= 100: + game = [] + belowNS, belowEW = 0, 0 + + return {(Direction.North, Direction.South): gamesNS, + (Direction.East, Direction.West): gamesEW} + + + def _getWinner(self): + """The rubber is won by the pair which have completed two games.""" + for pair, games in self.games.items(): + if len(games) >= 2: + return pair + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-27 19:05:05
|
Revision: 498 http://pybridge.svn.sourceforge.net/pybridge/?rev=498&view=rev Author: umgangee Date: 2007-07-27 12:05:07 -0700 (Fri, 27 Jul 2007) Log Message: ----------- Since determination of vulnerability in rubber bridge requires knowledge of previous results, pass list of results to board.next(), instead of just the most recent result. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py trunk/pybridge/pybridge/games/bridge/game.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-27 19:04:18 UTC (rev 497) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-07-27 19:05:07 UTC (rev 498) @@ -45,12 +45,7 @@ @type vuln: Vulnerable """ - __directionToVuln = {Direction.North: Vulnerable.NorthSouth, - Direction.East: Vulnerable.EastWest, - Direction.South: Vulnerable.NorthSouth, - Direction.West: Vulnerable.EastWest} - @classmethod def first(cls, deal=None): """Build an initial board. @@ -69,17 +64,20 @@ return board - def next(self, result, deal=None): - """Given the result for this board, builds the next board. + def next(self, results, deal=None): + """Given the results for this board (and all previous boards), + builds the next board. The dealer and vulnerability of the next board are calculated - from the result provided. + from the results provided. - @param result: the result of the this board. + @param result: a list of all previous results, ordered from earliest + to most recent, ie. this board's result is last in list. @param deal: if provided, the deal to be wrapped by next board. Otherwise, a randomly-generated deal is wrapped. """ - assert result.board == self + boardresult = results[-1] + assert boardresult.board == self board = Board(self.copy()) # copy() returns a dict. board['deal'] = deal or Deal.fromRandom() @@ -89,24 +87,33 @@ # Dealer rotates clockwise. board['dealer'] = Direction[(board['dealer'].index + 1) % 4] - if isinstance(result, DuplicateResult): + if isinstance(boardresult, DuplicateResult): # See http://www.d21acbl.com/References/Laws/node5.html#law2 i = (board['num'] - 1) % 16 # Map from duplicate board index range 1..16 to vulnerability. board['vuln'] = Vulnerable[(i%4 + i/4)%4] - elif isinstance(result, RubberResult): - # Determine vulnerability of board from result of previous board. - above, below = result.score - if below >= 100: # Game contract successful. - pair = __directionToVuln[result.contract.declarer] - # Vulnerability transitions. - if board['vuln'] == Vulnerable.None: - board['vuln'] = pair - elif board['vuln'] in (pair, Vulnerable.All): # Rubber won. - board['vuln'] = Vulnerable.None - else: # Pair not vulnerable, other pair are vulnerable. - board['vuln'] = Vulnerable.All + elif isinstance(boardresult, RubberResult): + belowNS, belowEW = 0, 0 # Running totals of below-the-line scores. + board['vuln'] = Vulnerable.None + # Only consider rounds which score below-the-line. + for result in (r for r in results if r.score.below > 0): + if result.contract.declarer in (Direction.North, Direction.South): + belowNS += result.score.below + pair = Vulnerable.NorthSouth + else: + belowEW += result.score.below + pair = Vulnerable.EastWest + # If either score exceeds 100, pair has made game. + if belowNS >= 100 or belowEW >= 100: + belowNS, belowEW = 0, 0 # Reset totals for next game. + # Vulnerability transitions. + if board['vuln'] == Vulnerable.None: + board['vuln'] = pair + elif board['vuln'] in (pair, Vulnerable.All): + board['vuln'] = Vulnerable.None + else: # Pair was not vulnerable, but other pair was. + board['vuln'] = Vulnerable.All return board Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-27 19:04:18 UTC (rev 497) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-27 19:05:07 UTC (rev 498) @@ -82,10 +82,8 @@ if board: # Use specified board. self.board = board - elif self.board: # Advance to next deal. - tricksMade, _ = self.play.wonTrickCount() - result = DuplicateResult(self.board, self.contract, tricksMade) - self.board = self.board.next(result) + elif self.board: # Advance to next round. + self.board = self.board.next(self.results) else: # Create a board. self.board = Board.first() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-27 19:04:16
|
Revision: 497 http://pybridge.svn.sourceforge.net/pybridge/?rev=497&view=rev Author: umgangee Date: 2007-07-27 12:04:18 -0700 (Fri, 27 Jul 2007) Log Message: ----------- Need to check whether a contract exists, just in case auction is passed out. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/result.py Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-07-26 19:03:38 UTC (rev 496) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-07-27 19:04:18 UTC (rev 497) @@ -40,8 +40,9 @@ self.contract = contract self.tricksMade = tricksMade - vuln = self.board.get('vuln', Vulnerable.None) - self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] + if self.contract: + vuln = self.board.get('vuln', Vulnerable.None) + self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] self.score = self._getScore() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-26 19:35:35
|
Revision: 496 http://pybridge.svn.sourceforge.net/pybridge/?rev=496&view=rev Author: umgangee Date: 2007-07-26 12:03:38 -0700 (Thu, 26 Jul 2007) Log Message: ----------- Incorporate support for Result objects in board, make appropriate tweaks to dependencies. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-07-26 19:03:38 UTC (rev 496) @@ -18,7 +18,9 @@ import random import time + from deal import Deal +from result import DuplicateResult, RubberResult from symbols import Direction, Vulnerable @@ -43,32 +45,68 @@ @type vuln: Vulnerable """ + __directionToVuln = {Direction.North: Vulnerable.NorthSouth, + Direction.East: Vulnerable.EastWest, + Direction.South: Vulnerable.NorthSouth, + Direction.West: Vulnerable.EastWest} - def nextDeal(self, result=None): - """Generates and stores a random deal for the board. + + @classmethod + def first(cls, deal=None): + """Build an initial board. - If result of a previous game is provided, the dealer and vulnerability - are rotated according to the rules of bridge. + @deal: if provided, the deal to be wrapped by board. + Otherwise, a randomly-generated deal is wrapped. + """ + board = cls() + board['deal'] = deal or Deal.fromRandom() + board['num'] = 1 + board['time'] = tuple(time.localtime()) + + board['dealer'] = Direction.North # Arbitary. + board['vuln'] = Vulnerable.None + + return board + + + def next(self, result, deal=None): + """Given the result for this board, builds the next board. - @param result: - @type result: + The dealer and vulnerability of the next board are calculated + from the result provided. + + @param result: the result of the this board. + @param deal: if provided, the deal to be wrapped by next board. + Otherwise, a randomly-generated deal is wrapped. """ - self['deal'] = Deal.fromRandom() + assert result.board == self - self['num'] = self.get('num', 0) + 1 - self['time'] = tuple(time.localtime()) + board = Board(self.copy()) # copy() returns a dict. + board['deal'] = deal or Deal.fromRandom() + board['num'] = board.get('num', 0) + 1 + board['time'] = tuple(time.localtime()) - if self.get('dealer'): # Rotate dealer. - self['dealer'] = Direction[(self['dealer'].index + 1) % 4] - else: # Select any player as the dealer. - self['dealer'] = random.choice(Direction) + # Dealer rotates clockwise. + board['dealer'] = Direction[(board['dealer'].index + 1) % 4] - if result: - # TODO: proper GameResult object. - # TODO: consider vulnerability rules for duplicate, rubber bridge. - #if result.bidding.isPassedOut(): - # self['vuln'] = result.board['vuln'] - #elif result.getScore() >= 0 - self['vuln'] = Vulnerable[(result.board['vuln'].index + 1) % 4] - else: - self['vuln'] = Vulnerable.None # The default value. + if isinstance(result, DuplicateResult): + # See http://www.d21acbl.com/References/Laws/node5.html#law2 + i = (board['num'] - 1) % 16 + # Map from duplicate board index range 1..16 to vulnerability. + board['vuln'] = Vulnerable[(i%4 + i/4)%4] + + elif isinstance(result, RubberResult): + # Determine vulnerability of board from result of previous board. + above, below = result.score + if below >= 100: # Game contract successful. + pair = __directionToVuln[result.contract.declarer] + # Vulnerability transitions. + if board['vuln'] == Vulnerable.None: + board['vuln'] = pair + elif board['vuln'] in (pair, Vulnerable.All): # Rubber won. + board['vuln'] = Vulnerable.None + else: # Pair not vulnerable, other pair are vulnerable. + board['vuln'] = Vulnerable.All + + return board + Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-26 19:03:38 UTC (rev 496) @@ -63,12 +63,14 @@ self.auction = None self.play = None - self.boardQueue = [] # Boards for successive games. + self.boardQueue = [] # Boards for successive rounds. + self.results = [] # Results of previous rounds. self.visibleHands = {} # A subset of deal, containing revealed hands. self.players = {} # One-to-one mapping from BridgePlayer to Direction. contract = property(lambda self: self.auction and self.auction.contract or None) trumpSuit = property(lambda self: self.play and self.play.trumpSuit or None) + result = property(lambda self: not self.inProgress() and self.results[-1]) # Implementation of ICardGame. @@ -81,10 +83,12 @@ if board: # Use specified board. self.board = board elif self.board: # Advance to next deal. - self.board.nextDeal(result=self) # TODO: proper GameResult object. + tricksMade, _ = self.play.wonTrickCount() + result = DuplicateResult(self.board, self.contract, tricksMade) + self.board = self.board.next(result) else: # Create a board. - self.board = Board() - self.board.nextDeal() + self.board = Board.first() + self.auction = Auction(self.board['dealer']) # Start auction. self.play = None self.visibleHands.clear() @@ -242,10 +246,14 @@ trumpSuit = self.__trumpMap[self.contract.bid.strain] self.play = TrickPlay(declarer, trumpSuit) + # If bidding is passed out, game is complete. + if not self.inProgress() and self.board['deal']: + self.results.append(DuplicateResult(self.board, contract=None)) + self.notify('makeCall', call=call, position=position) - # If bidding is passed out, reveal all hands. if not self.inProgress() and self.board['deal']: + # Reveal all unrevealed hands. for position in Direction: hand = self.board['deal'].get(position) if hand and position not in self.visibleHands: @@ -302,7 +310,6 @@ raise GameError, "Card cannot be played from hand" self.play.playCard(card, playfrom) - self.notify('playCard', card=card, position=position) # Dummy's hand is revealed when the first card of first trick is played. if len(self.play[0]) == 1: @@ -310,12 +317,21 @@ if dummyhand: # Reveal hand only if known. self.revealHand(dummyhand, self.play.dummy) - # If play is complete, reveal all hands. + # If play is complete, game is complete. if not self.inProgress() and self.board['deal']: + tricksMade, _ = self.play.wonTrickCount() + result = DuplicateResult(self.board, self.contract, tricksMade) + self.results.append(result) + + self.notify('playCard', card=card, position=position) + + if not self.inProgress() and self.board['deal']: + # Reveal all unrevealed hands. for position in Direction: hand = self.board['deal'].get(position) if hand and position not in self.visibleHands: self.revealHand(hand, position) + def revealHand(self, hand, position): @@ -363,24 +379,8 @@ raise GameError, "No game in progress" - def getScore(self): - """Returns the integer score value for declarer/dummy if: - - auction has been passed out, with no bids made. - - trick play is complete. - """ - if self.inProgress() or self.auction is None: - raise GameError, "Game not complete" - if self.auction.isPassedOut(): - return 0 # A passed out deal does not score. - tricksMade, _ = self.play.wonTrickCount() - result = DuplicateResult(self.contract, self.board['vuln'], tricksMade) - return result.score - - - - class BridgePlayer(pb.Referenceable): """Actor representing a player's view of a BridgeGame object.""" Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-07-26 19:03:38 UTC (rev 496) @@ -30,14 +30,17 @@ Vulnerable.All: tuple(Direction)} - def __init__(self, contract, vuln, tricksMade=None): + def __init__(self, board, contract, tricksMade=None): """ + @type board: Board @type contract: Contract - @type vuln: Vulnerable @type tricksMade: int or None """ + self.board = board self.contract = contract self.tricksMade = tricksMade + + vuln = self.board.get('vuln', Vulnerable.None) self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] self.score = self._getScore() Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-26 19:03:38 UTC (rev 496) @@ -103,7 +103,7 @@ def add_score(self, game): declarerWon, defenceWon = game.play.wonTrickCount() - score = game.getScore() + score = game.result.score textContract = render_contract(game.contract) textMade = str(declarerWon) @@ -339,13 +339,13 @@ if self.table.game.contract: self.scoreview.add_score(self.table.game) - declarerWon, defenceWon = self.table.game.play.wonTrickCount() - required = self.table.game.contract.bid.level.index + 7 - offset = declarerWon - required - score = self.table.game.getScore() + tricksMade = self.table.game.result.tricksMade + tricksRequired = self.table.game.contract.bid.level.index + 7 + offset = tricksMade - tricksRequired + score = self.table.game.result.score fields = {'contract': render_contract(self.table.game.contract), - 'offset': abs(offset) } + 'offset': abs(offset)} if offset > 0: if offset == 1: resultText = _('Contract %(contract)s made by 1 trick.') % fields This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-25 21:04:31
|
Revision: 495 http://pybridge.svn.sourceforge.net/pybridge/?rev=495&view=rev Author: umgangee Date: 2007-07-25 14:04:25 -0700 (Wed, 25 Jul 2007) Log Message: ----------- Split scoring down into component parts, to provide support for both duplicate and rubber scoring schemes. Fix a couple of bugs in scoring code as well! Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/game.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/result.py Removed Paths: ------------- trunk/pybridge/pybridge/games/bridge/scoring.py Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-24 19:30:16 UTC (rev 494) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-25 21:04:25 UTC (rev 495) @@ -13,7 +13,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not write to the Free Software -# Foundation Inc. 51 Franklin Street Fifth Floor Boston MA 02110-1301 USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from twisted.spread import pb @@ -26,7 +26,7 @@ from auction import Auction from board import Board from play import Trick, TrickPlay -from scoring import scoreDuplicate +from result import DuplicateResult, RubberResult from call import Bid, Pass, Double, Redouble from card import Card @@ -47,13 +47,13 @@ implements(ICardGame, ISubject) - # Valid positions. + # Valid positions (for Table). positions = Direction - # Mapping from Strain symbols (in bidding) to Suit symbols (in play). - trumpMap = {Strain.Club: Suit.Club, Strain.Diamond: Suit.Diamond, - Strain.Heart: Suit.Heart, Strain.Spade: Suit.Spade, - Strain.NoTrump: None} + # Mapping from Strain symbols (in auction) to Suit symbols (in play). + __trumpMap = {Strain.Club: Suit.Club, Strain.Diamond: Suit.Diamond, + Strain.Heart: Suit.Heart, Strain.Spade: Suit.Spade, + Strain.NoTrump: None} def __init__(self): @@ -239,7 +239,7 @@ if self.auction.isComplete() and not self.auction.isPassedOut(): declarer = self.auction.contract.declarer - trumpSuit = self.trumpMap[self.contract.bid.strain] + trumpSuit = self.__trumpMap[self.contract.bid.strain] self.play = TrickPlay(declarer, trumpSuit) self.notify('makeCall', call=call, position=position) @@ -374,23 +374,13 @@ if self.auction.isPassedOut(): return 0 # A passed out deal does not score. - declarer = self.contract.declarer - dummy = Direction[(declarer.index + 2) % 4] + tricksMade, _ = self.play.wonTrickCount() + result = DuplicateResult(self.contract, self.board['vuln'], tricksMade) + return result.score - if declarer in (Direction.North, Direction.South): - vulnerable = (self.board['vuln'] in (Vulnerable.NorthSouth, Vulnerable.All)) - else: # East or West - vulnerable = (self.board['vuln'] in (Vulnerable.EastWest, Vulnerable.All)) - declarerWon, defenceWon = self.play.wonTrickCount() - result = {'contract': self.contract, 'tricksMade': declarerWon, - 'vulnerable': vulnerable} - return scoreDuplicate(result) - - - class BridgePlayer(pb.Referenceable): """Actor representing a player's view of a BridgeGame object.""" Copied: trunk/pybridge/pybridge/games/bridge/result.py (from rev 492, trunk/pybridge/pybridge/games/bridge/scoring.py) =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-07-25 21:04:25 UTC (rev 495) @@ -0,0 +1,216 @@ +# 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 symbols import Direction, Strain, Vulnerable + + +class GameResult(object): + """Represents the result of a completed round of bridge.""" + + _getScore = NotImplemented # Expected to be implemented by subclasses. + + __vulnMapping = {Vulnerable.None: (), + Vulnerable.NorthSouth: (Direction.North, Direction.South), + Vulnerable.EastWest: (Direction.East, Direction.West), + Vulnerable.All: tuple(Direction)} + + + def __init__(self, contract, vuln, tricksMade=None): + """ + @type contract: Contract + @type vuln: Vulnerable + @type tricksMade: int or None + """ + self.contract = contract + self.tricksMade = tricksMade + self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] + + self.score = self._getScore() + + + def _getScoreComponents(self): + """Compute the component values which contribute to the score. + Note that particular scoring schemes may ignore some of the components. + + Scoring values: http://en.wikipedia.org/wiki/Bridge_scoring + + @return: a dict of component values. + @rtype: dict + """ + components = {} + + isDoubled = bool(self.contract.doubleBy) + isRedoubled = bool(self.contract.redoubleBy) + isVulnerable = self.isVulnerable + contractLevel = self.contract.bid.level.index + 1 + tricksMade = self.tricksMade + tricksRequired = contractLevel + 6 + trumpSuit = self.contract.bid.strain + + if tricksMade >= tricksRequired: # Contract successful. + + #### Contract tricks (bid and made) #### + if trumpSuit in (Strain.Club, Strain.Diamond): + # Clubs and Diamonds score 20 for each odd trick. + components['odd'] = contractLevel * 20 + else: # Hearts, Spades and NT score 30 for each odd trick. + components['odd'] = contractLevel * 30 + if trumpSuit == Strain.NoTrump: + components['odd'] += 10 # For NT, add a 10 point bonus. + if isRedoubled: + components['odd'] *= 4 # Double the doubled score. + elif isDoubled: + components['odd'] *= 2 # Double score. + + + #### Overtricks #### + overTricks = tricksMade - tricksRequired + + if isRedoubled: + # 400 for each overtrick if vulnerable, 200 if not. + if isVulnerable: + components['over'] = overTricks * 400 + else: + components['over'] = overTricks * 200 + + elif isDoubled: + # 200 for each overtrick if vulnerable, 100 if not. + if isVulnerable: + components['over'] = overTricks * 200 + else: + components['over'] = overTricks * 100 + + else: # Undoubled contract. + if trumpSuit in (Strain.Club, Strain.Diamond): + # Clubs and Diamonds score 20 for each overtrick. + components['over'] = overTricks * 20 + else: + # Hearts, Spades and NT score 30 for each overtrick. + components['over'] = overTricks * 30 + + + #### Premium bonuses #### + + if tricksRequired == 13: + # 1500 for grand slam if vulnerable, 1000 if not. + if isVulnerable: + components['slambonus'] = 1500 + else: + components['slambonus'] = 1000 + + elif tricksRequired == 12: + # 750 for small slam if vulnerable, 500 if not. + if isVulnerable: + components['slambonus'] = 750 + else: + components['slambonus'] = 500 + + elif components['odd'] >= 100: # Game contract (non-slam). + # 500 for game if vulnerable, 300 if not. + if isVulnerable: + components['gamebonus'] = 500 + else: + components['gamebonus'] = 300 + + else: # Non-game contract. + components['partscore'] = 50 + + + #### Insult bonus #### + if isRedoubled: + components['insultbonus'] = 100 + elif isDoubled: + components['insultbonus'] = 50 + + + else: # Contract not successful. + + underTricks = tricksRequired - tricksMade + + if isRedoubled: + if isVulnerable: + # -400 for first, then -600 each. + components['under'] = -400 + (underTricks - 1) * -600 + else: + # -200 for first, -400 for second and third, then -600 each. + components['under'] = -200 + (underTricks - 1) * -400 + if underTricks > 3: + components['under'] += (underTricks - 3) * -200 + + elif isDoubled: + if isVulnerable: + # -200 for first, then -300 each. + components['under'] = -200 + (underTricks - 1) * -300 + else: + # -100 for first, -200 for second and third, then -300 each. + components['under'] = -100 + (underTricks - 1) * -200 + if underTricks > 3: + components['under'] += (underTricks - 3) * -100 + else: + if isVulnerable: + # -100 each. + components['under'] = underTricks * -100 + else: + # -50 each. + components['under'] = underTricks * -50 + + return components + + + + +class DuplicateResult(GameResult): + """Represents the result of a completed round of duplicate bridge.""" + + + def _getScore(self): + """Duplicate bridge scoring scheme. + + @return: score value: positive for declarer, negative for defenders. + """ + score = 0 + if self.contract and self.tricksMade: + for key, value in self._getScoreComponents().items(): + if key in ('odd', 'over', 'under', 'slambonus', 'gamebonus', + 'partscore', 'insultbonus'): + score += value + return score + + + + +class RubberResult(GameResult): + """Represents the result of a completed round of rubber bridge.""" + + + def _getScore(self): + """Rubber bridge scoring scheme. + + @return: 2-tuple of numeric scores (above the line, below the line): + positive for declarer, negative for defenders. + """ + above, below = 0, 0 + if self.contract and self.tricksMade: + for key, value in self._getScoreComponents().items(): + # Note: gamebonus/partscore are not assigned in rubber bridge. + if key in ('over', 'under', 'slambonus', 'insultbonus'): + above += value + elif key == 'odd': + below += value + return above, below + Deleted: trunk/pybridge/pybridge/games/bridge/scoring.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-24 19:30:16 UTC (rev 494) +++ trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-25 21:04:25 UTC (rev 495) @@ -1,145 +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. - - -from symbols import Strain - - -# There are undoubtedly many minor variations of the score values. -# In the future, score values may be stored in separate XML format files. - - -def scoreDuplicate(result): - """Scoring algorithm for duplicate bridge. - - This code includes the scoring values from: - http://www.ebu.co.uk/lawsandethics/the_laws/chapter8.asp - """ - score = 0 - - isDoubled = bool(result['contract'].doubleBy) - isRedoubled = bool(result['contract'].redoubleBy) - isVulnerable = result['vulnerable'] - contractLevel = result['contract'].bid.level.index + 1 - tricksMade = result['tricksMade'] - tricksRequired = result['contract'].bid.level.index + 7 - trumpSuit = result['contract'].bid.strain - - if tricksMade >= tricksRequired: - # Contract fulfilled. - - # Calculate scores for tricks bid and made. - if trumpSuit in (Strain.Club, Strain.Diamond): - # Clubs and Diamonds score 20 for each odd trick. - score += contractLevel * 20 - else: - # Hearts, Spades and NT score 30 for each odd trick. - score += contractLevel * 30 - if trumpSuit is Strain.NoTrump: - score += 10 # For NT, add a 10 point bonus. - - # Calculate scores for doubles. - if isDoubled: - score *= 2 # Multiply score by 2 for each isDoubled odd trick. - elif isRedoubled: - score *= 4 # Multiply score by 4 for each isRedoubled odd trick. - - # Calculate premium scores. - if score >= 100: - if isVulnerable: - score += 500 # Game, vulnerable. - else: - # Game, not vulnerable. - score += 300 # Game, not vulnerable. - if tricksRequired == 13: - if isVulnerable: - score += 1500 # Grand slam, vulnerable. - else: - score += 1000 # Grand slam, not vulnerable. - elif tricksRequired == 12: - if isVulnerable: - score += 750 # Small slam, vulnerable. - else: - score += 500 # Small slam, not vulnerable. - - else: - score += 50 # Any part score. - - # Calculate "for the insult" bonuses. - if isDoubled: - score += 50 - elif isRedoubled: - score += 100 - - # Calculate scores for overtricks. - overTricks = tricksMade - tricksRequired - if isDoubled: - if isVulnerable: - # Score 200 for each doubled and vulnerable overtrick. - score += overTricks * 200 - else: - # Score 100 for each doubled and not vulnerable overtrick. - score += overTricks * 100 - elif isRedoubled: - if isVulnerable: - # Score 400 for each redoubled and vulnerable overtrick. - score += overTricks * 400 - else: - score += overTricks * 200 - else: - if trumpSuit in (Strain.Club, Strain.Diamond): - # Clubs and Diamonds score 20 for each undoubled overtrick. - score += overTricks * 20 - else: - # Hearts, Spades and NT score 30 for each undoubled overtrick. - score += overTricks * 30 - - else: - # Contract not fulfilled. - - underTricks = tricksRequired - tricksMade - if isDoubled: - if isVulnerable: - # Score 200 for the first doubled and vulnerable undertrick. - # Score 300 for all other undertricks. - score -= 200 + (underTricks - 1) * 300 - else: - # Score 100 for the first doubled and non-vulnerable undertrick. - # Score 200 for all other undertricks. - # Score 100 extra for third and greater undertricks. - score -= 100 + (underTricks - 1) * 200 - if underTricks > 3: - score -= (underTricks - 3) * 100 - elif isRedoubled: - if isVulnerable: - score -= 400 + (underTricks - 1) * 600 - else: - score -= 200 + (underTricks - 1) * 400 - if underTricks > 3: - score -= (underTricks - 3) * 200 - else: - if isVulnerable: - score -= 100 + (underTricks - 1) * 100 - else: - score -= 50 + (underTricks - 1) * 50 - - return score - - -def scoreRubber(result): - pass # TODO: implement. - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-24 19:30:20
|
Revision: 494 http://svn.sourceforge.net/pybridge/?rev=494&view=rev Author: umgangee Date: 2007-07-24 12:30:16 -0700 (Tue, 24 Jul 2007) Log Message: ----------- Improvements to the logic and appearance of tableview and userview widgets, plus a fix for https://bugs.launchpad.net/pybridge/+bug/127974 Modified Paths: -------------- trunk/pybridge/glade/pybridge.glade trunk/pybridge/pybridge/ui/window_main.py Modified: trunk/pybridge/glade/pybridge.glade =================================================================== --- trunk/pybridge/glade/pybridge.glade 2007-07-24 17:22:23 UTC (rev 493) +++ trunk/pybridge/glade/pybridge.glade 2007-07-24 19:30:16 UTC (rev 494) @@ -5,7 +5,7 @@ <widget class="GtkWindow" id="window_main"> <property name="visible">True</property> <property name="title" translatable="yes">PyBridge</property> - <signal name="delete_event" handler="on_window_main_delete_event"/> + <signal name="delete_event" handler="on_delete_event"/> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property> @@ -189,7 +189,7 @@ <property name="border_width">8</property> <property name="spacing">8</property> <child> - <widget class="GtkScrolledWindow" id="scrolledwindow5"> + <widget class="GtkScrolledWindow" id="scrolled_tableview"> <property name="width_request">320</property> <property name="height_request">240</property> <property name="visible">True</property> @@ -208,103 +208,33 @@ </widget> </child> <child> - <widget class="GtkVBox" id="vbox16"> - <property name="width_request">160</property> + <widget class="GtkToolbar" id="toolbar_tables"> <property name="visible">True</property> - <property name="spacing">4</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="orientation">GTK_ORIENTATION_VERTICAL</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> <child> - <widget class="GtkButton" id="newtable"> + <widget class="GtkToolButton" id="newtable"> <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">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="label">New Table...</property> + <property name="stock_id">gtk-new</property> <signal name="clicked" handler="on_newtable_clicked"/> - <child> - <widget class="GtkAlignment" id="alignment19"> - <property name="visible">True</property> - <property name="xscale">0</property> - <property name="yscale">0</property> - <child> - <widget class="GtkHBox" id="hbox24"> - <property name="visible">True</property> - <property name="spacing">2</property> - <child> - <widget class="GtkImage" id="image322"> - <property name="visible">True</property> - <property name="stock">gtk-new</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label55"> - <property name="visible">True</property> - <property name="label" translatable="yes">New 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> <packing> <property name="expand">False</property> - <property name="fill">False</property> </packing> </child> <child> - <widget class="GtkButton" id="jointable"> + <widget class="GtkToolButton" id="jointable"> <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">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="label">Join Table</property> + <property name="stock_id">gtk-jump-to</property> <signal name="clicked" handler="on_jointable_clicked"/> - <child> - <widget class="GtkAlignment" id="alignment20"> - <property name="visible">True</property> - <property name="xscale">0</property> - <property name="yscale">0</property> - <child> - <widget class="GtkHBox" id="hbox25"> - <property name="visible">True</property> - <property name="spacing">2</property> - <child> - <widget class="GtkImage" id="image323"> - <property name="visible">True</property> - <property name="stock">gtk-jump-to</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label56"> - <property name="visible">True</property> - <property name="label" translatable="yes">Join This 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> <packing> <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> </packing> </child> </widget> @@ -321,7 +251,7 @@ <child> <widget class="GtkLabel" id="label_tabletab"> <property name="visible">True</property> - <property name="label" translatable="yes">Tables</property> + <property name="label" translatable="yes">Open Tables</property> </widget> <packing> <property name="type">tab</property> @@ -335,7 +265,7 @@ <property name="border_width">8</property> <property name="spacing">8</property> <child> - <widget class="GtkScrolledWindow" id="scrolledwindow9"> + <widget class="GtkScrolledWindow" id="scrolled_userview"> <property name="width_request">320</property> <property name="height_request">240</property> <property name="visible">True</property> @@ -354,20 +284,21 @@ </widget> </child> <child> - <widget class="GtkVButtonBox" id="buttonbox_users"> + <widget class="GtkToolbar" id="toolbar_users"> <property name="visible">True</property> - <property name="sensitive">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="orientation">GTK_ORIENTATION_VERTICAL</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> <child> - <widget class="GtkButton" id="button_userinfo"> + <widget class="GtkToolButton" id="userinfo"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">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="label" translatable="yes">User Information</property> + <property name="stock_id">gtk-info</property> <signal name="clicked" handler="on_userinfo_clicked"/> </widget> + <packing> + <property name="expand">False</property> + </packing> </child> </widget> <packing> @@ -384,7 +315,7 @@ <child> <widget class="GtkLabel" id="label_usertab"> <property name="visible">True</property> - <property name="label" translatable="yes">Users</property> + <property name="label" translatable="yes">Users Online</property> </widget> <packing> <property name="type">tab</property> @@ -435,23 +366,30 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkEntry" id="entry_portnum"> - <property name="width_request">80</property> + <widget class="GtkLabel" id="label_portnum"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">5</property> - <property name="invisible_char">*</property> - <signal name="changed" handler="on_field_changed"/> + <property name="xalign">0</property> + <property name="label" translatable="yes">Port:</property> </widget> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> <child> + <widget class="GtkLabel" id="label_hostname"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Host Name:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> <widget class="GtkEntry" id="entry_hostname"> <property name="width_request">140</property> <property name="visible">True</property> @@ -466,26 +404,19 @@ </packing> </child> <child> - <widget class="GtkLabel" id="label_hostname"> + <widget class="GtkEntry" id="entry_portnum"> + <property name="width_request">80</property> <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Host Name:</property> + <property name="can_focus">True</property> + <property name="max_length">5</property> + <property name="invisible_char">*</property> + <signal name="changed" handler="on_field_changed"/> </widget> <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_portnum"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Port:</property> - </widget> - <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> @@ -528,20 +459,26 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkEntry" id="entry_password"> - <property name="width_request">120</property> + <widget class="GtkLabel" id="label_username"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">40</property> - <property name="visibility">False</property> - <property name="invisible_char">*</property> - <signal name="changed" handler="on_field_changed"/> + <property name="xalign">0</property> + <property name="label" translatable="yes">User Name:</property> </widget> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_password"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Password:</property> + </widget> + <packing> <property name="top_attach">1</property> <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> @@ -561,29 +498,23 @@ </packing> </child> <child> - <widget class="GtkLabel" id="label_password"> + <widget class="GtkEntry" id="entry_password"> + <property name="width_request">120</property> <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Password:</property> + <property name="can_focus">True</property> + <property name="max_length">40</property> + <property name="visibility">False</property> + <property name="invisible_char">*</property> + <signal name="changed" handler="on_field_changed"/> </widget> <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> - <child> - <widget class="GtkLabel" id="label_username"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">User Name:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> </widget> </child> <child> @@ -708,53 +639,53 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkLabel" id="label_tablename"> + <widget class="GtkLabel" id="label_gamelist"> <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="xalign">0</property> - <property name="label" translatable="yes">Table Name:</property> + <property name="label" translatable="yes">Game at Table:</property> </widget> <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkEntry" id="tablename"> - <property name="width_request">140</property> + <widget class="GtkComboBox" id="gamelist"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">*</property> - <signal name="changed" handler="on_tablename_changed"/> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <signal name="changed" handler="on_gamelist_changed"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="y_options">GTK_FILL</property> </packing> </child> <child> - <widget class="GtkComboBox" id="gamelist"> + <widget class="GtkEntry" id="tablename"> + <property name="width_request">140</property> <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> - <signal name="changed" handler="on_gamelist_changed"/> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + <signal name="changed" handler="on_tablename_changed"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> <property name="y_options">GTK_FILL</property> </packing> </child> <child> - <widget class="GtkLabel" id="label_gamelist"> + <widget class="GtkLabel" id="label_tablename"> <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="xalign">0</property> - <property name="label" translatable="yes">Game at Table:</property> + <property name="label" translatable="yes">Table Name:</property> </widget> <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> <property name="y_options"></property> </packing> </child> @@ -835,6 +766,72 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> + <widget class="GtkFileChooserButton" id="background"> + <property name="width_request">160</property> + <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> + <signal name="selection_changed" handler="on_background_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkComboBox" id="cardstyle"> + <property name="width_request">160</property> + <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> + <signal name="changed" handler="on_cardstyle_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_background"> + <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="xalign">0</property> + <property name="label" translatable="yes">Background:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_cardstyle"> + <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="xalign">0</property> + <property name="label" translatable="yes">Card Style:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_suitcolours"> + <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="xalign">0</property> + <property name="label" translatable="yes">Suit Colours:</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> <widget class="GtkHBox" id="hbox2"> <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> @@ -931,72 +928,6 @@ <property name="bottom_attach">3</property> </packing> </child> - <child> - <widget class="GtkLabel" id="label_suitcolours"> - <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="xalign">0</property> - <property name="label" translatable="yes">Suit Colours:</property> - </widget> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_cardstyle"> - <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="xalign">0</property> - <property name="label" translatable="yes">Card Style:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_background"> - <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="xalign">0</property> - <property name="label" translatable="yes">Background:</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkComboBox" id="cardstyle"> - <property name="width_request">160</property> - <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> - <signal name="changed" handler="on_cardstyle_changed"/> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - </packing> - </child> - <child> - <widget class="GtkFileChooserButton" id="background"> - <property name="width_request">160</property> - <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> - <signal name="selection_changed" handler="on_background_changed"/> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> </widget> <packing> <property name="expand">False</property> Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-07-24 17:22:23 UTC (rev 493) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-07-24 19:30:16 UTC (rev 494) @@ -28,6 +28,7 @@ from pybridge.network.client import client from eventhandler import SimpleEventHandler +from excepthook import exceptdialog from manager import wm from dialog_connection import DialogConnection @@ -39,33 +40,29 @@ # TODO: import all Window*Table classes automatically. from pybridge.games.bridge.ui.window_bridgetable import WindowBridgeTable -TABLE_ICON = env.find_pixmap("table.png") -USER_ICON = env.find_pixmap("user.png") +TABLE_ICON = env.find_pixmap('table.png') +USER_ICON = env.find_pixmap('user.png') class WindowMain(GladeWrapper): glade_name = 'window_main' - tableview_icon = gtk.gdk.pixbuf_new_from_file_at_size(TABLE_ICON, 48, 48) - userview_icon = gtk.gdk.pixbuf_new_from_file_at_size(USER_ICON, 48, 48) + table_icon = gtk.gdk.pixbuf_new_from_file_at_size(TABLE_ICON, 48, 48) + user_icon = gtk.gdk.pixbuf_new_from_file_at_size(USER_ICON, 48, 48) def setUp(self): - # Set up table model and icon view. - self.tableview.set_text_column(0) - self.tableview.set_pixbuf_column(1) - model = gtk.ListStore(str, gtk.gdk.Pixbuf) - model.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.tableview.set_model(model) + # Track iters in ListStore objects, for O(1) lookups. + self.tableview_iters, self.userview_iters = {}, {} + # Set up tableview and userview. + for view in self.tableview, self.userview: + view.set_text_column(0) + view.set_pixbuf_column(1) + model = gtk.ListStore(str, gtk.gdk.Pixbuf) + model.set_sort_column_id(0, gtk.SORT_ASCENDING) + view.set_model(model) - # Set up user model and icon view. - self.userview.set_text_column(0) - self.userview.set_pixbuf_column(1) - model = gtk.ListStore(str, gtk.gdk.Pixbuf) - model.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.userview.set_model(model) - # Attach event handler to listen for events. self.eventHandler = SimpleEventHandler(self) client.attach(self.eventHandler) @@ -75,25 +72,23 @@ def tearDown(self): - # TODO: detach event handler from all attached subjects. - - # Close all windows. - for window in wm.values(): - wm.close(window) client.disconnect() + client.detach(self.eventHandler) + # Terminate. + reactor.stop() + gtk.main_quit() def quit(self): """Shut down gracefully.""" - client.detach(self.eventHandler) - wm.close(self) - reactor.stop() - gtk.main_quit() + # TODO: if playing a game, ensure that user really wants to quit. + wm.close(self) # Triggers tearDown. def errback(self, failure): - print "Error: %s" % failure.getErrorMessage() - print failure.getTraceback() + message = "Network error: %s\n\n%s" % (failure.getErrorMessage(), + failure.getTraceback()) + exceptdialog(message) # Event handlers. @@ -104,7 +99,10 @@ 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) + self.jointable.set_property('sensitive', False) + self.userinfo.set_property('sensitive', False) def event_loggedOut(self): @@ -116,10 +114,9 @@ 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.get_model().clear() - self.userview.get_model().clear() + self.tableview.get_model().clear(); self.tableview_iters.clear() + self.userview.get_model().clear(); self.userview_iters.clear() def event_connectionLost(self, host, port): @@ -143,54 +140,48 @@ def event_gotRoster(self, name, roster): - lookup = {'tables' : (self.tableview.get_model(), self.tableview_icon), - 'users' : (self.userview.get_model(), self.userview_icon)} - + lookup = {'tables' : (self.tableview.get_model(), self.table_icon, self.tableview_iters), + 'users' : (self.userview.get_model(), self.user_icon, self.userview_iters)} try: - model, icon = lookup[name] + model, icon, view_iters = lookup[name] for id, info in roster.items(): - model.append([id, icon]) + iter = model.append([id, icon]) + view_iters[id] = iter roster.attach(self.eventHandler) except KeyError: pass # Ignore an unrecognised roster. def event_openTable(self, tableid, info): - """Adds a table to the table listing.""" # Only display table if it supported by client. if info['gamename'] in SUPPORTED_GAMES: - self.tableview.get_model().append([tableid, self.tableview_icon]) + model = self.tableview.get_model() + iter = model.append([tableid, self.table_icon]) + self.tableview_iters[tableid] = iter def event_closeTable(self, tableid): - """Removes a table from the table listing.""" - - def func(model, path, iter, user_data): - if model.get_value(iter, 0) in user_data: - model.remove(iter) - return True + iter = self.tableview_iters.get(tableid) + if iter: + model = self.tableview.get_model() + model.remove(iter) + del self.tableview_iters[tableid] - model = self.tableview.get_model() - model.foreach(func, tableid) - def event_userLogin(self, username, info): - """Adds a user to the people listing.""" - self.userview.get_model().append([username, self.userview_icon]) + model = self.userview.get_model() + iter = model.append([username, self.user_icon]) + self.userview_iters[username] = iter def event_userLogout(self, username): - """Removes a user from the people listing.""" - - def func(model, path, iter, user_data): - if model.get_value(iter, 0) in user_data: - model.remove(iter) - return True + iter = self.userview_iters.get(username) + if iter: + model = self.userview.get_model() + model.remove(iter) + del self.userview_iters[username] - model = self.userview.get_model() - model.foreach(func, username) - # Signal handlers. @@ -199,16 +190,16 @@ def on_tableview_item_activated(self, iconview, path, *args): + model = self.tableview.get_model() + iter = model.get_iter(path) + tableid = model.get_value(iter, 0) def joinedTable(table): # TODO: select correct table window class. window = wm.open(WindowBridgeTable, id=tableid) window.setTable(table) - model = self.tableview.get_model() - iter = model.get_iter(path) - tableid = model.get_value(iter, 0) - if tableid not in client.tables: + if tableid not in client.tables: # Already joined table? d = client.joinTable(tableid) d.addCallbacks(joinedTable, self.errback) self.jointable.set_property('sensitive', False) @@ -236,27 +227,20 @@ winid = (DialogUserInfo, username) def gotUserInfo(info): - w = wm.open(DialogUserInfo, winid) + w = wm.open(DialogUserInfo, id=winid) w.setUserInfo(username, info) if not wm.get(winid): - # TODO: if user info in cache, do not request again from server. d = client.getUserInformation(username) d.addCallback(gotUserInfo) def on_userview_selection_changed(self, iconview, *args): cursor = self.userview.get_cursor() - if cursor: # Ensure cursor contains a path, not None. - self.buttonbox_users.set_property('sensitive', True) - else: - self.buttonbox_users.set_property('sensitive', False) + # If cursor contains a path, enable User Info button. + self.userinfo.set_property('sensitive', bool(cursor)) - def on_window_main_delete_event(self, widget, *args): - self.quit() - - def on_newtable_clicked(self, widget, *args): if not wm.get(DialogNewtable): wm.open(DialogNewtable) @@ -335,3 +319,8 @@ about.connect('response', dialog_response_cb) about.show() + + def on_delete_event(self, widget, *args): + self.quit() + 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-07-24 17:22:20
|
Revision: 493 http://svn.sourceforge.net/pybridge/?rev=493&view=rev Author: umgangee Date: 2007-07-24 10:22:23 -0700 (Tue, 24 Jul 2007) Log Message: ----------- Open table window when it is created. 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-07-23 20:07:54 UTC (rev 492) +++ trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-07-24 17:22:23 UTC (rev 493) @@ -23,7 +23,10 @@ from pybridge.games import SUPPORTED_GAMES from manager import wm +# TODO: import all Window*Table classes automatically. +from pybridge.games.bridge.ui.window_bridgetable import WindowBridgeTable + class DialogNewtable(GladeWrapper): glade_name = 'dialog_newtable' @@ -45,6 +48,9 @@ def createSuccess(self, table): wm.close(self) + # TODO: select correct table window class. + window = wm.open(WindowBridgeTable, id=table.id) + window.setTable(table) def createFailure(self, reason): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-23 20:09:00
|
Revision: 492 http://svn.sourceforge.net/pybridge/?rev=492&view=rev Author: umgangee Date: 2007-07-23 13:07:54 -0700 (Mon, 23 Jul 2007) Log Message: ----------- Open table windows in response to successful joinTable() requests, instead of waiting for event_tableJoined(), so internal errors are reported to sys.excepthook instead of the errback attached to joinTable() deferred by caller. Modified Paths: -------------- trunk/pybridge/pybridge/ui/window_gametable.py trunk/pybridge/pybridge/ui/window_main.py Modified: trunk/pybridge/pybridge/ui/window_gametable.py =================================================================== --- trunk/pybridge/pybridge/ui/window_gametable.py 2007-07-23 11:55:00 UTC (rev 491) +++ trunk/pybridge/pybridge/ui/window_gametable.py 2007-07-23 20:07:54 UTC (rev 492) @@ -50,7 +50,7 @@ def setUp(self): - self.children = WindowManager() + self.children = WindowManager() # Private to this window. self.eventHandler = SimpleEventHandler(self) self.player = None @@ -192,14 +192,14 @@ dialog.destroy() if response_id == gtk.RESPONSE_OK: d = client.leaveTable(self.table.id) - #d.addErrback(self.errback) + d.addCallbacks(lambda _: wm.close(self), self.errback) dialog.connect('response', dialog_response_cb) dialog.show() else: d = client.leaveTable(self.table.id) - #d.addErrback(self.errback) + d.addCallbacks(lambda _: wm.close(self), self.errback) def on_fullscreen_clicked(self, widget, *args): Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-07-23 11:55:00 UTC (rev 491) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-07-23 20:07:54 UTC (rev 492) @@ -28,12 +28,13 @@ from pybridge.network.client import client from eventhandler import SimpleEventHandler -from manager import WindowManager, wm +from manager import wm from dialog_connection import DialogConnection from dialog_newtable import DialogNewtable from dialog_preferences import DialogPreferences from dialog_userinfo import DialogUserInfo +from window_gametable import WindowGameTable # TODO: import all Window*Table classes automatically. from pybridge.games.bridge.ui.window_bridgetable import WindowBridgeTable @@ -51,9 +52,6 @@ def setUp(self): - # Use a private WindowManager for table window instances. - self.tables = WindowManager() - # Set up table model and icon view. self.tableview.set_text_column(0) self.tableview.set_pixbuf_column(1) @@ -110,8 +108,9 @@ def event_loggedOut(self): - for table in self.tables.values(): - self.tables.close(table) + for window in wm.values(): + if isinstance(window, WindowGameTable): + wm.close(window) self.notebook.set_property('sensitive', False) self.menu_connect.set_property('visible', True) @@ -119,7 +118,6 @@ self.menu_newtable.set_property('sensitive', False) #self.newtable.set_property('sensitive', False) - print self.tableview.get_model() self.tableview.get_model().clear() self.userview.get_model().clear() @@ -157,15 +155,6 @@ pass # Ignore an unrecognised roster. - def event_joinTable(self, tableid, table): - window = self.tables.open(WindowBridgeTable, id=tableid) - window.setTable(table) - - - def event_leaveTable(self, tableid): - self.tables.close(self.tables[tableid]) # Close window. - - def event_openTable(self, tableid, info): """Adds a table to the table listing.""" # Only display table if it supported by client. @@ -210,12 +199,18 @@ def on_tableview_item_activated(self, iconview, path, *args): + + def joinedTable(table): + # TODO: select correct table window class. + window = wm.open(WindowBridgeTable, id=tableid) + window.setTable(table) + model = self.tableview.get_model() iter = model.get_iter(path) tableid = model.get_value(iter, 0) if tableid not in client.tables: d = client.joinTable(tableid) - d.addErrback(self.errback) + d.addCallbacks(joinedTable, self.errback) self.jointable.set_property('sensitive', False) @@ -285,7 +280,8 @@ def on_disconnect_activate(self, widget, *args): do_disconnect = True - if len([True for table in self.tables.values() if table.player]) > 0: + # TODO: avoid introspection of table windows. + if len([True for w in wm.values() if isinstance(w, WindowGameTable) and w.player]) > 0: dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_QUESTION) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-23 11:54:57
|
Revision: 491 http://svn.sourceforge.net/pybridge/?rev=491&view=rev Author: umgangee Date: 2007-07-23 04:55:00 -0700 (Mon, 23 Jul 2007) Log Message: ----------- Separate exception dialog from excepthook, so network errors etc. may be displayed. Modified Paths: -------------- trunk/pybridge/pybridge/ui/excepthook.py Modified: trunk/pybridge/pybridge/ui/excepthook.py =================================================================== --- trunk/pybridge/pybridge/ui/excepthook.py 2007-07-23 11:28:54 UTC (rev 490) +++ trunk/pybridge/pybridge/ui/excepthook.py 2007-07-23 11:55:00 UTC (rev 491) @@ -28,7 +28,7 @@ from StringIO import StringIO -def excepthook(type, value, tb): +def exceptdialog(errormessage): dialog = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL, buttons=gtk.BUTTONS_CLOSE, type=gtk.MESSAGE_WARNING) dialog.set_title(_('Program error')) @@ -47,9 +47,7 @@ frame.set_border_width(6) dialog.vbox.add(frame) textbuffer = textview.get_buffer() - trace = StringIO() - traceback.print_exception(type, value, tb, None, trace) - textbuffer.set_text(trace.getvalue()) + textbuffer.set_text(errormessage) textview.set_size_request(320, 240) dialog.details = frame @@ -61,3 +59,9 @@ dialog.connect('response', dialog_response_cb) dialog.run() + +def excepthook(type, value, tb): + trace = StringIO() + traceback.print_exception(type, value, tb, None, trace) + exceptdialog(trace.getvalue()) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-23 11:28:51
|
Revision: 490 http://svn.sourceforge.net/pybridge/?rev=490&view=rev Author: umgangee Date: 2007-07-23 04:28:54 -0700 (Mon, 23 Jul 2007) Log Message: ----------- Add support for requesting and displaying user information. Modified Paths: -------------- trunk/pybridge/glade/pybridge.glade trunk/pybridge/pybridge/server/user.py trunk/pybridge/pybridge/ui/window_main.py Added Paths: ----------- trunk/pybridge/pybridge/ui/dialog_userinfo.py Modified: trunk/pybridge/glade/pybridge.glade =================================================================== --- trunk/pybridge/glade/pybridge.glade 2007-07-22 18:02:34 UTC (rev 489) +++ trunk/pybridge/glade/pybridge.glade 2007-07-23 11:28:54 UTC (rev 490) @@ -307,93 +307,6 @@ <property name="position">1</property> </packing> </child> - <child> - <widget class="GtkFrame" id="frame_tableinfo"> - <property name="visible">True</property> - <property name="sensitive">False</property> - <child> - <widget class="GtkAlignment" id="alignment21"> - <property name="visible">True</property> - <property name="top_padding">4</property> - <property name="bottom_padding">4</property> - <property name="left_padding">4</property> - <property name="right_padding">4</property> - <child> - <widget class="GtkTable" id="table_tableinfo"> - <property name="visible">True</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <property name="column_spacing">8</property> - <property name="row_spacing">2</property> - <child> - <widget class="GtkLabel" id="label63"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">ID:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_tableid"> - <property name="visible">True</property> - <property name="xalign">0</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label65"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Type:</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_tabletype"> - <property name="visible">True</property> - <property name="xalign">0</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - <child> - <widget class="GtkLabel" id="label57"> - <property name="visible">True</property> - <property name="label" translatable="yes">Table Information</property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> - </widget> - <packing> - <property name="position">2</property> - </packing> - </child> </widget> <packing> <property name="expand">False</property> @@ -406,9 +319,9 @@ </packing> </child> <child> - <widget class="GtkLabel" id="tab_tables"> + <widget class="GtkLabel" id="label_tabletab"> <property name="visible">True</property> - <property name="label" translatable="yes">Available Tables</property> + <property name="label" translatable="yes">Tables</property> </widget> <packing> <property name="type">tab</property> @@ -431,75 +344,29 @@ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> <property name="shadow_type">GTK_SHADOW_IN</property> <child> - <widget class="GtkIconView" id="peopleview"> + <widget class="GtkIconView" id="userview"> <property name="visible">True</property> <property name="can_focus">True</property> - <signal name="item_activated" handler="on_peopleview_item_activated"/> - <signal name="selection_changed" handler="on_peopleview_selection_changed"/> + <signal name="item_activated" handler="on_userview_item_activated"/> + <signal name="selection_changed" handler="on_userview_selection_changed"/> </widget> </child> </widget> </child> <child> - <widget class="GtkVBox" id="vbox17"> - <property name="width_request">160</property> + <widget class="GtkVButtonBox" id="buttonbox_users"> <property name="visible">True</property> - <property name="spacing">4</property> + <property name="sensitive">False</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> <child> - <widget class="GtkFrame" id="frame_personinfo"> + <widget class="GtkButton" id="button_userinfo"> <property name="visible">True</property> - <property name="sensitive">False</property> - <child> - <widget class="GtkAlignment" id="alignment22"> - <property name="visible">True</property> - <property name="top_padding">4</property> - <property name="bottom_padding">4</property> - <property name="left_padding">4</property> - <property name="right_padding">4</property> - <child> - <widget class="GtkTable" id="table_personinfo"> - <property name="visible">True</property> - <property name="n_rows">1</property> - <property name="n_columns">2</property> - <property name="column_spacing">8</property> - <property name="row_spacing">2</property> - <child> - <widget class="GtkLabel" id="label67"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Name:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_personname"> - <property name="visible">True</property> - <property name="xalign">0</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - <child> - <widget class="GtkLabel" id="label58"> - <property name="visible">True</property> - <property name="label" translatable="yes">Person Information</property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> + <property name="can_focus">True</property> + <property name="receives_default">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="label" translatable="yes">User Information</property> + <signal name="clicked" handler="on_userinfo_clicked"/> </widget> </child> </widget> @@ -515,9 +382,9 @@ </packing> </child> <child> - <widget class="GtkLabel" id="label_people"> + <widget class="GtkLabel" id="label_usertab"> <property name="visible">True</property> - <property name="label" translatable="yes">People Online</property> + <property name="label" translatable="yes">Users</property> </widget> <packing> <property name="type">tab</property> @@ -568,30 +435,23 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkLabel" id="label_portnum"> + <widget class="GtkEntry" id="entry_portnum"> + <property name="width_request">80</property> <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Port:</property> + <property name="can_focus">True</property> + <property name="max_length">5</property> + <property name="invisible_char">*</property> + <signal name="changed" handler="on_field_changed"/> </widget> <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkLabel" id="label_hostname"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Host Name:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> <widget class="GtkEntry" id="entry_hostname"> <property name="width_request">140</property> <property name="visible">True</property> @@ -606,19 +466,26 @@ </packing> </child> <child> - <widget class="GtkEntry" id="entry_portnum"> - <property name="width_request">80</property> + <widget class="GtkLabel" id="label_hostname"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">5</property> - <property name="invisible_char">*</property> - <signal name="changed" handler="on_field_changed"/> + <property name="xalign">0</property> + <property name="label" translatable="yes">Host Name:</property> </widget> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_portnum"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Port:</property> + </widget> + <packing> <property name="top_attach">1</property> <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> @@ -661,26 +528,20 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkLabel" id="label_username"> + <widget class="GtkEntry" id="entry_password"> + <property name="width_request">120</property> <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">User Name:</property> + <property name="can_focus">True</property> + <property name="max_length">40</property> + <property name="visibility">False</property> + <property name="invisible_char">*</property> + <signal name="changed" handler="on_field_changed"/> </widget> <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_password"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Password:</property> - </widget> - <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> @@ -700,23 +561,29 @@ </packing> </child> <child> - <widget class="GtkEntry" id="entry_password"> - <property name="width_request">120</property> + <widget class="GtkLabel" id="label_password"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">40</property> - <property name="visibility">False</property> - <property name="invisible_char">*</property> - <signal name="changed" handler="on_field_changed"/> + <property name="xalign">0</property> + <property name="label" translatable="yes">Password:</property> </widget> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> + <child> + <widget class="GtkLabel" id="label_username"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">User Name:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> </widget> </child> <child> @@ -818,6 +685,7 @@ <property name="title" translatable="yes">Create a New Table</property> <property name="resizable">False</property> <property name="modal">True</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> <property name="skip_taskbar_hint">True</property> <property name="gravity">GDK_GRAVITY_CENTER</property> @@ -840,53 +708,53 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkLabel" id="label_gamelist"> + <widget class="GtkLabel" id="label_tablename"> <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="xalign">0</property> - <property name="label" translatable="yes">Game at Table:</property> + <property name="label" translatable="yes">Table Name:</property> </widget> <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkComboBox" id="gamelist"> + <widget class="GtkEntry" id="tablename"> + <property name="width_request">140</property> <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> - <signal name="changed" handler="on_gamelist_changed"/> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + <signal name="changed" handler="on_tablename_changed"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> <property name="y_options">GTK_FILL</property> </packing> </child> <child> - <widget class="GtkEntry" id="tablename"> - <property name="width_request">140</property> + <widget class="GtkComboBox" id="gamelist"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">*</property> - <signal name="changed" handler="on_tablename_changed"/> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <signal name="changed" handler="on_gamelist_changed"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="y_options">GTK_FILL</property> </packing> </child> <child> - <widget class="GtkLabel" id="label_tablename"> + <widget class="GtkLabel" id="label_gamelist"> <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="xalign">0</property> - <property name="label" translatable="yes">Table Name:</property> + <property name="label" translatable="yes">Game at Table:</property> </widget> <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="y_options"></property> </packing> </child> @@ -942,6 +810,7 @@ <property name="border_width">5</property> <property name="title" translatable="yes">PyBridge Preferences</property> <property name="resizable">False</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> <property name="has_separator">False</property> <child internal-child="vbox"> @@ -966,72 +835,6 @@ <property name="column_spacing">8</property> <property name="row_spacing">4</property> <child> - <widget class="GtkFileChooserButton" id="background"> - <property name="width_request">160</property> - <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> - <signal name="selection_changed" handler="on_background_changed"/> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> - <child> - <widget class="GtkComboBox" id="cardstyle"> - <property name="width_request">160</property> - <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> - <signal name="changed" handler="on_cardstyle_changed"/> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_background"> - <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="xalign">0</property> - <property name="label" translatable="yes">Background:</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_cardstyle"> - <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="xalign">0</property> - <property name="label" translatable="yes">Card Style:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_suitcolours"> - <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="xalign">0</property> - <property name="label" translatable="yes">Suit Colours:</property> - </widget> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> <widget class="GtkHBox" id="hbox2"> <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> @@ -1128,6 +931,72 @@ <property name="bottom_attach">3</property> </packing> </child> + <child> + <widget class="GtkLabel" id="label_suitcolours"> + <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="xalign">0</property> + <property name="label" translatable="yes">Suit Colours:</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_cardstyle"> + <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="xalign">0</property> + <property name="label" translatable="yes">Card Style:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_background"> + <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="xalign">0</property> + <property name="label" translatable="yes">Background:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkComboBox" id="cardstyle"> + <property name="width_request">160</property> + <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> + <signal name="changed" handler="on_cardstyle_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkFileChooserButton" id="background"> + <property name="width_request">160</property> + <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> + <signal name="selection_changed" handler="on_background_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> </widget> <packing> <property name="expand">False</property> @@ -1211,4 +1080,95 @@ </widget> </child> </widget> + <widget class="GtkDialog" id="dialog_userinfo"> + <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="border_width">5</property> + <property name="title" translatable="yes">User Information</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="has_separator">False</property> + <signal name="delete_event" handler="on_delete_event"/> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox6"> + <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="spacing">2</property> + <child> + <widget class="GtkAlignment" id="alignment1"> + <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="top_padding">4</property> + <property name="bottom_padding">4</property> + <property name="left_padding">4</property> + <property name="right_padding">4</property> + <child> + <widget class="GtkVBox" id="vbox3"> + <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="spacing">8</property> + <child> + <widget class="GtkLabel" id="label_userinfo"> + <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="xalign">0</property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="scrolled_userinfo"> + <property name="visible">True</property> + <property name="can_focus">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="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTextView" id="userinfo"> + <property name="visible">True</property> + <property name="can_focus">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="editable">False</property> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area6"> + <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="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="closebutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">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="label" translatable="yes">gtk-close</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_closebutton_clicked"/> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> </glade-interface> Modified: trunk/pybridge/pybridge/server/user.py =================================================================== --- trunk/pybridge/pybridge/server/user.py 2007-07-22 18:02:34 UTC (rev 489) +++ trunk/pybridge/pybridge/server/user.py 2007-07-23 11:28:54 UTC (rev 490) @@ -75,7 +75,7 @@ def perspective_getUserInformation(self, username=None): """Returns public information for user with specified username. - If username is unspecified, returns user's own profile. + If username is unspecified, returns user's own public information. """ if username is None: username = self.name @@ -85,8 +85,13 @@ except IndexError: raise DeniedRequest, "Specified user does not exist" - return {'realname': user.realname, 'email': user.email, - 'profile': user.profile} + info = {} + for field in 'realname', 'email', 'country', 'profile': + value = getattr(user, field, None) + # Do not send unspecified (null) values. + if value is not None: + info[field] = value + return info # def perspective_setProfile(self, **kwargs): Added: trunk/pybridge/pybridge/ui/dialog_userinfo.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_userinfo.py (rev 0) +++ trunk/pybridge/pybridge/ui/dialog_userinfo.py 2007-07-23 11:28:54 UTC (rev 490) @@ -0,0 +1,79 @@ +# PyBridge -- online contract bridge made easy. +# Copyright (C) 2004-2007 PyBridge Project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +import gtk, pango + +from manager import wm +from wrapper import GladeWrapper + + +class DialogUserInfo(GladeWrapper): + + glade_name = 'dialog_userinfo' + + fields = [('realname', _('Real Name')), + ('email', _('Email Address')), + ('country', ('Country')), + ('profile', _('Profile'))] + + texttags = {'heading': {'weight': pango.WEIGHT_BOLD}, + 'value': {}, + 'value-unknown': {'foreground': 'gray'}, + } + + + def setUp(self): + self.userinfo.set_size_request(320, 160) + + # Populate information textview with text tags. + tagtable = self.userinfo.get_buffer().get_tag_table() + for tagname, tagattrs in self.texttags.items(): + tag = gtk.TextTag(tagname) + for attrname, attrvalue in tagattrs.items(): + tag.set_property(attrname, attrvalue) + tagtable.add(tag) + + + def setUserInfo(self, username, info): + self.label_userinfo.set_markup("<span size='large'><b>%s</b></span>" % + _('Information for %(user)s') % {'user': username}) + + # Populate information textview. + buffer = self.userinfo.get_buffer() + + # Display recognised fields in order. + for id, title in self.fields: + buffer.insert_with_tags_by_name(buffer.get_end_iter(), title, 'heading') + buffer.insert(buffer.get_end_iter(), ': ') + + value = info.get(id) + if value: + buffer.insert_with_tags_by_name(buffer.get_end_iter(), value, 'value') + else: + buffer.insert_with_tags_by_name(buffer.get_end_iter(), _('not specified'), 'value-unknown') + + buffer.insert(buffer.get_end_iter(), '\n') + + + def on_closebutton_clicked(self, widget, *args): + wm.close(self) + + + def on_delete_event(self, widget, *args): + self.on_closebutton_clicked(widget, *args) + Modified: trunk/pybridge/pybridge/ui/window_main.py =================================================================== --- trunk/pybridge/pybridge/ui/window_main.py 2007-07-22 18:02:34 UTC (rev 489) +++ trunk/pybridge/pybridge/ui/window_main.py 2007-07-23 11:28:54 UTC (rev 490) @@ -33,6 +33,7 @@ from dialog_connection import DialogConnection from dialog_newtable import DialogNewtable from dialog_preferences import DialogPreferences +from dialog_userinfo import DialogUserInfo # TODO: import all Window*Table classes automatically. from pybridge.games.bridge.ui.window_bridgetable import WindowBridgeTable @@ -46,7 +47,7 @@ glade_name = 'window_main' 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) + userview_icon = gtk.gdk.pixbuf_new_from_file_at_size(USER_ICON, 48, 48) def setUp(self): @@ -56,17 +57,16 @@ # Set up table model and icon view. self.tableview.set_text_column(0) self.tableview.set_pixbuf_column(1) - self.tableview_model = gtk.ListStore(str, gtk.gdk.Pixbuf) - self.tableview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.tableview.set_model(self.tableview_model) + model = gtk.ListStore(str, gtk.gdk.Pixbuf) + model.set_sort_column_id(0, gtk.SORT_ASCENDING) + self.tableview.set_model(model) - # Set up people model and icon view. - # TODO: allow users to provide their own "avatar" icons. - self.peopleview.set_text_column(0) - self.peopleview.set_pixbuf_column(1) - self.peopleview_model = gtk.ListStore(str, gtk.gdk.Pixbuf) - self.peopleview_model.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.peopleview.set_model(self.peopleview_model) + # Set up user model and icon view. + self.userview.set_text_column(0) + self.userview.set_pixbuf_column(1) + model = gtk.ListStore(str, gtk.gdk.Pixbuf) + model.set_sort_column_id(0, gtk.SORT_ASCENDING) + self.userview.set_model(model) # Attach event handler to listen for events. self.eventHandler = SimpleEventHandler(self) @@ -87,6 +87,7 @@ def quit(self): """Shut down gracefully.""" + client.detach(self.eventHandler) wm.close(self) reactor.stop() gtk.main_quit() @@ -94,6 +95,7 @@ def errback(self, failure): print "Error: %s" % failure.getErrorMessage() + print failure.getTraceback() # Event handlers. @@ -115,10 +117,11 @@ 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.newtable.set_property('sensitive', False) - self.tableview_model.clear() - self.peopleview_model.clear() + print self.tableview.get_model() + self.tableview.get_model().clear() + self.userview.get_model().clear() def event_connectionLost(self, host, port): @@ -142,8 +145,8 @@ def event_gotRoster(self, name, roster): - lookup = {'tables' : (self.tableview_model, self.tableview_icon), - 'users' : (self.peopleview_model, self.peopleview_icon)} + lookup = {'tables' : (self.tableview.get_model(), self.tableview_icon), + 'users' : (self.userview.get_model(), self.userview_icon)} try: model, icon = lookup[name] @@ -167,7 +170,7 @@ """Adds a table to the table listing.""" # Only display table if it supported by client. if info['gamename'] in SUPPORTED_GAMES: - self.tableview_model.append([tableid, self.tableview_icon]) + self.tableview.get_model().append([tableid, self.tableview_icon]) def event_closeTable(self, tableid): @@ -177,13 +180,14 @@ if model.get_value(iter, 0) in user_data: model.remove(iter) return True - - self.tableview_model.foreach(func, tableid) + model = self.tableview.get_model() + model.foreach(func, tableid) + def event_userLogin(self, username, info): """Adds a user to the people listing.""" - self.peopleview_model.append([username, self.peopleview_icon]) + self.userview.get_model().append([username, self.userview_icon]) def event_userLogout(self, username): @@ -193,10 +197,11 @@ if model.get_value(iter, 0) in user_data: model.remove(iter) return True - - self.peopleview_model.foreach(func, username) + model = self.userview.get_model() + model.foreach(func, username) + # Signal handlers. @@ -205,8 +210,9 @@ def on_tableview_item_activated(self, iconview, path, *args): - iter = self.tableview_model.get_iter(path) - tableid = self.tableview_model.get_value(iter, 0) + model = self.tableview.get_model() + iter = model.get_iter(path) + tableid = model.get_value(iter, 0) if tableid not in client.tables: d = client.joinTable(tableid) d.addErrback(self.errback) @@ -216,32 +222,40 @@ def on_tableview_selection_changed(self, iconview, *args): cursor = self.tableview.get_cursor() if cursor: # Ensure cursor contains a path, not None. - iter = self.tableview_model.get_iter(cursor[0]) # Path. - tableid = self.tableview_model.get_value(iter, 0) + model = self.tableview.get_model() + iter = model.get_iter(cursor[0]) # Path. + tableid = model.get_value(iter, 0) # If client not joined to table, enable Join Table button. sensitive = tableid not in client.tables self.jointable.set_property('sensitive', sensitive) - # Display information about table. - self.frame_tableinfo.set_property('sensitive', True) - self.label_tableid.set_text(tableid) - self.label_tabletype.set_text(client.tableRoster[tableid]['gamename']) else: - self.frame_tableinfo.set_property('sensitive', False) - self.label_tableid.set_text('') - self.label_tabletype.set_text('') + self.jointable.set_property('sensitive', False) - def on_peopleview_selection_changed(self, iconview, *args): - cursor = self.peopleview.get_cursor() + def on_userview_item_activated(self, iconview, path, *args): + model = self.userview.get_model() + iter = model.get_iter(path) + username = model.get_value(iter, 0) + + # Allow one DialogUserInfo instance for each user. + winid = (DialogUserInfo, username) + + def gotUserInfo(info): + w = wm.open(DialogUserInfo, winid) + w.setUserInfo(username, info) + + if not wm.get(winid): + # TODO: if user info in cache, do not request again from server. + d = client.getUserInformation(username) + d.addCallback(gotUserInfo) + + + def on_userview_selection_changed(self, iconview, *args): + cursor = self.userview.get_cursor() if cursor: # Ensure cursor contains a path, not None. - iter = self.peopleview_model.get_iter(cursor[0]) # Path. - person = self.peopleview_model.get_value(iter, 0) - # Display information about person. - self.frame_personinfo.set_property('sensitive', True) - self.label_personname.set_text(person) + self.buttonbox_users.set_property('sensitive', True) else: - self.frame_personinfo.set_property('sensitive', False) - self.label_personname.set_text('') + self.buttonbox_users.set_property('sensitive', False) def on_window_main_delete_event(self, widget, *args): @@ -258,6 +272,11 @@ self.on_tableview_item_activated(self.tableview, path) + def on_userinfo_clicked(self, widget, *args): + path = self.userview.get_cursor()[0] + self.on_userview_item_activated(self.userview, path) + + def on_connect_activate(self, widget, *args): if not wm.get(DialogConnection): wm.open(DialogConnection) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-22 18:02:39
|
Revision: 489 http://svn.sourceforge.net/pybridge/?rev=489&view=rev Author: umgangee Date: 2007-07-22 11:02:34 -0700 (Sun, 22 Jul 2007) Log Message: ----------- Do not provide server with Referenceable object, as it is unnecessary. Modified Paths: -------------- trunk/pybridge/pybridge/network/client.py Modified: trunk/pybridge/pybridge/network/client.py =================================================================== --- trunk/pybridge/pybridge/network/client.py 2007-07-22 15:52:02 UTC (rev 488) +++ trunk/pybridge/pybridge/network/client.py 2007-07-22 18:02:34 UTC (rev 489) @@ -35,7 +35,7 @@ -class NetworkClient(pb.Referenceable): +class NetworkClient(object): """Provides the glue between the client code and the server.""" implements(ISubject) @@ -145,7 +145,7 @@ hash = sha.new(password).hexdigest() creds = credentials.UsernamePassword(username, hash) - d = self.factory.login(creds, client=self) + d = self.factory.login(creds, client=None) d.addCallback(connectedAsUser) return d @@ -208,5 +208,11 @@ return d + def getUserInformation(self, username): + # TODO: cache user information once retrieved. + d = self.avatar.callRemote('getUserInformation', username) + return d + + client = NetworkClient() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-22 15:52:06
|
Revision: 488 http://svn.sourceforge.net/pybridge/?rev=488&view=rev Author: umgangee Date: 2007-07-22 08:52:02 -0700 (Sun, 22 Jul 2007) Log Message: ----------- Borrow an email address validator from Django. Modified Paths: -------------- trunk/pybridge/pybridge/server/database.py Modified: trunk/pybridge/pybridge/server/database.py =================================================================== --- trunk/pybridge/pybridge/server/database.py 2007-07-22 15:49:44 UTC (rev 487) +++ trunk/pybridge/pybridge/server/database.py 2007-07-22 15:52:02 UTC (rev 488) @@ -79,6 +79,13 @@ sqlhub.processConnection = connection # Set all SQLObjects to use connection. +# Email address validator from Django: see django/core/validators.py +email_re = re.compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string + r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain + + class UserAccount(SQLObject): """A store of user information. @@ -89,6 +96,7 @@ password = StringCol(length=40, notNone=True) # Store SHA-1 hex hashes. allowLogin = BoolCol(default=True) # If False, account login is disabled. email = StringCol(default=None, length=320) # See RFC 2821 section 4.5.3.1. + # Don't split name field - see http://people.w3.org/rishida/blog/?p=100 realname = UnicodeCol(default=None, length=40) profile = UnicodeCol(default=None) created = DateTimeCol(default=datetime.now) @@ -109,7 +117,7 @@ def _set_email(self, value): # This regexp matches virtually all well-formatted email addresses. - if value and not re.match("^[A-z0-9_.+-]+@([A-z0-9-]+\.)+[A-z]{2,6}$", value): + if value and not email_re.search(value): raise ValueError, "Invalid or ill-formatted email address" self._SO_set_email(value) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-22 15:49:47
|
Revision: 487 http://svn.sourceforge.net/pybridge/?rev=487&view=rev Author: umgangee Date: 2007-07-22 08:49:44 -0700 (Sun, 22 Jul 2007) Log Message: ----------- Simplify some remote-invokable methods. Modified Paths: -------------- trunk/pybridge/pybridge/network/client.py trunk/pybridge/pybridge/server/server.py trunk/pybridge/pybridge/server/user.py Modified: trunk/pybridge/pybridge/network/client.py =================================================================== --- trunk/pybridge/pybridge/network/client.py 2007-07-20 17:35:23 UTC (rev 486) +++ trunk/pybridge/pybridge/network/client.py 2007-07-22 15:49:44 UTC (rev 487) @@ -188,12 +188,11 @@ self.notify('joinTable', tableid=tableid, table=table) return table + params = {} if host: - # TODO: why not just joinTable, host=True? - gamename = gameclass.__name__ - d = self.avatar.callRemote('hostTable', tableid, gamename) - else: - d = self.avatar.callRemote('joinTable', tableid) + params['gamename'] = gameclass.__name__ + + d = self.avatar.callRemote('joinTable', tableid, host, **params) d.addCallback(success) return d Modified: trunk/pybridge/pybridge/server/server.py =================================================================== --- trunk/pybridge/pybridge/server/server.py 2007-07-20 17:35:23 UTC (rev 486) +++ trunk/pybridge/pybridge/server/server.py 2007-07-22 15:49:44 UTC (rev 487) @@ -33,10 +33,9 @@ onlineUsers = LocalUserManager() -def getServerInfo(): - return {'compatibleVersions': (version, version), # minimum, maximum - 'supportedGames': 'bridge', # TODO - 'version': version} +serverData = {'compatibleClients': (version, version), # minimum, maximum + 'supportedGames': SUPPORTED_GAMES.keys(), + 'version': version} def registerUser(username, password): @@ -54,8 +53,8 @@ log.msg("New user %s registered" % username) -def changeUserPassword(username, password): - """Sets the password of user's account. +def setUserPassword(username, password): + """Changes the password of user's account. @param username: the user identifier. @param password: the new password for user. @@ -67,11 +66,12 @@ raise DeniedRequest, "User account does not exist" -def createTable(tableid, gamename): +def createTable(tableid, gamename, **tableOptions): """Create a new table for the specified game type. @param tableid: a unique identifier for the table. - @param gametype: a game identifier. + @param gamename: a game class identifier. + @param tableOptions: optional parameters for table initialisation. """ # TODO: convert gametype string to corresponding class. @@ -87,5 +87,6 @@ # Provide table instance with a means of closing itself. table.close = lambda: availableTables.closeTable(table) availableTables.openTable(table) + return table Modified: trunk/pybridge/pybridge/server/user.py =================================================================== --- trunk/pybridge/pybridge/server/user.py 2007-07-20 17:35:23 UTC (rev 486) +++ trunk/pybridge/pybridge/server/user.py 2007-07-22 15:49:44 UTC (rev 487) @@ -28,7 +28,8 @@ class RegisteredUser(pb.Avatar): - info = property(lambda self: {}) # TODO: Send profile data? + # Static for duration of connection. + info = property(lambda self: {'registered': True}) def __init__(self, name): @@ -56,11 +57,6 @@ # Perspective methods, accessible by client. - def perspective_getServerInfo(self): - """Provides a dict of information about the server.""" - return server.getServerInfo() - - def perspective_getRoster(self, name): """Provides roster requested by client.""" if name == 'tables': @@ -71,44 +67,59 @@ raise DeniedRequest, "Unknown roster name \'%s\'" % name - def perspective_getUserProfile(self, username): - """Provides profile information for user with specified username.""" - pass + def perspective_getServerData(self): + """Provides a dict of information about the server.""" + return server.serverData + def perspective_getUserInformation(self, username=None): + """Returns public information for user with specified username. + + If username is unspecified, returns user's own profile. + """ + if username is None: + username = self.name + + try: + user = db.UserAccount.selectBy(username=username)[0] + except IndexError: + raise DeniedRequest, "Specified user does not exist" + + return {'realname': user.realname, 'email': user.email, + 'profile': user.profile} + + +# def perspective_setProfile(self, **kwargs): +# """Sets avatar's user account profile information to that specified.""" +# pass + + def perspective_changePassword(self, password): """Sets avatar's user account password to that specified.""" if not isinstance(password, str): raise IllegalRequest, "Invalid parameter for password" - try: - server.changeUserPassword(self.name, password) + try: # Validate password before it is changed. + server.setUserPassword(self.name, password) except ValueError, err: # Password validation failed. raise DeniedRequest, err - def perspective_hostTable(self, tableid, gametype): - """Creates a new table.""" + def perspective_joinTable(self, tableid, host=False, **hostParams): + """Joins an existing table, or creates and joins a new table.""" if not isinstance(tableid, str): raise IllegalRequest, "Invalid parameter for table identifier" - if not isinstance(gametype, str): - raise IllegalRequest, "Invalid parameter for game type" - - table = server.createTable(tableid, gametype) - # Force client to join table. - return self.perspective_joinTable(tableid) + elif tableid in self.joinedTables: + raise DeniedRequest, "Already joined table" + if host: + table = server.createTable(tableid, **hostParams) + else: + try: + table = server.availableTables[tableid] + except KeyError: + raise DeniedRequest, "No such table" - def perspective_joinTable(self, tableid): - """Joins an existing table.""" - if not isinstance(tableid, str): - raise IllegalRequest, "Invalid parameter for table identifier" - elif tableid not in server.availableTables: - raise DeniedRequest, "No such table" - elif tableid in self.joinedTables: - raise DeniedRequest, "Already joined table" - - table = server.availableTables[tableid] self.joinedTables[tableid] = table return table @@ -135,3 +146,4 @@ server.registerUser(username, password) except ValueError, err: # Username/password validation failed. raise DeniedRequest, err + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-20 17:35:27
|
Revision: 486 http://svn.sourceforge.net/pybridge/?rev=486&view=rev Author: umgangee Date: 2007-07-20 10:35:23 -0700 (Fri, 20 Jul 2007) Log Message: ----------- Implement toString() method. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/deal.py Modified: trunk/pybridge/pybridge/games/bridge/deal.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/deal.py 2007-07-20 14:32:16 UTC (rev 485) +++ trunk/pybridge/pybridge/games/bridge/deal.py 2007-07-20 17:35:23 UTC (rev 486) @@ -145,8 +145,8 @@ return long(num) - __pbnDirection = dict(zip('NESW', Direction)) - __pbnRank = dict(zip('23456789TJQKA', Rank)) + __pbnDirection = dict(zip('NESW', Direction) + zip(Direction, 'NESW')) + __pbnRank = dict(zip('23456789TJQKA', Rank) + zip(Rank, '23456789TJQKA')) @classmethod @@ -175,10 +175,25 @@ return cls(deal) - def toString(self): + def toString(self, dealer=Direction.North): """Computes the PBN deal string which corresponds to this deal. + In PBN 'export format', the hands must be given in clockwise order, + starting from the dealer's position. + + @param dealer: if specified, the dealer of this deal. @return: a PBN deal string. """ - raise NotImplementedError + order = Direction[dealer.index:] + Direction[:dealer.index] + hs = {} + for position in order: + # Split hand into suits. + suits = dict((suit, '') for suit in Suit) + for card in sorted(self[position], reverse=True): # High to low. + suits[card.suit] += self.__pbnRank[card.rank] + hs[position] = '%s.%s.%s.%s' % tuple(suits[s] for s in reversed(Suit)) + + return self.__pbnDirection[dealer] + \ + ':%s %s %s %s' % tuple(hs[position] for position in order) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-20 14:32:17
|
Revision: 485 http://svn.sourceforge.net/pybridge/?rev=485&view=rev Author: umgangee Date: 2007-07-20 07:32:16 -0700 (Fri, 20 Jul 2007) Log Message: ----------- New Deal class replaces Deck. Deck-specific methods become Deal classmethods. Use 1..D range instead of 0..D-1 for consistency with IBB. (although the IBB algorithm itself deals in ranges 0..D-1) Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/deal.py trunk/pybridge/tests/bridge/test_deal.py Removed Paths: ------------- trunk/pybridge/pybridge/games/bridge/deck.py trunk/pybridge/tests/bridge/test_deck.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-19 15:30:30 UTC (rev 484) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-07-20 14:32:16 UTC (rev 485) @@ -18,7 +18,7 @@ import random import time -from deck import Deck +from deal import Deal from symbols import Direction, Vulnerable @@ -53,8 +53,7 @@ @param result: @type result: """ - deck = Deck() - self['deal'] = deck.randomDeal() + self['deal'] = Deal.fromRandom() self['num'] = self.get('num', 0) + 1 self['time'] = tuple(time.localtime()) Copied: trunk/pybridge/pybridge/games/bridge/deal.py (from rev 480, trunk/pybridge/pybridge/games/bridge/deck.py) =================================================================== --- trunk/pybridge/pybridge/games/bridge/deal.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/deal.py 2007-07-20 14:32:16 UTC (rev 485) @@ -0,0 +1,184 @@ +# 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 copy import copy +from operator import mul +import random + +from card import Card +from symbols import Direction, Rank, Suit + + +# See http://mail.python.org/pipermail/edu-sig/2001-May/001288.html for details. +comb = lambda n, k: reduce(mul, range(n, n-k, -1)) / reduce(mul, range(1, k+1)) + + +class Deal(dict): + """Represents a deal of hands as a dict containing lists of cards. + + Operations to encode/decode deals into a variety of formats are provided, + including "impossible bridge book" index values and PBN strings. + + Definitions: + - A hand is a collection of 13 cards from the deck. + - A deal is a distribution of all 52 cards to four hands. + + There are exactly 52! / (13!)**4 (comb(52,13) * comb(39,13) * comb(26,13)) + distinct deals of 13 cards to 4 positions from a standard 52-card deck. + """ + + # Required order: Ace of Spades, King of Spades, ..., Two of Clubs. + __cards = [Card(r, s) for s in reversed(Suit) for r in reversed(Rank)] + + __Nmax = comb(52, 13) + __Emax = comb(39, 13) + __Smax = comb(26, 13) + __D = __Nmax * __Emax * __Smax + + + def __init__(self, mapping): + super(Deal, self).__init__(mapping) + for hand in self.values(): + hand.sort() + + + @classmethod + def fromRandom(cls): + """Generates a random deal of hands from a 'shuffled' deck. + + @return: an instance of Deal. + """ + # This is more efficient than calling fromIndex() with a random number. + deck = copy(cls.__cards) + random.shuffle(deck) + hands = dict((pos, sorted(deck[13*i : 13*(i+1)])) + for i, pos in enumerate(Direction)) + return cls(hands) + + + @classmethod + def fromIndex(cls, num): + """Generates the deal which corresponds to the specified "page number". + + This implements the "impossible bridge book" decoding algorithm by + Thomas Andrews, see http://bridge.thomasoandrews.com/impossible/. + + @param num: integer in range 1..D. + @return: a Deal object containing the corresponding deal. + """ + assert isinstance(num, (int, long)), "index must be an integer" + assert 1 <= num <= cls.__D, "index not in range %s..%s" % (1, cls.__D) + + cardSeq = copy(cls.__cards) # Make a copy for modification. + hands = dict((pos, []) for pos in Direction) + num -= 1 # Decrement page number to fit within range 0..D-1. + + # Split index into hand indexes. + indexes = {Direction.North : (num / cls.__Smax) / cls.__Emax, + Direction.East : (num / cls.__Smax) % cls.__Emax, + Direction.South : (num % cls.__Smax) } + + for position in (Direction.North, Direction.East, Direction.South): + for k in range(13, 0, -1): + + # Find the largest n such that comb(n, k) <= indexes[position]. + n = k-1 # n < k implies comb(n, k) = 0 + # comb(n+1, k) = + # n-k == -1 => comb(n, k) * (n+1) + # otherwise => (comb(n, k) * (n+1)) / (n+1 - k) + while comb(n+1, k) <= indexes[position]: + n += 1 + + # Remove card index from indices, add card to hand. + indexes[position] -= comb(n, k) + card = cardSeq[n] + hands[position].append(card) + cardSeq.remove(card) + + hands[Direction.West] = cardSeq # West has the remaining cards. + + return cls(hands) + + + def toIndex(self): + """Computes the "page number" which corresponds to this deal. + + This implements the "impossible bridge book" encoding algorithm by + Thomas Andrews, see http://bridge.thomasoandrews.com/impossible/. + + @return: integer in range 1..D + """ + cardSeq = copy(self.__cards) # Make a copy for modification. + indexes = {} + + # For each hand, compute indexes of cards in cardSeq. + for position in (Direction.North, Direction.East, Direction.South): + indexes[position] = 0 + self[position].sort(reverse=False) + # It is desirable to remove cards from cardSeq when adding their + # indexes, instead of doing so in an extra step. + # Removing cards backwards preserves the indexes of later cards. + for i, card in enumerate(self[position]): + indexes[position] += comb(cardSeq.index(card), 13-i) + cardSeq.remove(card) + + # Deal index = (Nindex * Emax * Smax) + (Eindex * Smax) + Sindex + indexes[Direction.North] *= self.__Emax * self.__Smax + indexes[Direction.East] *= self.__Smax + + num = sum(indexes.values()) + 1 # Increment to fit within range 1..D. + return long(num) + + + __pbnDirection = dict(zip('NESW', Direction)) + __pbnRank = dict(zip('23456789TJQKA', Rank)) + + + @classmethod + def fromString(cls, dealstr): + """Generates the deal which corresponds to the given PBN deal string. + + As per the PBN specification, the given deal string should conform to + the format "<first>:<1st_hand> <2nd_hand> <3rd_hand> <4th_hand>". + + @param dealstr: a PBN deal string. + @return: a Deal object containing the corresponding deal. + """ + # Reconstruct deal. + first, hands = dealstr.split(":") + firstindex = cls.__pbnDirection[first.strip()].index + order = Direction[firstindex:] + Direction[:firstindex] + + deal = dict((pos, []) for pos in Direction) + + for position, hand in zip(order, hands.strip().split(' ')): + for suit, suitcards in zip(reversed(Suit), hand.split('.')): + for rank in suitcards: + card = Card(cls.__pbnRank[rank], suit) + deal[position].append(card) + + return cls(deal) + + + def toString(self): + """Computes the PBN deal string which corresponds to this deal. + + @return: a PBN deal string. + """ + raise NotImplementedError + Deleted: trunk/pybridge/pybridge/games/bridge/deck.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/deck.py 2007-07-19 15:30:30 UTC (rev 484) +++ trunk/pybridge/pybridge/games/bridge/deck.py 2007-07-20 14:32:16 UTC (rev 485) @@ -1,157 +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. - - -from copy import copy -from operator import mul -from random import shuffle - -from card import Card -from symbols import Direction, Rank, Suit - - -# See http://mail.python.org/pipermail/edu-sig/2001-May/001288.html for details. -comb = lambda n, k: reduce(mul, range(n, n-k, -1)) / reduce(mul, range(1, k+1)) - - -# TODO: consider making Hand a subclass of List, with additional constraints. - -class Deck(object): - """A Deck object provides operations for dealing Card objects. - - A hand is a collection of 13 cards from the deck. - A deal is a distribution of all 52 cards to four hands. - - A deal is represented as a dictionary, mapping Direction labels to - lists (hands) of Card objects. - - There are exactly 52! / (13!)**4 (comb(52,13) * comb(39,13) * comb(26,13)) - distinct deals of 13 cards to 4 positions from a standard 52-card deck. - """ - - cards = [Card(r, s) for r in Rank for s in Suit] - cardSeq = copy(cards) - cardSeq.sort(reverse=True) # Required order: Ace of Spades -> Two of Clubs. - - Nmax = comb(52, 13) - Emax = comb(39, 13) - Smax = comb(26, 13) - D = Nmax * Emax * Smax - - - def isValidDeal(self, deal): - """Checks that structure of deal conforms to requirements: - - * 4-element dict, mapping Direction objects to hand lists. - * Hand lists contain exactly 13 Card objects. - * No card may be repeated in the same hand, or between hands. - * The cards in hands may be in any order. - - @param deal: a deal dict. - @return: True if deal is valid, False otherwise. - """ - return True # TODO - if invalid, perhaps give reason - - - def randomDeal(self): - """Shuffles the deck and generates a random deal of hands. - - @return: a deal dictionary. - """ - shuffle(self.cards) - hands = {} - for position in Direction: - hands[position] = [] - for index, card in enumerate(self.cards): - hands[Direction[index % len(Direction)]].append(card) - for hand in hands.values(): - hand.sort() - return hands - - - def dealToIndex(self, deal): - """Computes the index which corresponds to the specified deal. - - This implements the "impossible bridge book" encoding algorithm by - Thomas Andrews, see http://bridge.thomasoandrews.com/impossible/. - - @param deal: dict representing a valid deal. - @return: integer in range 0..D-1 - """ - assert self.isValidDeal(deal) - - cardSeq = copy(self.cardSeq) # Make a copy for modification. - indexes = {} - - # For each hand, compute indexes of cards in cardSeq. - for position in (Direction.North, Direction.East, Direction.South): - indexes[position] = 0 - deal[position].sort(reverse=False) - # It is desirable to remove cards from cardSeq when adding their - # indexes, instead of doing so in an extra step. - # Removing cards backwards preserves the indexes of later cards. - for i, card in enumerate(deal[position]): - indexes[position] += comb(cardSeq.index(card), 13-i) - cardSeq.remove(card) - - # Deal index = (Nindex * Emax * Smax) + (Eindex * Smax) + Sindex - indexes[Direction.North] *= self.Emax * self.Smax - indexes[Direction.East] *= self.Smax - return long(sum(indexes.values())) - - - def indexToDeal(self, num): - """Generates the deal which corresponds to the specified index. - - This implements the "impossible bridge book" decoding algorithm by - Thomas Andrews, see http://bridge.thomasoandrews.com/impossible/. - - @param num: integer in range 0..D-1. - @return: dict representing a valid deal. - """ - assert type(num) in (int, long), "index must be an integer" - assert 0 <= num < self.D, "index not in required range" - - cardSeq = copy(self.cardSeq) # Make a copy for modification. - deal = {} - - # Split index into hand indexes. - indexes = {Direction.North : (num / self.Smax) / self.Emax, - Direction.East : (num / self.Smax) % self.Emax, - Direction.South : (num % self.Smax) } - - for position in (Direction.North, Direction.East, Direction.South): - deal[position] = [] - for k in range(13, 0, -1): - # Find the largest n such that comb(n, k) <= indexes[position]. - n = k-1 # n < k implies comb(n, k) = 0 - - # comb(n+1, k) = - # n-k = -1 => comb(n, k) * (n+1) - # otherwise => (comb(n, k) * (n+1)) / (n+1 - k) - while comb(n+1, k) <= indexes[position]: - n += 1 - - # Remove card index from indices, add card to hand. - indexes[position] -= comb(n, k) - card = cardSeq[n] - deal[position].append(card) - cardSeq.remove(card) - - deal[Direction.West] = cardSeq # West has the remaining cards. - return deal - Copied: trunk/pybridge/tests/bridge/test_deal.py (from rev 480, trunk/pybridge/tests/bridge/test_deck.py) =================================================================== --- trunk/pybridge/tests/bridge/test_deal.py (rev 0) +++ trunk/pybridge/tests/bridge/test_deal.py 2007-07-20 14:32:16 UTC (rev 485) @@ -0,0 +1,83 @@ +import unittest + +from pybridge.games.bridge.card import Card +from pybridge.games.bridge.deal import Deal +from pybridge.games.bridge.symbols import Direction, Rank, Suit + + +class TestDeck(unittest.TestCase): + + cards = sorted(Card(r, s) for r in Rank for s in Suit) + + samples = {} + + samples[1] = Deal({Direction.North: [Card(r, Suit.Spade) for r in Rank], + Direction.East: [Card(r, Suit.Heart) for r in Rank], + Direction.South: [Card(r, Suit.Diamond) for r in Rank], + Direction.West: [Card(r, Suit.Club) for r in Rank]}) + + # http://bridgehands.com/D/Duke_of_Cumberland_Hand.htm + samples[25216995119420903953708290155] = Deal.fromString( + "N:..Q8765432.AQT84 65432.T9872.JT9. T987.6543..76532 AKQJ.AKQJ.AK.KJ9") + + # http://bridgehands.com/B/John_Bennett_Murder.htm + samples[49115408832893597588305377049] = Deal.fromString( + "S:KJ985.K762.85.KT Q72.AJ3.AQT92.J6 AT63.T85.4.A9842 4.Q94.KJ763.Q753") + + # From the PBN v2.0 specification document. + samples[51845212465382378289082480212] = Deal.fromString( + "N:.63.AKQ987.A9732 A8654.KQ5.T.QJT6 J973.J98742.3.K4 KQT2.AT.J6542.85") + + # http://bridgehands.com/M/Mississippi_Heart_Hand.htm + samples[53520933857671775260919265981] = Deal.fromString( + "S:AKQ.AKQJT9..AKQJ .8765432.AKQJT9. T5432..5432.5432 J9876..876.T9876") + + + def validateDeal(self, deal): + """Checks that structure of deal conforms to requirements: + + - Each position in Direction maps to a hand, represented as a list. + - Hand lists contain exactly 13 Card objects. + - No card may be repeated in the same hand, or between hands. + + @param deal: a Deal instance. + """ + assert isinstance(deal, Deal), "deal not a Deal instance" + assert set(deal.keys()) == set(Direction), "invalid set of keys" + + extractcards = [] + for pos, hand in deal.items(): + assert len(hand) == 13, "%s hand does not contain 13 cards" % pos + extractcards.extend(hand) + assert self.cards == sorted(extractcards), "not a pure set of cards" + + + def test_generateRandom(self): + """Testing generation of random deals""" + deal = Deal.fromRandom() + try: + self.validateDeal(deal) + except Exception, e: + self.fail(e, deal) + + + def test_toIndex(self): + """Testing toIndex method over a set of known deals""" + for index, deal in self.samples.items(): + self.assertEqual(deal.toIndex(), index) + + + def test_fromIndex(self): + """Testing Deal.fromIndex over a set of known indexes""" + for index, deal in self.samples.items(): + self.assertEqual(Deal.fromIndex(index), deal) + + +def main(): + suite = unittest.makeSuite(TestDeck) + unittest.TextTestRunner(verbosity=2).run(suite) + + +if __name__ == '__main__': + main() + Deleted: trunk/pybridge/tests/bridge/test_deck.py =================================================================== --- trunk/pybridge/tests/bridge/test_deck.py 2007-07-19 15:30:30 UTC (rev 484) +++ trunk/pybridge/tests/bridge/test_deck.py 2007-07-20 14:32:16 UTC (rev 485) @@ -1,52 +0,0 @@ -import unittest - -from pybridge.games.bridge.deck import Deck - - -class TestDeck(unittest.TestCase): - - - def setUp(self): - self.deck = Deck() - - - def tearDown(self): - self.deck = None - - - def testRandomDeal(self): - """Testing randomDeal""" - for i in range(100): - deal = self.deck.randomDeal() - self.assert_(self.deck.isValidDeal(deal)) - - - def testDealToIndex(self): - """Testing dealToIndex (assuming indexToDeal correct)""" - n = 1 - while n < self.deck.D: - deal = self.deck.indexToDeal(n) - pn = self.deck.dealToIndex(deal) - self.assertEqual(n, pn) - n = n*2 + 1 - - - def testIndexToDeal(self): - """Testing indexToDeal (assuming dealToIndex and isValidDeal correct)""" - n = 1 - while n < self.deck.D: - deal = self.deck.indexToDeal(n) - self.assert_(self.deck.isValidDeal(deal)) - pn = self.deck.dealToIndex(deal) - self.assertEqual(n, pn) - n = n*2 + 1 - - -def main(): - suite = unittest.makeSuite(TestDeck) - unittest.TextTestRunner(verbosity=2).run(suite) - - -if __name__ == '__main__': - main() - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-19 15:30:38
|
Revision: 484 http://svn.sourceforge.net/pybridge/?rev=484&view=rev Author: umgangee Date: 2007-07-19 08:30:30 -0700 (Thu, 19 Jul 2007) Log Message: ----------- Allow user to specify which game is played at new table. Modified Paths: -------------- trunk/pybridge/glade/pybridge.glade trunk/pybridge/pybridge/ui/dialog_newtable.py Modified: trunk/pybridge/glade/pybridge.glade =================================================================== --- trunk/pybridge/glade/pybridge.glade 2007-07-16 15:28:30 UTC (rev 483) +++ trunk/pybridge/glade/pybridge.glade 2007-07-19 15:30:30 UTC (rev 484) @@ -13,9 +13,9 @@ <widget class="GtkMenuBar" id="menubar_main"> <property name="visible">True</property> <child> - <widget class="GtkMenuItem" id="menu_server"> + <widget class="GtkMenuItem" id="menu_connection"> <property name="visible">True</property> - <property name="label" translatable="yes">_Server</property> + <property name="label" translatable="yes">_Connection</property> <property name="use_underline">True</property> <child> <widget class="GtkMenu" id="menu_server_menu"> @@ -815,7 +815,7 @@ </widget> <widget class="GtkDialog" id="dialog_newtable"> <property name="visible">True</property> - <property name="title" translatable="yes">New Table</property> + <property name="title" translatable="yes">Create a New Table</property> <property name="resizable">False</property> <property name="modal">True</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> @@ -835,12 +835,39 @@ <child> <widget class="GtkTable" id="table2"> <property name="visible">True</property> - <property name="n_rows">1</property> + <property name="n_rows">2</property> <property name="n_columns">2</property> - <property name="column_spacing">4</property> - <property name="row_spacing">2</property> + <property name="column_spacing">8</property> + <property name="row_spacing">4</property> <child> - <widget class="GtkEntry" id="entry_tablename"> + <widget class="GtkLabel" id="label_gamelist"> + <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="xalign">0</property> + <property name="label" translatable="yes">Game at Table:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkComboBox" id="gamelist"> + <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> + <signal name="changed" handler="on_gamelist_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="tablename"> <property name="width_request">140</property> <property name="visible">True</property> <property name="can_focus">True</property> @@ -850,16 +877,16 @@ <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="y_options"></property> + <property name="y_options">GTK_FILL</property> </packing> </child> <child> <widget class="GtkLabel" id="label_tablename"> <property name="visible">True</property> + <property name="xalign">0</property> <property name="label" translatable="yes">Table Name:</property> </widget> <packing> - <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> Modified: trunk/pybridge/pybridge/ui/dialog_newtable.py =================================================================== --- trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-07-16 15:28:30 UTC (rev 483) +++ trunk/pybridge/pybridge/ui/dialog_newtable.py 2007-07-19 15:30:30 UTC (rev 484) @@ -30,8 +30,17 @@ def setUp(self): + # Build and populate list of supported games. + model = gtk.ListStore(str) + self.gamelist.set_model(model) + cell = gtk.CellRendererText() + self.gamelist.pack_start(cell, True) + self.gamelist.add_attribute(cell, 'text', 0) + + for gamename in sorted(SUPPORTED_GAMES): + iter = model.append((gamename, )) + self.gamelist.set_active_iter(iter) # TODO: display intersection of games supported by client and server. - pass def createSuccess(self, table): @@ -43,13 +52,16 @@ dialog = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_MODAL, 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.set_markup(_('The table was not created by the server.')) dialog.format_secondary_text(_('Reason: %s') % error) - dialog.run() - dialog.destroy() + def dialog_response_cb(dialog, response_id): + dialog.destroy() + dialog.connect('response', dialog_response_cb) + dialog.show() + # Signal handlers. @@ -58,15 +70,19 @@ def on_okbutton_clicked(self, widget, *args): - tableid = self.entry_tablename.get_text() - d = client.joinTable(tableid, gameclass=SUPPORTED_GAMES['Bridge'], - host=True) + model = self.gamelist.get_model() + iter = self.gamelist.get_active_iter() + gamename = model.get_value(iter, 0) + + tableid = self.tablename.get_text() + gameclass = SUPPORTED_GAMES[gamename] + d = client.joinTable(tableid, gameclass, 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() != "" + sensitive = self.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-07-16 15:28:34
|
Revision: 483 http://svn.sourceforge.net/pybridge/?rev=483&view=rev Author: umgangee Date: 2007-07-16 08:28:30 -0700 (Mon, 16 Jul 2007) Log Message: ----------- Refactor Auction class (previously Bidding) as a subclass of list. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/scoring.py trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py trunk/pybridge/pybridge/ui/vocabulary.py trunk/pybridge/tests/bridge/test_game.py trunk/pybridge/tests/monkey.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/auction.py trunk/pybridge/tests/bridge/test_auction.py Removed Paths: ------------- trunk/pybridge/pybridge/games/bridge/bidding.py trunk/pybridge/tests/bridge/test_bidding.py Copied: trunk/pybridge/pybridge/games/bridge/auction.py (from rev 480, trunk/pybridge/pybridge/games/bridge/bidding.py) =================================================================== --- trunk/pybridge/pybridge/games/bridge/auction.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/auction.py 2007-07-16 15:28:30 UTC (rev 483) @@ -0,0 +1,194 @@ +# 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 call import Bid, Pass, Double, Redouble +from symbols import Direction + + +class Contract(object): + """Represents the result of an auction.""" + + + def __init__(self, auction): + """ + @param auction: a completed, but not passed out, auction. + @type auction: Auction + """ + assert auction.isComplete() and not auction.isPassedOut() + + # The contract is the last (and highest) bid. + self.bid = auction.currentBid + + # The declarer is the first partner to bid the contract denomination. + caller = auction.whoCalled(self.bid) + partnership = (caller, Direction[(caller.index + 2) % 4]) + # Determine which partner is declarer. + for call in auction: + if isinstance(call, Bid) and call.strain == self.bid.strain: + bidder = auction.whoCalled(call) + if bidder in partnership: + self.declarer = bidder + break + + self.doubleBy, self.redoubleBy = None, None + if auction.currentDouble: + # The opponent who doubled the contract bid. + self.doubleBy = auction.whoCalled(auction.currentDouble) + if auction.currentRedouble: + # The partner who redoubled an opponent's double. + self.redoubleBy = auction.whoCalled(auction.currentRedouble) + + + + +class Auction(list): + """The auction (bidding phase) of a game of bridge.""" + + + def __init__(self, dealer): + """ + @param dealer: who distributes the cards and makes the first call. + @type dealer: Direction + """ + self.dealer = dealer + self.contract = None + + currentBid = property(lambda self: self._getCurrentCall(Bid)) + currentDouble = property(lambda self: self._getCurrentCall(Double)) + currentRedouble = property(lambda self: self._getCurrentCall(Redouble)) + + + def isComplete(self): + """Auction is complete if all players have called (ie. 4 or more calls) + and the last 3 calls are Pass calls. + + @return: True if bidding is complete, False if not. + @rtype: bool + """ + passes = len([c for c in self[-3:] if isinstance(c, Pass)]) + return len(self) >= 4 and passes == 3 + + + def isPassedOut(self): + """Auction is passed out if each player has passed on their first turn. + In this case, the bidding is complete, but no contract is established. + + @return: True if bidding is passed out, False if not. + @rtype: bool + """ + passes = len([call for call in self if isinstance(call, Pass)]) + return len(self) == 4 and passes == 4 + + + def makeCall(self, call): + """Appends call from position to the calls list. + + Please note that call validity should be checked with isValidCall() + before calling this method! + + @param call: a candidate call. + """ + assert call not in self # Calls must be distinct. + assert self.isValidCall(call) + + self.append(call) + if self.isComplete() and not self.isPassedOut(): + self.contract = Contract(self) + + + def isValidCall(self, call, position=None): + """Check that call can be made, according to the rules of bidding. + + @param call: the candidate call. + @param position: if specified, the position from which the call is made. + @return: True if call is available, False if not. + """ + # The bidding must not be complete. + if self.isComplete(): + return False + + # Position's turn to play. + if position and position != self.whoseTurn(): + return False + + # A pass is always available. + if isinstance(call, Pass): + return True + + # A bid must be greater than the current bid. + if isinstance(call, Bid): + return not self.currentBid or call > self.currentBid + + # Doubles and redoubles only when a bid has been made. + if self.currentBid: + bidder = self.whoCalled(self.currentBid) + + # A double must be made on the current bid from opponents, + # with has not been already doubled by partnership. + if isinstance(call, Double): + opposition = (Direction[(self.whoseTurn().index + 1) % 4], + Direction[(self.whoseTurn().index + 3) % 4]) + return bidder in opposition and not self.currentDouble + + # A redouble must be made on the current bid from partnership, + # which has been doubled by an opponent. + elif isinstance(call, Redouble): + partnership = (self.whoseTurn(), + Direction[(self.whoseTurn().index + 2) % 4]) + return bidder in partnership and self.currentDouble \ + and not self.currentRedouble + + return False # Otherwise unavailable. + + + def whoCalled(self, call): + """Returns the position from which the specified call was made. + + @param call: a call made in the auction. + @return: the position of the player who made call, or None. + """ + if call in self: + return Direction[(self.dealer.index + self.index(call)) % 4] + return None # Call not made by any player. + + + def whoseTurn(self): + """Returns the position from which the next call should be made. + + @return: the next position to make a call, or None. + """ + if self.isComplete(): + return + return Direction[(self.dealer.index + len(self)) % 4] + + + def _getCurrentCall(self, callclass): + """Returns most recent current call of specified class, or None. + + @param callclass: call class, in (Bid, Pass, Double, Redouble). + @return: most recent call matching type, or None. + """ + assert callclass in (Bid, Pass, Double, Redouble) + + for call in reversed(self): + if isinstance(call, callclass): + return call + elif isinstance(call, Bid): + break # Bids cancel all preceding calls. + return None + Deleted: trunk/pybridge/pybridge/games/bridge/bidding.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/bidding.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/bidding.py 2007-07-16 15:28:30 UTC (rev 483) @@ -1,201 +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. - - -from pybridge.network.error import GameError - -from call import Bid, Pass, Double, Redouble -from symbols import Direction, Level, Strain - - -class Bidding(object): - """This class models the bidding (auction) phase of a game of bridge. - - A bidding session is a list of Call objects and the dealer. - """ - - - def __init__(self, dealer): - if dealer not in Direction: - raise TypeError, "Expected Direction, got %s" % type(dealer) - self.calls = [] - self.dealer = dealer - - - def isComplete(self): - """Bidding is complete if 4 or more calls have been made, - and the last 3 calls are Pass calls. - - @return: True if bidding is complete, False if not. - @rtype: bool - """ - passes = len([c for c in self.calls[-3:] if isinstance(c, Pass)]) - return len(self.calls) >= 4 and passes == 3 - - - def isPassedOut(self): - """Bidding is passed out if each player has passed on their first turn. - In this case, the bidding is complete, but no contract is established. - - @return: True if bidding is passed out, False if not. - @rtype: bool - """ - passes = len([call for call in self.calls if isinstance(call, Pass)]) - return len(self.calls) == 4 and passes == 4 - - - def getContract(self): - """When the bidding is complete, the contract is the last and highest - bid, which may be doubled or redoubled. - - Hence, the contract represents the "final state" of the bidding. - - @return: a dict containing the keywords: - @keyword bid: the last and highest bid. - @keyword declarer: the partner who first bid the contract strain. - @keyword doubleBy: the opponent who doubled the contract, or None. - @keyword redoubleBy: the partner who redoubled an opponent's double - on the contract, or None. - """ - if self.isComplete() and not self.isPassedOut(): - bid = self.getCurrentCall(Bid) - double = self.getCurrentCall(Double) - redouble = self.getCurrentCall(Redouble) - # Determine partnership. - caller = self.whoCalled(bid) - partnership = (caller, Direction[(caller.index + 2) % 4]) - # Determine declarer. - for call in self.calls: - if isinstance(call, Bid) and call.strain == bid.strain \ - and self.whoCalled(call) in partnership: - declarerBid = call - break - - return {'bid' : bid, - 'declarer' : self.whoCalled(declarerBid), - 'doubleBy' : double and self.whoCalled(double), - 'redoubleBy' : redouble and self.whoCalled(redouble) } - return None # Bidding passed out or not complete, no contract. - - - def getCurrentCall(self, calltype): - """Returns most recent current call of specified type, or None. - - @param calltype: call type, in (Bid, Pass, Double, Redouble). - @return: most recent call matching type, or None. - """ - if calltype not in (Bid, Pass, Double, Redouble): - raise GameError, "Expected call type, got %s" % type(calltype) - - for call in self.calls[::-1]: - if isinstance(call, calltype): - return call - elif isinstance(call, Bid): - break - return None - - - def makeCall(self, call, player=None): - """Appends call from player to the calls list. - - @param call: the Call object representing player's call. - @param player: the player making call, or None. - """ - if not isinstance(call, (Bid, Pass, Double, Redouble)): - raise GameError, "Expected call type, got %s" % type(call) - if not self.isValidCall(call, player): - raise GameError, "Invalid call" - - self.calls.append(call) - - - def isValidCall(self, call, player=None): - """Check that specified call is available to player, with respect to - current state of bidding. If specified, player's turn will be checked. - - @param call: the Call object to be tested for validity. - @param player: the player attempting to call, or None. - @return: True if call is available, False if not. - """ - if not isinstance(call, (Bid, Pass, Double, Redouble)): - raise GameError, "Expected call type, got %s" % type(call) - assert player in Direction or player is None - - # The bidding must not be complete. - if self.isComplete(): - return False - - # It must be player's turn to call. - if player and player != self.whoseTurn(): - return False - - # Bidding is not complete; a pass is always available. - elif isinstance(call, Pass): - return True - - currentBid = self.getCurrentCall(Bid) - - # A bid must be greater than the current bid. - if isinstance(call, Bid): - return not currentBid or call > currentBid - - # Doubles and redoubles only when a bid has been made. - if currentBid: - bidder = self.whoCalled(currentBid) - - # A double must be made on the current bid from opponents, - # with has not been already doubled by partnership. - if isinstance(call, Double): - opposition = (Direction[(self.whoseTurn().index + 1) % 4], - Direction[(self.whoseTurn().index + 3) % 4]) - return bidder in opposition and not self.getCurrentCall(Double) - - # A redouble must be made on the current bid from partnership, - # which has been doubled by an opponent. - elif isinstance(call, Redouble): - partnership = (self.whoseTurn(), - Direction[(self.whoseTurn().index + 2) % 4]) - return bidder in partnership and self.getCurrentCall(Double) \ - and not self.getCurrentCall(Redouble) - - return False # Otherwise unavailable. - - - def whoCalled(self, call): - """Returns the player who made the specified call. - - @param call: a Call. - @return: the player who made call, or False. - """ - if not isinstance(call, (Bid, Pass, Double, Redouble)): - raise GameError, "Expected call type, got %s" % type(call) - - if call in self.calls: - return Direction[(self.calls.index(call) + self.dealer.index) % 4] - return False # Call not made by any player. - - - def whoseTurn(self): - """Returns position of player who is next to make a call. - - @return: the current turn. - @rtype: Direction - """ - if self.isComplete(): - raise GameError, "Bidding complete" - return Direction[(len(self.calls) + self.dealer.index) % 4] - Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-16 15:28:30 UTC (rev 483) @@ -23,7 +23,7 @@ from pybridge.interfaces.observer import ISubject from pybridge.network.error import GameError -from bidding import Bidding +from auction import Auction from board import Board from play import Trick, TrickPlay from scoring import scoreDuplicate @@ -34,7 +34,7 @@ class Bridge(object): - """A bridge game models the bidding and play sequence. + """A bridge game sequences the auction and trick play. The methods of this class comprise the interface of a state machine. Clients should only use the class methods to interact with the game state. @@ -59,18 +59,18 @@ def __init__(self): self.listeners = [] - # TODO: are these necessary? self.board = None - self.bidding = None - self.contract = None - self.trumpSuit = None + self.auction = None self.play = None self.boardQueue = [] # Boards for successive games. self.visibleHands = {} # A subset of deal, containing revealed hands. self.players = {} # One-to-one mapping from BridgePlayer to Direction. + contract = property(lambda self: self.auction and self.auction.contract or None) + trumpSuit = property(lambda self: self.play and self.play.trumpSuit or None) + # Implementation of ICardGame. @@ -85,9 +85,7 @@ else: # Create a board. self.board = Board() self.board.nextDeal() - self.bidding = Bidding(self.board['dealer']) # Start bidding. - self.contract = None - self.trumpSuit = None + self.auction = Auction(self.board['dealer']) # Start auction. self.play = None self.visibleHands.clear() @@ -101,8 +99,8 @@ def inProgress(self): if self.play is not None: return not self.play.isComplete() - elif self.bidding: - return not self.bidding.isPassedOut() + elif self.auction is not None: + return not self.auction.isPassedOut() else: return False @@ -120,8 +118,8 @@ visibleBoard['deal'] = self.visibleHands state['board'] = visibleBoard - if self.bidding: - state['calls'] = self.bidding.calls + if self.auction: + state['auction'] = list(self.auction) if self.play is not None: state['play'] = [dict(trick) for trick in self.play] @@ -132,15 +130,23 @@ if state.get('board'): self.start(state['board']) - for call in state.get('calls', []): + # Perform validation on game provided by server. + # Better to encounter errors earlier than later. + + for call in state.get('auction', []): turn = self.getTurn() self.makeCall(call, position=turn) for trick in state.get('play', []): - turn = self.play.whoseTurn() - trickobj = Trick(leader=turn, trumpSuit=self.trumpSuit) - trickobj.update(trick) # Populate with cards. - self.play.append(trickobj) + # TODO: clean this up. + leader = self.getTurn() + for turn in Direction[leader.index:] + Direction[:leader.index]: + if turn in trick: + card = trick[turn] + if turn == self.play.dummy: + self.playCard(card, position=self.play.declarer) + else: + self.playCard(card, position=turn) def updateState(self, event, *args, **kwargs): @@ -200,7 +206,7 @@ def makeCall(self, call, player=None, position=None): - """Make a call in the current bidding session. + """Make a call in the current auction. This method expects to receive either a player argument or a position. If both are given, the position argument is disregarded. @@ -222,19 +228,19 @@ raise TypeError, "Expected Direction, got %s" % type(position) # Validate call according to game state. - if not self.bidding or self.bidding.isComplete(): - raise GameError, "No game in progress, or bidding complete" + if self.auction is None or self.auction.isComplete(): + raise GameError, "No game in progress, or auction complete" if self.getTurn() != position: raise GameError, "Call made out of turn" - if not self.bidding.isValidCall(call, position): + if not self.auction.isValidCall(call, position): raise GameError, "Call cannot be made" - self.bidding.makeCall(call, position) + self.auction.makeCall(call) - if self.bidding.isComplete() and not self.bidding.isPassedOut(): - self.contract = self.bidding.getContract() # TODO: make a property - self.trumpSuit = self.trumpMap[self.contract['bid'].strain] - self.play = TrickPlay(self.contract['declarer'], self.trumpSuit) + if self.auction.isComplete() and not self.auction.isPassedOut(): + declarer = self.auction.contract.declarer + trumpSuit = self.trumpMap[self.contract.bid.strain] + self.play = TrickPlay(declarer, trumpSuit) self.notify('makeCall', call=call, position=position) @@ -349,10 +355,10 @@ def getTurn(self): if self.inProgress(): - if self.bidding.isComplete(): # In trick play. + if self.auction.isComplete(): # In trick play. return self.play.whoseTurn() - else: # Currently in the bidding. - return self.bidding.whoseTurn() + else: # Currently in the auction. + return self.auction.whoseTurn() else: # Not in game. raise GameError, "No game in progress" @@ -360,16 +366,15 @@ def getScore(self): """Returns the integer score value for declarer/dummy if: - - bidding stage has been passed out, with no bids made. - - play stage is complete. + - auction has been passed out, with no bids made. + - trick play is complete. """ - if self.inProgress() or self.bidding is None: + if self.inProgress() or self.auction is None: raise GameError, "Game not complete" - if self.bidding.isPassedOut(): + if self.auction.isPassedOut(): return 0 # A passed out deal does not score. - contract = self.bidding.getContract() - declarer = contract['declarer'] + declarer = self.contract.declarer dummy = Direction[(declarer.index + 2) % 4] if declarer in (Direction.North, Direction.South): @@ -379,7 +384,7 @@ declarerWon, defenceWon = self.play.wonTrickCount() - result = {'contract': contract, 'tricksMade': declarerWon, + result = {'contract': self.contract, 'tricksMade': declarerWon, 'vulnerable': vulnerable} return scoreDuplicate(result) Modified: trunk/pybridge/pybridge/games/bridge/scoring.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-16 15:28:30 UTC (rev 483) @@ -31,13 +31,13 @@ """ score = 0 - isDoubled = result['contract']['doubleBy'] - isRedoubled = result['contract']['redoubleBy'] + isDoubled = bool(result['contract'].doubleBy) + isRedoubled = bool(result['contract'].redoubleBy) isVulnerable = result['vulnerable'] - contractLevel = result['contract']['bid'].level.index + 1 + contractLevel = result['contract'].bid.level.index + 1 tricksMade = result['tricksMade'] - tricksRequired = result['contract']['bid'].level.index + 7 - trumpSuit = result['contract']['bid'].strain + tricksRequired = result['contract'].bid.level.index + 7 + trumpSuit = result['contract'].bid.strain if tricksMade >= tricksRequired: # Contract fulfilled. Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py 2007-07-16 15:28:30 UTC (rev 483) @@ -26,7 +26,7 @@ class WindowBidbox(object): - """The bidding box is presented to a player, during bidding. + """The bidding box is presented to the playing user, during an auction. Each call (bid, pass, double or redouble) is displayed as a button. When it is the player's turn to bid, a call is made by clicking the @@ -104,9 +104,9 @@ def setTable(self, table, position): - """Monitor the state of bidding in game at specified table. + """Monitor the state of auction in game at specified table. - @param table: the BridgeGame for which to observe bidding session. + @param table: the BridgeGame for which to observe auction session. @param: """ if self.table: @@ -134,7 +134,7 @@ if self.position == self.table.game.getTurn(): self.window.set_property('sensitive', True) for call, button in self.callButtons.items(): - isvalid = self.table.game.bidding.isValidCall(call) + isvalid = self.table.game.auction.isValidCall(call) button.set_property('sensitive', isvalid) else: self.window.set_property('sensitive', False) Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-16 15:28:30 UTC (rev 483) @@ -107,8 +107,8 @@ textContract = render_contract(game.contract) textMade = str(declarerWon) - if game.contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ - or game.contract['declarer'] in (Direction.East, Direction.West) and score < 0: + if game.contract.declarer in (Direction.North, Direction.South) and score > 0 \ + or game.contract.declarer in (Direction.East, Direction.West) and score < 0: textNS, textEW = str(abs(score)), '' else: textNS, textEW = '', str(abs(score)) @@ -170,7 +170,7 @@ def set_trickcount(self, game): if game.play: declarerWon, defenceWon = game.play.wonTrickCount() - required = game.contract['bid'].level.index + 7 + required = game.contract.bid.level.index + 7 declarerNeeds = max(0, required - declarerWon) defenceNeeds = max(0, 13 + 1 - required - defenceWon) else: @@ -292,12 +292,12 @@ self.setTurnIndicator() - for call in self.table.game.bidding.calls: - position = self.table.game.bidding.whoCalled(call) + for call in self.table.game.auction: + position = self.table.game.auction.whoCalled(call) self.biddingview.add_call(call, position) - # If user is a player and bidding in progress, open bidding box. - if self.player and not self.table.game.bidding.isComplete(): + # If user is a player and auction in progress, open bidding box. + if self.player and not self.table.game.auction.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) @@ -340,7 +340,7 @@ self.scoreview.add_score(self.table.game) declarerWon, defenceWon = self.table.game.play.wonTrickCount() - required = self.table.game.contract['bid'].level.index + 7 + required = self.table.game.contract.bid.level.index + 7 offset = declarerWon - required score = self.table.game.getScore() @@ -519,7 +519,7 @@ self.biddingview.add_call(call, position) self.setTurnIndicator() - if self.table.game.bidding.isComplete(): + if self.table.game.auction.isComplete(): self.dashboard.set_contract(self.table.game) if self.children.get(WindowBidbox): # If a player. self.children.close(self.children[WindowBidbox]) @@ -578,8 +578,8 @@ d = self.player.callRemote('getHand') d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) - # If game is running and bidding is active, open bidding box. - if not self.table.game.bidding.isComplete(): + # If game is running and auction is active, open bidding box. + if not self.table.game.auction.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) Modified: trunk/pybridge/pybridge/ui/vocabulary.py =================================================================== --- trunk/pybridge/pybridge/ui/vocabulary.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/ui/vocabulary.py 2007-07-16 15:28:30 UTC (rev 483) @@ -191,11 +191,11 @@ @return: a format string representing the contract. @rtype: str """ - doubled = contract['redoubleBy'] and CALLTYPE_SYMBOLS[Call.Redouble] \ - or contract['doubleBy'] and CALLTYPE_SYMBOLS[Call.Double] or '' + doubled = contract.redoubleBy and CALLTYPE_SYMBOLS[Call.Redouble] \ + or contract.doubleBy and CALLTYPE_SYMBOLS[Call.Double] or '' - fields = {'bid': render_call(contract['bid']), 'doubled': doubled, - 'declarer': DIRECTION_NAMES[contract['declarer']]} + fields = {'bid': render_call(contract.bid), 'doubled': doubled, + 'declarer': DIRECTION_NAMES[contract.declarer]} if doubled: return _('%(bid)s %(doubled)s by %(declarer)s') % fields Copied: trunk/pybridge/tests/bridge/test_auction.py (from rev 480, trunk/pybridge/tests/bridge/test_bidding.py) =================================================================== --- trunk/pybridge/tests/bridge/test_auction.py (rev 0) +++ trunk/pybridge/tests/bridge/test_auction.py 2007-07-16 15:28:30 UTC (rev 483) @@ -0,0 +1,113 @@ +import unittest + +from pybridge.games.bridge.auction import Auction +from pybridge.games.bridge.call import Bid, Pass, Double, Redouble +from pybridge.games.bridge.symbols import Direction, Level, Strain + + +class TestAuction(unittest.TestCase): + +# bids = [Bid(l, s) for l in Level for s in Strain] + + dealer = Direction.North + calls = [Pass(), Pass(), Bid(Level.One, Strain.Club), Double(), + Redouble(), Pass(), Pass(), Bid(Level.One, Strain.NoTrump), + Pass(), Bid(Level.Three, Strain.NoTrump), Pass(), Pass(), + Pass()] + + + def setUp(self): + self.auction = Auction(dealer=self.dealer) + + + def tearDown(self): + self.auction = None + + + def stepThroughAuction(self): + for call in self.calls: + yield call + self.auction.makeCall(call) + + + def testIsComplete(self): + """Checking isComplete() only when auction completed""" + s = self.stepThroughAuction() + self.assertEqual(self.auction.isComplete(), False) + try: + while s.next(): + self.assertEqual(self.auction.isComplete(), False) + except StopIteration: + self.assertEqual(self.auction.isComplete(), True) + + + def testIsPassedOut(self): + """Checking isPassedOut() when all players pass""" + for call in [Pass(), Pass(), Pass(), Pass()]: + self.assertEqual(self.auction.isPassedOut(), False) + self.auction.makeCall(call) + self.assertEqual(self.auction.isPassedOut(), True) + + + def testCurrentCalls(self): + """Checking currentBid/currentDouble/currentRedouble values""" + + def testCurrent(bid, double, redouble): + self.assertEqual(self.auction.currentBid, bid) + self.assertEqual(self.auction.currentDouble, double) + self.assertEqual(self.auction.currentRedouble, redouble) + + testCurrent(None, None, None) + + bid = Bid(Level.One, Strain.Diamond) + self.auction.makeCall(bid) + testCurrent(bid, None, None) # Ensure that currentBid is set. + + double = Double() + self.auction.makeCall(double) + testCurrent(bid, double, None) # Ensure currentBid/Double set. + + redouble = Redouble() + self.auction.makeCall(redouble) + testCurrent(bid, double, redouble) # Ensure currentBid/Double/Redouble set. + + bid = Bid(Level.One, Strain.Spade) + self.auction.makeCall(bid) + testCurrent(bid, None, None) # Ensure currentBid set, Double/Redouble reset. + + + def testWhoseTurn(self): + """Checking whoseTurn() returns position on turn""" + s = self.stepThroughAuction() + try: + turn = self.dealer + while s.next(): + self.assertEqual(self.auction.whoseTurn(), turn) + turn = Direction[(turn.index + 1) % 4] # Turn moves clockwise. + except StopIteration: + self.assertEqual(self.auction.whoseTurn(), None) + + + def testIsValidCall(self): + """Checking isValidCall() identifies valid and invalid calls.""" + s = self.stepThroughAuction() + try: + while True: + candidate = s.next() + self.assert_(self.auction.isValidCall(candidate)) + if self.auction.currentBid: + pass + #self.assert_(self.auction.isValidCall + except StopIteration: + pass + + + +def main(): + suite = unittest.makeSuite(TestAuction) + unittest.TextTestRunner(verbosity=2).run(suite) + + +if __name__ == '__main__': + main() + Deleted: trunk/pybridge/tests/bridge/test_bidding.py =================================================================== --- trunk/pybridge/tests/bridge/test_bidding.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/tests/bridge/test_bidding.py 2007-07-16 15:28:30 UTC (rev 483) @@ -1,67 +0,0 @@ -import unittest -import random - -from pybridge.games.bridge.bidding import Bidding -from pybridge.games.bridge.call import Bid, Pass, Double, Redouble -from pybridge.games.bridge.symbols import Direction, Level, Strain - - -class TestBidding(unittest.TestCase): - - - # Generate some bids. - bids = [Bid(l, s) for l in Level for s in Strain] - - - def setUp(self): - dealer = random.choice(Direction) - self.bidding = Bidding(dealer) - - - def tearDown(self): - self.bidding = None - - - def testGetCurrentCall(self): - """getCurrentCall""" - for calltype in [Bid, Pass, Double, Redouble]: - self.assertEqual(self.bidding.getCurrentCall(calltype), None) - - for call, calltype in [(Pass(), Pass), (Bid(Level.One, Strain.Club), Bid), - (Double(), Double), (Redouble(), Redouble)]: - self.assertEqual(self.bidding.getCurrentCall(calltype), None) - self.bidding.makeCall(call) - self.assertEqual(self.bidding.getCurrentCall(calltype), call) - - # A bid should clear types Pass, Double, Redouble. - bid = Bid(Level.One, Strain.Diamond) - self.bidding.makeCall(bid) - self.assertEqual(self.bidding.getCurrentCall(Bid), bid) - for calltype in [Pass, Double, Redouble]: - self.assertEqual(self.bidding.getCurrentCall(calltype), None) - - - def testWhoseTurn(self): - """whoseTurn""" - # Tests whoseTurn() before and after making calls. - turn = Direction[self.bidding.dealer.index] - for call in self.bids: - self.assertEqual(self.bidding.whoseTurn(), turn) - self.bidding.makeCall(call) - turn = Direction[(turn.index + 1) % 4] - self.assertEqual(self.bidding.whoseTurn(), turn) - - - def testIsValidCall(self): - """isValidCall""" - pass - - -def main(): - suite = unittest.makeSuite(TestBidding) - unittest.TextTestRunner(verbosity=2).run(suite) - - -if __name__ == '__main__': - main() - Modified: trunk/pybridge/tests/bridge/test_game.py =================================================================== --- trunk/pybridge/tests/bridge/test_game.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/tests/bridge/test_game.py 2007-07-16 15:28:30 UTC (rev 483) @@ -96,7 +96,7 @@ 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 + calls = [Bid(l, s) for l in Level for s in Strain] + [Pass(), Pass(), Pass()] self.game.start(board) for call in calls: Modified: trunk/pybridge/tests/monkey.py =================================================================== --- trunk/pybridge/tests/monkey.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/tests/monkey.py 2007-07-16 15:28:30 UTC (rev 483) @@ -221,7 +221,7 @@ def loop(): if self.table.game.inProgress(): - if not self.table.game.bidding.isComplete(): + if not self.table.game.auction.isComplete(): self.chooseCall() else: self.chooseCard() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |