2 # -*- coding: utf-8 -*-
5 Mucked cards display for FreePokerTools HUD.
7 # Copyright 2008-2011, Ray E. Barker
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 ########################################################################
27 # Standard Library modules
36 # FreePokerTools modules
41 class Aux_Window(object):
42 def __init__(self
, hud
, params
, config
):
47 # Override these methods as needed
48 def update_data(self
, *args
): pass
49 def update_gui(self
, *args
): pass
50 def create(self
, *args
): pass
51 def relocate(self
, *args
): pass
52 def save_layout(self
, *args
): pass
53 def update_card_positions(self
, *args
): pass
56 self
.container
.destroy()
60 ############################################################################
61 # Some utility routines useful for Aux_Windows
63 def get_card_images(self
):
65 card_images
= 53 * [0]
66 suits
= ('s', 'h', 'd', 'c')
67 ranks
= (14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2)
68 deckimg
= self
.params
['deck']
70 pb
= gtk
.gdk
.pixbuf_new_from_file(self
.config
.execution_path(deckimg
))
72 #FIXME: this can't be right? /usr will not exist on windows
73 stockpath
= '/usr/share/python-fpdb/' + deckimg
74 pb
= gtk
.gdk
.pixbuf_new_from_file(stockpath
)
76 for j
in range(0, 13):
78 card_images
[Card
.cardFromValueSuit(ranks
[j
], suits
[i
])] = self
.cropper(pb
, i
, j
)
79 temp_pb
= gtk
.gdk
.Pixbuf(gtk
.gdk
.COLORSPACE_RGB
, pb
.get_has_alpha(), pb
.get_bits_per_sample(), 30, 42)
80 # also pick out a card back and store in [0]
81 card_images
[0] = self
.cropper(pb
, 2, 13)
83 # cards are 30 wide x 42 high
85 def cropper(self
, pb
, i
, j
):
86 """Crop out a card image given an FTP deck and the i, j position."""
87 temp_pb
= gtk
.gdk
.Pixbuf(gtk
.gdk
.COLORSPACE_RGB
, pb
.get_has_alpha(), pb
.get_bits_per_sample(), 30, 42)
88 pb
.copy_area(30*j
, 42*i
, 30, 42, temp_pb
, 0, 0)
91 def has_cards(self
, cards
):
92 """Returns the number of cards in the list."""
95 if c
!= None and c
> 0: n
= n
+ 1
98 def get_id_from_seat(self
, seat
):
99 """Determine player id from seat number, given stat_dict."""
100 for id, dict in self
.hud
.stat_dict
.iteritems():
101 if seat
== dict['seat']:
105 class Stud_mucked(Aux_Window
):
106 def __init__(self
, hud
, config
, params
):
108 self
.hud
= hud
# hud object that this aux window supports
109 self
.config
= config
# configuration object for this aux window to use
110 self
.params
= params
# hash aux params from config
113 site_params
= self
.config
.get_site_parameters(self
.hud
.site
)
114 self
.hero
= site_params
['screen_name']
118 self
.mucked_list
= Stud_list(self
, params
, config
, self
.hero
)
119 self
.mucked_cards
= Stud_cards(self
, params
, config
)
120 self
.mucked_list
.mucked_cards
= self
.mucked_cards
124 self
.container
= gtk
.Window()
125 self
.vbox
= gtk
.VBox()
126 self
.container
.add(self
.vbox
)
127 self
.container
.set_title(self
.hud
.table
.name
)
129 self
.mucked_list
.create(self
.vbox
)
130 self
.mucked_cards
.create(self
.vbox
)
131 self
.container
.show_all()
133 def update_data(self
, new_hand_id
, db_connection
):
134 # uncomment next line when action is available in the db
135 # self.mucked_cards.update_data(new_hand_id, db_connection)
136 self
.mucked_list
.update_data(new_hand_id
, db_connection
)
138 def update_gui(self
, new_hand_id
):
139 self
.mucked_cards
.update_gui(new_hand_id
)
140 self
.mucked_list
.update_gui(new_hand_id
)
143 def __init__(self
, parent
, params
, config
, hero
):
150 def create(self
, container
):
151 # set up a scrolled window to hold the listbox
152 self
.container
= container
153 self
.scrolled_window
= gtk
.ScrolledWindow()
154 self
.scrolled_window
.set_policy(gtk
.POLICY_NEVER
, gtk
.POLICY_ALWAYS
)
155 self
.container
.add(self
.scrolled_window
)
157 # create a ListStore to use as the model
158 self
.liststore
= gtk
.ListStore(str, str, str, str)
159 self
.treeview
= gtk
.TreeView(self
.liststore
)
160 self
.tvcolumn0
= gtk
.TreeViewColumn('HandID')
161 self
.tvcolumn1
= gtk
.TreeViewColumn('Cards')
162 self
.tvcolumn2
= gtk
.TreeViewColumn('Net')
163 self
.tvcolumn3
= gtk
.TreeViewColumn('Winner')
165 # add tvcolumn to treeview
166 self
.treeview
.append_column(self
.tvcolumn0
)
167 self
.treeview
.append_column(self
.tvcolumn1
)
168 self
.treeview
.append_column(self
.tvcolumn2
)
169 self
.treeview
.append_column(self
.tvcolumn3
)
171 # create a CellRendererText to render the data
172 self
.cell
= gtk
.CellRendererText()
174 # add the cell to the tvcolumn and allow it to expand
175 self
.tvcolumn0
.pack_start(self
.cell
, True)
176 self
.tvcolumn1
.pack_start(self
.cell
, True)
177 self
.tvcolumn2
.pack_start(self
.cell
, True)
178 self
.tvcolumn3
.pack_start(self
.cell
, True)
179 self
.tvcolumn0
.add_attribute(self
.cell
, 'text', 0)
180 self
.tvcolumn1
.add_attribute(self
.cell
, 'text', 1)
181 self
.tvcolumn2
.add_attribute(self
.cell
, 'text', 2)
182 self
.tvcolumn3
.add_attribute(self
.cell
, 'text', 3)
183 # resize the cols if nec
184 self
.tvcolumn0
.set_resizable(True)
185 self
.tvcolumn1
.set_resizable(True)
186 self
.tvcolumn2
.set_resizable(True)
187 self
.tvcolumn3
.set_resizable(True)
188 self
.treeview
.connect("row-activated", self
.activated_event
)
190 self
.scrolled_window
.add_with_viewport(self
.treeview
)
192 def activated_event(self
, path
, column
, data
=None):
194 # sel = self.treeview.get_selection()
195 # (model, iter) = sel.get_selected()
196 # self.mucked_cards.update_data(model.get_value(iter, 0))
197 # self.mucked_cards.update_gui(model.get_value(iter, 0))
199 def update_data(self
, new_hand_id
, db_connection
):
200 """Updates the data needed for the list box."""
202 # db_connection = Database.Database(self.config, 'fpdb', '')
203 self
.winners
= db_connection
.get_winners_from_hand(new_hand_id
)
206 for player
in self
.winners
.keys():
207 pot
= pot
+ int(self
.winners
[player
])
208 if not winners
== '':
209 winners
= winners
+ ", "
210 winners
= winners
+ player
211 pot_dec
= "%.2f" % (float(pot
)/100)
213 hero_cards
= self
.get_hero_cards(self
.parent
.hero
, self
.parent
.hud
.cards
)
214 self
.info_row
= ((new_hand_id
, hero_cards
, pot_dec
, winners
), )
216 def get_hero_cards(self
, hero
, cards
):
217 """Formats the hero cards for inclusion in the tree."""
218 trans
= ('0', 'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
222 # find the hero's seat from the stat_dict
223 for stat
in self
.parent
.hud
.stat_dict
.itervalues():
224 if stat
['screen_name'] == hero
:
225 return Card
.valueSuitFromCard(self
.parent
.hud
.cards
[stat
['seat']][0]) +\
226 Card
.valueSuitFromCard(self
.parent
.hud
.cards
[stat
['seat']][1]) +\
227 Card
.valueSuitFromCard(self
.parent
.hud
.cards
[stat
['seat']][2])
230 def update_gui(self
, new_hand_id
):
231 iter = self
.liststore
.append(self
.info_row
[0])
232 sel
= self
.treeview
.get_selection()
233 #sel.select_iter(iter)
235 vadj
= self
.scrolled_window
.get_vadjustment()
236 #vadj.set_value(vadj.upper)
239 def __init__(self
, parent
, params
, config
):
244 # self.db_name = db_name
246 self
.card_images
= self
.parent
.get_card_images()
248 self
.grid_contents
= {}
254 def create(self
, container
):
255 self
.container
= container
256 self
.grid
= gtk
.Table(self
.rows
, self
.cols
+ 4, homogeneous
= False)
258 for r
in range(0, self
.rows
):
259 for c
in range(0, self
.cols
):
260 self
.seen_cards
[(c
, r
)] = gtk
.image_new_from_pixbuf(self
.card_images
[(0)])
261 self
.eb
[(c
, r
)]= gtk
.EventBox()
263 # set up the contents for the cells
264 for r
in range(0, self
.rows
):
265 self
.grid_contents
[( 0, r
)] = gtk
.Label("%d" % (r
+ 1))
266 self
.grid_contents
[( 1, r
)] = gtk
.Label("player %d" % (r
+ 1))
267 self
.grid_contents
[( 1, r
)].set_property("width-chars", 12)
268 self
.grid_contents
[( 4, r
)] = gtk
.Label("-")
269 self
.grid_contents
[( 9, r
)] = gtk
.Label("-")
270 self
.grid_contents
[( 2, r
)] = self
.eb
[( 0, r
)]
271 self
.grid_contents
[( 3, r
)] = self
.eb
[( 1, r
)]
272 self
.grid_contents
[( 5, r
)] = self
.eb
[( 2, r
)]
273 self
.grid_contents
[( 6, r
)] = self
.eb
[( 3, r
)]
274 self
.grid_contents
[( 7, r
)] = self
.eb
[( 4, r
)]
275 self
.grid_contents
[( 8, r
)] = self
.eb
[( 5, r
)]
276 self
.grid_contents
[(10, r
)] = self
.eb
[( 6, r
)]
277 for c
in range(0, self
.cols
):
278 self
.eb
[(c
, r
)].add(self
.seen_cards
[(c
, r
)])
280 # add the cell contents to the table
281 for c
in range(0, self
.cols
+ 4):
282 for r
in range(0, self
.rows
):
283 self
.grid
.attach(self
.grid_contents
[(c
, r
)], c
, c
+1, r
, r
+1, xpadding
= 1, ypadding
= 1)
285 self
.container
.add(self
.grid
)
287 def update_data(self
, new_hand_id
, db_connection
):
289 action
= db_connection
.get_action_from_hand(new_hand_id
)
290 for street
in action
:
293 temp
= temp
+ act
[0] + " " + act
[1] + "s "
296 temp
= temp
+ "%4.2f\n" % (float(act
[2])/100)
298 temp
= temp
+ "%d\n" % (act
[2]/100)
301 self
.tips
.append(temp
)
303 def update_gui(self
, new_hand_id
):
305 for c
, cards
in self
.parent
.hud
.cards
.iteritems():
306 if c
== 'common': continue
307 self
.grid_contents
[(1, c
- 1)].set_text(self
.get_screen_name(c
))
308 for i
in ((0, cards
[0]), (1, cards
[1]), (2, cards
[2]), (3, cards
[3]),
309 (4, cards
[4]), (5, cards
[5]), (6, cards
[6])):
311 self
.seen_cards
[(i
[0], c
- 1)]. \
312 set_from_pixbuf(self
.card_images
[i
[1]])
313 ## action in tool tips for 3rd street cards
315 for r
in range(0, self
.rows
):
316 #self.eb[(c, r)].set_tooltip_text(self.tips[0])
319 # action in tools tips for later streets
320 round_to_col
= (0, 3, 4, 5, 6)
321 #for round in range(1, len(self.tips)):
322 # for r in range(0, self.rows):
323 # self.eb[(round_to_col[round], r)].set_tooltip_text(self.tips[round])
325 def get_screen_name(self
, seat_no
):
326 """Gets and returns the screen name from stat_dict, given seat number."""
327 for k
in self
.parent
.hud
.stat_dict
.keys():
328 if self
.parent
.hud
.stat_dict
[k
]['seat'] == seat_no
:
329 return self
.parent
.hud
.stat_dict
[k
]['screen_name']
333 for r
in range(0, self
.rows
):
334 self
.grid_contents
[(1, r
)].set_text(" ")
335 for c
in range(0, 7):
336 self
.seen_cards
[(c
, r
)].set_from_pixbuf(self
.card_images
[0])
337 self
.eb
[(c
, r
)].set_tooltip_text('')
339 class Seat_Window(gtk
.Window
):
340 """Subclass gtk.Window for the seat windows."""
341 def __init__(self
, aw
= None):
342 super(Seat_Window
, self
).__init
__()
345 class Aux_Seats(Aux_Window
):
346 """A super class to display an aux_window at each seat."""
348 def __init__(self
, hud
, config
, params
):
349 self
.hud
= hud
# hud object that this aux window supports
350 self
.config
= config
# configuration object for this aux window to use
351 self
.params
= params
# dict aux params from config
352 self
.positions
= {} # dict of window positions
353 self
.displayed
= False # the seat windows are displayed
354 self
.uses_timer
= False # the Aux_seats object uses a timer to control hiding
355 self
.timer_on
= False # bool = Ture if the timeout for removing the cards is on
357 self
.aw_window_type
= Seat_Window
359 # placeholders that should be overridden--so we don't throw errors
360 def create_contents(self
): pass
361 def update_contents(self
): pass
363 def update_card_positions(self
):
364 # self.adj does not exist until .create() has been run
367 except AttributeError:
369 loc
= self
.config
.get_aux_locations(self
.params
['name'], int(self
.hud
.max))
370 width
= self
.hud
.table
.width
371 height
= self
.hud
.table
.height
373 for i
in (range(1, self
.hud
.max + 1) + ['common']):
375 (x
, y
) = self
.params
['layout'][self
.hud
.max].common
378 # self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y)
379 self
.positions
[i
] = self
.card_positions(x
, self
.hud
.table
.x
, y
, self
.hud
.table
.y
)
380 self
.m_windows
[i
].move(self
.positions
[i
][0], self
.positions
[i
][1])
383 self
.adj
= self
.hud
.adj_seats(0, self
.config
) # move adj_seats to aux and get rid of it in Hud.py
384 loc
= self
.config
.get_aux_locations(self
.params
['name'], int(self
.hud
.max))
386 self
.m_windows
= {} # windows to put the card images in
387 width
= self
.hud
.table
.width
388 height
= self
.hud
.table
.height
389 for i
in (range(1, self
.hud
.max + 1) + ['common']):
391 (x
, y
) = self
.params
['layout'][self
.hud
.max].common
393 (x
, y
) = loc
[self
.adj
[i
]]
394 self
.m_windows
[i
] = self
.aw_window_type(self
)
395 self
.m_windows
[i
].set_decorated(False)
396 self
.m_windows
[i
].set_property("skip-taskbar-hint", True)
397 self
.m_windows
[i
].set_transient_for(self
.hud
.main_window
) # FIXME: shouldn't this be the table window??
398 self
.m_windows
[i
].set_focus_on_map(False)
399 self
.m_windows
[i
].connect("configure_event", self
.configure_event_cb
, i
)
400 # self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y)
401 self
.positions
[i
] = self
.card_positions(x
, self
.hud
.table
.x
, y
, self
.hud
.table
.y
)
402 self
.m_windows
[i
].move(self
.positions
[i
][0], self
.positions
[i
][1])
403 if self
.params
.has_key('opacity'):
404 self
.m_windows
[i
].set_opacity(float(self
.params
['opacity']))
406 # the create_contents method is supplied by the subclass
407 self
.create_contents(self
.m_windows
[i
], i
)
409 self
.m_windows
[i
].show_all()
411 self
.m_windows
[i
].hide()
414 def card_positions(self
, x
, table_x
, y
, table_y
):
415 _x
= int(x
) + int(table_x
)
416 _y
= int(y
) + int(table_y
)
420 def update_gui(self
, new_hand_id
):
421 """Update the gui, LDO."""
422 for i
in self
.m_windows
.keys():
423 self
.update_contents(self
.m_windows
[i
], i
)
425 # Methods likely to be of use for any Seat_Window implementation
427 """Destroy all of the seat windows."""
429 for i
in self
.m_windows
.keys():
430 self
.m_windows
[i
].destroy()
431 del(self
.m_windows
[i
])
432 except AttributeError:
435 # Methods likely to be useful for mucked card windows (or similar) only
437 """Hide the seat windows."""
438 for (i
, w
) in self
.m_windows
.iteritems():
440 self
.displayed
= False
442 def save_layout(self
, *args
):
443 """Save new layout back to the aux element in the config file."""
445 # print "adj =", self.adj
446 witdh
= self
.hud
.table
.width
447 height
= self
.hud
.table
.height
448 for (i
, pos
) in self
.positions
.iteritems():
450 # new_locs[self.adj[int(i)]] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
451 new_locs
[self
.adj
[int(i
)]] = ((pos
[0] - self
.hud
.table
.x
), (pos
[1] - self
.hud
.table
.y
) )
453 # new_locs[i] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
454 new_locs
[i
] = ((pos
[0] - self
.hud
.table
.x
), (pos
[1] - self
.hud
.table
.y
))
455 self
.config
.edit_aux_layout(self
.params
['name'], self
.hud
.max, locations
= new_locs
)
457 def configure_event_cb(self
, widget
, event
, i
, *args
):
458 self
.positions
[i
] = widget
.get_position()
459 # self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
461 class Flop_Mucked(Aux_Seats
):
462 """Aux_Window class for displaying mucked cards for flop games."""
464 def __init__(self
, hud
, config
, params
):
465 super(Flop_Mucked
, self
).__init
__(hud
, config
, params
)
466 self
.card_images
= self
.get_card_images()
467 self
.uses_timer
= True # this Aux_seats object uses a timer to control hiding
469 def create_contents(self
, container
, i
):
470 """Create the widgets for showing the contents of the Aux_seats window."""
471 container
.eb
= gtk
.EventBox()
472 container
.eb
.connect("button_press_event", self
.button_press_cb
)
473 container
.add(container
.eb
)
474 container
.seen_cards
= gtk
.image_new_from_pixbuf(self
.card_images
[0])
475 container
.eb
.add(container
.seen_cards
)
477 def update_contents(self
, container
, i
):
478 if not self
.hud
.cards
.has_key(i
): return
479 cards
= self
.hud
.cards
[i
]
480 n_cards
= self
.has_cards(cards
)
483 # scratch is a working pixbuf, used to assemble the image
484 scratch
= gtk
.gdk
.Pixbuf(gtk
.gdk
.COLORSPACE_RGB
, True, 8,
485 int(self
.params
['card_wd'])*n_cards
,
486 int(self
.params
['card_ht']))
487 x
= 0 # x coord where the next card starts in scratch
489 # concatenate each card image to scratch
490 if card
== None or card
==0:
492 self
.card_images
[card
].copy_area(0, 0,
493 int(self
.params
['card_wd']), int(self
.params
['card_ht']),
495 x
= x
+ int(self
.params
['card_wd'])
496 container
.seen_cards
.set_from_pixbuf(scratch
)
497 container
.resize(1,1)
499 container
.move(self
.positions
[i
][0], self
.positions
[i
][1]) # here is where I move back
500 self
.displayed
= True
502 id = self
.get_id_from_seat(i
)
503 # sc: had KeyError here with new table so added id != None test as a guess:
505 self
.m_windows
[i
].eb
.set_tooltip_text(self
.hud
.stat_dict
[id]['screen_name'])
507 def update_gui(self
, new_hand_id
):
508 """Prepare and show the mucked cards."""
509 if self
.displayed
: self
.hide()
511 # See how many players showed a hand. Skip if only 1 shows (= hero)
513 for (i
, cards
) in self
.hud
.cards
.iteritems():
514 n_cards
= self
.has_cards(cards
)
515 if n_cards
> 0 and i
!= 'common':
520 super(Flop_Mucked
, self
).update_gui(new_hand_id
)
522 if self
.displayed
and float(self
.params
['timeout']) > 0:
524 gobject
.timeout_add(int(1000*float(self
.params
['timeout'])), self
.timed_out
)
527 # this is the callback from the timeout
529 # if timer_on is False the user has cancelled the timer with a click
530 # so just return False to cancel the timer
531 if not self
.timer_on
:
537 def button_press_cb(self
, widget
, event
, *args
):
538 """Handle button clicks in the event boxes."""
540 # shift-any button exposes all the windows and turns off the timer
541 if event
.state
& gtk
.gdk
.SHIFT_MASK
:
542 self
.timer_on
= False
546 if event
.button
== 3: # right button event
549 elif event
.button
== 2: # middle button event
550 if self
.timer_on
== True:
551 self
.timer_on
= False
553 self
.timer_on
= False
556 elif event
.button
== 1: # left button event
557 window
= widget
.get_parent()
558 window
.begin_move_drag(event
.button
, int(event
.x_root
), int(event
.y_root
), event
.time
)
560 def expose_all(self
):
561 for (i
, cards
) in self
.hud
.cards
.iteritems():
562 self
.m_windows
[i
].show()
563 self
.m_windows
[i
].move(self
.positions
[i
][0], self
.positions
[i
][1]) # here is where I move back
564 self
.displayed
= True