Use debian 2.7 only
[fpbd-bostik.git] / pyfpdb / HUD_main.pyw
blob017f067e4820cc82cb77f21e85e2b119a723320f
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 # Copyright 2008-2011, Ray E. Barker
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 ########################################################################
22 """Hud_main.py
24 Main for FreePokerTools HUD.
25 """
26 import L10n
27 _ = L10n.init_translation()
29 # Standard Library modules
30 import sys
31 import os
32 import traceback
33 import thread
34 import time
35 import string
36 import logging
38 # pyGTK modules
39 import gtk
40 import gobject
42 # FreePokerTools modules
43 import Configuration
44 import Database
45 import Hud
46 import Options
48 (options, argv) = Options.fpdb_options()
50 # get the correct module for the current os
51 if sys.platform[0:5] == 'linux':
52 import XTables as Tables
53 elif sys.platform == 'darwin':
54 import OSXTables as Tables
55 else: # This is bad--figure out the values for the various windows flavors
56 is_windows = True
57 import WinTables as Tables
59 # get config and set up logger
60 Configuration.set_logfile("HUD-log.txt")
61 c = Configuration.Config(file=options.config, dbname=options.dbname)
62 log = logging.getLogger("hud")
64 class HUD_main(object):
65 """A main() object to own both the read_stdin thread and the gui."""
66 # This class mainly provides state for controlling the multiple HUDs.
68 def __init__(self, db_name='fpdb'):
69 self.db_name = db_name
70 self.config = c
71 log.info(_("HUD_main starting") + ": " + _("Using db name = %s") % (db_name))
73 try:
74 if not options.errorsToConsole:
75 fileName = os.path.join(self.config.dir_log, 'HUD-errors.txt')
76 log.info(_("Note: error output is being diverted to %s.") % fileName)
77 log.info(_("Any major error will be reported there _only_."))
78 errorFile = open(fileName, 'w', 0)
79 sys.stderr = errorFile
80 log.info(_("HUD_main starting"))
82 self.hud_dict = {}
83 self.hud_params = self.config.get_hud_ui_parameters()
85 # a thread to read stdin
86 gobject.threads_init() # this is required
87 thread.start_new_thread(self.read_stdin, ()) # starts the thread
89 # a main window
90 self.main_window = gtk.Window()
92 if options.minimized:
93 self.main_window.iconify()
94 if options.hidden:
95 self.main_window.hide()
97 if options.xloc is not None or options.yloc is not None:
98 if options.xloc is None:
99 options.xloc = 0
100 if options.yloc is None:
101 options.yloc = 0
102 self.main_window.move(options.xloc,options.yloc)
103 self.main_window.connect("client_moved", self.client_moved)
104 self.main_window.connect("client_resized", self.client_resized)
105 self.main_window.connect("client_destroyed", self.client_destroyed)
106 self.main_window.connect("game_changed", self.game_changed)
107 self.main_window.connect("table_changed", self.table_changed)
108 self.main_window.connect("destroy", self.destroy)
109 self.vb = gtk.VBox()
110 self.label = gtk.Label(_('Closing this window will exit from the HUD.'))
111 self.vb.add(self.label)
112 self.main_window.add(self.vb)
113 self.main_window.set_title("HUD Main Window")
114 cards = os.path.join(os.getcwd(), '..','gfx','fpdb-cards.png')
115 if os.path.exists(cards):
116 self.main_window.set_icon_from_file(cards)
117 elif os.path.exists('/usr/share/pixmaps/fpdb-cards.png'):
118 self.main_window.set_icon_from_file('/usr/share/pixmaps/fpdb-cards.png')
120 if not options.hidden:
121 self.main_window.show_all()
122 gobject.timeout_add(800, self.check_tables)
124 except:
125 log.exception(_("Error initializing main_window"))
126 gtk.main_quit() # we're hosed, just terminate
128 def client_moved(self, widget, hud):
129 log.debug("client_moved event")
130 hud.up_update_table_position()
132 def client_resized(self, widget, hud):
133 log.debug("client_resized event")
134 gobject.idle_add(idle_resize, hud)
136 def client_destroyed(self, widget, hud): # call back for terminating the main eventloop
137 log.debug("client_destroyed event")
138 self.kill_hud(None, hud.table.key)
140 def game_changed(self, widget, hud):
141 print "hud_main: " + _("Game changed.")
143 def table_changed(self, widget, hud):
144 print "hud_main: " + _("Table changed")
145 self.kill_hud(None, hud.table.key)
147 def destroy(self, *args): # call back for terminating the main eventloop
148 log.info(_("Quitting normally"))
149 gtk.main_quit()
151 def kill_hud(self, event, table):
152 log.debug("kill_hud event")
153 gobject.idle_add(idle_kill, self, table)
155 def check_tables(self):
156 for hud in self.hud_dict.keys():
157 self.hud_dict[hud].table.check_table(self.hud_dict[hud])
158 return True
160 def create_HUD(self, new_hand_id, table, temp_key, max, poker_game, type, stat_dict, cards):
161 """type is "ring" or "tour" used to set hud_params"""
163 self.hud_dict[temp_key] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection)
164 self.hud_dict[temp_key].table_name = temp_key
165 self.hud_dict[temp_key].stat_dict = stat_dict
166 self.hud_dict[temp_key].cards = cards
167 table.hud = self.hud_dict[temp_key]
169 # set agg_bb_mult so that aggregate_tour and aggregate_ring can be ignored,
170 # agg_bb_mult == 1 means no aggregation after these if statements:
171 if type == "tour" and self.hud_params['aggregate_tour'] == False:
172 self.hud_dict[temp_key].hud_params['agg_bb_mult'] = 1
173 elif type == "ring" and self.hud_params['aggregate_ring'] == False:
174 self.hud_dict[temp_key].hud_params['agg_bb_mult'] = 1
175 if type == "tour" and self.hud_params['h_aggregate_tour'] == False:
176 self.hud_dict[temp_key].hud_params['h_agg_bb_mult'] = 1
177 elif type == "ring" and self.hud_params['h_aggregate_ring'] == False:
178 self.hud_dict[temp_key].hud_params['h_agg_bb_mult'] = 1
179 # sqlcoder: I forget why these are set to true (aren't they ignored from now on?)
180 # but I think it's needed:
181 self.hud_params['aggregate_ring'] = True
182 self.hud_params['h_aggregate_ring'] = True
183 # so maybe the tour ones should be set as well? does this fix the bug I see mentioned?
184 self.hud_params['aggregate_tour'] = True
185 self.hud_params['h_aggregate_tour'] = True
187 [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows]
188 gobject.idle_add(idle_create, self, new_hand_id, table, temp_key, max, poker_game, type, stat_dict, cards)
190 def update_HUD(self, new_hand_id, table_name, config):
191 """Update a HUD gui from inside the non-gui read_stdin thread."""
192 gobject.idle_add(idle_update, self, new_hand_id, table_name, config)
194 def read_stdin(self): # This is the thread function
195 """Do all the non-gui heavy lifting for the HUD program."""
197 # This db connection is for the read_stdin thread only. It should not
198 # be passed to HUDs for use in the gui thread. HUD objects should not
199 # need their own access to the database, but should open their own
200 # if it is required.
201 self.db_connection = Database.Database(self.config)
203 # get hero's screen names and player ids
204 self.hero, self.hero_ids = {}, {}
205 found = False
207 while 1: # wait for a new hand number on stdin
208 new_hand_id = sys.stdin.readline()
209 new_hand_id = string.rstrip(new_hand_id)
210 log.debug(_("Received hand no %s") % new_hand_id)
211 if new_hand_id == "": # blank line means quit
212 self.destroy()
213 break # this thread is not always killed immediately with gtk.main_quit()
215 # This block cannot be hoisted outside the while loop, because it would
216 # cause a problem when auto importing into an empty db.
218 # FIXME: This doesn't work in the case of the player playing on 2
219 # sites at once (???) Eratosthenes
220 if not found:
221 for site in self.config.get_supported_sites():
222 result = self.db_connection.get_site_id(site)
223 if result:
224 site_id = result[0][0]
225 self.hero[site_id] = self.config.supported_sites[site].screen_name
226 self.hero_ids[site_id] = self.db_connection.get_player_id(self.config, site, self.hero[site_id])
227 if self.hero_ids[site_id] is not None:
228 found = True
229 else:
230 self.hero_ids[site_id] = -1
232 # get basic info about the new hand from the db
233 # if there is a db error, complain, skip hand, and proceed
234 log.info("HUD_main.read_stdin: " + _("Hand processing starting."))
235 try:
236 (table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \
237 self.db_connection.get_table_info(new_hand_id)
238 except Exception:
239 log.exception(_("database error: skipping %s") % new_hand_id)
240 continue
242 if type == "tour": # hand is from a tournament
243 temp_key = "%s Table %s" % (tour_number, tab_number)
244 else:
245 temp_key = table_name
247 # Update an existing HUD
248 if temp_key in self.hud_dict:
249 # get stats using hud's specific params and get cards
250 self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days']
251 , self.hud_dict[temp_key].hud_params['h_hud_days'])
252 stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params,
253 self.hero_ids[site_id], num_seats)
255 try:
256 self.hud_dict[temp_key].stat_dict = stat_dict
257 except KeyError: # HUD instance has been killed off, key is stale
258 log.error(_('%s was not found') % ("hud_dict[%s]" % temp_key))
259 log.error(_('will not send hand'))
260 # Unlocks table, copied from end of function
261 self.db_connection.connection.rollback()
262 return
264 self.hud_dict[temp_key].cards = self.get_cards(new_hand_id)
265 [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows]
266 self.update_HUD(new_hand_id, temp_key, self.config)
268 # Or create a new HUD
269 else:
270 # get stats using default params--also get cards
271 self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] )
272 stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params,
273 self.hero_ids[site_id], num_seats)
274 cards = self.get_cards(new_hand_id)
275 table_kwargs = dict(table_name=table_name, tournament=tour_number, table_number=tab_number)
276 tablewindow = Tables.Table(self.config, site_name, **table_kwargs)
277 if tablewindow.number is None:
278 # If no client window is found on the screen, complain and continue
279 if type == "tour":
280 table_name = "%s %s" % (tour_number, tab_number)
281 log.error(_("HUD create: table name %s not found, skipping.") % table_name)
282 else:
283 tablewindow.key = temp_key
284 tablewindow.max = max
285 tablewindow.site = site_name
286 # Test that the table window still exists
287 if hasattr(tablewindow, 'number'):
288 self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards)
289 else:
290 log.error(_('Table "%s" no longer exists') % table_name)
291 return
293 self.db_connection.connection.rollback()
294 if type == "tour":
295 try:
296 self.hud_dict[temp_key].table.check_table_no(self.hud_dict[temp_key])
297 except KeyError:
298 pass
300 def get_cards(self, new_hand_id):
301 cards = self.db_connection.get_cards(new_hand_id)
302 comm_cards = self.db_connection.get_common_cards(new_hand_id)
303 if comm_cards != {}: # stud!
304 cards['common'] = comm_cards['common']
305 return cards
306 ######################################################################
307 # idle FUNCTIONS
309 # These are passed to the event loop by the non-gui thread to do
310 # gui things in a thread-safe way. They are passed to the event
311 # loop using the gobject.idle_add() function.
313 # A general rule for gtk is that only 1 thread should be messing
314 # with the gui.
316 def idle_resize(hud):
317 gtk.gdk.threads_enter()
318 try:
319 [aw.update_card_positions() for aw in hud.aux_windows]
320 hud.resize_windows()
321 except:
322 log.exception(_("Error resizing HUD for table: %s.") % hud.table.title)
323 finally:
324 gtk.gdk.threads_leave()
326 def idle_kill(hud_main, table):
327 gtk.gdk.threads_enter()
328 try:
329 if table in hud_main.hud_dict:
330 hud_main.vb.remove(hud_main.hud_dict[table].tablehudlabel)
331 hud_main.hud_dict[table].main_window.destroy()
332 hud_main.hud_dict[table].kill()
333 del(hud_main.hud_dict[table])
334 hud_main.main_window.resize(1, 1)
335 except:
336 log.exception(_("Error killing HUD for table: %s.") % table.title)
337 finally:
338 gtk.gdk.threads_leave()
340 def idle_create(hud_main, new_hand_id, table, temp_key, max, poker_game, type, stat_dict, cards):
342 gtk.gdk.threads_enter()
343 try:
344 if table.gdkhandle is not None: # on windows this should already be set
345 table.gdkhandle = gtk.gdk.window_foreign_new(table.number)
346 newlabel = gtk.Label("%s - %s" % (table.site, temp_key))
347 hud_main.vb.add(newlabel)
348 newlabel.show()
349 hud_main.main_window.resize_children()
351 hud_main.hud_dict[temp_key].tablehudlabel = newlabel
352 hud_main.hud_dict[temp_key].create(new_hand_id, hud_main.config, stat_dict, cards)
353 for m in hud_main.hud_dict[temp_key].aux_windows:
354 m.create()
355 m.update_gui(new_hand_id)
356 hud_main.hud_dict[temp_key].update(new_hand_id, hud_main.config)
357 hud_main.hud_dict[temp_key].reposition_windows()
358 except:
359 log.exception(_("Error creating HUD for hand %s.") % new_hand_id)
360 finally:
361 gtk.gdk.threads_leave()
362 return False
364 def idle_update(hud_main, new_hand_id, table_name, config):
365 gtk.gdk.threads_enter()
366 try:
367 hud_main.hud_dict[table_name].update(new_hand_id, config)
368 [aw.update_gui(new_hand_id) for aw in hud_main.hud_dict[table_name].aux_windows]
369 except:
370 log.exception(_("Error updating HUD for hand %s.") % new_hand_id)
371 finally:
372 gtk.gdk.threads_leave()
373 return False
375 if __name__== "__main__":
377 # start the HUD_main object
378 hm = HUD_main(db_name = options.dbname)
380 # start the event loop
381 gtk.main()