fpdb/pyfpdb/HUD_main.py
unknown 02e8154710 remove error trap on read_stdin() - please fix bugs instead of relying on error trap
some reformatting in Tables.py, as well as some new debug prints to deal with some potential issues. Add code to deal with potential problems in Win x64, that are biting me at random.  Not finished, but the problems stopped happening so can't continue.
2009-10-28 19:53:31 -04:00

292 lines
14 KiB
Python
Executable File

#!/usr/bin/env python
"""Hud_main.py
Main for FreePokerTools HUD.
"""
# 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
########################################################################
# to do allow window resizing
# to do hud to echo, but ignore non numbers
# to do no stat window for hero
# to do things to add to config.xml
# Standard Library modules
import sys
import os
import Options
import traceback
(options, sys.argv) = Options.fpdb_options()
if not options.errorsToConsole:
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
errorFile = open('HUD-error.txt', 'w', 0)
sys.stderr = errorFile
import thread
import time
import string
import re
# pyGTK modules
import pygtk
import gtk
import gobject
# FreePokerTools modules
import Configuration
import Database
import Tables
import Hud
# HUD params:
# - Set aggregate_ring and/or aggregate_tour to True is you want to include stats from other blind levels in the HUD display
# - If aggregation is used, the value of agg_bb_mult determines what levels are included. If
# agg_bb_mult is M and current blind level is L, blinds between L/M and L*M are included. e.g.
# if agg_bb_mult is 100, almost all levels are included in all HUD displays
# if agg_bb_mult is 2, levels from half to double the current blind level are included in the HUD
# if agg_bb_mult is 1 only the current level is included
# - Set hud_style to A to see stats for all-time
# Set hud_style to S to only see stats for current session (currently this shows stats for the last 24 hours)
# Set hud_style to T to only see stats for the last N days (uses value in hud_days)
# - Set hud_days to N to see stats for the last N days in the HUD (only applies if hud_style is T)
def_hud_params = { # Settings for all players apart from program owner ('hero')
'aggregate_ring' : False
, 'aggregate_tour' : True
, 'hud_style' : 'A'
, 'hud_days' : 90
, 'agg_bb_mult' : 10000 # 1 means no aggregation
# , 'hud_session_gap' : 30 not currently used
# Second set of variables for hero - these settings only apply to the program owner
, 'h_aggregate_ring' : False
, 'h_aggregate_tour' : True
, 'h_hud_style' : 'S' # A(ll) / S(ession) / T(ime in days)
, 'h_hud_days' : 60
, 'h_agg_bb_mult' : 10000 # 1 means no aggregation
# , 'h_hud_session_gap' : 30 not currently used
}
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.
def __init__(self, db_name = 'fpdb'):
self.db_name = db_name
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.hud_dict = {}
self.hud_params = self.config.get_hud_ui_parameters()
# a thread to read stdin
gobject.threads_init() # this is required
thread.start_new_thread(self.read_stdin, ()) # starts the thread
# 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 kill_hud(self, event, table):
# called by an event in the HUD, to kill this specific HUD
if table in self.hud_dict:
self.hud_dict[table].kill()
self.hud_dict[table].main_window.destroy()
self.vb.remove(self.hud_dict[table].tablehudlabel)
del(self.hud_dict[table])
self.main_window.resize(1,1)
def create_HUD(self, new_hand_id, table, table_name, max, poker_game, type, stat_dict, cards):
"""type is "ring" or "tour" used to set hud_params"""
def idle_func():
gtk.gdk.threads_enter()
try: # TODO: seriously need to decrease the scope of this block.. what are we expecting to error?
# TODO: The purpose of this try/finally block is to make darn sure that threads_leave()
# TODO: gets called. If there is an exception and threads_leave() doesn't get called we
# TODO: lock up. REB
newlabel = gtk.Label("%s - %s" % (table.site, table_name))
self.vb.add(newlabel)
newlabel.show()
self.main_window.resize_children()
self.hud_dict[table_name].tablehudlabel = newlabel
self.hud_dict[table_name].create(new_hand_id, self.config, stat_dict, cards)
for m in self.hud_dict[table_name].aux_windows:
m.create()
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()
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
self.hud_dict[table_name].stat_dict = stat_dict
self.hud_dict[table_name].cards = cards
if type == "tour" and self.hud_params['aggregate_tour'] == False:
self.hud_dict[table_name].hud_params['agg_bb_mult'] = 1
elif type == "ring" and self.hud_params['aggregate_ring'] == False:
self.hud_dict[table_name].hud_params['agg_bb_mult'] = 1
if type == "tour" and self.hud_params['h_aggregate_tour'] == False:
self.hud_dict[table_name].hud_params['h_agg_bb_mult'] = 1
elif type == "ring" and self.hud_params['h_aggregate_ring'] == False:
self.hud_dict[table_name].hud_params['h_agg_bb_mult'] = 1
self.hud_params['aggregate_ring'] == True
self.hud_params['h_aggregate_ring'] == True
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows]
gobject.idle_add(idle_func)
def update_HUD(self, new_hand_id, table_name, config):
"""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)
[aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows]
# finally:
gtk.gdk.threads_leave()
return False
gobject.idle_add(idle_func)
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)
tourny_finder = re.compile('(\d+) (\d+)')
# get hero's screen names and player ids
self.hero, self.hero_ids = {}, {}
for site in self.config.get_supported_sites():
result = self.db_connection.get_site_id(site)
if result:
site_id = result[0][0]
self.hero[site_id] = self.config.supported_sites[site].screen_name
self.hero_ids[site_id] = self.db_connection.get_player_id(self.config, site, self.hero[site_id])
while 1: # 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
# if there is a db error, complain, skip hand, and proceed
try:
(table_name, max, poker_game, type, site_id) = self.db_connection.get_table_name(new_hand_id)
cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud!
cards['common'] = comm_cards['common']
except Exception, err:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue
if type == "tour": # hand is from a tournament
mat_obj = tourny_finder.search(table_name)
if mat_obj:
(tour_number, tab_number) = mat_obj.group(1, 2)
temp_key = tour_number
else: # tourney, but can't get number and table
print "could not find tournament: skipping "
#sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
continue
else:
temp_key = table_name
# Update an existing HUD
if temp_key in self.hud_dict:
try:
# get stats using hud's specific params
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])
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db get_stats error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database get_stats error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue
self.hud_dict[temp_key].stat_dict = stat_dict
self.hud_dict[temp_key].cards = cards
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows]
self.update_HUD(new_hand_id, temp_key, self.config)
# Or create a new HUD
else:
try:
# get stats using default params
self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] )
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params, self.hero_ids[site_id])
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db get_stats error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database get_stats error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue
if type == "tour":
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
else:
tablewindow = Tables.discover_table_by_name(self.config, table_name)
if tablewindow == None:
# If no client window is found on the screen, complain and continue
if type == "tour":
table_name = "%s %s" % (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, type, stat_dict, cards)
self.db_connection.connection.rollback()
if __name__== "__main__":
sys.stderr.write("HUD_main starting\n")
sys.stderr.write("Using db name = %s\n" % (options.dbname))
# start the HUD_main object
hm = HUD_main(db_name = options.dbname)
# start the event loop
gtk.main()