## vim:ts=4:et:nowrap ## ##---------------------------------------------------------------------------## ## ## PySol -- a Python Solitaire game ## ## Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ## All Rights Reserved. ## ## 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; see the file COPYING. ## If not, write to the Free Software Foundation, Inc., ## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ## ## Markus F.X.J. Oberhumer ## ## http://www.oberhumer.com/pysol ## ##---------------------------------------------------------------------------## # imports import os, string, sys, types, Tkinter # PySol imports from mfxtools import * from mfxutil import destruct, Struct, kwdefault, KwStruct from util import * from stats import PysolStatsFormatter # Toolkit imports from tkutil import bind, unbind_destroy, getFont, loadImage from tkwidget import _ToplevelDialog, MfxDialog from tkwidget import MfxScrolledCanvas # FIXME - this file a quick hack and needs a rewrite # /*********************************************************************** # // # ************************************************************************/ class SingleGame_StatsDialog(MfxDialog): def __init__(self, parent, title, app, player, gameid, **kw): kw = self.initKw(kw) _ToplevelDialog.__init__(self, parent, title, kw.resizable, kw.default) top_frame, bottom_frame = self.createFrames(kw) self.createBitmaps(top_frame, kw) # self.player = player or "Demo games" self.top.wm_minsize(200, 200) self.button = kw.default # createChart = self.create3DBarChart createChart = self.createPieChart if parent.winfo_screenwidth() < 800 or parent.winfo_screenheight() < 600: createChart = self.createPieChart createChart = self.createSimpleChart # won, lost = app.stats.getStats(player, gameid) createChart(top_frame, app, won, lost, "Total") g = app.stats.session_games.get(player, []) g = filter(lambda a, b=gameid: a[0] == b, g) won = len(filter(lambda a, b=gameid: a[2] > 0, g)) lost = len(filter(lambda a, b=gameid: a[2] == 0, g)) createChart(top_frame, app, won, lost, "Current session") # focus = self.createButtons(bottom_frame, kw) self.mainloop(focus, kw.timeout) # # helpers # def _getPwon(self, won, lost): pwon, plost = 0.0, 0.0 if won + lost > 0: pwon = float(won) / (won + lost) pwon = min(max(pwon, 0.00001), 0.99999) plost = 1.0 - pwon return pwon, plost def _createChartInit(self, frame, w, h, text): tfont = getFont("tree_small") # frame = Tkinter.Frame(frame) c = Tkinter.Canvas(frame, width=w, height=h) fg = c.cget("insertbackground") frame.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=0, padx=20, pady=10) c.pack(side=Tkinter.TOP) # c.create_rectangle(2, 7, w, h, fill="", outline="#7f7f7f") l = Tkinter.Label(c, text=text, font=tfont, bd=0, padx=3, pady=1) c.create_window(20, 0, window=l, anchor="nw") return c, tfont, fg def _createChartTexts(self, c, tx, ty, won, lost): tfont = getFont("tree_small") fg = c.cget("insertbackground") pwon, plost = self._getPwon(won, lost) # x = tx[0] c.create_text(x, ty[0], text="Won:", anchor="nw", font=tfont, fill=fg) c.create_text(x, ty[1], text="Lost:", anchor="nw", font=tfont, fill=fg) c.create_text(x, ty[2], text="Total:", anchor="nw", font=tfont, fill=fg) x = tx[1] - 16 c.create_text(x, ty[0], text="%d" % won, anchor="ne", font=tfont, fill=fg) c.create_text(x, ty[1], text="%d" % lost, anchor="ne", font=tfont, fill=fg) c.create_text(x, ty[2], text="%d" % (won + lost), anchor="ne", font=tfont, fill=fg) y = ty[2] - 11 c.create_line(tx[0], y, x, y, fill=fg) if won + lost > 0: x = tx[2] pw = int(round(100.0 * pwon)) c.create_text(x, ty[0], text="%d%%" % pw, anchor="ne", font=tfont, fill=fg) c.create_text(x, ty[1], text="%d%%" % (100-pw), anchor="ne", font=tfont, fill=fg) def _createChart3DBar(self, canvas, perc, x, y, p, col): if perc < 0.005: return # translate and scale p = list(p[:]) for i in (0, 1, 2, 3): p[i] = (x + p[i][0], y + p[i][1]) j = i + 4 dx = int(round(p[j][0] * perc)) dy = int(round(p[j][1] * perc)) p[j] = (p[i][0] + dx, p[i][1] + dy) # draw rects def draw_rect(a, b, c, d, col, canvas=canvas, p=p): points = (p[a][0], p[a][1], p[b][0], p[b][1], p[c][0], p[c][1], p[d][0], p[d][1]) canvas.create_polygon(points, fill=col) draw_rect(0, 1, 5, 4, col[0]) draw_rect(1, 2, 6, 5, col[1]) draw_rect(4, 5, 6, 7, col[2]) # draw lines def draw_line(a, b, canvas=canvas, p=p): ##print a, b, p[a], p[b] canvas.create_line(p[a][0], p[a][1], p[b][0], p[b][1]) draw_line(0, 1) draw_line(1, 2) draw_line(0, 4) draw_line(1, 5) draw_line(2, 6) ###draw_line(3, 7) ## test draw_line(4, 5) draw_line(5, 6) draw_line(6, 7) draw_line(7, 4) # # charts # def createSimpleChart(self, frame, app, won, lost, text): c, tfont, fg = self._createChartInit(frame, 300, 100, text) # tx = (90, 180, 210) ty = (21, 41, 75) self._createChartTexts(c, tx, ty, won, lost) def create3DBarChart(self, frame, app, won, lost, text): image = app.gimages.stats[0] iw, ih = image.width(), image.height() c, tfont, fg = self._createChartInit(frame, iw+160, ih, text) pwon, plost = self._getPwon(won, lost) # tx = (iw+20, iw+110, iw+140) yy = ih/2 ## + 7 ty = (yy+21-46, yy+41-46, yy+75-46) # c.create_image(0, 7, image=image, anchor="nw") # p = ((0, 0), (44, 6), (62, -9), (20, -14), (-3, -118), (-1, -120), (-1, -114), (-4, -112)) col = ("#00ff00", "#008200", "#00c300") self._createChart3DBar(c, pwon, 102, 145+7, p, col) p = ((0, 0), (49, 6), (61, -10), (15, -15), (1, -123), (3, -126), (4, -120), (1, -118)) col = ("#ff0000", "#860400", "#c70400") self._createChart3DBar(c, plost, 216, 159+7, p, col) # self._createChartTexts(c, tx, ty, won, lost) c.create_text(tx[0], ty[0]-48, text=self.player, anchor="nw", font=tfont, fill=fg) def createPieChart(self, frame, app, won, lost, text): c, tfont, fg = self._createChartInit(frame, 300, 100, text) pwon, plost = self._getPwon(won, lost) # tx = (160, 250, 280) ty = (21, 41, 75) # if won + lost > 0: ##s, ewon, elost = 90.0, -360.0 * pwon, -360.0 * plost s, ewon, elost = 0.0, 360.0 * pwon, 360.0 * plost c.create_arc(20, 25+9, 110, 75+9, fill="#007f00", start=s, extent=ewon) c.create_arc(20, 25+9, 110, 75+9, fill="#7f0000", start=s+ewon, extent=elost) c.create_arc(20, 25, 110, 75, fill="#00ff00", start=s, extent=ewon) c.create_arc(20, 25, 110, 75, fill="#ff0000", start=s+ewon, extent=elost) x, y = tx[0] - 25, ty[0] c.create_rectangle(x, y, x+10, y+10, fill="#00ff00") y = ty[1] c.create_rectangle(x, y, x+10, y+10, fill="#ff0000") else: c.create_oval(20, 25+10, 110, 75+10, fill="#7f7f7f") c.create_oval(20, 25, 110, 75, fill="#f0f0f0") c.create_text(65, 50, text="No games", anchor="center", font=tfont, fill="#bfbfbf") # self._createChartTexts(c, tx, ty, won, lost) # # # def initKw(self, kw): kw = KwStruct(kw, strings=("OK", ("All games...", 102), ("Reset...", 302)), default=0, separatorwidth=2, resizable=1, padx=10, pady=10, ) return MfxDialog.initKw(self, kw) def getDefaultFont(self): return getFont("small") # /*********************************************************************** # // # ************************************************************************/ class AllGames_StatsDialogScrolledCanvas(MfxScrolledCanvas): pass class AllGames_StatsDialog(MfxDialog): # for font "canvas_fixed" CHAR_W, CHAR_H = 7, 16 if os.name == "mac": CHAR_W = 6 # YVIEW = 0 def __init__(self, parent, title, app, player, **kw): lines = 25 if parent and parent.winfo_screenheight() < 600: lines = 20 kwdefault(kw, height=lines*self.CHAR_H) kw = self.initKw(kw) _ToplevelDialog.__init__(self, parent, title, kw.resizable, kw.default) top_frame, bottom_frame = self.createFrames(kw) self.createBitmaps(top_frame, kw) # self.app = app self.top.wm_minsize(200, 200) self.button = kw.default self.font = kw.font self.sc = AllGames_StatsDialogScrolledCanvas(top_frame, width=kw.width, height=kw.height) self.sc.pack(fill=Tkinter.BOTH, expand=1, padx=kw.padx, pady=kw.pady) # self.nodes = {} self.canvas = self.sc.canvas self.canvas.dialog = self bind(self.canvas, "", self.singleClick) self.fillCanvas(player, title) bbox = self.canvas.bbox("all") ##print bbox ##self.canvas.config(scrollregion=bbox) self.canvas.config(scrollregion=(0,0,bbox[2],bbox[3])) self.canvas.yview_moveto(self.YVIEW) # focus = self.createButtons(bottom_frame, kw) self.mainloop(focus, kw.timeout) def initKw(self, kw): kw = KwStruct(kw, strings=("OK", ("Save to file", 202), ("Reset all...", 301),), default=0, separatorwidth=2, resizable=1, padx=10, pady=10, width=500, ) return MfxDialog.initKw(self, kw) def getDefaultFont(self): return getFont("small") def destroy(self): self.app = None self.canvas.dialog = None self.nodes = {} self.sc.destroy() MfxDialog.destroy(self) def singleClick(self, event=None): id = self.canvas.find_withtag(Tkinter.CURRENT) if not id: return gameid, gamenumber = self.nodes.get(id[0], (None, None)) ## FIXME / TODO return if gameid and gamenumber: print gameid, gamenumber elif gameid: print gameid # # # def fillCanvas(self, player, header): a = PysolStatsFormatter(self.app) writer = self.CanvasWriter(self.canvas, self.font, self.CHAR_H) if not a.writeStats(writer, player, header): writer.p("No entries for player " + ustr(player) + "\n") destruct(writer) destruct(a) class CanvasWriter(PysolStatsFormatter.StringWriter): def __init__(self, canvas, font, h): self.canvas = canvas self.fg = canvas.cget("insertbackground") self.font = font self.h = h self.x = self.y = 0 self.gameid = None self.gamenumber = None self.canvas.config(yscrollincrement=h) def _addItem(self, id): self.canvas.dialog.nodes[id] = (self.gameid, self.gamenumber) def p(self, s): if self.y > 16000: return h1, h2 = 0, 0 while s and s[0] == "\n": s = s[1:] h1 = h1 + self.h while s and s[-1] == "\n": s = s[:-1] h2 = h2 + self.h self.y = self.y + h1 if s: id = self.canvas.create_text(self.x, self.y, text=s, anchor="nw", font=self.font, fill=self.fg) self._addItem(id) self.y = self.y + h2 def pheader(self, s): pass def pstats(self, t1, t2, t3, t4, t5, gameid=None): if self.y > 16000: return self.gameid = gameid self.gamenumber = None x, y = 1, self.y p = self._pstats_text h = 0 h = max(h, p(x , y, anchor="nw", text=ustr(t1))) h = max(h, p(x+200, y, anchor="ne", text=ustr(t2))) h = max(h, p(x+250, y, anchor="ne", text=ustr(t3))) h = max(h, p(x+300, y, anchor="ne", text=ustr(t4))) h = max(h, p(x+350, y, anchor="ne", text=ustr(t5))) self.pstats_perc(x+372, y, ustr(t5)) self.y = self.y + h self.gameid = None def _pstats_text(self, x, y, **kw): kwdefault(kw, font=self.font, fill=self.fg) id = apply(self.canvas.create_text, (x, y), kw) self._addItem(id) return self.h ##bbox = self.canvas.bbox(id) ##return bbox[3] - bbox[1] def pstats_perc(self, x, y, t): if not (t and "0" <= t[0] <= "9"): return perc = int(round(string.atof(str(t)))) if perc < 1: return rx, ry, rw, rh = x, y+1, 2 + 8*10, self.h-5 if 1: w = int(round(rw*perc/100.0)) if 1 and w < 1: return if w > 0: w = max(3, w) w = min(rw - 2, w) id = self.canvas.create_rectangle(rx, ry, rx+w, ry+rh, width=1, fill="#00ff00", outline="#000000") if w < rw: id = self.canvas.create_rectangle(rx+w, ry, rx+rw, ry+rh, width=1, fill="#ff0000", outline="#000000") return ##fill = "#ffffff" ##fill = self.canvas["bg"] fill = None id = self.canvas.create_rectangle(rx, ry, rx+rw, ry+rh, width=1, fill=fill, outline="#808080") if 1: rx, rw = rx + 1, rw - 1 ry, rh = ry + 1, rh - 1 w = int(round(rw*perc/100.0)) if w > 0: id = self.canvas.create_rectangle(rx, ry, rx+w, ry+rh, width=0, fill="#00ff00", outline="") if w < rw: id = self.canvas.create_rectangle(rx+w, ry, rx+rw, ry+rh, width=0, fill="#ff0000", outline="") return p = 1.0 ix = rx + 2 for i in (1, 11, 21, 31, 41, 51, 61, 71, 81, 91): if perc < i: break ##c = "#ff8040" r, g, b = 255, 128*p, 64*p c = "#%02x%02x%02x" % (int(r), int(g), int(b)) id = self.canvas.create_rectangle(ix, ry+2, ix+6, ry+rh-2, width=0, fill=c, outline=c) ix = ix + 8 p = max(0.0, p - 0.1) def plog(self, gamename, gamenumber, date, status, gameid=-1, won=-1): if gameid > 0 and "0" <= gamenumber[0:1] <= "9": self.gameid = gameid self.gamenumber = gamenumber self.p("%-25s %-20s %17s %s\n" % (gamename, gamenumber, date, status)) self.gameid = None self.gamenumber = None # /*********************************************************************** # // # ************************************************************************/ class FullLog_StatsDialog(AllGames_StatsDialog): YVIEW = 1 def fillCanvas(self, player, header): a = PysolStatsFormatter(self.app) writer = self.CanvasWriter(self.canvas, self.font, self.CHAR_H) if not a.writeFullLog(writer, player, header): writer.p("No log entries for " + ustr(player) + "\n") destruct(a) def initKw(self, kw): kw = KwStruct(kw, strings=("OK", ("Session log...", 104), ("Save to file", 203)), default=0, font=getFont("canvas_fixed"), width=76*self.CHAR_W, ) return AllGames_StatsDialog.initKw(self, kw) class SessionLog_StatsDialog(FullLog_StatsDialog): def fillCanvas(self, player, header): a = PysolStatsFormatter(self.app) writer = self.CanvasWriter(self.canvas, self.font, self.CHAR_H) if not a.writeSessionLog(writer, player, header): writer.p("No current session log entries for " + ustr(player) + "\n") destruct(a) def initKw(self, kw): kw = KwStruct(kw, strings=("OK", ("Full log...", 103), ("Save to file", 204)), default=0, ) return FullLog_StatsDialog.initKw(self, kw)