diff --git a/pyfpdb/GuiReplayer.py b/pyfpdb/GuiReplayer.py new file mode 100644 index 00000000..4b95faf2 --- /dev/null +++ b/pyfpdb/GuiReplayer.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#Copyright 2010 Maxime Grandchamp +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#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 Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in agpl-3.0.txt. + +import L10n +_ = L10n.get_translation() + + +from Hand import * +import Configuration +import Database +import SQL +import fpdb_import +import Filters +import pygtk +pygtk.require('2.0') +import gtk +import math +import gobject + + +class GuiReplayer: + def __init__(self, config, querylist, mainwin, debug=True): + self.debug = debug + self.conf = config + self.main_window = mainwin + self.sql = querylist + + self.db = Database.Database(self.conf, sql=self.sql) + + filters_display = { "Heroes" : True, + "Sites" : True, + "Games" : True, + "Limits" : True, + "LimitSep" : True, + "LimitType" : True, + "Type" : True, + "Seats" : True, + "SeatSep" : True, + "Dates" : True, + "Groups" : True, + "GroupsAll" : True, + "Button1" : True, + "Button2" : True + } + + + self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) + #self.filters.registerButton1Name(_("Import Hand")) + #self.filters.registerButton1Callback(self.importhand) + #self.filters.registerButton2Name(_("temp")) + #self.filters.registerButton2Callback(self.temp()) + + # hierarchy: self.mainHBox / self.hpane / self.replayBox / self.area + + self.mainHBox = gtk.HBox(False, 0) + self.mainHBox.show() + + self.leftPanelBox = self.filters.get_vbox() + + self.hpane = gtk.HPaned() + self.hpane.pack1(self.leftPanelBox) + self.mainHBox.add(self.hpane) + + self.replayBox = gtk.VBox(False, 0) + self.replayBox.show() + + self.hpane.pack2(self.replayBox) + self.hpane.show() + + self.area=gtk.DrawingArea() + self.pangolayout = self.area.create_pango_layout("") + self.area.connect("expose-event", self.area_expose) + self.style = self.area.get_style() + self.gc = self.style.fg_gc[gtk.STATE_NORMAL] + self.area.show() + + self.replayBox.pack_start(self.area) + + self.MyHand = self.importhand() + + self.maxseats=self.MyHand.maxseats + + if self.MyHand.gametype['currency']=="USD": #TODO: check if there are others .. + self.currency="$" + elif self.MyHand.gametype['currency']=="EUR": + self.currency="€" + + + self.table={} #create table with positions, player names, status (live/folded), stacks and chips on table + for i in range(0,self.maxseats): # radius: 200, center: 250,250 + x= int (round(250+200*math.cos(2*i*math.pi/self.maxseats))) + y= int (round(250+200*math.sin(2*i*math.pi/self.maxseats))) + try: + self.table[i]={"name":self.MyHand.players[i][1],"stack":Decimal(self.MyHand.players[i][2]),"x":x,"y":y,"chips":0,"status":"live"} #save coordinates of each player + try: + self.table[i]['holecards']=self.MyHand.holecards["PREFLOP"][self.MyHand.players[i][1]][1]+' '+self.MyHand.holecards["PREFLOP"][self.MyHand.players[i][1]][2] + print "holecards",self.table[i]['holecards'] + except: + self.table[i]['holecards']='' + except IndexError: #if seat is empty + print "seat",i+1,"out of",self.maxseats,"empty" + + self.actions=[] #create list with all actions + + if isinstance(self.MyHand, HoldemOmahaHand): + if self.MyHand.gametype['category'] == 'holdem': + self.play_holdem() + + self.action_number=0 + self.action_level=0 + self.pot=0 + gobject.timeout_add(1000,self.draw_action) + + + def area_expose(self, area, event): + self.style = self.area.get_style() + self.gc = self.style.fg_gc[gtk.STATE_NORMAL] + + playerid='999' #makes sure we have an error if player is not recognised + for i in range(0,len(self.table)): #surely there must be a better way to find the player id in the table... + if self.table[i]['name']==self.actions[self.action_number][1]: + playerid=i + + if self.actions[self.action_number][2]=="folds": + self.table[playerid]["status"]="folded" + + if self.actions[self.action_number][3]: + self.table[playerid]["stack"] -= Decimal(self.actions[self.action_number][3]) #decreases stack if player bets + self.pot += Decimal(self.actions[self.action_number][3]) #increase pot + self.table[playerid]["chips"] += Decimal(self.actions[self.action_number][3]) #increase player's chips on table + + + cm = self.gc.get_colormap() #create colormap toi be able to play with colours + + color = cm.alloc_color("black") #defaults to black + self.gc.set_foreground(color) + + self.area.window.draw_arc(self.gc, 0, 125, 125, 300, 300, 0, 360*64) #table + + for i in self.table: + if self.table[i]["status"]=="folded": + color = cm.alloc_color("grey") #player has folded => greyed out + self.gc.set_foreground(color) + else: + color = cm.alloc_color("black") #player is live + self.gc.set_foreground(color) + self.pangolayout.set_text(self.table[i]["name"]+self.table[i]["holecards"]) #player names + holecards + self.area.window.draw_layout(self.gc, self.table[i]["x"],self.table[i]["y"], self.pangolayout) + self.pangolayout.set_text('$'+str(self.table[i]["stack"])) #player stacks + self.area.window.draw_layout(self.gc, self.table[i]["x"]+10,self.table[i]["y"]+20, self.pangolayout) + + color = cm.alloc_color("green") + self.gc.set_foreground(color) + + self.pangolayout.set_text(self.currency+str(self.pot)) #displays pot + self.area.window.draw_layout(self.gc,270,270, self.pangolayout) + + if self.actions[self.action_number][0]>1: #displays flop + self.pangolayout.set_text(self.MyHand.board['FLOP'][0]+" "+self.MyHand.board['FLOP'][1]+" "+self.MyHand.board['FLOP'][2]) + self.area.window.draw_layout(self.gc,210,240, self.pangolayout) + if self.actions[self.action_number][0]>2: #displays turn + self.pangolayout.set_text(self.MyHand.board['TURN'][0]) + self.area.window.draw_layout(self.gc,270,240, self.pangolayout) + if self.actions[self.action_number][0]>3: #displays river + self.pangolayout.set_text(self.MyHand.board['RIVER'][0]) + self.area.window.draw_layout(self.gc,290,240, self.pangolayout) + + color = cm.alloc_color("red") #highlights the action + self.gc.set_foreground(color) + + self.pangolayout.set_text(self.actions[self.action_number][2]) #displays action + self.area.window.draw_layout(self.gc, self.table[playerid]["x"]+10,self.table[playerid]["y"]+35, self.pangolayout) + if self.actions[self.action_number][3]: #displays amount + self.pangolayout.set_text(self.currency+self.actions[self.action_number][3]) + self.area.window.draw_layout(self.gc, self.table[playerid]["x"]+10,self.table[playerid]["y"]+55, self.pangolayout) + + color = cm.alloc_color("black") #we don't want to draw the filters and others in red + self.gc.set_foreground(color) + + def play_holdem(self): + actions=('BLINDSANTES','PREFLOP','FLOP','TURN','RIVER') + for action in actions: + for i in range(0,len(self.MyHand.actions[action])): + player=self.MyHand.actions[action][i][0] + act=self.MyHand.actions[action][i][1] + try: + amount=str(self.MyHand.actions[action][i][2]) + except: + amount='' #no amount + self.actions.append([actions.index(action),player,act,amount]) #create table with all actions + + + def draw_action(self): + if self.action_number==len(self.actions)-1: #no more actions, we exit the loop + return False + + if self.actions[self.action_number][0]!=self.action_level: #have we changed street ? + self.action_level=self.actions[self.action_number][0] #record the new street + if self.action_level>1: #we don't want to refresh if simply moving from antes/blinds to preflop action + alloc = self.area.get_allocation() + rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height) + self.area.window.invalidate_rect(rect, True) #make sure we refresh the whole screen + + self.action_number+=1 + if self.area.window: + playerid='999' #makes sure we have an error if player is not recognised + for i in range(0,len(self.table)): #surely there must be a better way to find the player id in the table... + if self.table[i]['name']==self.actions[self.action_number][1]: + playerid=i + rect = gtk.gdk.Rectangle(self.table[playerid]["x"],self.table[playerid]["y"],100,100) + self.area.window.invalidate_rect(rect, True) #refresh player area of the screen + rect = gtk.gdk.Rectangle(270,270,100,50) + self.area.window.invalidate_rect(rect, True) #refresh pot area + self.area.window.process_updates(True) + print "draw action",self.action_number,self.actions[self.action_number][1],self.actions[self.action_number][2],self.actions[self.action_number][3] + return True + + + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainHBox + + def importhand(self, handnumber=1): + """Temporary function that grabs a Hand object from a specified file. Obviously this will + be replaced by a function to select a hand from the db in the not so distant future. + This code has been shamelessly stolen from Carl + """ + config = Configuration.Config(file = "HUD_config.test.xml") + db = Database.Database(config) + sql = SQL.Sql(db_server = 'sqlite') + settings = {} + settings.update(config.get_db_parameters()) + settings.update(config.get_import_parameters()) + settings.update(config.get_default_paths()) + #db.recreate_tables() + importer = fpdb_import.Importer(False, settings, config, None) + importer.setDropIndexes("don't drop") + importer.setFailOnError(True) + importer.setThreads(-1) + importer.setCallHud(False) + importer.setFakeCacheHHC(True) + + #Get a simple regression file with a few hands of Hold'em + filename="regression-test-files/cash/Stars/Flop/NLHE-FR-USD-0.01-0.02-201005.microgrind.txt" + site="PokerStars" + + + importer.addBulkImportImportFileOrDir(filename, site=site) + (stored, dups, partial, errs, ttime) = importer.runImport() + + + hhc = importer.getCachedHHC() + handlist = hhc.getProcessedHands() + + return handlist[0] + + + def temp(self): + pass + diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index 0d40fd59..39ea97b4 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -75,10 +75,6 @@ onlinehelp = {'Game':_('Type of Game'), class DemoTips(TreeViewTooltips): def __init__(self, customer_column): - # customer_column is an instance of gtk.TreeViewColumn and - # is being used in the gtk.TreeView to show customer names. - # self.cust_col = customer_column - # call base class init TreeViewTooltips.__init__(self) @@ -91,11 +87,6 @@ class DemoTips(TreeViewTooltips): return (display) def location(self, x, y, w, h): - # rename me to "location" so I override the base class - # method. This will demonstrate being able to change - # where the tooltip window popups, relative to the - # pointer. - # this will place the tooltip above and to the right return x + 30, y - (h + 10) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 6bc4c95b..6dc2feb2 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -115,6 +115,7 @@ import GuiAutoImport import GuiGraphViewer import GuiTourneyGraphViewer import GuiSessionViewer +import GuiReplayer import GuiStove import SQL import Database @@ -778,6 +779,7 @@ class fpdb: + @@ -821,6 +823,7 @@ class fpdb: ('tourneyviewer', None, _('Tourney _Viewer'), None, 'Tourney Viewer)', self.tab_tourney_viewer_stats), ('posnstats', None, _('P_ositional Stats (tabulated view, not on sqlite)'), _('O'), 'Positional Stats (tabulated view, not on sqlite)', self.tab_positional_stats), ('sessionstats', None, _('Session Stats'), None, 'Session Stats', self.tab_session_stats), + ('replayer', None, _('Hand _Replayer'), None, 'Hand Replayer', self.tab_replayer), ('database', None, _('_Database')), ('maintaindbs', None, _('_Maintain Databases'), None, 'Maintain Databases', self.dia_maintain_dbs), ('createtabs', None, _('Create or Recreate _Tables'), None, 'Create or Recreate Tables ', self.dia_recreate_tables), @@ -1049,6 +1052,12 @@ class fpdb: ps_tab=new_ps_thread.get_vbox() self.add_and_display_tab(ps_tab, _("Session Stats")) + def tab_replayer(self, widget, data=None): + new_ps_thread = GuiReplayer.GuiReplayer(self.config, self.sql, self.window) + self.threads.append(new_ps_thread) + ps_tab=new_ps_thread.get_vbox() + self.add_and_display_tab(ps_tab, _("Hand Replayer")) + def tab_main_help(self, widget, data=None): """Displays a tab with the main fpdb help screen""" mh_tab=gtk.Label(_("""Fpdb needs translators!