You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
17 KiB
387 lines
17 KiB
#!/usr/bin/env python |
|
# -*- coding: utf-8 -*- |
|
|
|
# Copyright 2008-2011, 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 |
|
|
|
######################################################################## |
|
|
|
"""Hud_main.py |
|
|
|
Main for FreePokerTools HUD. |
|
""" |
|
import L10n |
|
_ = L10n.get_translation() |
|
|
|
# Standard Library modules |
|
import sys |
|
import os |
|
import traceback |
|
import thread |
|
import time |
|
import string |
|
|
|
# pyGTK modules |
|
import gtk |
|
import gobject |
|
|
|
# FreePokerTools modules |
|
import Configuration |
|
import Database |
|
import Hud |
|
import Options |
|
|
|
(options, argv) = Options.fpdb_options() |
|
|
|
# get the correct module for the current os |
|
if sys.platform == 'linux2': |
|
import XTables as Tables |
|
elif sys.platform == 'darwin': |
|
import OSXTables as Tables |
|
else: # This is bad--figure out the values for the various windows flavors |
|
is_windows = True |
|
import WinTables as Tables |
|
|
|
# get config and set up logger |
|
c = Configuration.Config(file=options.config, dbname=options.dbname) |
|
log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt') |
|
|
|
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 = c |
|
log.info(_("HUD_main starting: using db name = %s") % (db_name)) |
|
|
|
try: |
|
if not options.errorsToConsole: |
|
fileName = os.path.join(self.config.dir_log, 'HUD-errors.txt') |
|
log.info(_("Note: error output is being diverted to:") + fileName) |
|
log.info(_("Any major error will be reported there _only_.")) |
|
errorFile = open(fileName, 'w', 0) |
|
sys.stderr = errorFile |
|
log.info(_("HUD_main: starting ...\n")) |
|
|
|
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() |
|
|
|
if os.name == 'nt': # Check for admin rights, don't start auto import if we don't have them |
|
if (os.sys.getwindowsversion()[0] >= 6): |
|
import ctypes |
|
if not ctypes.windll.shell32.IsUserAnAdmin(): |
|
dia = gtk.MessageDialog(parent=self.main_window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_ERROR, buttons=(gtk.BUTTONS_OK), message_format=_("No admin rights for HUD")) |
|
dia.format_secondary_text(_("Please right click fpdb.exe and HUD_main.exe, select properties, and set them both to run as admin.")+" "+_("You will need to restart fpdb afterwards.")) |
|
response = dia.run() |
|
dia.destroy() |
|
return |
|
|
|
if options.minimized: |
|
self.main_window.iconify() |
|
if options.hidden: |
|
self.main_window.hide() |
|
|
|
if options.xloc is not None or options.yloc is not None: |
|
if options.xloc is None: |
|
options.xloc = 0 |
|
if options.yloc is None: |
|
options.yloc = 0 |
|
self.main_window.move(options.xloc,options.yloc) |
|
self.main_window.connect("client_moved", self.client_moved) |
|
self.main_window.connect("client_resized", self.client_resized) |
|
self.main_window.connect("client_destroyed", self.client_destroyed) |
|
self.main_window.connect("game_changed", self.game_changed) |
|
self.main_window.connect("table_changed", self.table_changed) |
|
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") |
|
cards = os.path.join(os.getcwd(), '..','gfx','fpdb-cards.png') |
|
if os.path.exists(cards): |
|
self.main_window.set_icon_from_file(cards) |
|
elif os.path.exists('/usr/share/pixmaps/fpdb-cards.png'): |
|
self.main_window.set_icon_from_file('/usr/share/pixmaps/fpdb-cards.png') |
|
else: |
|
self.main_window.set_icon_stock(gtk.STOCK_HOME) |
|
if not options.hidden: |
|
self.main_window.show_all() |
|
gobject.timeout_add(800, self.check_tables) |
|
|
|
except: |
|
log.exception(_("Error initializing main_window")) |
|
gtk.main_quit() # we're hosed, just terminate |
|
|
|
def client_moved(self, widget, hud): |
|
hud.up_update_table_position() |
|
|
|
def client_resized(self, widget, hud): |
|
#TODO Don't forget to get rid of this. |
|
if not is_windows: |
|
gigobject.idle_add(idle_resize, hud) |
|
|
|
def client_destroyed(self, widget, hud): # call back for terminating the main eventloop |
|
self.kill_hud(None, hud.table.key) |
|
|
|
def game_changed(self, widget, hud): |
|
print _("hud_main: Game changed.") |
|
|
|
def table_changed(self, widget, hud): |
|
self.kill_hud(None, hud.table.key) |
|
|
|
def destroy(self, *args): # call back for terminating the main eventloop |
|
log.info(_("Quitting normally")) |
|
gtk.main_quit() |
|
|
|
def kill_hud(self, event, table): |
|
gobject.idle_add(idle_kill, self, table) |
|
|
|
def check_tables(self): |
|
for hud in self.hud_dict.keys(): |
|
self.hud_dict[hud].table.check_table(self.hud_dict[hud]) |
|
return True |
|
|
|
def create_HUD(self, new_hand_id, table, temp_key, max, poker_game, type, stat_dict, cards): |
|
"""type is "ring" or "tour" used to set hud_params""" |
|
|
|
self.hud_dict[temp_key] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) |
|
self.hud_dict[temp_key].table_name = temp_key |
|
self.hud_dict[temp_key].stat_dict = stat_dict |
|
self.hud_dict[temp_key].cards = cards |
|
table.hud = self.hud_dict[temp_key] |
|
|
|
# set agg_bb_mult so that aggregate_tour and aggregate_ring can be ignored, |
|
# agg_bb_mult == 1 means no aggregation after these if statements: |
|
if type == "tour" and self.hud_params['aggregate_tour'] == False: |
|
self.hud_dict[temp_key].hud_params['agg_bb_mult'] = 1 |
|
elif type == "ring" and self.hud_params['aggregate_ring'] == False: |
|
self.hud_dict[temp_key].hud_params['agg_bb_mult'] = 1 |
|
if type == "tour" and self.hud_params['h_aggregate_tour'] == False: |
|
self.hud_dict[temp_key].hud_params['h_agg_bb_mult'] = 1 |
|
elif type == "ring" and self.hud_params['h_aggregate_ring'] == False: |
|
self.hud_dict[temp_key].hud_params['h_agg_bb_mult'] = 1 |
|
# sqlcoder: I forget why these are set to true (aren't they ignored from now on?) |
|
# but I think it's needed: |
|
self.hud_params['aggregate_ring'] = True |
|
self.hud_params['h_aggregate_ring'] = True |
|
# so maybe the tour ones should be set as well? does this fix the bug I see mentioned? |
|
self.hud_params['aggregate_tour'] = True |
|
self.hud_params['h_aggregate_tour'] = True |
|
|
|
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows] |
|
gobject.idle_add(idle_create, self, new_hand_id, table, temp_key, max, poker_game, type, stat_dict, cards) |
|
|
|
def update_HUD(self, new_hand_id, table_name, config): |
|
"""Update a HUD gui from inside the non-gui read_stdin thread.""" |
|
gobject.idle_add(idle_update, self, new_hand_id, table_name, config) |
|
|
|
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) |
|
|
|
# get hero's screen names and player ids |
|
self.hero, self.hero_ids = {}, {} |
|
found = False |
|
|
|
while 1: # wait for a new hand number on stdin |
|
new_hand_id = sys.stdin.readline() |
|
new_hand_id = string.rstrip(new_hand_id) |
|
log.debug(_("Received hand no %s") % 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() |
|
|
|
# This block cannot be hoisted outside the while loop, because it would |
|
# cause a problem when auto importing into an empty db. |
|
|
|
# FIXME: This doesn't work in the case of the player playing on 2 |
|
# sites at once (???) Eratosthenes |
|
if not found: |
|
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]) |
|
if self.hero_ids[site_id] is not None: |
|
found = True |
|
else: |
|
self.hero_ids[site_id] = -1 |
|
|
|
# get basic info about the new hand from the db |
|
# if there is a db error, complain, skip hand, and proceed |
|
log.info(_("HUD_main.read_stdin: hand processing starting ...")) |
|
try: |
|
(table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \ |
|
self.db_connection.get_table_info(new_hand_id) |
|
except Exception: |
|
log.exception(_("db error: skipping %s") % new_hand_id) |
|
continue |
|
|
|
if type == "tour": # hand is from a tournament |
|
temp_key = "%s Table %s" % (tour_number, tab_number) |
|
else: |
|
temp_key = table_name |
|
|
|
# Update an existing HUD |
|
if temp_key in self.hud_dict: |
|
# get stats using hud's specific params and get cards |
|
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], num_seats) |
|
|
|
try: |
|
self.hud_dict[temp_key].stat_dict = stat_dict |
|
except KeyError: # HUD instance has been killed off, key is stale |
|
log.error(_('hud_dict[%s] was not found\n') % temp_key) |
|
log.error(_('will not send hand\n')) |
|
# Unlocks table, copied from end of function |
|
self.db_connection.connection.rollback() |
|
return |
|
|
|
self.hud_dict[temp_key].cards = self.get_cards(new_hand_id) |
|
[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: |
|
# get stats using default params--also get cards |
|
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], num_seats) |
|
cards = self.get_cards(new_hand_id) |
|
table_kwargs = dict(table_name=table_name, tournament=tour_number, table_number=tab_number) |
|
tablewindow = Tables.Table(self.config, site_name, **table_kwargs) |
|
if tablewindow.number is None: |
|
# If no client window is found on the screen, complain and continue |
|
if type == "tour": |
|
table_name = "%s %s" % (tour_number, tab_number) |
|
log.error(_("HUD create: table name %s not found, skipping.") % table_name) |
|
else: |
|
tablewindow.key = temp_key |
|
tablewindow.max = max |
|
tablewindow.site = site_name |
|
# 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: |
|
log.error(_('Table "%s" no longer exists\n') % table_name) |
|
return |
|
|
|
self.db_connection.connection.rollback() |
|
if type == "tour": |
|
try: |
|
self.hud_dict[temp_key].table.check_table_no(self.hud_dict[temp_key]) |
|
except KeyError: |
|
pass |
|
|
|
def get_cards(self, 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'] |
|
return cards |
|
###################################################################### |
|
# idle FUNCTIONS |
|
# |
|
# These are passed to the event loop by the non-gui thread to do |
|
# gui things in a thread-safe way. They are passed to the event |
|
# loop using the gobject.idle_add() function. |
|
# |
|
# A general rule for gtk is that only 1 thread should be messing |
|
# with the gui. |
|
|
|
def idle_resize(hud): |
|
gtk.gdk.threads_enter() |
|
try: |
|
[aw.update_card_positions() for aw in hud.aux_windows] |
|
hud.resize_windows() |
|
except: |
|
log.exception(_("Error resizing HUD for table: %s.") % hud.table.title) |
|
finally: |
|
gtk.gdk.threads_leave() |
|
|
|
def idle_kill(hud_main, table): |
|
gtk.gdk.threads_enter() |
|
try: |
|
if table in hud_main.hud_dict: |
|
hud_main.vb.remove(hud_main.hud_dict[table].tablehudlabel) |
|
hud_main.hud_dict[table].main_window.destroy() |
|
hud_main.hud_dict[table].kill() |
|
del(hud_main.hud_dict[table]) |
|
hud_main.main_window.resize(1, 1) |
|
except: |
|
log.exception(_("Error killing HUD for table: %s.") % table.title) |
|
finally: |
|
gtk.gdk.threads_leave() |
|
|
|
def idle_create(hud_main, new_hand_id, table, temp_key, max, poker_game, type, stat_dict, cards): |
|
|
|
gtk.gdk.threads_enter() |
|
try: |
|
if table.gdkhandle is not None: # on windows this should already be set |
|
table.gdkhandle = gtk.gdk.window_foreign_new(table.number) |
|
newlabel = gtk.Label("%s - %s" % (table.site, temp_key)) |
|
hud_main.vb.add(newlabel) |
|
newlabel.show() |
|
hud_main.main_window.resize_children() |
|
|
|
hud_main.hud_dict[temp_key].tablehudlabel = newlabel |
|
hud_main.hud_dict[temp_key].create(new_hand_id, hud_main.config, stat_dict, cards) |
|
for m in hud_main.hud_dict[temp_key].aux_windows: |
|
m.create() |
|
m.update_gui(new_hand_id) |
|
hud_main.hud_dict[temp_key].update(new_hand_id, hud_main.config) |
|
hud_main.hud_dict[temp_key].reposition_windows() |
|
except: |
|
log.exception(_("Error creating HUD for hand %s.") % new_hand_id) |
|
finally: |
|
gtk.gdk.threads_leave() |
|
return False |
|
|
|
def idle_update(hud_main, new_hand_id, table_name, config): |
|
gtk.gdk.threads_enter() |
|
try: |
|
hud_main.hud_dict[table_name].update(new_hand_id, config) |
|
[aw.update_gui(new_hand_id) for aw in hud_main.hud_dict[table_name].aux_windows] |
|
except: |
|
log.exception(_("Error updating HUD for hand %s.") % new_hand_id) |
|
finally: |
|
gtk.gdk.threads_leave() |
|
return False |
|
|
|
if __name__== "__main__": |
|
|
|
# start the HUD_main object |
|
hm = HUD_main(db_name = options.dbname) |
|
|
|
# start the event loop |
|
gtk.main()
|
|
|