diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 154edec5..edf99cf0 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -165,19 +165,11 @@ class Database: c = self.connection.cursor() if aggregate: - query = 'get_stats_from_hand' - subs = (hand, hand) - else: query = 'get_stats_from_hand_aggregated' subs = (hand, hand, hand) - -# get the players in the hand and their seats - c.execute(self.sql.query['get_players_from_hand'], (hand, )) - names = {} - seats = {} - for row in c.fetchall(): - names[row[0]] = row[2] - seats[row[0]] = row[1] + else: + query = 'get_stats_from_hand' + subs = (hand, hand) # now get the stats c.execute(self.sql.query[query], subs) @@ -187,9 +179,6 @@ class Database: t_dict = {} for name, val in zip(colnames, row): t_dict[name] = val -# print t_dict - t_dict['screen_name'] = names[t_dict['player_id']] - t_dict['seat'] = seats[t_dict['player_id']] stat_dict[t_dict['player_id']] = t_dict return stat_dict diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 38535ad1..eccba8cd 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -106,7 +106,8 @@ class GuiAutoImport (threading.Thread): """Callback for timer to do an import iteration.""" if self.doAutoImportBool: self.importer.runUpdated() - print "GuiAutoImport.import_dir done" + sys.stdout.write(".") + sys.stdout.flush() return True else: return False @@ -128,12 +129,12 @@ class GuiAutoImport (threading.Thread): widget.set_label(u'Stop Autoimport') if self.pipe_to_hud is None: if os.name == 'nt': - command = "python HUD_run_me.py" + " %s" % (self.database) + command = "python HUD_main.py" + " %s" % (self.database) bs = 0 # windows is not happy with line buffing here self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, universal_newlines=True) else: - command = self.config.execution_path('HUD_run_me.py') + command = self.config.execution_path('HUD_main.py') bs = 1 self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, universal_newlines=True) diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index af7e4e9f..a8b800d1 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -26,7 +26,6 @@ Main for FreePokerTools HUD. # to do hud to echo, but ignore non numbers # to do no stat window for hero # to do things to add to config.xml -# to do font and size # Standard Library modules import sys @@ -47,141 +46,153 @@ import Database import Tables import Hud -# global dict for keeping the huds -hud_dict = {} -eb = 0 # our former event-box +class HUD_main(object): + """A main() object to own both the read_stdin thread and the gui.""" +# This class mainly provides state for controlling the multiple HUDs. -db_connection = 0; -config = 0; + def __init__(self, db_name = 'fpdb'): + self.db_name = db_name + self.config = Configuration.Config() + self.hud_dict = {} -def destroy(*args): # call back for terminating the main eventloop - gtk.main_quit() +# a thread to read stdin + gobject.threads_init() # this is required + thread.start_new_thread(self.read_stdin, ()) # starts the thread -def create_HUD(new_hand_id, table, db_name, table_name, max, poker_game, db_connection, config, stat_dict): - global hud_dict, eb +# a main window + self.main_window = gtk.Window() + self.main_window.connect("destroy", self.destroy) + self.vb = gtk.VBox() + self.label = gtk.Label('Closing this window will exit from the HUD.') + self.vb.add(self.label) + self.main_window.add(self.vb) + self.main_window.set_title("HUD Main Window") + self.main_window.show_all() + + def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() - def idle_func(): - global hud_dict, eb + def create_HUD(self, new_hand_id, table, table_name, max, poker_game, is_tournament, stat_dict): - gtk.gdk.threads_enter() - try: - newlabel = gtk.Label(table.site + " - " + table_name) - eb.add(newlabel) - newlabel.show() - - hud_dict[table_name] = Hud.Hud(table, max, poker_game, config, db_connection) - hud_dict[table_name].tablehudlabel = newlabel - hud_dict[table_name].create(new_hand_id, config) - for m in hud_dict[table_name].aux_windows: - m.update_data(new_hand_id, db_connection) - m.update_gui(new_hand_id) - hud_dict[table_name].update(new_hand_id, config, stat_dict) - hud_dict[table_name].reposition_windows() - return False - finally: - gtk.gdk.threads_leave() - gobject.idle_add(idle_func) - -def update_HUD(new_hand_id, table_name, config, stat_dict): - global hud_dict - def idle_func(): - gtk.gdk.threads_enter() - try: - hud_dict[table_name].update(new_hand_id, config, stat_dict) - for m in hud_dict[table_name].aux_windows: - m.update_gui(new_hand_id) - return False - finally: - gtk.gdk.threads_leave() - gobject.idle_add(idle_func) - -def HUD_removed(tablename): - global hud_dict, eb + def idle_func(): + + gtk.gdk.threads_enter() + try: + newlabel = gtk.Label(table.site + " - " + table_name) + self.vb.add(newlabel) + newlabel.show() - tablename = Tables.clean_title(tablename) - # TODO: There's a potential problem here somewhere, that this hacks around .. the table_name as being passed to HUD_create is cleaned, - # but the table.name as being passed here is not cleaned. I don't know why. -eric - if tablename in hud_dict and hud_dict[tablename].deleted: - eb.remove(hud_dict[tablename].tablehudlabel) - del hud_dict[tablename] - return False + self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) + self.hud_dict[table_name].tablehudlabel = newlabel + self.hud_dict[table_name].create(new_hand_id, self.config, stat_dict) + for m in self.hud_dict[table_name].aux_windows: + m.update_data(new_hand_id, self.db_connection) + m.update_gui(new_hand_id) + self.hud_dict[table_name].update(new_hand_id, self.config, stat_dict) + self.hud_dict[table_name].reposition_windows() + return False + finally: + gtk.gdk.threads_leave() + gobject.idle_add(idle_func) - return True - -def read_stdin(): # This is the thread function - global hud_dict, eb - - db_connection = Database.Database(config, db_name, 'temp') - tourny_finder = re.compile('(\d+) (\d+)') - - while True: # wait for a new hand number on stdin - new_hand_id = sys.stdin.readline() - new_hand_id = string.rstrip(new_hand_id) - if new_hand_id == "": # blank line means quit - destroy() - break # this thread is not always killed immediately with gtk.main_quit() + def update_HUD(self, new_hand_id, table_name, config, stat_dict): + """Update a HUD gui from inside the non-gui read_stdin thread.""" +# This is written so that only 1 thread can touch the gui--mainly +# for compatibility with Windows. This method dispatches the +# function idle_func() to be run by the gui thread, at its leisure. + def idle_func(): + gtk.gdk.threads_enter() + try: + self.hud_dict[table_name].update(new_hand_id, config, stat_dict) + for m in self.hud_dict[table_name].aux_windows: + m.update_gui(new_hand_id) + return False + finally: + gtk.gdk.threads_leave() + gobject.idle_add(idle_func) + + def HUD_removed(self, tablename): + + tablename = Tables.clean_title(tablename) + # TODO: There's a potential problem here somewhere, that this hacks around .. the table_name as being passed to HUD_create is cleaned, + # but the table.name as being passed here is not cleaned. I don't know why. -eric + if tablename in self.hud_dict and self.hud_dict[tablename].deleted: + self.vb.remove(self.hud_dict[tablename].tablehudlabel) + del self.hud_dict[tablename] + return False + + return True + + def read_stdin(self): # This is the thread function + """Do all the non-gui heavy lifting for the HUD program.""" +# This db connection is for the read_stdin thread only. It should not +# be passed to HUDs for use in the gui thread. HUD objects should not +# need their own access to the database, but should open their own +# if it is required. + self.db_connection = Database.Database(self.config, self.db_name, 'temp') + tourny_finder = re.compile('(\d+) (\d+)') + + while True: # wait for a new hand number on stdin + new_hand_id = sys.stdin.readline() + new_hand_id = string.rstrip(new_hand_id) + if new_hand_id == "": # blank line means quit + self.destroy() + break # this thread is not always killed immediately with gtk.main_quit() + # get basic info about the new hand from the db - (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id) +# if there is a db error, complain, skip hand, and proceed + try: + (table_name, max, poker_game) = self.db_connection.get_table_name(new_hand_id) + stat_dict = self.db_connection.get_stats_from_hand(new_hand_id) + except: + print "skipping ", new_hand_id + sys.stderr.write("Database error in hand %d. Skipping.\n" % int(new_hand_id)) + continue # find out if this hand is from a tournament - is_tournament = False - (tour_number, tab_number) = (0, 0) - mat_obj = tourny_finder.search(table_name) - if mat_obj: - is_tournament = True - (tour_number, tab_number) = mat_obj.group(1, 2) - - stat_dict = db_connection.get_stats_from_hand(new_hand_id) - -# if a hud for this CASH table exists, just update it - if table_name in hud_dict: -# update the data for the aux_windows - for aw in hud_dict[table_name].aux_windows: - aw.update_data(new_hand_id, db_connection) - update_HUD(new_hand_id, table_name, config, stat_dict) - -# if a hud for this TOURNAMENT table exists, just update it - elif tour_number in hud_dict: - update_HUD(new_hand_id, tour_number, config, stat_dict) - -# otherwise create a new hud - else: - if is_tournament: - tablewindow = Tables.discover_tournament_table(config, tour_number, tab_number) - if tablewindow == None: - sys.stderr.write("tournament %s, table %s not found\n" % (tour_number, tab_number)) - else: - create_HUD(new_hand_id, tablewindow, db_name, tour_number, max, poker_game, db_connection, config, stat_dict) + mat_obj = tourny_finder.search(table_name) + if mat_obj: + is_tournament = True + (tour_number, tab_number) = mat_obj.group(1, 2) + temp_key = tour_number else: - tablewindow = Tables.discover_table_by_name(config, table_name) - if tablewindow == None: - sys.stderr.write("table name "+table_name+" not found\n") + is_tournament = False + (tour_number, tab_number) = (0, 0) + temp_key = table_name + +# Update an existing HUD + if temp_key in self.hud_dict: + for aw in self.hud_dict[temp_key].aux_windows: + aw.update_data(new_hand_id, self.db_connection) + self.update_HUD(new_hand_id, temp_key, self.config, stat_dict) + +# Or create a new HUD + else: + if is_tournament: + tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number) else: - create_HUD(new_hand_id, tablewindow, db_name, table_name, max, poker_game, db_connection, config, stat_dict) + tablewindow = Tables.discover_table_by_name(self.config, table_name) + + if tablewindow == None: + if is_tournament: + table_name = tour_number + " " + tab_number + sys.stderr.write("table name "+table_name+" not found, skipping.\n") + else: + self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, is_tournament, stat_dict) if __name__== "__main__": sys.stderr.write("HUD_main starting\n") +# database name can be passed on command line try: db_name = sys.argv[1] except: db_name = 'fpdb' sys.stderr.write("Using db name = %s\n" % (db_name)) - config = Configuration.Config() +# start the HUD_main object + hm = HUD_main(db_name = db_name) - gobject.threads_init() # this is required - thread.start_new_thread(read_stdin, ()) # starts the thread - - main_window = gtk.Window() - main_window.connect("destroy", destroy) - eb = gtk.VBox() - label = gtk.Label('Closing this window will exit from the HUD.') - eb.add(label) - main_window.add(eb) - - main_window.set_title("HUD Main Window") - main_window.show_all() - +# start the event loop gtk.main() diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index b1794c5d..17497d90 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -47,7 +47,8 @@ import HUD_main class Hud: - def __init__(self, table, max, poker_game, config, db_connection): + def __init__(self, parent, table, max, poker_game, config, db_connection): + self.parent = parent self.table = table self.config = config self.poker_game = poker_game @@ -175,7 +176,7 @@ class Hud: self.deleted = True self.main_window.disconnect(self.main_window.destroyhandler) # so we don't potentially infiniteloop in here, right self.main_window.destroy() - HUD_main.HUD_removed(self.table.name) + self.parent.HUD_removed(self.table.name) def kill_hud_menu(self, *args): self.main_window.destroy() @@ -200,31 +201,43 @@ class Hud: self.config.save() def adj_seats(self, hand, config): + adj = range(0, self.max + 1) # default seat adjustments = no adjustment # does the user have a fav_seat? try: + sys.stderr.write("site = %s, max = %d, fav seat = %d\n" % (self.table.site, self.max, config.supported_sites[self.table.site].layout[self.max].fav_seat)) if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0: fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat -# db_connection = Database.Database(config, self.db_name, 'temp') - actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name) -# db_connection.close_connection() + sys.stderr.write("found fav seat = %d\n" % fav_seat) +# actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name) + actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name) + sys.stderr.write("found actual seat = %d\n" % actual_seat) for i in range(0, self.max + 1): j = actual_seat + i if j > self.max: j = j - self.max adj[j] = fav_seat + i if adj[j] > self.max: adj[j] = adj[j] - self.max - except: - pass + except Exception, inst: + sys.stderr.write("exception in adj!!!\n\n") + sys.stderr.write("error is %s" % inst) # __str__ allows args to printed directly return adj - def create(self, hand, config): + def get_actual_seat(self, name): + for key in self.stat_dict.keys(): + if self.stat_dict[key]['screen_name'] == name: + return self.stat_dict[key]['seat'] + sys.stderr.write("Error finding actual seat.\n") + + def create(self, hand, config, stat_dict): # update this hud, to the stats and players as of "hand" # hand is the hand id of the most recent hand played at this table # # this method also manages the creating and destruction of stat # windows via calls to the Stat_Window class - + self.stat_dict = stat_dict + sys.stderr.write("------------------------------------------------------------\nCreating hud from hand %s\n" % hand) adj = self.adj_seats(hand, config) + sys.stderr.write("adj = %s\n" % adj) loc = self.config.get_locations(self.table.site, self.max) # create the stat windows @@ -233,6 +246,7 @@ class Hud: if i in self.stat_windows: self.stat_windows[i].relocate(x, y) else: + sys.stderr.write("actual seat = %d, x = %d, y= %d\n" % (i, x, y)) self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], parent = self, table = self.table, @@ -291,9 +305,6 @@ class Hud: tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \ number[3] + ", " + number[4] Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip) -# for m in self.aux_windows: -# m.update_data(hand) -# m.update_gui(hand) def topify_window(self, window): """Set the specified gtk window to stayontop in MS Windows.""" diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index f37ddf37..162e68f0 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -173,6 +173,8 @@ class Sql: self.query['get_stats_from_hand'] = """ SELECT HudCache.playerId AS player_id, + seatNo AS seat, + name AS screen_name, sum(HDs) AS n, sum(street0VPI) AS vpip, sum(street0Aggr) AS pfr, @@ -233,6 +235,7 @@ class Sql: INNER JOIN HandsPlayers ON (HandsPlayers.handId = %s) INNER JOIN HudCache ON ( HudCache.PlayerId = HandsPlayers.PlayerId+0 AND HudCache.gametypeId+0 = Hands.gametypeId+0) + INNER JOIN Players ON (Players.id = HandsPlayers.PlayerId+0) WHERE Hands.id = %s GROUP BY HudCache.PlayerId """ diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index cdfee353..49657762 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -360,7 +360,7 @@ class Importer: sys.exit(0) startpos=endpos ttime = time() - starttime - print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", ttime + print "\rTotal stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", ttime if stored==0: if duplicates>0: