From a735ab67b2e341649ced31c35d16667eb19595a2 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Mon, 23 Nov 2009 10:24:38 -0500 Subject: [PATCH 1/7] stuck an error print in wintables if self.window doesn't exist --- pyfpdb/WinTables.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py index d8f138eb..f834eac9 100644 --- a/pyfpdb/WinTables.py +++ b/pyfpdb/WinTables.py @@ -9,12 +9,12 @@ Routines for detecting and handling poker client windows for MS Windows. # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -56,9 +56,13 @@ class Table(Table_Window): if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows self.window = hwnd break - - if self.window == None: - print "Window %s not found. Skipping." % search_string + + try: + if self.window == None: + print "Window %s not found. Skipping." % search_string + return None + except AttributeError: + print "self.window doesn't exist? why?" return None (x, y, width, height) = win32gui.GetWindowRect(hwnd) @@ -70,7 +74,7 @@ class Table(Table_Window): print "x = %s y = %s width = %s height = %s" % (self.x, self.y, self.width, self.height) #self.height = int(height) - b_width - tb_height #self.width = int(width) - 2*b_width - + self.exe = self.get_nt_exe(hwnd) self.title = titles[hwnd] self.site = "" @@ -99,37 +103,37 @@ class Table(Table_Window): def get_nt_exe(self, hwnd): """Finds the name of the executable that the given window handle belongs to.""" - + # Request privileges to enable "debug process", so we can later use PROCESS_VM_READ, retardedly required to GetModuleFileNameEx() priv_flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY hToken = win32security.OpenProcessToken (win32api.GetCurrentProcess(), priv_flags) # enable "debug process" privilege_id = win32security.LookupPrivilegeValue (None, win32security.SE_DEBUG_NAME) old_privs = win32security.AdjustTokenPrivileges (hToken, 0, [(privilege_id, win32security.SE_PRIVILEGE_ENABLED)]) - + # Open the process, and query it's filename processid = win32process.GetWindowThreadProcessId(hwnd) pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1]) exename = win32process.GetModuleFileNameEx(pshandle, 0) - + # clean up win32api.CloseHandle(pshandle) win32api.CloseHandle(hToken) - + return exename def topify(self, hud): """Set the specified gtk window to stayontop in MS Windows.""" - + # def windowEnumerationHandler(hwnd, resultList): # '''Callback for win32gui.EnumWindows() to generate list of window handles.''' # resultList.append((hwnd, win32gui.GetWindowText(hwnd))) -# +# # unique_name = 'unique name for finding this window' # real_name = hud.main_window.get_title() # hud.main_window.set_title(unique_name) # tl_windows = [] # win32gui.EnumWindows(windowEnumerationHandler, tl_windows) -# +# # for w in tl_windows: # if w[1] == unique_name: # hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0]) @@ -139,14 +143,13 @@ class Table(Table_Window): (innerx, innery) = self.gdkhandle.get_origin() b_width = rect.x - innerx tb_height = rect.y - innery -# +# # style = win32gui.GetWindowLong(self.number, win32con.GWL_EXSTYLE) # style |= win32con.WS_CLIPCHILDREN # win32gui.SetWindowLong(self.number, win32con.GWL_EXSTYLE, style) # break - + # hud.main_window.set_title(real_name) def win_enum_handler(hwnd, titles): titles[hwnd] = win32gui.GetWindowText(hwnd) - From 9bf5017ff1c63b758a524f9f2703ee1432a9a159 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Tue, 24 Nov 2009 06:08:43 -0500 Subject: [PATCH 2/7] re-enable exception handling in idle_func() now that i know what we could expect to trap reasonably. Deal in update() with what might happen if update_table_position() fails --- pyfpdb/HUD_main.py | 14 ++-- pyfpdb/Hud.py | 199 +++++++++++++++++++++++---------------------- 2 files changed, 109 insertions(+), 104 deletions(-) diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 7965f0b5..2897cfba 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -159,11 +159,15 @@ class HUD_main(object): # 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) - [aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows] -# finally: - gtk.gdk.threads_leave() + # The HUD could get destroyed in the above call ^^, which leaves us with a KeyError here vv + # if we ever get an error we need to expect ^^ then we need to handle it vv - Eric + try: + [aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows] + except KeyError: + pass + finally: + gtk.gdk.threads_leave() return False gobject.idle_add(idle_func) @@ -198,7 +202,7 @@ class HUD_main(object): try: (table_name, max, poker_game, type, site_id, site_name, tour_number, tab_number) = \ self.db_connection.get_table_info(new_hand_id) - except Exception, err: + except Exception, err: # TODO: we need to make this a much less generic Exception lulz print "db error: skipping %s" % new_hand_id sys.stderr.write("Database error: could not find hand %s.\n" % new_hand_id) continue diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 674b0a09..4b69f1b3 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -6,17 +6,17 @@ Create and manage the hud overlays. """ # Copyright 2008, 2009 Ray E. Barker -# +# # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -57,7 +57,7 @@ def importName(module_name, name): return(getattr(module, name)) class Hud: - + def __init__(self, parent, table, max, poker_game, config, db_connection): # __init__ is (now) intended to be called from the stdin thread, so it # cannot touch the gui @@ -74,16 +74,16 @@ class Hud: self.site = table.site self.mw_created = False self.hud_params = parent.hud_params - + self.stat_windows = {} self.popup_windows = {} self.aux_windows = [] - + (font, font_size) = config.get_default_font(self.table.site) self.colors = config.get_default_colors(self.table.site) self.hud_ui = config.get_hud_ui_parameters() - + self.backgroundcolor = gtk.gdk.color_parse(self.colors['hudbgcolor']) self.foregroundcolor = gtk.gdk.color_parse(self.colors['hudfgcolor']) @@ -98,7 +98,7 @@ class Hud: if my_import == None: continue self.aux_windows.append(my_import(self, config, aux_params)) - + self.creation_attrs = None def create_mw(self): @@ -110,16 +110,16 @@ class Hud: win.set_skip_taskbar_hint(True) win.set_decorated(False) win.set_opacity(self.colors["hudopacity"]) - + eventbox = gtk.EventBox() label = gtk.Label(self.hud_ui['label']) - + win.add(eventbox) eventbox.add(label) - + label.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor) label.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor) - + eventbox.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor) eventbox.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor) @@ -128,20 +128,20 @@ class Hud: # A popup menu for the main window menu = gtk.Menu() - + killitem = gtk.MenuItem('Kill This HUD') menu.append(killitem) if self.parent is not None: killitem.connect("activate", self.parent.kill_hud, self.table_name) - + saveitem = gtk.MenuItem('Save HUD Layout') menu.append(saveitem) saveitem.connect("activate", self.save_layout) - + repositem = gtk.MenuItem('Reposition StatWindows') menu.append(repositem) repositem.connect("activate", self.reposition_windows) - + aggitem = gtk.MenuItem('Show Player Stats') menu.append(aggitem) self.aggMenu = gtk.Menu() @@ -150,49 +150,49 @@ class Hud: item = gtk.CheckMenuItem('For This Blind Level Only') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('P',1)) - setattr(self, 'h_aggBBmultItem1', item) - # + setattr(self, 'h_aggBBmultItem1', item) + # item = gtk.MenuItem('For Multiple Blind Levels:') self.aggMenu.append(item) - # + # item = gtk.CheckMenuItem(' 0.5 to 2.0 x Current Blinds') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('P',2)) - setattr(self, 'h_aggBBmultItem2', item) - # + setattr(self, 'h_aggBBmultItem2', item) + # item = gtk.CheckMenuItem(' 0.33 to 3.0 x Current Blinds') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('P',3)) - setattr(self, 'h_aggBBmultItem3', item) - # + setattr(self, 'h_aggBBmultItem3', item) + # item = gtk.CheckMenuItem(' 0.1 to 10 x Current Blinds') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('P',10)) - setattr(self, 'h_aggBBmultItem10', item) - # + setattr(self, 'h_aggBBmultItem10', item) + # item = gtk.CheckMenuItem(' All Levels') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('P',10000)) - setattr(self, 'h_aggBBmultItem10000', item) - # + setattr(self, 'h_aggBBmultItem10000', item) + # item = gtk.MenuItem('Since:') self.aggMenu.append(item) - # + # item = gtk.CheckMenuItem(' All Time') self.aggMenu.append(item) item.connect("activate", self.set_hud_style, ('P','A')) setattr(self, 'h_hudStyleOptionA', item) - # + # item = gtk.CheckMenuItem(' Session') self.aggMenu.append(item) item.connect("activate", self.set_hud_style, ('P','S')) - setattr(self, 'h_hudStyleOptionS', item) - # + setattr(self, 'h_hudStyleOptionS', item) + # item = gtk.CheckMenuItem(' %s Days' % (self.hud_params['h_hud_days'])) self.aggMenu.append(item) item.connect("activate", self.set_hud_style, ('P','T')) - setattr(self, 'h_hudStyleOptionT', item) - + setattr(self, 'h_hudStyleOptionT', item) + aggitem = gtk.MenuItem('Show Opponent Stats') menu.append(aggitem) self.aggMenu = gtk.Menu() @@ -201,48 +201,48 @@ class Hud: item = gtk.CheckMenuItem('For This Blind Level Only') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('O',1)) - setattr(self, 'aggBBmultItem1', item) - # + setattr(self, 'aggBBmultItem1', item) + # item = gtk.MenuItem('For Multiple Blind Levels:') self.aggMenu.append(item) - # + # item = gtk.CheckMenuItem(' 0.5 to 2.0 x Current Blinds') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('O',2)) - setattr(self, 'aggBBmultItem2', item) - # + setattr(self, 'aggBBmultItem2', item) + # item = gtk.CheckMenuItem(' 0.33 to 3.0 x Current Blinds') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('O',3)) - setattr(self, 'aggBBmultItem3', item) - # + setattr(self, 'aggBBmultItem3', item) + # item = gtk.CheckMenuItem(' 0.1 to 10 x Current Blinds') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('O',10)) - setattr(self, 'aggBBmultItem10', item) - # + setattr(self, 'aggBBmultItem10', item) + # item = gtk.CheckMenuItem(' All Levels') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('O',10000)) - setattr(self, 'aggBBmultItem10000', item) - # + setattr(self, 'aggBBmultItem10000', item) + # item = gtk.MenuItem('Since:') self.aggMenu.append(item) - # + # item = gtk.CheckMenuItem(' All Time') self.aggMenu.append(item) item.connect("activate", self.set_hud_style, ('O','A')) setattr(self, 'hudStyleOptionA', item) - # + # item = gtk.CheckMenuItem(' Session') self.aggMenu.append(item) item.connect("activate", self.set_hud_style, ('O','S')) - setattr(self, 'hudStyleOptionS', item) - # + setattr(self, 'hudStyleOptionS', item) + # item = gtk.CheckMenuItem(' %s Days' % (self.hud_params['h_hud_days'])) self.aggMenu.append(item) item.connect("activate", self.set_hud_style, ('O','T')) - setattr(self, 'hudStyleOptionT', item) + setattr(self, 'hudStyleOptionT', item) # set active on current options: if self.hud_params['h_agg_bb_mult'] == 1: @@ -280,13 +280,13 @@ class Hud: getattr(self, 'hudStyleOptionS').set_active(True) elif self.hud_params['hud_style'] == 'T': getattr(self, 'hudStyleOptionT').set_active(True) - + eventbox.connect_object("button-press-event", self.on_button_press, menu) - + debugitem = gtk.MenuItem('Debug StatWindows') menu.append(debugitem) debugitem.connect("activate", self.debug_stat_windows) - + item5 = gtk.MenuItem('Set max seats') menu.append(item5) maxSeatsMenu = gtk.Menu() @@ -296,8 +296,8 @@ class Hud: item.ms = i maxSeatsMenu.append(item) item.connect("activate", self.change_max_seats) - setattr(self, 'maxSeatsMenuItem%d' % (i-1), item) - + setattr(self, 'maxSeatsMenuItem%d' % (i-1), item) + eventbox.connect_object("button-press-event", self.on_button_press, menu) self.mw_created = True @@ -305,7 +305,7 @@ class Hud: menu.show_all() self.main_window.show_all() self.topify_window(self.main_window) - + def change_max_seats(self, widget): if self.max != widget.ms: print 'change_max_seats', widget.ms @@ -352,7 +352,7 @@ class Hud: else: param = 'hud_style' prefix = '' - + if style == 'A' and getattr(self, prefix+'hudStyleOptionA').get_active(): self.hud_params[param] = 'A' getattr(self, prefix+'hudStyleOptionS').set_active(False) @@ -431,7 +431,7 @@ class Hud: # print self.table, "\n", self.main_window.window.get_transient_for() for w in self.stat_windows: print self.stat_windows[w].window.window.get_transient_for() - + def save_layout(self, *args): new_layout = [(0, 0)] * self.max for sw in self.stat_windows: @@ -447,7 +447,7 @@ class Hud: def adj_seats(self, hand, config): -# Need range here, not xrange -> need the actual list +# Need range here, not xrange -> need the actual list adj = range(0, self.max + 1) # default seat adjustments = no adjustment # does the user have a fav_seat? if self.max not in config.supported_sites[self.table.site].layout: @@ -481,12 +481,12 @@ class Hud: # # this method also manages the creating and destruction of stat # windows via calls to the Stat_Window class - self.creation_attrs = hand, config, stat_dict, cards + self.creation_attrs = hand, config, stat_dict, cards - self.hand = hand + self.hand = hand if not self.mw_created: self.create_mw() - + self.stat_dict = stat_dict self.cards = cards sys.stderr.write("------------------------------------------------------------\nCreating hud from hand %s\n" % hand) @@ -498,24 +498,24 @@ class Hud: loc = self.config.get_locations(self.table.site, 9) # create the stat windows - for i in xrange(1, self.max + 1): + for i in xrange(1, self.max + 1): (x, y) = loc[adj[i]] if i in self.stat_windows: self.stat_windows[i].relocate(x, y) else: self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], parent = self, - table = self.table, + table = self.table, x = x, y = y, seat = i, - adj = adj[i], + adj = adj[i], player_id = 'fake', font = self.font) self.stats = [] game = config.supported_games[self.poker_game] - + for i in xrange(0, game.rows + 1): row_list = [''] * game.cols self.stats.append(row_list) @@ -523,14 +523,15 @@ class Hud: self.stats[config.supported_games[self.poker_game].stats[stat].row] \ [config.supported_games[self.poker_game].stats[stat].col] = \ config.supported_games[self.poker_game].stats[stat].stat_name - + if os.name == "nt": gobject.timeout_add(500, self.update_table_position) - + def update(self, hand, config): self.hand = hand # this is the last hand, so it is available later if os.name == 'nt': - self.update_table_position() + if self.update_table_position() == False: # we got killed by finding our table was gone + return for s in self.stat_dict: try: @@ -546,18 +547,18 @@ class Hud: self.max = 10 self.create(hand, config, self.stat_dict, self.cards) self.stat_windows[statd['seat']].player_id = statd['player_id'] - + for r in xrange(0, config.supported_games[self.poker_game].rows): for c in xrange(0, config.supported_games[self.poker_game].cols): this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]] number = Stats.do_stat(self.stat_dict, player = statd['player_id'], stat = self.stats[r][c]) statstring = "%s%s%s" % (this_stat.hudprefix, str(number[1]), this_stat.hudsuffix) window = self.stat_windows[statd['seat']] - + if this_stat.hudcolor != "": self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor)) - + window.label[r][c].set_text(statstring) if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no? window.window.show_all() @@ -567,7 +568,7 @@ class Hud: def topify_window(self, window): window.set_focus_on_map(False) window.set_accept_focus(False) - + if not self.table.gdkhandle: self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window window.window.set_transient_for(self.table.gdkhandle) @@ -575,7 +576,7 @@ class Hud: class Stat_Window: def button_press_cb(self, widget, event, *args): -# This handles all callbacks from button presses on the event boxes in +# This handles all callbacks from button presses on the event boxes in # the stat windows. There is a bit of an ugly kludge to separate single- # and double-clicks. self.window.show_all() @@ -599,15 +600,15 @@ class Stat_Window: self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time) return True return False - + def noop(self, arga=None, argb=None): # i'm going to try to connect the focus-in and focus-out events here, to see if that fixes any of the focus problems. return True - + def kill_popup(self, popup): print "remove popup", popup - self.popups.remove(popup) + self.popups.remove(popup) popup.window.destroy() - + def kill_popups(self): map(lambda x: x.window.destroy(), self.popups) self.popups = { } @@ -639,10 +640,10 @@ class Stat_Window: self.window.set_focus_on_map(False) grid = gtk.Table(rows = game.rows, columns = game.cols, homogeneous = False) - self.grid = grid + self.grid = grid self.window.add(grid) self.window.modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) - + self.e_box = [] self.frame = [] self.label = [] @@ -658,10 +659,10 @@ class Stat_Window: if usegtkframes: self.frame[r].append( gtk.Frame() ) e_box[r].append( gtk.EventBox() ) - + e_box[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) e_box[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor) - + Stats.do_tip(e_box[r][c], 'stuff') if usegtkframes: grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad) @@ -669,7 +670,7 @@ class Stat_Window: else: grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad) label[r].append( gtk.Label('xxx') ) - + if usegtkframes: self.frame[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) label[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) @@ -690,17 +691,17 @@ class Stat_Window: self.window.set_focus_on_map(False) self.window.set_accept_focus(False) - + self.window.move(self.x, self.y) self.window.realize() # window must be realized before it has a gdkwindow so we can attach it to the table window.. self.topify_window(self.window) - + self.window.hide() - + def topify_window(self, window): window.set_focus_on_map(False) window.set_accept_focus(False) - + if not self.table.gdkhandle: self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window # window.window.reparent(self.table.gdkhandle, 0, 0) @@ -723,11 +724,11 @@ class Popup_window: self.window.set_title("popup") self.window.set_property("skip-taskbar-hint", True) self.window.set_focus_on_map(False) - self.window.set_accept_focus(False) + self.window.set_accept_focus(False) self.window.set_transient_for(parent.get_toplevel()) - + self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT) - + self.ebox = gtk.EventBox() self.ebox.connect("button_press_event", self.button_press_cb) self.lab = gtk.Label("stuff\nstuff\nstuff") @@ -735,14 +736,14 @@ class Popup_window: # need an event box so we can respond to clicks self.window.add(self.ebox) self.ebox.add(self.lab) - + self.ebox.modify_bg(gtk.STATE_NORMAL, stat_window.parent.backgroundcolor) self.ebox.modify_fg(gtk.STATE_NORMAL, stat_window.parent.foregroundcolor) self.window.modify_bg(gtk.STATE_NORMAL, stat_window.parent.backgroundcolor) self.window.modify_fg(gtk.STATE_NORMAL, stat_window.parent.foregroundcolor) self.lab.modify_bg(gtk.STATE_NORMAL, stat_window.parent.backgroundcolor) self.lab.modify_fg(gtk.STATE_NORMAL, stat_window.parent.foregroundcolor) - + # figure out the row, col address of the click that activated the popup row = 0 col = 0 @@ -769,7 +770,7 @@ class Popup_window: # get a database connection # db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp') - + # calculate the stat_dict and then create the text for the pu # stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id) # stat_dict = self.db_connection.get_stats_from_hand(stat_window.parent.hand) @@ -781,16 +782,16 @@ class Popup_window: number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s) mo_text += number[5] + " " + number[4] + "\n" pu_text += number[3] + "\n" - + self.lab.set_text(pu_text) Stats.do_tip(self.lab, mo_text) self.window.show_all() - + self.window.set_transient_for(stat_window.window) def button_press_cb(self, widget, event, *args): -# This handles all callbacks from button presses on the event boxes in +# This handles all callbacks from button presses on the event boxes in # the popup windows. There is a bit of an ugly kludge to separate single- # and double-clicks. This is the same code as in the Stat_window class if event.button == 1: # left button event @@ -808,7 +809,7 @@ class Popup_window: def toggle_decorated(self, widget): top = widget.get_toplevel() (x, y) = top.get_position() - + if top.get_decorated(): top.set_decorated(0) top.move(x, y) @@ -819,7 +820,7 @@ class Popup_window: def topify_window(self, window): window.set_focus_on_map(False) window.set_accept_focus(False) - + if not self.table.gdkhandle: self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window # window.window.reparent(self.table.gdkhandle, 0, 0) @@ -833,14 +834,14 @@ if __name__== "__main__": label = gtk.Label('Fake main window, blah blah, blah\nblah, blah') main_window.add(label) main_window.show_all() - + c = Configuration.Config() #tables = Tables.discover(c) t = Tables.discover_table_by_name(c, "Corona") if t is None: print "Table not found." db = Database.Database(c, 'fpdb', 'holdem') - + stat_dict = db.get_stats_from_hand(1) # for t in tables: From 0b4f5acec1bc16aaba6588328aed00ed84cbe390 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Wed, 25 Nov 2009 08:22:14 -0500 Subject: [PATCH 3/7] some weird whitespace churning, and again comment out "sending hand to hud" message --- pyfpdb/fpdb_import.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 850f34b6..fb80d968 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -51,7 +51,7 @@ except ImportError: log.debug("Import database module: MySQLdb not found") else: mysqlLibFound = True - + try: import psycopg2 except ImportError: @@ -81,7 +81,7 @@ class Importer: self.pos_in_file = {} # dict to remember how far we have read in the file #Set defaults self.callHud = self.config.get_import_parameters().get("callFpdbHud") - + # CONFIGURATION OPTIONS self.settings.setdefault("minPrint", 30) self.settings.setdefault("handCount", 0) @@ -243,9 +243,9 @@ class Importer: #self.writeq.join() #using empty() might be more reliable: while not self.writeq.empty() and len(threading.enumerate()) > 1: - # TODO: Do we need to actually tell the progress indicator to move, or is it already moving, and we just need to process events... + # TODO: Do we need to actually tell the progress indicator to move, or is it already moving, and we just need to process events... while gtk.events_pending(): # see http://faq.pygtk.org/index.py?req=index for more hints (3.7) - gtk.main_iteration(False) + gtk.main_iteration(False) sleep(0.5) print " ... writers finished" @@ -267,7 +267,7 @@ class Importer: """"Read filenames in self.filelist and pass to import_file_dict(). Uses a separate database connection if created as a thread (caller passes None or no param as db).""" - + totstored = 0 totdups = 0 totpartial = 0 @@ -300,7 +300,7 @@ class Importer: except: pass # if this fails we're probably doomed anyway if self.settings['handsInDB'] < 5000: return "drop" - if len(self.filelist) < 50: return "don't drop" + if len(self.filelist) < 50: return "don't drop" if self.settings['handsInDB'] > 50000: return "don't drop" return "drop" @@ -313,7 +313,7 @@ class Importer: size_per_hand = 1300.0 # wag based on a PS 6-up FLHE file. Actual value not hugely important # as values of scale and increment compensate for it anyway. # decimal used to force float arithmetic - + # get number of hands in db if 'handsInDB' not in self.settings: try: @@ -322,7 +322,7 @@ class Importer: self.settings['handsInDB'] = tmpcursor.fetchone()[0] except: pass # if this fails we're probably doomed anyway - + # add up size of import files total_size = 0.0 for file in self.filelist: @@ -344,12 +344,12 @@ class Importer: #Check for new files in monitored directories #todo: make efficient - always checks for new file, should be able to use mtime of directory # ^^ May not work on windows - + #rulog = open('runUpdated.txt', 'a') #rulog.writelines("runUpdated ... ") for site in self.dirlist: self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) - + for file in self.filelist: if os.path.exists(file): stat_info = os.stat(file) @@ -369,13 +369,13 @@ class Importer: self.updatedtime[file] = time() else: self.removeFromFileList[file] = True - + self.addToDirList = filter(lambda x: self.addImportDirectory(x, True, self.addToDirList[x][0], self.addToDirList[x][1]), self.addToDirList) for file in self.removeFromFileList: if file in self.filelist: del self.filelist[file] - + self.addToDirList = {} self.removeFromFileList = {} self.database.rollback() @@ -385,7 +385,7 @@ class Importer: # This is now an internal function that should not be called directly. def import_file_dict(self, db, file, site, filter, q=None): #print "import_file_dict" - + if os.path.isdir(file): self.addToDirList[file] = [site] + [filter] return @@ -393,7 +393,7 @@ class Importer: conv = None (stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0) - file = file.decode(fpdb_simple.LOCALE_ENCODING) + file = file.decode(fpdb_simple.LOCALE_ENCODING) # Load filter, process file, pass returned filename to import_fpdb_file if self.settings['threads'] > 0 and self.writeq is not None: @@ -473,7 +473,7 @@ class Importer: ttime = time() - starttime if q is None: log.info("Total stored: %(stored)d\tduplicates:%(duplicates)d\terrors:%(errors)d\ttime:%(ttime)s" % locals()) - + if not stored: if duplicates: for line_no in xrange(len(self.lines)): @@ -488,7 +488,7 @@ class Importer: return (stored, duplicates, partial, errors, ttime) # end def import_fpdb_file - + def import_fpdb_lines(self, db, lines, starttime, file, site, q = None): """Import an fpdb hand history held in the list lines, could be one hand or many""" @@ -496,7 +496,7 @@ class Importer: #db.lock_for_insert() # should be ok when using one thread, but doesn't help?? while gtk.events_pending(): gtk.main_iteration(False) - + try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return. firstline = lines[0] except: @@ -524,7 +524,7 @@ class Importer: if len(lines[i]) < 2: #Wierd way to detect for '\r\n' or '\n' endpos = i hand = lines[startpos:endpos] - + if len(hand[0]) < 2: hand=hand[1:] @@ -548,7 +548,7 @@ class Importer: if self.callHud: #print "call to HUD here. handsId:",handsId #pipe the Hands.id out to the HUD - print "fpdb_import: sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud + # print "fpdb_import: sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) except Exceptions.DuplicateError: duplicates += 1 @@ -574,7 +574,7 @@ class Importer: if self.settings['minPrint']: if not ((stored+duplicates+errors) % self.settings['minPrint']): print "stored:", stored, " duplicates:", duplicates, "errors:", errors - + if self.settings['handCount']: if ((stored+duplicates+errors) >= self.settings['handCount']): if not self.settings['quiet']: From cb9e2cb6e7dce51a1aa59749b9251b8a8c0cf4c2 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Wed, 25 Nov 2009 20:31:01 +0200 Subject: [PATCH 4/7] Try to fix two HUD main hangs The main HUD process can hang due to unhandled exceptions, which occurred in two separate situations: 1. Table window is closed and HUD instance killed before auto-importer knows about it 2. Sometimes the threading can jam These changes attempt to counter the effect of race-conditions. The dictionary key (table name) is properly tested at the beginning of update/create block, *but* there are two short round-trips to database before the key is used. While these occur, the HUD instance can vanish and thus get its key removed from the dictionary. Also, when Tables.Table() is created, it will be populated on-demand, and have child attributes only when such are found from the system. The new table code pulls in data from actual windows. Again, there is a query involved and while it runs, the table may have vanished. This ended up as an error in this call: foo = gtk.gdk.window_foreign_new(table.number) The object 'table' is valid (not None) but it has been populated only after actual table window was killed. Therefore it may not have .number attribute, which raised an AttributeError. Now the presence of table.number attribute is tested before the object can be sent to create_HUD(). --- pyfpdb/HUD_main.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 7965f0b5..27d7eea0 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -122,9 +122,9 @@ class HUD_main(object): m.update_gui(new_hand_id) self.hud_dict[table_name].update(new_hand_id, self.config) self.hud_dict[table_name].reposition_windows() - return False finally: gtk.gdk.threads_leave() + return False self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) self.hud_dict[table_name].table_name = table_name @@ -214,7 +214,14 @@ class HUD_main(object): self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days'] , self.hud_dict[temp_key].hud_params['h_hud_days']) stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params, self.hero_ids[site_id]) - self.hud_dict[temp_key].stat_dict = stat_dict + try: + self.hud_dict[temp_key].stat_dict = stat_dict + except KeyError: # HUD instance has been killed off, key is stale + sys.stderr.write('hud_dict[%s] was not found\n' % temp_key) + sys.stderr.write('will not send hand\n') + # Unlocks table, copied from end of function + self.db_connection.connection.rollback() + return cards = self.db_connection.get_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id) if comm_cards != {}: # stud! @@ -245,7 +252,12 @@ class HUD_main(object): else: tablewindow.max = max tablewindow.site = site_name - self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards) + # Test that the table window still exists + if hasattr(tablewindow, 'number'): + self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards) + else: + sys.stderr.write('Table "%s" no longer exists\n', table_name) + self.db_connection.connection.rollback() if __name__== "__main__": From 9d754e5e4b82eee146eaf46a1d4cdece7ea08270 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Wed, 25 Nov 2009 20:55:58 +0000 Subject: [PATCH 5/7] make sure db locks are released --- pyfpdb/GuiGraphViewer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index e6d0c6d2..b81f2d5e 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -152,14 +152,17 @@ class GuiGraphViewer (threading.Thread): if not sitenos: #Should probably pop up here. print "No sites selected - defaulting to PokerStars" + self.db.rollback() return if not playerids: print "No player ids found" + self.db.rollback() return if not limits: print "No limits found" + self.db.rollback() return #Set graph properties From df6d9a0a56b575cc375173571aaa6351e531953c Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Thu, 26 Nov 2009 23:07:58 +0200 Subject: [PATCH 6/7] Fix missing checks before .set_active() Some validity tests were still missing. Also, typofix: self.cbFl -> self.cbFL --- pyfpdb/Filters.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 5beef90f..c74d5579 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -312,8 +312,8 @@ class Filters(threading.Thread): self.cbAllLimits.set_active(False) if not self.limits[limit]: if limit.isdigit(): - if self.cbFl is not None: - self.cbFl.set_active(False) + if self.cbFL is not None: + self.cbFL.set_active(False) else: if self.cbNL is not None: self.cbNL.set_active(False) @@ -329,8 +329,10 @@ class Filters(threading.Thread): if self.limits[limit]: for cb in self.cbLimits.values(): cb.set_active(False) - self.cbNL.set_active(False) - self.cbFL.set_active(False) + if self.cbNL is not None: + self.cbNL.set_active(False) + if self.cbFL is not None: + self.cbFL.set_active(False) elif limit == "fl": if not self.limits[limit]: # only toggle all fl limits off if they are all currently on From 6dec7f38f3988a276b28548b4b9d96c962a56f3b Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Fri, 27 Nov 2009 22:21:45 +0200 Subject: [PATCH 7/7] Fix a stupid syntax error Use python's format-string syntax. --- pyfpdb/HUD_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 6f63a2b5..39096065 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -260,7 +260,7 @@ class HUD_main(object): if hasattr(tablewindow, 'number'): self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards) else: - sys.stderr.write('Table "%s" no longer exists\n', table_name) + sys.stderr.write('Table "%s" no longer exists\n' % table_name) self.db_connection.connection.rollback()