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
, card_width
=30, card_height
=42):
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
, card_width
, card_height
)
79 # also pick out a card back and store in [0]
80 card_images
[0] = self
.cropper(pb
, 2, 13, card_width
, card_height
)
82 # cards are 30 wide x 42 high
84 def cropper(self
, pb
, i
, j
, card_width
, card_height
):
85 """Crop out a card image given an FTP deck and the i, j position."""
86 cropped_pb
= gtk
.gdk
.Pixbuf(gtk
.gdk
.COLORSPACE_RGB
, pb
.get_has_alpha(),
87 pb
.get_bits_per_sample(), 30, 42)
88 pb
.copy_area(30*j
, 42*i
, 30, 42, cropped_pb
, 0, 0)
94 """Apply scaling to the the 30w x 42h card image """
95 scaled_pb
= gtk
.gdk
.Pixbuf(gtk
.gdk
.COLORSPACE_RGB
, pb
.get_has_alpha(),
96 pb
.get_bits_per_sample(),
97 card_width
, card_height
)
98 scaled_card
= cropped_pb
.scale_simple(card_width
, card_height
,
99 gtk
.gdk
.INTERP_BILINEAR
)
101 scaled_card
.copy_area(0, 0, self
.card_width
, self
.card_height
,
105 def has_cards(self
, cards
):
106 """Returns the number of cards in the list."""
109 if c
!= None and c
> 0: n
= n
+ 1
112 def get_id_from_seat(self
, seat
):
113 """Determine player id from seat number, given stat_dict."""
114 for id, dict in self
.hud
.stat_dict
.iteritems():
115 if seat
== dict['seat']:
119 class Stud_mucked(Aux_Window
):
120 def __init__(self
, hud
, config
, params
):
122 self
.hud
= hud
# hud object that this aux window supports
123 self
.config
= config
# configuration object for this aux window to use
124 self
.params
= params
# hash aux params from config
127 site_params
= self
.config
.get_site_parameters(self
.hud
.site
)
128 self
.hero
= site_params
['screen_name']
132 self
.mucked_list
= Stud_list(self
, params
, config
, self
.hero
)
133 self
.mucked_cards
= Stud_cards(self
, params
, config
)
134 self
.mucked_list
.mucked_cards
= self
.mucked_cards
138 self
.container
= gtk
.Window()
139 self
.vbox
= gtk
.VBox()
140 self
.container
.add(self
.vbox
)
141 self
.container
.set_title(self
.hud
.table
.name
)
143 self
.mucked_list
.create(self
.vbox
)
144 self
.mucked_cards
.create(self
.vbox
)
145 self
.container
.show_all()
147 def update_data(self
, new_hand_id
, db_connection
):
148 # uncomment next line when action is available in the db
149 # self.mucked_cards.update_data(new_hand_id, db_connection)
150 self
.mucked_list
.update_data(new_hand_id
, db_connection
)
152 def update_gui(self
, new_hand_id
):
153 self
.mucked_cards
.update_gui(new_hand_id
)
154 self
.mucked_list
.update_gui(new_hand_id
)
157 def __init__(self
, parent
, params
, config
, hero
):
164 def create(self
, container
):
165 # set up a scrolled window to hold the listbox
166 self
.container
= container
167 self
.scrolled_window
= gtk
.ScrolledWindow()
168 self
.scrolled_window
.set_policy(gtk
.POLICY_NEVER
, gtk
.POLICY_ALWAYS
)
169 self
.container
.add(self
.scrolled_window
)
171 # create a ListStore to use as the model
172 self
.liststore
= gtk
.ListStore(str, str, str, str)
173 self
.treeview
= gtk
.TreeView(self
.liststore
)
174 self
.tvcolumn0
= gtk
.TreeViewColumn('HandID')
175 self
.tvcolumn1
= gtk
.TreeViewColumn('Cards')
176 self
.tvcolumn2
= gtk
.TreeViewColumn('Net')
177 self
.tvcolumn3
= gtk
.TreeViewColumn('Winner')
179 # add tvcolumn to treeview
180 self
.treeview
.append_column(self
.tvcolumn0
)
181 self
.treeview
.append_column(self
.tvcolumn1
)
182 self
.treeview
.append_column(self
.tvcolumn2
)
183 self
.treeview
.append_column(self
.tvcolumn3
)
185 # create a CellRendererText to render the data
186 self
.cell
= gtk
.CellRendererText()
188 # add the cell to the tvcolumn and allow it to expand
189 self
.tvcolumn0
.pack_start(self
.cell
, True)
190 self
.tvcolumn1
.pack_start(self
.cell
, True)
191 self
.tvcolumn2
.pack_start(self
.cell
, True)
192 self
.tvcolumn3
.pack_start(self
.cell
, True)
193 self
.tvcolumn0
.add_attribute(self
.cell
, 'text', 0)
194 self
.tvcolumn1
.add_attribute(self
.cell
, 'text', 1)
195 self
.tvcolumn2
.add_attribute(self
.cell
, 'text', 2)
196 self
.tvcolumn3
.add_attribute(self
.cell
, 'text', 3)
197 # resize the cols if nec
198 self
.tvcolumn0
.set_resizable(True)
199 self
.tvcolumn1
.set_resizable(True)
200 self
.tvcolumn2
.set_resizable(True)
201 self
.tvcolumn3
.set_resizable(True)
202 self
.treeview
.connect("row-activated", self
.activated_event
)
204 self
.scrolled_window
.add_with_viewport(self
.treeview
)
206 def activated_event(self
, path
, column
, data
=None):
208 # sel = self.treeview.get_selection()
209 # (model, iter) = sel.get_selected()
210 # self.mucked_cards.update_data(model.get_value(iter, 0))
211 # self.mucked_cards.update_gui(model.get_value(iter, 0))
213 def update_data(self
, new_hand_id
, db_connection
):
214 """Updates the data needed for the list box."""
216 # db_connection = Database.Database(self.config, 'fpdb', '')
217 self
.winners
= db_connection
.get_winners_from_hand(new_hand_id
)
220 for player
in self
.winners
.keys():
221 pot
= pot
+ int(self
.winners
[player
])
222 if not winners
== '':
223 winners
= winners
+ ", "
224 winners
= winners
+ player
225 pot_dec
= "%.2f" % (float(pot
)/100)
227 hero_cards
= self
.get_hero_cards(self
.parent
.hero
, self
.parent
.hud
.cards
)
228 self
.info_row
= ((new_hand_id
, hero_cards
, pot_dec
, winners
), )
230 def get_hero_cards(self
, hero
, cards
):
231 """Formats the hero cards for inclusion in the tree."""
232 trans
= ('0', 'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
236 # find the hero's seat from the stat_dict
237 for stat
in self
.parent
.hud
.stat_dict
.itervalues():
238 if stat
['screen_name'] == hero
:
239 return Card
.valueSuitFromCard(self
.parent
.hud
.cards
[stat
['seat']][0]) +\
240 Card
.valueSuitFromCard(self
.parent
.hud
.cards
[stat
['seat']][1]) +\
241 Card
.valueSuitFromCard(self
.parent
.hud
.cards
[stat
['seat']][2])
244 def update_gui(self
, new_hand_id
):
245 iter = self
.liststore
.append(self
.info_row
[0])
246 sel
= self
.treeview
.get_selection()
247 #sel.select_iter(iter)
249 vadj
= self
.scrolled_window
.get_vadjustment()
250 #vadj.set_value(vadj.upper)
253 def __init__(self
, parent
, params
, config
):
258 # self.db_name = db_name
260 self
.card_images
= self
.parent
.get_card_images()
262 self
.grid_contents
= {}
268 def create(self
, container
):
269 self
.container
= container
270 self
.grid
= gtk
.Table(self
.rows
, self
.cols
+ 4, homogeneous
= False)
272 for r
in range(0, self
.rows
):
273 for c
in range(0, self
.cols
):
274 self
.seen_cards
[(c
, r
)] = gtk
.image_new_from_pixbuf(self
.card_images
[(0)])
275 self
.eb
[(c
, r
)]= gtk
.EventBox()
277 # set up the contents for the cells
278 for r
in range(0, self
.rows
):
279 self
.grid_contents
[( 0, r
)] = gtk
.Label("%d" % (r
+ 1))
280 self
.grid_contents
[( 1, r
)] = gtk
.Label("player %d" % (r
+ 1))
281 self
.grid_contents
[( 1, r
)].set_property("width-chars", 12)
282 self
.grid_contents
[( 4, r
)] = gtk
.Label("-")
283 self
.grid_contents
[( 9, r
)] = gtk
.Label("-")
284 self
.grid_contents
[( 2, r
)] = self
.eb
[( 0, r
)]
285 self
.grid_contents
[( 3, r
)] = self
.eb
[( 1, r
)]
286 self
.grid_contents
[( 5, r
)] = self
.eb
[( 2, r
)]
287 self
.grid_contents
[( 6, r
)] = self
.eb
[( 3, r
)]
288 self
.grid_contents
[( 7, r
)] = self
.eb
[( 4, r
)]
289 self
.grid_contents
[( 8, r
)] = self
.eb
[( 5, r
)]
290 self
.grid_contents
[(10, r
)] = self
.eb
[( 6, r
)]
291 for c
in range(0, self
.cols
):
292 self
.eb
[(c
, r
)].add(self
.seen_cards
[(c
, r
)])
294 # add the cell contents to the table
295 for c
in range(0, self
.cols
+ 4):
296 for r
in range(0, self
.rows
):
297 self
.grid
.attach(self
.grid_contents
[(c
, r
)], c
, c
+1, r
, r
+1, xpadding
= 1, ypadding
= 1)
299 self
.container
.add(self
.grid
)
301 def update_data(self
, new_hand_id
, db_connection
):
303 action
= db_connection
.get_action_from_hand(new_hand_id
)
304 for street
in action
:
307 temp
= temp
+ act
[0] + " " + act
[1] + "s "
310 temp
= temp
+ "%4.2f\n" % (float(act
[2])/100)
312 temp
= temp
+ "%d\n" % (act
[2]/100)
315 self
.tips
.append(temp
)
317 def update_gui(self
, new_hand_id
):
319 for c
, cards
in self
.parent
.hud
.cards
.iteritems():
320 if c
== 'common': continue
321 self
.grid_contents
[(1, c
- 1)].set_text(self
.get_screen_name(c
))
322 for i
in ((0, cards
[0]), (1, cards
[1]), (2, cards
[2]), (3, cards
[3]),
323 (4, cards
[4]), (5, cards
[5]), (6, cards
[6])):
325 self
.seen_cards
[(i
[0], c
- 1)]. \
326 set_from_pixbuf(self
.card_images
[i
[1]])
327 ## action in tool tips for 3rd street cards
329 for r
in range(0, self
.rows
):
330 #self.eb[(c, r)].set_tooltip_text(self.tips[0])
333 # action in tools tips for later streets
334 round_to_col
= (0, 3, 4, 5, 6)
335 #for round in range(1, len(self.tips)):
336 # for r in range(0, self.rows):
337 # self.eb[(round_to_col[round], r)].set_tooltip_text(self.tips[round])
339 def get_screen_name(self
, seat_no
):
340 """Gets and returns the screen name from stat_dict, given seat number."""
341 for k
in self
.parent
.hud
.stat_dict
.keys():
342 if self
.parent
.hud
.stat_dict
[k
]['seat'] == seat_no
:
343 return self
.parent
.hud
.stat_dict
[k
]['screen_name']
347 for r
in range(0, self
.rows
):
348 self
.grid_contents
[(1, r
)].set_text(" ")
349 for c
in range(0, 7):
350 self
.seen_cards
[(c
, r
)].set_from_pixbuf(self
.card_images
[0])
351 self
.eb
[(c
, r
)].set_tooltip_text('')
353 class Seat_Window(gtk
.Window
):
354 """Subclass gtk.Window for the seat windows."""
355 def __init__(self
, aw
= None):
356 super(Seat_Window
, self
).__init
__()
359 class Aux_Seats(Aux_Window
):
360 """A super class to display an aux_window at each seat."""
362 def __init__(self
, hud
, config
, params
):
363 self
.hud
= hud
# hud object that this aux window supports
364 self
.config
= config
# configuration object for this aux window to use
365 self
.params
= params
# dict aux params from config
366 self
.positions
= {} # dict of window positions
367 self
.displayed
= False # the seat windows are displayed
368 self
.uses_timer
= False # the Aux_seats object uses a timer to control hiding
369 self
.timer_on
= False # bool = Ture if the timeout for removing the cards is on
371 self
.aw_window_type
= Seat_Window
373 # placeholders that should be overridden--so we don't throw errors
374 def create_contents(self
): pass
375 def update_contents(self
): pass
377 def update_card_positions(self
):
378 # self.adj does not exist until .create() has been run
381 except AttributeError:
383 loc
= self
.config
.get_aux_locations(self
.params
['name'], int(self
.hud
.max))
384 width
= self
.hud
.table
.width
385 height
= self
.hud
.table
.height
387 for i
in (range(1, self
.hud
.max + 1) + ['common']):
389 (x
, y
) = self
.params
['layout'][self
.hud
.max].common
392 # self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y)
393 self
.positions
[i
] = self
.card_positions(x
, self
.hud
.table
.x
, y
, self
.hud
.table
.y
)
394 self
.m_windows
[i
].move(self
.positions
[i
][0], self
.positions
[i
][1])
397 self
.adj
= self
.hud
.adj_seats(0, self
.config
) # move adj_seats to aux and get rid of it in Hud.py
398 loc
= self
.config
.get_aux_locations(self
.params
['name'], int(self
.hud
.max))
400 self
.m_windows
= {} # windows to put the card images in
401 width
= self
.hud
.table
.width
402 height
= self
.hud
.table
.height
403 for i
in (range(1, self
.hud
.max + 1) + ['common']):
405 (x
, y
) = self
.params
['layout'][self
.hud
.max].common
407 (x
, y
) = loc
[self
.adj
[i
]]
408 self
.m_windows
[i
] = self
.aw_window_type(self
)
409 self
.m_windows
[i
].set_decorated(False)
410 self
.m_windows
[i
].set_property("skip-taskbar-hint", True)
411 self
.m_windows
[i
].set_transient_for(self
.hud
.main_window
) # FIXME: shouldn't this be the table window??
412 self
.m_windows
[i
].set_focus_on_map(False)
413 self
.m_windows
[i
].connect("configure_event", self
.configure_event_cb
, i
)
414 # self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y)
415 self
.positions
[i
] = self
.card_positions(x
, self
.hud
.table
.x
, y
, self
.hud
.table
.y
)
416 self
.m_windows
[i
].move(self
.positions
[i
][0], self
.positions
[i
][1])
417 if self
.params
.has_key('opacity'):
418 self
.m_windows
[i
].set_opacity(float(self
.params
['opacity']))
420 # the create_contents method is supplied by the subclass
421 self
.create_contents(self
.m_windows
[i
], i
)
423 self
.m_windows
[i
].show_all()
425 self
.m_windows
[i
].hide()
428 def card_positions(self
, x
, table_x
, y
, table_y
):
429 _x
= int(x
) + int(table_x
)
430 _y
= int(y
) + int(table_y
)
434 def update_gui(self
, new_hand_id
):
435 """Update the gui, LDO."""
436 for i
in self
.m_windows
.keys():
437 self
.update_contents(self
.m_windows
[i
], i
)
439 # Methods likely to be of use for any Seat_Window implementation
441 """Destroy all of the seat windows."""
443 for i
in self
.m_windows
.keys():
444 self
.m_windows
[i
].destroy()
445 del(self
.m_windows
[i
])
446 except AttributeError:
449 # Methods likely to be useful for mucked card windows (or similar) only
451 """Hide the seat windows."""
452 for (i
, w
) in self
.m_windows
.iteritems():
454 self
.displayed
= False
456 def save_layout(self
, *args
):
457 """Save new layout back to the aux element in the config file."""
459 # print "adj =", self.adj
460 witdh
= self
.hud
.table
.width
461 height
= self
.hud
.table
.height
462 for (i
, pos
) in self
.positions
.iteritems():
464 # new_locs[self.adj[int(i)]] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
465 new_locs
[self
.adj
[int(i
)]] = ((pos
[0] - self
.hud
.table
.x
), (pos
[1] - self
.hud
.table
.y
) )
467 # new_locs[i] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
468 new_locs
[i
] = ((pos
[0] - self
.hud
.table
.x
), (pos
[1] - self
.hud
.table
.y
))
469 self
.config
.edit_aux_layout(self
.params
['name'], self
.hud
.max, locations
= new_locs
)
471 def configure_event_cb(self
, widget
, event
, i
, *args
):
472 self
.positions
[i
] = widget
.get_position()
473 # self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
475 class Flop_Mucked(Aux_Seats
):
476 """Aux_Window class for displaying mucked cards for flop games."""
478 def __init__(self
, hud
, config
, params
):
479 super(Flop_Mucked
, self
).__init
__(hud
, config
, params
)
481 self
.card_height
= int(self
.params
['card_ht'])
482 if (self
.card_height
> 84): self
.card_height
= 84
483 if (self
.card_height
< 21): self
.card_height
= 21
484 self
.card_width
= int(30. * (self
.card_height
/ 42.))
486 self
.card_images
= self
.get_card_images(self
.card_width
, self
.card_height
)
487 self
.uses_timer
= True # this Aux_seats object uses a timer to control hiding
489 def create_contents(self
, container
, i
):
490 """Create the widgets for showing the contents of the Aux_seats window."""
491 container
.eb
= gtk
.EventBox()
492 container
.eb
.connect("button_press_event", self
.button_press_cb
)
493 container
.add(container
.eb
)
494 container
.seen_cards
= gtk
.image_new_from_pixbuf(self
.card_images
[0])
495 container
.eb
.add(container
.seen_cards
)
497 def update_contents(self
, container
, i
):
498 if not self
.hud
.cards
.has_key(i
): return
499 cards
= self
.hud
.cards
[i
]
500 n_cards
= self
.has_cards(cards
)
503 # scratch is a working pixbuf, used to assemble the image
504 scratch
= gtk
.gdk
.Pixbuf(gtk
.gdk
.COLORSPACE_RGB
, True, 8,
505 self
.card_width
* n_cards
,
507 x
= 0 # x coord where the next card starts in scratch
509 # concatenate each card image to scratch
510 if card
== None or card
==0:
513 self
.card_images
[card
].copy_area(0, 0,
514 self
.card_width
, self
.card_height
,
516 x
= x
+ self
.card_width
517 container
.seen_cards
.set_from_pixbuf(scratch
)
518 container
.resize(1,1)
520 container
.move(self
.positions
[i
][0], self
.positions
[i
][1]) # here is where I move back
521 self
.displayed
= True
523 id = self
.get_id_from_seat(i
)
524 # sc: had KeyError here with new table so added id != None test as a guess:
526 self
.m_windows
[i
].eb
.set_tooltip_text(self
.hud
.stat_dict
[id]['screen_name'])
528 def update_gui(self
, new_hand_id
):
529 """Prepare and show the mucked cards."""
530 if self
.displayed
: self
.hide()
532 # See how many players showed a hand. Skip if only 1 shows (= hero)
534 for (i
, cards
) in self
.hud
.cards
.iteritems():
535 n_cards
= self
.has_cards(cards
)
536 if n_cards
> 0 and i
!= 'common':
541 super(Flop_Mucked
, self
).update_gui(new_hand_id
)
543 if self
.displayed
and float(self
.params
['timeout']) > 0:
545 gobject
.timeout_add(int(1000*float(self
.params
['timeout'])), self
.timed_out
)
548 # this is the callback from the timeout
550 # if timer_on is False the user has cancelled the timer with a click
551 # so just return False to cancel the timer
552 if not self
.timer_on
:
558 def button_press_cb(self
, widget
, event
, *args
):
559 """Handle button clicks in the event boxes."""
561 # shift-any button exposes all the windows and turns off the timer
562 if event
.state
& gtk
.gdk
.SHIFT_MASK
:
563 self
.timer_on
= False
567 if event
.button
== 3: # right button event
570 elif event
.button
== 2: # middle button event
571 if self
.timer_on
== True:
572 self
.timer_on
= False
574 self
.timer_on
= False
577 elif event
.button
== 1: # left button event
578 window
= widget
.get_parent()
579 window
.begin_move_drag(event
.button
, int(event
.x_root
), int(event
.y_root
), event
.time
)
581 def expose_all(self
):
582 for (i
, cards
) in self
.hud
.cards
.iteritems():
583 self
.m_windows
[i
].show()
584 self
.m_windows
[i
].move(self
.positions
[i
][0], self
.positions
[i
][1]) # here is where I move back
585 self
.displayed
= True