diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 4e29dba1..5e599d4b 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -53,6 +53,10 @@ class Site: self.site_path = node.getAttribute("site_path") self.HH_path = node.getAttribute("HH_path") self.decoder = node.getAttribute("decoder") + self.hudopacity = node.getAttribute("hudopacity") + self.hudbgcolor = node.getAttribute("bgcolor") + self.hudfgcolor = node.getAttribute("fgcolor") + self.converter = node.getAttribute("converter") self.layout = {} for layout_node in node.getElementsByTagName('layout'): @@ -74,7 +78,7 @@ class Site: if key == 'layout': continue value = getattr(self, key) if callable(value): continue - temp = temp + ' ' + key + " = " + value + "\n" + temp = temp + ' ' + key + " = " + str(value) + "\n" for layout in self.layout: temp = temp + "%s" % self.layout[layout] @@ -105,6 +109,8 @@ class Game: stat.tip = stat_node.getAttribute("tip") stat.click = stat_node.getAttribute("click") stat.popup = stat_node.getAttribute("popup") + stat.hudprefix = stat_node.getAttribute("hudprefix") + stat.hudsuffix = stat_node.getAttribute("hudsuffix") self.stats[stat.stat_name] = stat @@ -193,30 +199,28 @@ class Config: # "file" is a path to an xml file with the fpdb/HUD configuration # we check the existence of "file" and try to recover if it doesn't exist + self.default_config_path = self.get_default_config_path() if not file == None: # configuration file path has been passed if not os.path.exists(file): print "Configuration file %s not found. Using defaults." % (file) sys.stderr.write("Configuration file %s not found. Using defaults." % (file)) file = None -# if "file" is invalid or None, we look for a HUD_config in the cwd if file == None: # configuration file path not passed or invalid - if os.path.exists('HUD_config.xml'): # there is a HUD_config in the cwd - file = 'HUD_config.xml' # so we use it - else: # no HUD_config in the cwd, look where it should be in the first place -# find the path to the default HUD_config for the current os - if os.name == 'posix': - config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'HUD_config.xml') - elif os.name == 'nt': - config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'HUD_config.xml') - else: config_path = False - - if config_path and os.path.exists(config_path): - file = config_path - else: - print "No HUD_config_xml found. Exiting" - sys.stderr.write("No HUD_config_xml found. Exiting") - sys.exit() + file = self.find_config() #Look for a config file in the normal places + + if file == None: # no config file in the normal places + file = self.find_example_config() #Look for an example file to edit + if not file == None: + pass + + if file == None: # that didn't work either, just die + print "No HUD_config_xml found. Exiting" + sys.stderr.write("No HUD_config_xml found. Exiting") + sys.exit() + +# Parse even if there was no real config file found and we are using the example +# If using the example, we'll edit it later try: print "Reading configuration file %s\n" % (file) doc = xml.dom.minidom.parse(file) @@ -268,11 +272,86 @@ class Config: tv = Tv(node = tv_node) self.tv = tv + db = self.get_db_parameters('fpdb') + if db['db-password'] == 'YOUR MYSQL PASSWORD': + df_file = self.find_default_conf() + if df_file == None: # this is bad + pass + else: + df_parms = self.read_default_conf(df_file) + self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'], + db_user = df_parms['db-user'], + db_pass = df_parms['db-password']) + self.save(file=os.path.join(self.default_config_path, "HUD_config.xml")) + + + def find_config(self): + """Looks in cwd and in self.default_config_path for a config file.""" + if os.path.exists('HUD_config.xml'): # there is a HUD_config in the cwd + file = 'HUD_config.xml' # so we use it + else: # no HUD_config in the cwd, look where it should be in the first place + config_path = os.path.join(self.default_config_path, 'HUD_config.xml') + if os.path.exists(config_path): + file = config_path + else: + file = None + return file + + def get_default_config_path(self): + """Returns the path where the fpdb config file _should_ be stored.""" + if os.name == 'posix': + config_path = os.path.join(os.path.expanduser("~"), '.fpdb') + elif os.name == 'nt': + config_path = os.path.join(os.environ["APPDATA"], 'fpdb') + else: config_path = None + return config_path + + + def find_default_conf(self): + if os.name == 'posix': + config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf') + elif os.name == 'nt': + config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'default.conf') + else: config_path = False + + if config_path and os.path.exists(config_path): + file = config_path + else: + file = None + return file + + def read_default_conf(self, file): + parms = {} + fh = open(file, "r") + for line in fh: + line = string.strip(line) + (key, value) = line.split('=') + parms[key] = value + fh.close + return parms + + def find_example_config(self): + if os.path.exists('HUD_config.xml.example'): # there is a HUD_config in the cwd + file = 'HUD_config.xml.example' # so we use it + print "No HUD_config.xml found, using HUD_config.xml.example.\n", \ + "A HUD_config.xml will be written. You will probably have to edit it." + sys.stderr.write("No HUD_config.xml found, using HUD_config.xml.example.\n" + \ + "A HUD_config.xml will be written. You will probably have to edit it.") + else: + file = None + return file + def get_site_node(self, site): for site_node in self.doc.getElementsByTagName("site"): if site_node.getAttribute("site_name") == site: return site_node + def get_db_node(self, db_name): + for db_node in self.doc.getElementsByTagName("database"): + if db_node.getAttribute("db_name") == db_name: + return db_node + return None + def get_layout_node(self, site_node, layout): for layout_node in site_node.getElementsByTagName("layout"): if layout_node.getAttribute("max") == None: @@ -326,6 +405,23 @@ class Config: pass return db + def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None, + db_pass = None, db_server = None, db_type = None): + db_node = self.get_db_node(db_name) + if not db_node == None: + if not db_ip == None: db_node.setAttribute("db_ip", db_ip) + if not db_user == None: db_node.setAttribute("db_user", db_user) + if not db_pass == None: db_node.setAttribute("db_pass", db_pass) + if not db_server == None: db_node.setAttribute("db_server", db_server) + if not db_type == None: db_node.setAttribute("db_type", db_type) + if self.supported_databases.has_key(db_name): + if not db_ip == None: self.supported_databases[db_name].dp_ip = db_ip + if not db_user == None: self.supported_databases[db_name].dp_user = db_user + if not db_pass == None: self.supported_databases[db_name].dp_pass = db_pass + if not db_server == None: self.supported_databases[db_name].dp_server = db_server + if not db_type == None: self.supported_databases[db_name].dp_type = db_type + return + def get_tv_parameters(self): tv = {} try: @@ -358,6 +454,78 @@ class Config: paths['bulkImport-defaultPath'] = "default" return paths + def get_default_colors(self, site = "PokerStars"): + colors = {} + if self.supported_sites[site].hudopacity == "": + colors['hudopacity'] = 0.90 + else: + colors['hudopacity'] = float(self.supported_sites[site].hudopacity) + if self.supported_sites[site].hudbgcolor == "": + colors['hudbgcolor'] = "#FFFFFF" + else: + colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor + if self.supported_sites[site].hudfgcolor == "": + colors['hudfgcolor'] = "#000000" + else: + colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor + return colors + + def get_locations(self, site = "PokerStars", max = "8"): + + try: + locations = self.supported_sites[site].layout[max].location + except: + locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346), + (586, 393), (421, 440), (267, 440), ( 0, 361), + ( 0, 280), (121, 280), ( 46, 30) ) + return locations + + def get_site_parameters(self, site): + """Returns a dict of the site parameters for the specified site""" + if not self.supported_sites.has_key(site): + return None + parms = {} + parms["converter"] = self.supported_sites[site].converter + parms["decoder"] = self.supported_sites[site].decoder + parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor + parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor + parms["hudopacity"] = self.supported_sites[site].hudopacity + parms["screen_name"] = self.supported_sites[site].screen_name + parms["site_path"] = self.supported_sites[site].site_path + parms["table_finder"] = self.supported_sites[site].table_finder + parms["HH_path"] = self.supported_sites[site].HH_path + return parms + + def set_site_parameters(self, site_name, converter = None, decoder = None, + hudbgcolor = None, hudfgcolor = None, + hudopacity = None, screen_name = None, + site_path = None, table_finder = None, + HH_path = None): + """Sets the specified site parameters for the specified site.""" + site_node = self.get_site_node(site_name) + if not db_node == None: + if not converter == None: site_node.setAttribute("converter", converter) + if not decoder == None: site_node.setAttribute("decoder", decoder) + if not hudbgcolor == None: site_node.setAttribute("hudbgcolor", hudbgcolor) + if not hudfgcolor == None: site_node.setAttribute("hudfgcolor", hudfgcolor) + if not hudopacity == None: site_node.setAttribute("hudopacity", hudopacity) + if not screen_name == None: site_node.setAttribute("screen_name", screen_name) + if not site_path == None: site_node.setAttribute("site_path", site_path) + if not table_finder == None: site_node.setAttribute("table_finder", table_finder) + if not HH_path == None: site_node.setAttribute("HH_path", HH_path) + + if self.supported_databases.has_key(db_name): + if not converter == None: self.supported_sites[site].converter = converter + if not decoder == None: self.supported_sites[site].decoder = decoder + if not hudbgcolor == None: self.supported_sites[site].hudbgcolor = hudbgcolor + if not hudfgcolor == None: self.supported_sites[site].hudfgcolor = hudfgcolor + if not hudopacity == None: self.supported_sites[site].hudopacity = hudopacity + if not screen_name == None: self.supported_sites[site].screen_name = screen_name + if not site_path == None: self.supported_sites[site].site_path = site_path + if not table_finder == None: self.supported_sites[site].table_finder = table_finder + if not HH_path == None: self.supported_sites[site].HH_path = HH_path + return + if __name__== "__main__": c = Config() @@ -389,17 +557,22 @@ if __name__== "__main__": print "----------- END MUCKED WINDOW FORMATS -----------" print "\n----------- IMPORT -----------" - print c.imp +# print c.imp print "----------- END IMPORT -----------" print "\n----------- TABLE VIEW -----------" - print c.tv +# print c.tv print "----------- END TABLE VIEW -----------" c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) )) c.save(file="testout.xml") - print "db = ", c.get_db_parameters() - print "tv = ", c.get_tv_parameters() - print "imp = ", c.get_import_parameters() - print "paths = ", c.get_default_paths("PokerStars") + print "db = ", c.get_db_parameters() +# print "tv = ", c.get_tv_parameters() +# print "imp = ", c.get_import_parameters() + print "paths = ", c.get_default_paths("PokerStars") + print "colors = ", c.get_default_colors("PokerStars") + print "locs = ", c.get_locations("PokerStars", 8) + for site in c.supported_sites.keys(): + print "site = ", site, + print c.get_site_parameters(site) \ No newline at end of file diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 63de54b2..4039b256 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -59,6 +59,10 @@ class Database: user = c.supported_databases[db_name].db_user, passwd = c.supported_databases[db_name].db_pass, db = c.supported_databases[db_name].db_name) + cur_iso = self.connection.cursor() + cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') + cur_iso.close() + except: print "Error opening database connection %s. See error log file." % (file) traceback.print_exc(file=sys.stderr) diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 7a6313cc..916170f0 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -110,8 +110,8 @@ class GuiAutoImport (threading.Thread): self.tiltpath=self.tiltDirPath.get_text() # Add directory to importer object. - self.importer.addImportDirectory(self.starspath, True) - self.importer.addImportDirectory(self.tiltpath, True) + self.importer.addImportDirectory(self.starspath, True, "PokerStars", "passthrough") + self.importer.addImportDirectory(self.tiltpath, True, "FullTilt", "passthrough") self.do_import() interval=int(self.intervalEntry.get_text()) diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index abdf7618..3a595fe2 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -2,7 +2,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -84,6 +84,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index b1d5df14..b1955a6b 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -29,7 +29,6 @@ Main for FreePokerTools HUD. # to do no hud window for hero # to do things to add to config.xml # to do font and size -# to do bg and fg color # to do opacity # Standard Library modules @@ -90,6 +89,7 @@ def update_HUD(new_hand_id, table_name, config, stat_dict): def read_stdin(): # This is the thread function global hud_dict + db_connection = Database.Database(config, db_name, 'temp') while True: # wait for a new hand number on stdin new_hand_id = sys.stdin.readline() new_hand_id = string.rstrip(new_hand_id) @@ -102,21 +102,19 @@ def read_stdin(): # This is the thread function del(hud_dict[h]) # connect to the db and get basic info about the new hand - db_connection = Database.Database(config, db_name, 'temp') (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id) stat_dict = db_connection.get_stats_from_hand(new_hand_id) - db_connection.close_connection() # if a hud for this table exists, just update it if hud_dict.has_key(table_name): update_HUD(new_hand_id, table_name, config, stat_dict) # otherwise create a new hud else: - table_windows = Tables.discover(config) - for t in table_windows.keys(): - if table_windows[t].name == table_name: - create_HUD(new_hand_id, table_windows[t], db_name, table_name, max, poker_game, db_connection, config, stat_dict) - break + tablewindow = Tables.discover_table_by_name(config, table_name) + if tablewindow == None: + sys.stderr.write("table name "+table_name+" not found\n") + else: + create_HUD(new_hand_id, tablewindow, db_name, table_name, max, poker_game, db_connection, config, stat_dict) if __name__== "__main__": sys.stderr.write("HUD_main starting\n") @@ -142,3 +140,4 @@ if __name__== "__main__": main_window.show_all() gtk.main() + diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index b6233ec6..2712c55b 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -23,6 +23,7 @@ Create and manage the hud overlays. ######################################################################## # Standard Library modules import os +import sys # pyGTK modules import pygtk @@ -34,6 +35,7 @@ import gobject if os.name == 'nt': import win32gui import win32con + import win32api # FreePokerTools modules import Tables # needed for testing only @@ -41,7 +43,7 @@ import Configuration import Stats import Mucked import Database -import HUD_main +import HUD_main class Hud: @@ -53,27 +55,32 @@ class Hud: self.db_name = db_name self.deleted = False self.stacked = True + self.colors = config.get_default_colors(self.table.site) self.stat_windows = {} self.popup_windows = {} self.font = pango.FontDescription("Sans 8") -# Set up a main window for this this instance of the HUD +# Set up a main window for this this instance of the HUD self.main_window = gtk.Window() # self.window.set_decorated(0) self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC) - self.main_window.set_keep_above(1) - self.main_window.set_title(table.name) - + self.main_window.set_title(table.name + " FPDBHUD") self.main_window.connect("destroy", self.kill_hud) - if self.stacked: - self.main_window.connect("window-state-event", self.on_window_event) + self.main_window.set_decorated(False) + #self.main_window.set_transient_for(parent.get_toplevel()) self.ebox = gtk.EventBox() - self.label = gtk.Label("Close this window to\nkill the HUD for\n %s\nMinimizing it hides stats." % - (table.name)) + self.label = gtk.Label("Right click to close HUD for %s\nor Save Stat Positions." % (table.name)) + + self.label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor'])) + self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) + self.main_window.add(self.ebox) self.ebox.add(self.label) + self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor'])) + self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) + self.main_window.move(self.table.x, self.table.y) # A popup window for the main window @@ -90,7 +97,14 @@ class Hud: self.main_window.show_all() # set_keep_above(1) for windows - if os.name == 'nt': self.topify_window(self.main_window) + if os.name == 'nt': + self.topify_window(self.main_window) + else: + self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number) # gets a gdk handle for poker client + self.main_window.gdkhandle = gtk.gdk.window_foreign_new(self.main_window.window.xid) # gets a gdk handle for the hud table window + self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) # + + self.main_window.set_destroy_with_parent(True) def on_button_press(self, widget, event): if event.button == 3: @@ -98,16 +112,6 @@ class Hud: return True return False - def on_window_event(self, widget, event): - - if self.stacked: - if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: - for sw in self.stat_windows.keys(): - self.stat_windows[sw].window.iconify() - else: - for sw in self.stat_windows: - self.stat_windows[sw].window.deiconify() - def kill_hud(self, args): for k in self.stat_windows.keys(): self.stat_windows[k].window.destroy() @@ -116,6 +120,7 @@ class Hud: def save_layout(self, *args): new_layout = [] +# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y. for sw in self.stat_windows: loc = self.stat_windows[sw].window.get_position() new_loc = (loc[0] - self.table.x, loc[1] - self.table.y) @@ -150,10 +155,15 @@ class Hud: # windows via calls to the Stat_Window class adj = self.adj_seats(hand, config) + loc = self.config.get_locations(self.table.site, self.max) + # create the stat windows - for i in range(1, self.max + 1): - (x, y) = config.supported_sites[self.table.site].layout[self.max].location[adj[i]] - self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], + for i in range(1, self.max + 1): + (x, y) = loc[adj[i]] + if self.stat_windows.has_key(i): + 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, x = x, @@ -178,11 +188,19 @@ class Hud: def update(self, hand, config, stat_dict): self.hand = hand # this is the last hand, so it is available later for s in stat_dict.keys(): - self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] + try: + self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] + except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here + self.max = 10 + self.create(hand, config) + self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] + for r in range(0, config.supported_games[self.poker_game].rows): for c in range(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(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c]) - self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(number[1]) + statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix + self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring) 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) @@ -203,7 +221,21 @@ class Hud: for w in tl_windows: if w[1] == unique_name: - win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + #win32gui.ShowWindow(w[0], win32con.SW_HIDE) + window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number)) + self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0]) + self.main_window.gdkhandle.set_transient_for(window.parentgdkhandle) + #win32gui.ShowWindow(w[0], win32con.SW_SHOW) + + style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE) + #style |= win32con.WS_EX_TOOLWINDOW + #style &= ~win32con.WS_EX_APPWINDOW + style |= win32con.WS_CLIPCHILDREN + win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style) + + + #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + # notify_id = (w[0], # 0, # win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, @@ -247,7 +279,7 @@ class Stat_Window: Popup_window(widget, self) return False - def double_click(self, widget, event, *args): + def double_click(self, widget, event, *args): self.toggle_decorated(widget) def toggle_decorated(self, widget): @@ -260,6 +292,11 @@ class Stat_Window: else: top.set_decorated(1) top.move(x, y) + + def relocate(self, x, y): + self.x = x + self.table.x + self.y = y + self.table.y + self.window.move(self.x, self.y) def __init__(self, parent, game, table, seat, x, y, player_id, font): self.parent = parent # Hud object that this stat window belongs to @@ -273,9 +310,10 @@ class Stat_Window: self.window = gtk.Window() self.window.set_decorated(0) self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) - self.window.set_keep_above(1) + self.window.set_title("%s" % seat) self.window.set_property("skip-taskbar-hint", True) + self.window.set_transient_for(parent.main_window) self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False) self.window.add(self.grid) @@ -288,13 +326,23 @@ class Stat_Window: self.label.append([]) for c in range(self.game.cols): self.e_box[r].append( gtk.EventBox() ) + + self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor'])) + self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor'])) + Stats.do_tip(self.e_box[r][c], 'farts') self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) self.label[r].append( gtk.Label('xxx') ) + + self.label[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor'])) + self.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor'])) + self.e_box[r][c].add(self.label[r][c]) self.e_box[r][c].connect("button_press_event", self.button_press_cb) # font = pango.FontDescription("Sans 8") self.label[r][c].modify_font(font) + + self.window.set_opacity(parent.colors['hudopacity']) self.window.realize self.window.move(self.x, self.y) self.window.show_all() @@ -316,16 +364,15 @@ class Stat_Window: for w in tl_windows: if w[1] == unique_name: - win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) -# notify_id = (w[0], -# 0, -# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, -# win32con.WM_USER+20, -# 0, -# '') -# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) -# - window.set_title(real_name) + + #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + +# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE) +# style |= win32con.WS_EX_TOOLWINDOW +# style &= ~win32con.WS_EX_APPWINDOW +# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style) + win32gui.ShowWindow(w[0], win32con.SW_SHOW) + window.set_title(real_name) def destroy(*args): # call back for terminating the main eventloop gtk.main_quit() @@ -338,10 +385,11 @@ class Popup_window: self.window = gtk.Window() self.window.set_decorated(0) self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) - self.window.set_keep_above(1) +# self.window.set_keep_above(1) self.window.set_title("popup") self.window.set_property("skip-taskbar-hint", True) self.window.set_transient_for(parent.get_toplevel()) + self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.ebox = gtk.EventBox() @@ -351,6 +399,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, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) + self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) + self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) + self.window.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) + self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) + self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) + self.window.realize # figure out the row, col address of the click that activated the popup @@ -392,6 +448,9 @@ class Popup_window: self.lab.set_text(pu_text) self.window.show_all() + + self.window.set_transient_for(stat_window.main_window) + # set_keep_above(1) for windows if os.name == 'nt': self.topify_window(self.window) @@ -454,7 +513,14 @@ class Popup_window: for w in tl_windows: if w[1] == unique_name: - win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) +# win32gui.ShowWindow(w[0], win32con.SW_HIDE) +# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE) +# style |= win32con.WS_EX_TOOLWINDOW +# style &= ~win32con.WS_EX_APPWINDOW +# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style) +# win32gui.ShowWindow(w[0], win32con.SW_SHOW) + win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + # notify_id = (w[0], # 0, # win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, @@ -473,12 +539,16 @@ if __name__== "__main__": main_window.show_all() c = Configuration.Config() - tables = Tables.discover(c) + #tables = Tables.discover(c) + t = Tables.discover_table_by_name(c, "Chelsea") + if t is None: + print "Table not found." db = Database.Database(c, 'fpdb', 'holdem') - for t in tables: - win = Hud(t, 8, c, db) +# for t in tables: + win = Hud(t, 10, 'holdem', c, db) + win.create(1, c) # t.get_details() - win.update(8300, db, c) + win.update(8300, db, c) gtk.main() diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py index 3531c017..7bb9a8c5 100644 --- a/pyfpdb/Stats.py +++ b/pyfpdb/Stats.py @@ -58,6 +58,7 @@ def do_tip(widget, tip): def do_stat(stat_dict, player = 24, stat = 'vpip'): return eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player}) + # OK, for reference the tuple returned by the stat is: # 0 - The stat, raw, no formating, eg 0.33333333 # 1 - formatted stat with appropriate precision and punctuation, eg 33% @@ -68,6 +69,15 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'): ########################################### # functions that return individual stats + +def playername(stat_dict, player): + return (stat_dict[player]['screen_name'], + stat_dict[player]['screen_name'], + stat_dict[player]['screen_name'], + stat_dict[player]['screen_name'], + stat_dict[player]['screen_name'], + stat_dict[player]['screen_name']) + def vpip(stat_dict, player): """ Voluntarily put $ in the pot.""" stat = 0.0 @@ -372,7 +382,7 @@ def a_freq_3(stat_dict, player): '%3.1f' % (100*stat) + '%', 'a3=%3.1f' % (100*stat) + '%', 'a_fq_3=%3.1f' % (100*stat) + '%', - '(%d/%d)' % (stat_dict[player]['aggr_1'], stat_dict[player]['saw_1']), + '(%d/%d)' % (stat_dict[player]['aggr_3'], stat_dict[player]['saw_3']), 'Aggression Freq river/6th' ) except: diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index a0f367da..752c5929 100644 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -33,6 +33,9 @@ import re if os.name == 'nt': import win32gui + import win32process + import win32api + import win32con # FreePokerTools modules import Configuration @@ -57,17 +60,26 @@ class Table_Window: def discover(c): if os.name == 'posix': tables = discover_posix(c) - return tables elif os.name == 'nt': tables = discover_nt(c) - return tables elif os.name == 'mac': tables = discover_mac(c) - return tables - else: tables = {} + else: + tables = {} return(tables) +def discover_table_by_name(c, tablename): + if os.name == 'posix': + table = discover_posix_by_name(c, tablename) + elif os.name == 'nt': + table = discover_nt_by_name(c, tablename) + elif os.name == 'mac': + table = discover_mac_by_name(c, tablename) + else: + table = None + return(table) + def discover_posix(c): """ Poker client table window finder for posix/Linux = XWindows.""" tables = {} @@ -84,7 +96,7 @@ def discover_posix(c): if re.match('[\(\)\d\s]+', mo.group(2)): continue # this is a popup tw = Table_Window() tw.site = c.supported_sites[s].site_name - tw.number = mo.group(1) + tw.number = int(mo.group(1), 0) tw.title = mo.group(2) tw.width = int( mo.group(3) ) tw.height = int( mo.group(4) ) @@ -94,8 +106,17 @@ def discover_posix(c): # use this eval thingie to call the title bar decoder specified in the config file eval("%s(tw)" % c.supported_sites[s].decoder) + tables[tw.name] = tw return tables + +def discover_posix_by_name(c, tablename): + tables = discover_posix(c) + for t in tables: + if tables[t].name.find(tablename) > -1: + return tables[t] + return None + # # The discover_xx functions query the system and report on the poker clients # currently displayed on the screen. The discover_posix should give you @@ -120,6 +141,7 @@ def discover_posix(c): def win_enum_handler(hwnd, titles): titles[hwnd] = win32gui.GetWindowText(hwnd) + def child_enum_handler(hwnd, children): print hwnd, win32.GetWindowRect(hwnd) @@ -150,7 +172,7 @@ def discover_nt(c): tw.y = int( y ) + tb_height if re.search('Logged In as', titles[hwnd]): tw.site = "PokerStars" - elif re.search('Logged In As', titles[hwnd]): + elif re.search('Logged In As', titles[hwnd]): #wait, what??! tw.site = "Full Tilt" else: tw.site = "Unknown" @@ -159,14 +181,73 @@ def discover_nt(c): eval("%s(tw)" % c.supported_sites[tw.site].decoder) else: tw.name = "Unknown" - tables[tw.name] = tw + tables[len(tables)] = tw return tables +def discover_nt_by_name(c, tablename): + # this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name + # it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should + # be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work. + # - eric + b_width = 3 + tb_height = 29 + titles = {} +# tables = discover_nt(c) + win32gui.EnumWindows(win_enum_handler, titles) + for s in c.supported_sites.keys(): + for hwnd in titles.keys(): + processid = win32process.GetWindowThreadProcessId(hwnd) + pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1]) + exe = win32process.GetModuleFileNameEx(pshandle, 0) + if exe.find(c.supported_sites[s].table_finder) == -1: + continue + if titles[hwnd].find(tablename) > -1: + if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1: + continue + tw = Table_Window() + tw.number = hwnd + (x, y, width, height) = win32gui.GetWindowRect(hwnd) + tw.title = titles[hwnd] + tw.width = int(width) - 2 * b_width + tw.height = int(height) - b_width - tb_height + tw.x = int(x) + b_width + tw.y = int(y) + tb_height + tw.site = c.supported_sites[s].site_name + if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown": + eval("%s(tw)" % c.supported_sites[tw.site].decoder) + else: + tw.name = tablename + return tw + + # if we don't find anything by process name, let's search one more time, and call it Unknown ? + for hwnd in titles.keys(): + if titles[hwnd].find(tablename) > -1: + if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1: + continue + tw = Table_Window() + tw.number = hwnd + (x, y, width, height) = win32gui.GetWindowRect(hwnd) + tw.title = titles[hwnd] + tw.width = int(width) - 2 * b_width + tw.height = int(height) - b_width - tb_height + tw.x = int(x) + b_width + tw.y = int(y) + tb_height + tw.site = "Unknown" + tw.name = tablename + return tw + + return None + def discover_mac(c): """ Poker client table window finder for Macintosh.""" tables = {} return tables +def discover_mac_by_name(c, tablename): + # again, i have no mac to test this on, sorry -eric + return discover_mac(c) + + def pokerstars_decode_table(tw): # extract the table name OR the tournament number and table name from the title # other info in title is redundant with data in the database @@ -221,6 +302,7 @@ def fulltilt_decode_table(tw): if __name__=="__main__": c = Configuration.Config() + print discover_table_by_name(c, "Catacaos") tables = discover(c) for t in tables.keys(): diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py old mode 100644 new mode 100755 diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index fb170240..ffa06cff 100755 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -47,8 +47,8 @@ class Importer: self.caller=caller self.db = None self.cursor = None - self.filelist = [] - self.dirlist = [] + self.filelist = {} + self.dirlist = {} self.monitor = False self.updated = {} #Time last import was run {file:mtime} self.callHud = False @@ -100,29 +100,25 @@ class Importer: # self.updated = time() def clearFileList(self): - self.filelist = [] + self.filelist = {} #Add an individual file to filelist - def addImportFile(self, filename): + def addImportFile(self, filename, site = "default", filter = "passthrough"): #TODO: test it is a valid file - self.filelist = self.filelist + [filename] - #Remove duplicates - self.filelist = list(set(self.filelist)) + self.filelist[filename] = [site] + [filter] #Add a directory of files to filelist - def addImportDirectory(self,dir,monitor = False): + #Only one import directory per site supported. + #dirlist is a hash of lists: + #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } + def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): if os.path.isdir(dir): if monitor == True: self.monitor = True - self.dirlist = self.dirlist + [dir] + self.dirlist[site] = [dir] + [filter] for file in os.listdir(dir): - if os.path.isdir(file): - print "BulkImport is not recursive - please select the final directory in which the history files are" - else: - self.filelist = self.filelist + [os.path.join(dir, file)] - #Remove duplicates - self.filelist = list(set(self.filelist)) + self.addImportFile(os.path.join(dir, file), site, filter) else: print "Warning: Attempted to add: '" + str(dir) + "' as an import directory" @@ -136,23 +132,22 @@ class Importer: #Check for new files in directory #todo: make efficient - always checks for new file, should be able to use mtime of directory # ^^ May not work on windows - for dir in self.dirlist: - for file in os.listdir(dir): - self.filelist = self.filelist + [dir+os.sep+file] - - self.filelist = list(set(self.filelist)) + for site in self.dirlist: + self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) for file in self.filelist: stat_info = os.stat(file) try: lastupdate = self.updated[file] -# print "Is " + str(stat_info.st_mtime) + " > " + str(lastupdate) if stat_info.st_mtime > lastupdate: self.import_file_dict(file) self.updated[file] = time() except: -# print "Adding " + str(file) + " at approx " + str(time()) self.updated[file] = time() + # This codepath only runs first time the file is found, if modified in the last + # minute run an immediate import. + if (time() - stat_info.st_mtime) < 60: + self.import_file_dict(file) # This is now an internal function that should not be called directly. def import_file_dict(self, file): diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 217b9163..ab442d85 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -330,6 +330,8 @@ def filterCrap(site, hand, isTourney): toRemove.append(hand[i]) elif (hand[i].find(" out of hand ")!=-1): hand[i]=hand[i][:-56] + elif (hand[i].find("($0 in chips)") != -1): + toRemove.append(hand[i]) elif (hand[i]=="*** HOLE CARDS ***"): toRemove.append(hand[i]) elif (hand[i].endswith("has been disconnected")): diff --git a/pyfpdb/schema.postgres.sql b/pyfpdb/schema.postgres.sql deleted file mode 100644 index 2affb1c6..00000000 --- a/pyfpdb/schema.postgres.sql +++ /dev/null @@ -1,218 +0,0 @@ - -DROP TABLE IF EXISTS Settings CASCADE; -CREATE TABLE Settings (version SMALLINT); - -DROP TABLE IF EXISTS Sites CASCADE; -CREATE TABLE Sites ( - id SERIAL UNIQUE, PRIMARY KEY (id), - name varchar(32), - currency char(3)); - -DROP TABLE IF EXISTS Gametypes CASCADE; -CREATE TABLE Gametypes ( - id SERIAL UNIQUE, PRIMARY KEY (id), - siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), - type char(4), - base char(4), - category varchar(9), - limitType char(2), - hiLo char(1), - smallBlind int, - bigBlind int, - smallBet int, - bigBet int); - -DROP TABLE IF EXISTS Players CASCADE; -CREATE TABLE Players ( - id SERIAL UNIQUE, PRIMARY KEY (id), - name VARCHAR(32), - siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), - comment text, - commentTs timestamp without time zone); - -DROP TABLE IF EXISTS Autorates CASCADE; -CREATE TABLE Autorates ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), - gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), - description varchar(50), - shortDesc char(8), - ratingTime timestamp without time zone, - handCount int); - -DROP TABLE IF EXISTS Hands CASCADE; -CREATE TABLE Hands ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - tableName VARCHAR(20), - siteHandNo BIGINT, - gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), - handStart timestamp without time zone, - importTime timestamp without time zone, - seats SMALLINT, - maxSeats SMALLINT, - comment TEXT, - commentTs timestamp without time zone); - -DROP TABLE IF EXISTS BoardCards CASCADE; -CREATE TABLE BoardCards ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id), - card1Value smallint, - card1Suit char(1), - card2Value smallint, - card2Suit char(1), - card3Value smallint, - card3Suit char(1), - card4Value smallint, - card4Suit char(1), - card5Value smallint, - card5Suit char(1)); - -DROP TABLE IF EXISTS TourneyTypes CASCADE; -CREATE TABLE TourneyTypes ( - id SERIAL, PRIMARY KEY (id), - siteId INT, FOREIGN KEY (siteId) REFERENCES Sites(id), - buyin INT, - fee INT, - knockout INT, - rebuyOrAddon BOOLEAN); - -DROP TABLE IF EXISTS Tourneys CASCADE; -CREATE TABLE Tourneys ( - id SERIAL UNIQUE, PRIMARY KEY (id), - tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), - siteTourneyNo BIGINT, - entries INT, - prizepool INT, - startTime timestamp without time zone, - comment TEXT, - commentTs timestamp without time zone); - -DROP TABLE IF EXISTS TourneysPlayers CASCADE; -CREATE TABLE TourneysPlayers ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id), - playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), - payinAmount INT, - rank INT, - winnings INT, - comment TEXT, - commentTs timestamp without time zone); - -DROP TABLE IF EXISTS HandsPlayers CASCADE; -CREATE TABLE HandsPlayers ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id), - playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), - startCash INT, - position CHAR(1), - seatNo SMALLINT, - ante INT, - - card1Value smallint, - card1Suit char(1), - card2Value smallint, - card2Suit char(1), - card3Value smallint, - card3Suit char(1), - card4Value smallint, - card4Suit char(1), - card5Value smallint, - card5Suit char(1), - card6Value smallint, - card6Suit char(1), - card7Value smallint, - card7Suit char(1), - - winnings int, - rake int, - comment text, - commentTs timestamp without time zone, - tourneysPlayersId BIGINT, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id)); - -DROP TABLE IF EXISTS HandsActions CASCADE; -CREATE TABLE HandsActions ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id), - street SMALLINT, - actionNo SMALLINT, - action CHAR(5), - allIn BOOLEAN, - amount INT, - comment TEXT, - commentTs timestamp without time zone); - -DROP TABLE IF EXISTS HudCache CASCADE; -CREATE TABLE HudCache ( - id BIGSERIAL UNIQUE, PRIMARY KEY (id), - gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), - playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), - activeSeats SMALLINT, - position CHAR(1), - tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), - - HDs INT, - street0VPI INT, - street0Aggr INT, - street0_3B4BChance INT, - street0_3B4BDone INT, - street1Seen INT, - street2Seen INT, - street3Seen INT, - street4Seen INT, - sawShowdown INT, - street1Aggr INT, - street2Aggr INT, - street3Aggr INT, - street4Aggr INT, - otherRaisedStreet1 INT, - otherRaisedStreet2 INT, - otherRaisedStreet3 INT, - otherRaisedStreet4 INT, - foldToOtherRaisedStreet1 INT, - foldToOtherRaisedStreet2 INT, - foldToOtherRaisedStreet3 INT, - foldToOtherRaisedStreet4 INT, - wonWhenSeenStreet1 FLOAT, - wonAtSD FLOAT, - - stealAttemptChance INT, - stealAttempted INT, - foldBbToStealChance INT, - foldedBbToSteal INT, - foldSbToStealChance INT, - foldedSbToSteal INT, - - street1CBChance INT, - street1CBDone INT, - street2CBChance INT, - street2CBDone INT, - street3CBChance INT, - street3CBDone INT, - street4CBChance INT, - street4CBDone INT, - - foldToStreet1CBChance INT, - foldToStreet1CBDone INT, - foldToStreet2CBChance INT, - foldToStreet2CBDone INT, - foldToStreet3CBChance INT, - foldToStreet3CBDone INT, - foldToStreet4CBChance INT, - foldToStreet4CBDone INT, - - totalProfit INT, - - street1CheckCallRaiseChance INT, - street1CheckCallRaiseDone INT, - street2CheckCallRaiseChance INT, - street2CheckCallRaiseDone INT, - street3CheckCallRaiseChance INT, - street3CheckCallRaiseDone INT, - street4CheckCallRaiseChance INT, - street4CheckCallRaiseDone INT); - -INSERT INTO Settings VALUES (118); -INSERT INTO Sites ("name", currency) VALUES ('Full Tilt Poker', 'USD'); -INSERT INTO Sites ("name", currency) VALUES ('PokerStars', 'USD'); -INSERT INTO TourneyTypes (buyin, fee, knockout, rebuyOrAddon) VALUES (0, 0, 0, FALSE);