class GtkTimeTicker(TaskBaseWindow): """ plot against time a specific item / set of items Only works on arrays - so no singles! """ def __init__(self, tick_cb, title=None, dt=1.0, len=100, limits=None): super(GtkTimeTicker, self).__init__(tick_cb=tick_cb, title=title, dt=dt) self._no_given_limits = limits is None self._len = len self._fig = fig = Figure() self._axis = axis = fig.add_subplot(111) self._times = [] self._values = [] self._lines = [] self._gtkfig = FigureCanvasGTK(fig) self._w.add(self._gtkfig) self._w.set_size_request(300, 300) self._w.show_all() self._min_y, self._max_y = 0.0, 0.0 self._limits = limits if limits: self.setYLimits(*limits) self._startTaskFirstTime() def _update(self, result): if len(result) == 0: print "GtkTimeTicker: empty length result, nothing to do\r\n" return if len(result) > len(self._lines): print "updating GtkTimeTicker to %s lines" % len(result) for i in xrange(len(result) - len(self._lines)): self._lines.append(self._axis.plot([],[])[0]) if len(self._values) != len(result): # strange feature - if a new plot is added erase the old ones. self._values = [[] for i in xrange(len(result))] if self._limits: self.setYLimits(*self._limits) self._min_y, self._max_y = min(self._min_y, *result), max(self._max_y, *result) self._times.append(time.time() - self._start) for i, v in enumerate(result): self._values[i].append(v) if len(self._values[0]) > self._len: for values in self._values: del values[:(len(values) - self._len)] del self._times[:(len(self._times) - self._len)] self.updatePlot() def updatePlot(self): # TODO - better way then resetting the whole number of points. for line, values in zip(self._lines, self._values): line.set_data(self._times, values) if len(line._x) != len(line._y): import pdb; pdb.set_trace() self._axis.set_xlim(self._times[0], self._times[-1]) if self._no_given_limits: self.setYLimits(self._min_y, self._max_y) self._gtkfig.draw() def setYLimits(self, lim_min, lim_max): self._axis.set_ylim(lim_min, lim_max)
class DataManager(gtk.Window): numRows, numCols = 20,10 data = random((numRows, numCols)) def __init__(self): gtk.Window.__init__(self) self.set_default_size(600, 600) self.connect('destroy', lambda win: gtk.main_quit()) self.set_title('GtkListStore demo') self.set_border_width(8) vbox = gtk.VBox(False, 8) self.add(vbox) label = gtk.Label('Double click a row to plot the data') vbox.pack_start(label, False, False) sw = gtk.ScrolledWindow() sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) vbox.pack_start(sw, True, True) model = self.create_model() self.treeview = gtk.TreeView(model) self.treeview.set_rules_hint(True) fig = Figure(figsize=(6,4)) self.canvas = FigureCanvas(fig) # a gtk.DrawingArea vbox.pack_start(self.canvas, True, True) ax = fig.add_subplot(111) self.line, = ax.plot(self.data[0,:], 'go') # plot the first row self.treeview.connect('row-activated', self.plot_row) sw.add(self.treeview) self.add_columns() self.add_events(gdk.BUTTON_PRESS_MASK | gdk.KEY_PRESS_MASK| gdk.KEY_RELEASE_MASK) def plot_row(self, treeview, path, view_column): ind, = path # get the index into data points = self.data[ind,:] self.line.set_ydata(points) self.canvas.draw() def add_columns(self): for i in range(self.numCols): column = gtk.TreeViewColumn('%d'%i, gtk.CellRendererText(), text=i) self.treeview.append_column(column) def create_model(self): types = [float]*self.numCols store = gtk.ListStore(*types) for row in self.data: store.append(row) return store
class Graph(gtk.Frame): def __init__(self, label): self.plots = dict() gtk.Frame.__init__(self) self.set_label(label) self.fig = Figure(figsize=(6, 4)) self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea #self.add(self.canvas) vbox = gtk.VBox() vbox.pack_start(self.canvas) toolbar = NavigationToolbar(self.canvas, self) vbox.pack_start(toolbar, False, False) self.add(vbox) def clear(self): self.fig.clear() self.plots.clear() def draw(self, symbol, dates, values): years = YearLocator() # every year months = MonthLocator() # every month yearsFmt = DateFormatter('%Y') #monthFmt = DateFormatter('%m') # coeffs = polyfit(dates, values, 3) # besty = polyval(coeffs, dates) ax = self.fig.add_subplot(111) ax.plot(dates, values, label=symbol) ax.legend(loc='best', prop=FontProperties(size='9')) ax.grid(True) ax.xaxis.set_major_locator(years) ax.xaxis.set_major_formatter(yearsFmt) #ax.xaxis.set_minor_formatter(monthFmt) ax.xaxis.set_minor_locator(months) ax.autoscale_view() self.plots[symbol] = ax #self.fig1.autofmt_xdate() self.canvas.draw() def insert_annotations(self, symbol, cursor): ax = self.plots[symbol] for row in cursor: ax.annotate(row[0], xy=(row[1], row[2])) self.canvas.draw()
class JwsFileChooserDialog(gtk.FileChooserDialog): def __init__(self, parent, current_folder=None, title=_("Open spectra...")): gtk.FileChooserDialog.__init__( self, title=title, parent=parent, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_ADD, gtk.RESPONSE_OK)) self.figure = Figure(figsize=(5, 4)) self.canvas = FigureCanvas(self.figure) self.canvas.set_size_request(200, 200) #tamaño mínimo del widget self.add_filter(ff_jws) self.set_select_multiple(True) if current_folder: self.set_current_folder(current_folder) self.set_preview_widget(self.canvas) self.connect("selection-changed", self._update_preview_cb) self.show_all() def _update_preview_cb(self, widget): input_fn = self.get_preview_filename() error = True if input_fn is not None: results = jwslib.read_file(input_fn) if results[0] == jwslib.JWS_ERROR_SUCCESS: header = results[1] channels = results[2] if len(channels) > 0: error = False if not error: xdata = arange( header.x_for_first_point, #start header.x_for_last_point + header.x_increment, #end+incr. header.x_increment) #increment ellipticity = array(channels[0], float32) self.figure.clear() p = self.figure.add_subplot(111) p.plot(xdata, ellipticity) self.canvas.draw() self.set_preview_widget_active(not error)
class JwsFileChooserDialog(gtk.FileChooserDialog): def __init__(self, parent, current_folder=None, title=_("Open spectra...")): gtk.FileChooserDialog.__init__( self, title=title, parent=parent, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_ADD, gtk.RESPONSE_OK), ) self.figure = Figure(figsize=(5, 4)) self.canvas = FigureCanvas(self.figure) self.canvas.set_size_request(200, 200) # tamaño mínimo del widget self.add_filter(ff_jws) self.set_select_multiple(True) if current_folder: self.set_current_folder(current_folder) self.set_preview_widget(self.canvas) self.connect("selection-changed", self._update_preview_cb) self.show_all() def _update_preview_cb(self, widget): input_fn = self.get_preview_filename() error = True if input_fn is not None: results = jwslib.read_file(input_fn) if results[0] == jwslib.JWS_ERROR_SUCCESS: header = results[1] channels = results[2] if len(channels) > 0: error = False if not error: xdata = arange( header.x_for_first_point, # start header.x_for_last_point + header.x_increment, # end+incr. header.x_increment, ) # increment ellipticity = array(channels[0], float32) self.figure.clear() p = self.figure.add_subplot(111) p.plot(xdata, ellipticity) self.canvas.draw() self.set_preview_widget_active(not error)
class GuiSessionViewer: def __init__(self, config, querylist, mainwin, owner, debug=True): self.debug = debug self.conf = config self.sql = querylist self.window = mainwin self.owner = owner self.liststore = None self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 self.fig = None self.canvas = None self.ax = None self.graphBox = None # create new db connection to avoid conflicts with other threads self.db = Database.Database(self.conf, sql=self.sql) self.cursor = self.db.cursor settings = {} settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_default_paths()) # text used on screen stored here so that it can be configured self.filterText = { 'handhead': _('Hand Breakdown for all levels listed above') } filters_display = { "Heroes": True, "Sites": True, "Games": True, "Currencies": True, "Limits": True, "LimitSep": True, "LimitType": True, "Type": False, "Seats": True, "SeatSep": False, "Dates": True, "Groups": False, "GroupsAll": False, "Button1": True, "Button2": False } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name("_Refresh") self.filters.registerButton1Callback(self.refreshStats) # ToDo: store in config # ToDo: create popup to adjust column config # columns to display, keys match column name returned by sql, values in tuple are: # is column displayed, column heading, xalignment, formatting self.columns = [(1.0, "SID"), (1.0, "Hands"), (0.5, "Start"), (0.5, "End"), (1.0, "Rate"), (1.0, "Open"), (1.0, "Close"), (1.0, "Low"), (1.0, "High"), (1.0, "Range"), (1.0, "Profit")] self.detailFilters = [] # the data used to enhance the sql select self.main_hbox = gtk.HPaned() self.stats_frame = gtk.Frame() self.stats_frame.show() main_vbox = gtk.VPaned() main_vbox.show() self.graphBox = gtk.VBox(False, 0) self.graphBox.set_size_request(400, 400) self.graphBox.show() self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) self.main_hbox.pack1(self.filters.get_vbox()) self.main_hbox.pack2(main_vbox) main_vbox.pack1(self.graphBox) main_vbox.pack2(self.stats_frame) self.main_hbox.show() # make sure Hand column is not displayed #[x for x in self.columns if x[0] == 'hand'][0][1] = False # if DEBUG == False: # warning_string = _("Session Viewer is proof of concept code only, and contains many bugs.\n") # warning_string += _("Feel free to use the viewer, but there is no guarantee that the data is accurate.\n") # warning_string += _("If you are interested in developing the code further please contact us via the usual channels.\n") # warning_string += _("Thank you") # self.warning_box(warning_string) def warning_box(self, str, diatitle=_("FPDB WARNING")): diaWarning = gtk.Dialog(title=diatitle, parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK)) label = gtk.Label(str) diaWarning.vbox.add(label) label.show() response = diaWarning.run() diaWarning.destroy() return response def get_vbox(self): """returns the vbox of this thread""" return self.main_hbox def refreshStats(self, widget, data): try: self.stats_vbox.destroy() except AttributeError: pass self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) self.fillStatsFrame(self.stats_vbox) def fillStatsFrame(self, vbox): sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() games = self.filters.getGames() currencies = self.filters.getCurrencies() limits = self.filters.getLimits() seats = self.filters.getSeats() sitenos = [] playerids = [] for i in ('show', 'none'): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(result) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") sitenos = [2] if not games: print _("No games found") return if not currencies: print _("No currencies found") return if not playerids: print _("No player ids found") return if not limits: print _("No limits found") return self.createStatsPane(vbox, playerids, sitenos, games, currencies, limits, seats) def createStatsPane(self, vbox, playerids, sitenos, games, currencies, limits, seats): starttime = time() (results, quotes) = self.generateDatasets(playerids, sitenos, games, currencies, limits, seats) if DEBUG: for x in quotes: print "start %s\tend %s \thigh %s\tlow %s" % (x[1], x[2], x[3], x[4]) self.generateGraph(quotes) heading = gtk.Label(self.filterText['handhead']) heading.show() vbox.pack_start(heading, expand=False, padding=3) # Scrolled window for detailed table (display by hand) swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.show() vbox.pack_start(swin, expand=True, padding=3) vbox1 = gtk.VBox(False, 0) vbox1.show() swin.add_with_viewport(vbox1) self.addTable(vbox1, results) self.db.rollback() print _("Stats page displayed in %4.2f seconds") % (time() - starttime) #end def fillStatsFrame(self, vbox): def generateDatasets(self, playerids, sitenos, games, currencies, limits, seats): if (DEBUG): print "DEBUG: Starting generateDatasets" THRESHOLD = 1800 # Min # of secs between consecutive hands before being considered a new session PADDING = 5 # Additional time in minutes to add to a session, session startup, shutdown etc # Get a list of timestamps and profits q = self.sql.query['sessionStats'] start_date, end_date = self.filters.getDates() q = q.replace("<datestest>", " BETWEEN '" + start_date + "' AND '" + end_date + "'") l = [] for m in self.filters.display.items(): if m[0] == 'Games' and m[1]: for n in games: if games[n]: l.append(n) if len(l) > 0: gametest = str(tuple(l)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)", ")") gametest = gametest.replace("u'", "'") gametest = "AND gt.category in %s" % gametest else: gametest = "AND gt.category IS NULL" q = q.replace("<game_test>", gametest) limittest = self.filters.get_limits_where_clause(limits) q = q.replace("<limit_test>", limittest) l = [] for n in currencies: if currencies[n]: l.append(n) currencytest = str(tuple(l)) currencytest = currencytest.replace(",)", ")") currencytest = currencytest.replace("u'", "'") currencytest = "AND gt.currency in %s" % currencytest q = q.replace("<currency_test>", currencytest) if seats: q = q.replace( '<seats_test>', 'AND h.seats BETWEEN ' + str(seats['from']) + ' AND ' + str(seats['to'])) else: q = q.replace('<seats_test>', 'AND h.seats BETWEEN 0 AND 100') nametest = str(tuple(playerids)) nametest = nametest.replace("L", "") nametest = nametest.replace(",)", ")") q = q.replace("<player_test>", nametest) q = q.replace("<ampersand_s>", "%s") if DEBUG: hands = [ (u'10000', 10), (u'10000', 20), (u'10000', 30), (u'20000', -10), (u'20000', -20), (u'20000', -30), (u'30000', 40), (u'40000', 0), (u'50000', -40), (u'60000', 10), (u'60000', 30), (u'60000', -20), (u'70000', -20), (u'70000', 10), (u'70000', 30), (u'80000', -10), (u'80000', -30), (u'80000', 20), (u'90000', 20), (u'90000', -10), (u'90000', -30), (u'100000', 30), (u'100000', -50), (u'100000', 30), (u'110000', -20), (u'110000', 50), (u'110000', -20), (u'120000', -30), (u'120000', 50), (u'120000', -30), (u'130000', 20), (u'130000', -50), (u'130000', 20), (u'140000', 40), (u'140000', -40), (u'150000', -40), (u'150000', 40), (u'160000', -40), (u'160000', 80), (u'160000', -40), ] else: self.db.cursor.execute(q) hands = self.db.cursor.fetchall() #fixme - nasty hack to ensure that the hands.insert() works # for mysql data. mysql returns tuples which can't be inserted # into so convert explicity to list. hands = list(hands) if (not hands): return ([], []) hands.insert(0, (hands[0][0], 0)) # Take that list and create an array of the time between hands times = map(lambda x: long(x[0]), hands) profits = map(lambda x: float(x[1]), hands) #print "DEBUG: times : %s" % times #print "DEBUG: profits: %s" % profits #print "DEBUG: len(times) %s" %(len(times)) diffs = diff( times ) # This array is the difference in starttime between consecutive hands diffs2 = append( diffs, THRESHOLD + 1 ) # Append an additional session to the end of the diffs, so the next line # includes an index into the last 'session' index = nonzero( diffs2 > THRESHOLD ) # This array represents the indexes into 'times' for start/end times of sessions # times[index[0][0]] is the end of the first session, #print "DEBUG: len(index[0]) %s" %(len(index[0])) if len(index[0]) > 0: #print "DEBUG: index[0][0] %s" %(index[0][0]) #print "DEBUG: index %s" %(index) pass else: index = [[0]] #print "DEBUG: index %s" %(index) #print "DEBUG: index[0][0] %s" %(index[0][0]) pass first_idx = 1 quotes = [] results = [] cum_sum = cumsum(profits) / 100 sid = 1 total_hands = 0 total_time = 0 global_open = None global_lwm = None global_hwm = None self.times = [] # Take all results and format them into a list for feeding into gui model. #print "DEBUG: range(len(index[0]): %s" % range(len(index[0])) for i in range(len(index[0])): last_idx = index[0][i] hds = last_idx - first_idx + 1 # Number of hands in session if hds > 0: stime = strftime("%d/%m/%Y %H:%M", localtime( times[first_idx])) # Formatted start time etime = strftime("%d/%m/%Y %H:%M", localtime( times[last_idx])) # Formatted end time self.times.append((times[first_idx], times[last_idx])) minutesplayed = (times[last_idx] - times[first_idx]) / 60 minutesplayed = minutesplayed + PADDING if minutesplayed == 0: minutesplayed = 1 hph = hds * 60 / minutesplayed # Hands per hour end_idx = last_idx + 1 won = sum(profits[first_idx:end_idx]) / 100.0 #print "DEBUG: profits[%s:%s]: %s" % (first_idx, end_idx, profits[first_idx:end_idx]) hwm = max(cum_sum[first_idx - 1:end_idx]) # include the opening balance, lwm = min(cum_sum[first_idx - 1:end_idx]) # before we win/lose first hand open = (sum(profits[:first_idx])) / 100 close = (sum(profits[:end_idx])) / 100 #print "DEBUG: range: (%s, %s) - (min, max): (%s, %s) - (open,close): (%s, %s)" %(first_idx, end_idx, lwm, hwm, open, close) total_hands = total_hands + hds total_time = total_time + minutesplayed if (global_lwm == None or global_lwm > lwm): global_lwm = lwm if (global_hwm == None or global_hwm < hwm): global_hwm = hwm if (global_open == None): global_open = open global_stime = stime results.append([ sid, hds, stime, etime, hph, "%.2f" % open, "%.2f" % close, "%.2f" % lwm, "%.2f" % hwm, "%.2f" % (hwm - lwm), "%.2f" % won ]) quotes.append((sid, open, close, hwm, lwm)) #print "DEBUG: Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won) first_idx = end_idx sid = sid + 1 else: print "hds <= 0" global_close = close global_etime = etime results.append([''] * 11) results.append([ _("all"), total_hands, global_stime, global_etime, total_hands * 60 / total_time, "%.2f" % global_open, "%.2f" % global_close, "%.2f" % global_lwm, "%.2f" % global_hwm, "%.2f" % (global_hwm - global_lwm), "%.2f" % (global_close - global_open) ]) return (results, quotes) def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig is not None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("Error:") + " " + err[2] + "(" + str(err[1]) + "): " + str( sys.exc_info()[1]) raise def generateGraph(self, quotes): self.clearGraphData() #print "DEBUG:" #print "\tquotes = %s" % quotes #for i in range(len(highs)): # print "DEBUG: (%s, %s, %s, %s)" %(lows[i], opens[i], closes[i], highs[i]) # print "DEBUG: diffs h/l: %s o/c: %s" %(lows[i] - highs[i], opens[i] - closes[i]) self.ax = self.fig.add_subplot(111) self.ax.set_title(_("Session candlestick graph")) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Sessions"), fontsize=12) self.ax.set_ylabel("$", fontsize=12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) candlestick(self.ax, quotes, width=0.50, colordown='r', colorup='g', alpha=1.00) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() def addTable(self, vbox, results): row = 0 sqlrow = 0 colxalign, colheading = range(2) self.liststore = gtk.ListStore(*([str] * len(self.columns))) for row in results: iter = self.liststore.append(row) view = gtk.TreeView(model=self.liststore) view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) vbox.add(view) print view.connect("row-activated", self.row_activated) cell05 = gtk.CellRendererText() cell05.set_property('xalign', 0.5) cell10 = gtk.CellRendererText() cell10.set_property('xalign', 1.0) listcols = [] # Create header row eg column: ("game", True, "Game", 0.0, "%s") for col, column in enumerate(self.columns): treeviewcolumn = gtk.TreeViewColumn(column[colheading]) listcols.append(treeviewcolumn) treeviewcolumn.set_alignment(column[colxalign]) view.append_column(listcols[col]) if (column[colxalign] == 0.5): cell = cell05 else: cell = cell10 listcols[col].pack_start(cell, expand=True) listcols[col].add_attribute(cell, 'text', col) listcols[col].set_expand(True) vbox.show_all() def row_activated(self, view, path, column): if path[0] < len(self.times): replayer = None for tabobject in self.owner.threads: if isinstance(tabobject, GuiHandViewer.GuiHandViewer): replayer = tabobject self.owner.tab_hand_viewer(None) break if replayer is None: self.owner.tab_hand_viewer(None) for tabobject in self.owner.threads: if isinstance(tabobject, GuiHandViewer.GuiHandViewer): replayer = tabobject break # added the timezone offset ('+00:00') to make the db query work. Otherwise the hands # at the edges of the date range are not included. A better solution may be possible. # Optionally the end date in the call below, which is a Long gets a '+1'. reformat = lambda t: strftime("%Y-%m-%d %H:%M:%S+00:00", gmtime(t)) handids = replayer.get_hand_ids_from_date_range( reformat(self.times[path[0]][0]), reformat(self.times[path[0]][1]), save_date=True) replayer.reload_hands(handids)
class GuiGraphViewer (threading.Thread): def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes" : True, "Sites" : True, "Games" : True, "Limits" : True, "LimitSep" : True, "LimitType" : True, "Type" : False, "UseType" : 'ring', "Seats" : False, "SeatSep" : False, "Dates" : True, "GraphOps" : True, "Groups" : False, "Button1" : True, "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("Error:")+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) raise def generateGraph(self, widget, data): try: self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() games = self.filters.getGames() graphops = self.filters.getGraphOps() names = "" for i in ('show', 'none'): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) names = names + "\n"+_hname + " on "+site if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return if not limits: print _("No limits found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, blue, red) = self.getRingProfitGraph(playerids, sitenos, limits, games, graphops['dspin']) print _("Graph generated in: %s") %(time() - starttime) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Hands"), fontsize = 12) # SET LABEL FOR X AXIS self.ax.set_ylabel(graphops['dspin'], fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) self.ax.plot(green, color='green', label=_('Hands: %d\nProfit: (%s): %.2f') %(len(green), green[-1])) self.ax.plot(blue, color='blue', label=_('Showdown') + ': $%.2f' %(blue[-1])) self.ax.plot(red, color='red', label=_('Non-showdown') + ': $%.2f' %(red[-1])) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user #print "No hands returned by graph query" else: self.ax.set_title((_("Profit graph for ring games")+names),fontsize=12) #Draw plot self.ax.plot(green, color='green', label=_('Hands: %d\nProfit: (%s): %.2f') %(len(green),graphops['dspin'], green[-1])) if graphops['showdown'] == 'ON': self.ax.plot(blue, color='blue', label=_('Showdown') + ' (%s): %.2f' %(graphops['dspin'], blue[-1])) if graphops['nonshowdown'] == 'ON': self.ax.plot(red, color='red', label=_('Non-showdown') + ' (%s): %.2f' %(graphops['dspin'], red[-1])) if sys.version[0:3] == '2.5': self.ax.legend(loc='upper left', shadow=True, prop=FontProperties(size='smaller')) else: self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("Error:")+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) #end of def showClicked def getRingProfitGraph(self, names, sites, limits, games, units): # tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] # print "DEBUG: getRingProfitGraph" if units == '$': tmp = self.sql.query['getRingProfitAllHandsPlayerIdSiteInDollars'] elif units == 'BB': tmp = self.sql.query['getRingProfitAllHandsPlayerIdSiteInBB'] start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #nametest = nametest.replace("L", "") q = [] for m in self.filters.display.items(): if m[0] == 'Games' and m[1]: for n in games: if games[n]: q.append(n) if len(q) > 0: gametest = str(tuple(q)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)",")") gametest = gametest.replace("u'","'") gametest = "and gt.category in %s" % gametest else: gametest = "and gt.category IS NULL" tmp = tmp.replace("<game_test>", gametest) lims = [int(x) for x in limits if x.isdigit()] potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl'] nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] capnolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'cn'] limittest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " # and ( (limit and bb in()) or (nolimit and bb in ()) ) if lims: blindtest = str(tuple(lims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'pl' and gt.bigBlind in " if potlims: blindtest = str(tuple(potlims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'nl' and gt.bigBlind in " if nolims: blindtest = str(tuple(nolims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'cn' and gt.bigBlind in " if capnolims: blindtest = str(tuple(capnolims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) )' else: limittest = limittest + '(-1) ) )' if type == 'ring': limittest = limittest + " and gt.type = 'ring' " elif type == 'tour': limittest = limittest + " and gt.type = 'tour' " #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace("<limit_test>", limittest) tmp = tmp.replace(",)", ")") #print "DEBUG: sql query:" #print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None, None) green = map(lambda x:float(x[1]), winnings) blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) blueline = cumsum(blue) redline = cumsum(red) return (greenline/100, blueline/100, redline/100) #end of def getRingProfitGraph def exportGraph (self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog(title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OK,gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename(self.exportFile) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename() + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class GuiGraphViewer(threading.Thread): def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent # print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes": True, "Sites": True, "Games": True, "Currencies": True, "Limits": True, "LimitSep": True, "LimitType": True, "Type": False, "UseType": "ring", "Seats": False, "SeatSep": False, "Dates": True, "GraphOps": True, "Groups": False, "Button1": True, "Button2": True, } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None # self.exportButton.set_sensitive(False) self.canvas = None self.exportFile = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox # end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() games = self.filters.getGames() currencies = self.filters.getCurrencies() graphops = self.filters.getGraphOps() names = "" for i in ("show", "none"): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) names = names + "\n" + _hname + " on " + site if not sitenos: # Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return if not limits: print _("No limits found") self.db.rollback() return # Set graph properties self.ax = self.fig.add_subplot(111) # Get graph data from DB starttime = time() (green, blue, red, orange) = self.getRingProfitGraph( playerids, sitenos, limits, games, currencies, graphops["dspin"] ) print _("Graph generated in: %s") % (time() - starttime) # Set axis labels and grid overlay properites self.ax.set_xlabel(_("Hands")) # SET LABEL FOR X AXIS self.ax.set_ylabel(graphops["dspin"]) self.ax.grid(color="g", linestyle=":", linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = [ 0.0, 0.0, 0.0, 0.0, 500.0, 1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0, 500.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 875.0, 750.0, 625.0, 500.0, 375.0, 250.0, 125.0, 0.0, 0.0, 0.0, 0.0, 500.0, 1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0, 500.0, 1000.0, 1000.0, ] red = [ 0.0, 0.0, 0.0, 0.0, 500.0, 1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 125.0, 250.0, 375.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 375.0, 250.0, 125.0, 0.0, 0.0, 0.0, 0.0, 500.0, 1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0, 500.0, 1000.0, 1000.0, ] blue = [ 0.0, 0.0, 0.0, 0.0, 500.0, 1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 125.0, 250.0, 375.0, 500.0, 625.0, 750.0, 875.0, 1000.0, 875.0, 750.0, 625.0, 500.0, 375.0, 250.0, 125.0, 0.0, 0.0, 0.0, 0.0, 500.0, 1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0, 500.0, 1000.0, 1000.0, ] self.ax.plot( green, color="green", label=_("Hands") + ": %d\n" % len(green) + _("Profit") + ": %.2f" % green[-1] ) self.ax.plot(blue, color="blue", label=_("Showdown") + ": $%.2f" % (blue[-1])) self.ax.plot(red, color="red", label=_("Non-showdown") + ": $%.2f" % (red[-1])) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() else: self.ax.set_title((_("Profit graph for ring games") + names)) # Draw plot if graphops["showdown"] == "ON": self.ax.plot(blue, color="blue", label=_("Showdown") + " (%s): %.2f" % (graphops["dspin"], blue[-1])) if graphops["nonshowdown"] == "ON": self.ax.plot(red, color="red", label=_("Non-showdown") + " (%s): %.2f" % (graphops["dspin"], red[-1])) if graphops["ev"] == "ON": self.ax.plot( orange, color="orange", label=_("All-in EV") + " (%s): %.2f" % (graphops["dspin"], orange[-1]) ) self.ax.plot( green, color="green", label=_("Hands") + ": %d\n" % len(green) + _("Profit") + ": (%s): %.2f" % (graphops["dspin"], green[-1]), ) # order legend, greenline on top handles, labels = self.ax.get_legend_handles_labels() handles = handles[-1:] + handles[:-1] labels = labels[-1:] + labels[:-1] legend = self.ax.legend( handles, labels, loc="upper left", fancybox=True, shadow=True, prop=FontProperties(size="smaller") ) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() # self.exportButton.set_sensitive(True) # end of def showClicked def getRingProfitGraph(self, names, sites, limits, games, currencies, units): # tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] # print "DEBUG: getRingProfitGraph" if units == "$": tmp = self.sql.query["getRingProfitAllHandsPlayerIdSiteInDollars"] elif units == "BB": tmp = self.sql.query["getRingProfitAllHandsPlayerIdSiteInBB"] start_date, end_date = self.filters.getDates() # Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) # nametest = nametest.replace("L", "") q = [] for m in self.filters.display.items(): if m[0] == "Games" and m[1]: for n in games: if games[n]: q.append(n) if len(q) > 0: gametest = str(tuple(q)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)", ")") gametest = gametest.replace("u'", "'") gametest = "and gt.category in %s" % gametest else: gametest = "and gt.category IS NULL" tmp = tmp.replace("<game_test>", gametest) limittest = self.filters.get_limits_where_clause(limits) q = [] for n in currencies: if currencies[n]: q.append(n) currencytest = str(tuple(q)) currencytest = currencytest.replace(",)", ")") currencytest = currencytest.replace("u'", "'") currencytest = "AND gt.currency in %s" % currencytest if type == "ring": limittest = limittest + " and gt.type = 'ring' " elif type == "tour": limittest = limittest + " and gt.type = 'tour' " # Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace("<limit_test>", limittest) tmp = tmp.replace("<currency_test>", currencytest) tmp = tmp.replace(",)", ")") # print "DEBUG: sql query:" # print tmp self.db.cursor.execute(tmp) # returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None, None) green = map(lambda x: float(x[1]), winnings) blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) orange = map(lambda x: float(x[3]), winnings) greenline = cumsum(green) blueline = cumsum(blue) redline = cumsum(red) orangeline = cumsum(orange) return (greenline / 100, blueline / 100, redline / 100, orangeline / 100) # end of def getRingProfitGraph def exportGraph(self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. png_filter = gtk.FileFilter() png_filter.add_pattern("*.png") dia_chooser = gtk.FileChooserDialog( title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK), ) dia_chooser.set_filter(png_filter) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) if self.exportFile is not None: dia_chooser.set_filename(self.exportFile) # use previously chosen export path as default else: dia_chooser.set_current_name("fpdbgraph.png") response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _("Closed, no graph exported") dia_chooser.destroy() return self.exportFile = dia_chooser.get_filename() dia_chooser.destroy() self.fig.savefig(self.exportFile, format="png") # Display info box to confirm graph created. diainfo = gtk.MessageDialog( parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created"), ) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class DataManager(gtk.Window): numRows, numCols = 20, 10 data = random((numRows, numCols)) def __init__(self): gtk.Window.__init__(self) self.set_default_size(600, 600) self.connect('destroy', lambda win: gtk.main_quit()) self.set_title('GtkListStore demo') self.set_border_width(8) vbox = gtk.VBox(False, 8) self.add(vbox) label = gtk.Label('Double click a row to plot the data') vbox.pack_start(label, False, False) sw = gtk.ScrolledWindow() sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) vbox.pack_start(sw, True, True) model = self.create_model() self.treeview = gtk.TreeView(model) self.treeview.set_rules_hint(True) # matplotlib stuff fig = Figure(figsize=(6, 4)) self.canvas = FigureCanvas(fig) # a gtk.DrawingArea vbox.pack_start(self.canvas, True, True) ax = fig.add_subplot(111) self.line, = ax.plot(self.data[0, :], 'go') # plot the first row self.treeview.connect('row-activated', self.plot_row) sw.add(self.treeview) self.add_columns() self.add_events(gdk.BUTTON_PRESS_MASK | gdk.KEY_PRESS_MASK | gdk.KEY_RELEASE_MASK) def plot_row(self, treeview, path, view_column): ind, = path # get the index into data points = self.data[ind, :] self.line.set_ydata(points) self.canvas.draw() def add_columns(self): for i in range(self.numCols): column = gtk.TreeViewColumn('%d' % i, gtk.CellRendererText(), text=i) self.treeview.append_column(column) def create_model(self): types = [float] * self.numCols store = gtk.ListStore(*types) for row in self.data: store.append(row) return store
class GuiTourneyGraphViewer: def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes": True, "Sites": True, "Games": False, "Currencies": True, "Limits": False, "LimitSep": False, "LimitType": False, "Type": False, "UseType": 'tour', "Seats": False, "SeatSep": False, "Dates": True, "GraphOpsTour": True, "Groups": False, "Button1": True, "Button2": True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() #update the graph at entry (simulate a "Refresh Graph" click) gobject.GObject.emit(self.filters.Button1, "clicked") def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() currencies = self.filters.getCurrencies() # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, datesXAbs) = self.getData(playerids, sitenos, currencies) print _("Graph generated in: %s") % (time() - starttime) currencyLabel = ','.join(['%s' % key for key in currencies.keys()]) #Set axis labels and grid overlay properites self.ax.set_ylabel(currencyLabel, fontsize=12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) self.ax.plot(green, color='green', label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + '(' + currencyLabel + '): %.2f' % green[-1]) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user else: self.ax.set_title(_("Tournament Results")) useDates = True #nothing to draw if (len(green) == 0): return #Get the dates of tourneys #if first tourney has no date, get the most ancient date and assume it's his one if datesXAbs[0] is None: i = 1 while i < len(datesXAbs) and type(datesXAbs[i]) is None: i = i + 1 if i == len(datesXAbs): print "Wow wow wow : no dates in your whole tourneys" useDates = False else: datesXAbs[0] = datesXAbs[i] #now convert date to dateTime format if useDates: for i in range(0, len(datesXAbs)): if datesXAbs[i] is None: datesXAbs[i] = datesXAbs[i - 1] else: datesXAbs[i] = datetime.datetime.strptime( datesXAbs[i], "%Y-%m-%d %H:%M:%S") datesXAbs[i] = datesXAbs[i].strftime('%d/%m') mycolor = 'red' if green[0] > 0: mycolor = 'green' self.ax.plot([0, 1], [0, green[0]], color=mycolor, label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + '(' + currencyLabel + '): %.2f' % green[-1]) for i in range(1, len(green)): final = green[i] - green[i - 1] mycolor = 'red' if (green[i] > 0): mycolor = 'green' self.ax.plot([i, i + 1], [green[i - 1], green[i]], color=mycolor) if (i % (len(green) / 5) == 0): gain = "" if (green[i] == 0): gain = "=" else: if (green[i] > 0): gain = "+" gain += str(green[i]) self.ax.annotate(gain, xy=(i, 0), color=mycolor, xycoords=('data', 'axes fraction'), xytext=(0, 18), textcoords='offset points', va='top', ha='left') if useDates: self.ax.annotate(datesXAbs[i], xy=(i, 0), xycoords=('data', 'axes fraction'), xytext=(0, -18), textcoords='offset points', va='top', ha='left') #~self.ax.axhline(0, color='black', lw=2) legend = self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) #end of def showClicked def getData(self, names, sites, currencies): tmp = self.sql.query['tourneyGraph'] # print "DEBUG: getData. :" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) currencytest = str(tuple()) #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) currencytest = str(tuple(currencies)) currencytest = currencytest.replace(",)", ")") currencytest = currencytest.replace("u'", "'") currencytest = "AND tt.currency in %s" % currencytest tmp = tmp.replace("<currency_test>", currencytest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace(",)", ")") # print "DEBUG: sql query:", tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None) green = map(lambda x: float(x[1]), winnings) datesXAbs = map(lambda x: x[8], winnings) #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) #blueline = cumsum(blue) #redline = cumsum(red) return (greenline / 100, datesXAbs) def exportGraph(self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog( title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename( self.exportFile ) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename( ) + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class Backend( backend.Plotter ): def init(self): # line_cache: key = id(Curve), value=mpl line object self.line_cache = dict() self.layer_to_axes = dict() self.axes_to_layer = dict() self.layers_cache = list() # copy of self.plot.layers def connect(self): logger.debug("Opening matplotlib session.") self.figure = Figure(dpi=100, facecolor="white") # figsize=(5,4), dpi=100) self.canvas = FigureCanvas(self.figure) self.canvas.show() self.line_cache.clear() self.layer_to_axes.clear() self.axes_to_layer.clear() backend.Plotter.connect(self) logger.debug("Init finished") def disconnect(self): logger.debug("Closing matplotlib session.") if not self.canvas is None: self.canvas.destroy() self.canvas = None if not self.figure is None: self.figure = None backend.Plotter.disconnect(self) #---------------------------------------------------------------------- def arrange(self, rows=1, cols=1): layers = self.plot.layers n = len(layers) if n > (rows*cols): rows = int((rows*cols) / n) + 1 cols = rows * n #raise ValueError("Not enough rows and cols for all layers!") self.figure.clear() self.figure.axes = [] self.layer_to_axes.clear() self.axes_to_layer.clear() self.layers_cache = list() j = 1 for layer in layers: print "Setting up layer", layer axes = self.figure.add_subplot("%d%d%d" % (rows,cols,j)) self.layer_to_axes[layer] = axes self.axes_to_layer[axes] = layer self.layers_cache.append(layer) j += 1 def draw_layer(self, layer, group_info): ax = self.layer_to_axes[layer] print "DRAWING AXES ", ax ax.lines = [] line_cache = [] line_count = 0 last_cx = -1 # Default values come in two flavors: # group-wise and single default values group_colors = uwrap.get(layer, 'group_colors') group_styles = uwrap.get(layer, 'group_styles') group_markers = uwrap.get(layer, 'group_markers') #default_color = 'r' default_color = None default_style = 'solid' default_marker = 'None' #:layer.visible if uwrap.get(layer, 'visible') is False: return #:layer.title title = uwrap.get(layer, 'title', None) if title is not None: ax.set_title(title) #:layer.grid grid = uwrap.get(layer, 'grid') ax.grid(grid) #:layer.lines for line in layer.lines: data_to_plot = [] #:line.visible if uwrap.get(line, 'visible') is False: if line in ax.lines: ax.lines.remove(line) continue #:line.source if line.source is None: logger.warn("No Dataset specified for Line!") continue else: ds = line.source if ds.is_empty() is True: logger.warn("No data for Line!") continue table = ds.get_data() if not isinstance(table, Table): raise TypeError("Matplotlib Backend currently only supports data of type Table, while this is of %s" % type(table)) #:line.cx if line.cx is None or line.cy is None: logger.error("No x or y source given for Line. Line skipped.") continue else: cx, cy = line.cx, line.cy try: xdata = table[cx] except IndexError: logger.error("X-Index out of range (%s). Line skipped." % cx) continue #:line.cy try: ydata = table[cy] except IndexError: logger.error("Y-Index out of range (%s). Line skipped." % cy) continue #:line.style global linestyle_mappings default = default_style or group_styles[line_count % len(group_styles)] style = uwrap.get(line, 'style', default) style = linestyle_mappings[style] #:line.marker global linemarker_mappings default = default_marker or group_markers[line_count % len(group_markers)] marker = uwrap.get(line, 'marker', default) marker = linemarker_mappings[marker] #:line.width width = uwrap.get(line, 'width') #:line.color default = default_color or group_colors[line_count % len(group_colors)] color = uwrap.get(line, 'color', default) #--- PLOT LINE --- l, = ax.plot( xdata, ydata, linewidth=width, linestyle=style, marker=marker, color=color) line_cache.append(l) # TODO: if we set the label afterwards, don't we then have a redraw? #:line.label label = line.label if label is None: column = table.column(cy) label = column.label or column.key or uwrap.get(line, 'label') l.set_label(label) line_count += 1 # # # # additional lines # # # p = len(xdata) # if p > 2: p = p/2 # atpoint = xdata[max(p-1,0)] # print "Printing vertical line at ", atpoint # ax.axvline(atpoint) #:layer.legend legend = uwrap.get(layer, 'legend') if legend is not None and line_count > 0: visible = uwrap.get(legend, 'visible') if visible is True: #:legend.label TODO label = uwrap.get(legend, 'visible') if label is not None: pass #:legend.border TODO border = uwrap.get(legend, 'border') #:legend.position TODO position = uwrap.get(legend, 'position', 'best') if position == 'at position': position = (uwrap.get(legend, 'x'), uwrap.get(legend, 'y')) # create legend entries from line labels labels = [l.get_label() for l in line_cache] ax.legend(line_cache, labels, loc=position) else: ax.legend_ = None else: ax.legend_ = None #:layer.axes for (key, axis) in layer.axes.iteritems(): #:axis.label #:axis.scale #:axis.start #:axis.end label = uwrap.get(axis, 'label') scale = uwrap.get(axis, 'scale') start = uwrap.get(axis, 'start') end = uwrap.get(axis, 'end') print "START = %s, END = %s" % (str(start), str(end)) if key == 'x': set_label = ax.set_xlabel set_scale = ax.set_xscale set_start = (lambda l: ax.set_xlim(xmin=l)) set_end = (lambda l: ax.set_xlim(xmax=l)) elif key == 'y': set_label = ax.set_ylabel set_scale = ax.set_yscale set_start = (lambda l: ax.set_ylim(ymin=l)) set_end = (lambda l: ax.set_ylim(ymax=l)) else: raise RuntimeError("Invalid axis key '%s'" % key) if label is not None: set_label(label) if scale is not None: set_scale(scale) if start is not None: set_start(start) if end is not None: set_end(end) def draw(self): logger.debug("Matplotlib: draw()") self.check_connection() # plot all curves together in one plot legend_list = [] # for legend later on curve_count = 0 # if self.plot.layers != self.layers_cache: self.arrange() for layer in self.plot.layers: group_info = {} self.draw_layer(layer, group_info) self.canvas.draw()
class RAMSTKPlot(object): """ The RAMSTKPlot class. This module contains RAMSTK plot class. This class is derived from the applicable pyGTK widgets and matplotlib plots, but are provided with RAMSTK specific property values and methods. This ensures a consistent look and feel to widgets in the RAMSTK application. """ def __init__(self): """Initialize an instance of the RAMSTKPlot.""" # Initialize private dictionary attributes. # Initialize private list attributes. self._lst_max = [] self._lst_min = [0.0] # Initialize private scalar attributes. # Initialize public dictionary attributes. # Initialize public list attributes. # Initialize public scalar attributes. self.figure = Figure() self.plot = FigureCanvas(self.figure) self.axis = self.figure.add_subplot(111) def _do_make_date_plot(self, x_values, y_values, marker='g-'): """ Make a date plot. :param list x_values: the list of x-values (dates) for the plot. :param list y_values: the list of y-values for the plot. :keyword str marker: the type and color of marker to use for the plot. Default is a solid green line. """ _line, = self.axis.plot_date(x_values, y_values, marker, xdate=True, linewidth=2) self._lst_min.append(min(y_values)) self._lst_max.append(max(y_values)) return _line def _do_make_histogram(self, x_values, y_values, marker='g'): """ Make a histogram. :param list x_values: the list of x-values for the plot. :param list y_values: the list of bin edges for the plot. :keyword str marker: the color of bars to use for the histogram. Default is green. """ self.axis.grid(False, which='both') _values, _edges, __ = self.axis.hist(x_values, bins=y_values, color=marker) self._lst_min.append(min(_values)) self._lst_max.append(max(_values) + 1) return _values, _edges def _do_make_scatter_plot(self, x_values, y_values, marker='go'): """ Make a scatter plot. :param list x_values: the list of x-values for the plot. :param list y_values: the list of y-values for the plot. :keyword str marker: the type and color of marker to use for the plot. Default is open green circles. """ _line, = self.axis.plot(x_values, y_values, marker, linewidth=2) _line.set_ydata(y_values) self._lst_min.append(min(y_values)) self._lst_max.append(max(y_values)) return _line def _do_make_step_plot(self, x_values, y_values, marker='g-'): """ Make a step plot. :param list x_values: the list of x-values for the plot. :param list y_values: the list of y-values for the plot. :keyword str marker: the type and color of marker to use for the plot. Default is a solid green line. """ _line, = self.axis.step(x_values, y_values, marker, where='mid') _line.set_ydata(y_values) self._lst_min.append(min(y_values)) self._lst_max.append(max(y_values)) return _line def do_load_plot(self, x_values, y_values=None, plot_type='scatter', marker='g-'): """ Load the RAMSTKPlot. :param list x_values: list of the x-values to plot. :keyword list y_values: list of the y-values to plot or list of bin edges if plotting a histogram. :keyword str plot_type: list of the type of line to plot. Options are: * 'date' * 'histogram' * 'scatter' (default) * 'step' :keyword str marker: the marker to use on the plot. Defaults is 'g-' or a solid green line. See matplotlib documentation for other options. :return: False if successful or True if an error is encountered. :rtype: bool """ if y_values is not None: if plot_type == 'step': self._do_make_step_plot(x_values, y_values, marker) elif plot_type == 'scatter': self._do_make_scatter_plot(x_values, y_values, marker) elif plot_type == 'histogram': _values, _edges = self._do_make_histogram( x_values, y_values, marker) elif plot_type == 'date': self._do_make_date_plot(x_values, y_values, marker) # Get the minimum and maximum y-values to set the axis bounds. If the # maximum value is infinity, use the next largest value and so forth. _min = min(self._lst_min) _max = self._lst_max[0] for i in range(1, len(self._lst_max)): if _max < self._lst_max[i] and self._lst_max[i] != float('inf'): _max = self._lst_max[i] self.axis.set_ybound(_min, 1.05 * _max) self.plot.draw() return False def do_add_line(self, x_values, y_values=None, color='k', marker='^'): """ Load the RAMSTKPlot. :param list x_values: list of the x-values to plot. :keyword list y_values: list of the y-values to plot or list of bin edges if plotting a histogram. :keyword str color: the color of the line to add to the plot. Black is the default. See matplotlib documentation for options. :keyword str marker: the marker to use on the plot. Defaults is '^' or an upward pointing triangle. See matplotlib documentation for other options. :return: False if successful or True if an error is encountered. :rtype: bool """ _line = Line2D(x_values, y_values, lw=0.0, color=color, marker=marker, markersize=10) self.axis.add_line(_line) self.figure.canvas.draw() return False def do_close_plot(self, __window, __event, parent): """ Return the plot to the Work Book page it is part of. :param __window: the gtk.Window() that is being destroyed. :type __window: :class:`gtk.Window` :param __event: the gtk.gdk.Event() that called this method. :type __event: :class:`gtk.gdk.Event` :param parent: the original parent gtk.Widget() for the plot. :type parent: :class:`gtk.Widget` :return: False if successful or True if an error is encountered. :rtype: bool """ self.plot.reparent(parent) return False def do_expand_plot(self, event): """ Display a plot in it's own window. :param event: the matplotlib.MouseEvent() that called this method. :type event: :class:`matplotlib.MouseEvent` :return: False if successful or True if an error is encountered. :rtype: bool """ self.plot = event.canvas _parent = self.plot.get_parent() if event.button == 3: # Right click. _window = gtk.Window() _window.set_skip_pager_hint(True) _window.set_skip_taskbar_hint(True) _window.set_default_size(800, 400) _window.set_border_width(5) _window.set_position(gtk.WIN_POS_NONE) _window.set_title(_(u"RAMSTK Plot")) _window.connect('delete_event', self.close_plot, _parent) self.plot.reparent(_window) _window.show_all() return False def do_make_labels(self, label, x_pos, y_pos, **kwargs): r""" Make the abscissa or ordinate label. :param str label: the text to display as the abscissa or ordinate label. :param float x_pos: the position along the abscissa to place the label. :param float y_pos: the position along the ordinate to place the label. :param \**kwargs: See below :Keyword Arguments: * *set_x* (bool) -- whether to set the abscissa (default) or ordinate label. * *fontsize* (int) -- the size of the font to use for the axis label. * *fontweight* (str) -- the weight of the font to use for the axis label. :return: matplotlib text instance representing the label. :rtype: :class:`matplotlib.text.Text` """ try: _set_x = kwargs['set_x'] except KeyError: _set_x = True try: _fontsize = kwargs['fontsize'] except KeyError: _fonsize = 14 try: _fontweight = kwargs['fontweight'] except KeyError: _fontweight = 'bold' _label = None if _set_x: _label = self.axis.set_xlabel( label, { 'fontsize': _fontsize, 'fontweight': _fontweight, 'verticalalignment': 'center', 'horizontalalignment': 'center', 'x': x_pos, 'y': y_pos }) else: _label = self.axis.set_ylabel( label, { 'fontsize': _fontsize, 'fontweight': _fontweight, 'verticalalignment': 'center', 'horizontalalignment': 'center', 'rotation': 'vertical' }) return _label # pylint: disable=too-many-arguments def do_make_legend(self, text, **kwargs): r""" Make a legend on the RAMSTKPlot. :param tuple text: the text to display in the legend. :param \**kwargs: See below :Keyword Arguments: * *fontsize* (str) -- the size of the font to use for the legend. Options are: - xx-small - x-small - small (default) - medium - large - x-large - xx-large * *frameon* (bool) -- whether or not there is a frame around the legend. * *location* (str) -- the location of the legend on the plot. Options are: - best - upper right (default) - upper left - lower left - lower right - right - center left - center right - lower center - upper center - center * *ncol* (int) -- the number columns in the legend. Default is 1. * *shadow* (bool) -- whether or not to display a shadow behind the legend block. Default is True. * *title* (str) -- the title of the legend. Default is an empty string. * *lwd* (float) -- the linewidth of the box around the legend. :return: None :rtype: None """ try: _fontsize = kwargs['fontsize'] except KeyError: _fontsize = 'small' try: _frameon = kwargs['frameon'] except KeyError: _frameon = False try: _location = kwargs['location'] except KeyError: _location = 'upper right' try: _ncol = kwargs['ncol'] except KeyError: _ncol = 1 try: _shadow = kwargs['shadow'] except KeyError: _shadow = True try: _title = kwargs['title'] except KeyError: _title = "" try: _lwd = kwargs['lwd'] except KeyError: _lwd = 0.5 _legend = self.axis.legend(text, frameon=_frameon, loc=_location, ncol=_ncol, shadow=_shadow, title=_title) for _text in _legend.get_texts(): _text.set_fontsize(_fontsize) for _line in _legend.get_lines(): _line.set_linewidth(_lwd) return None def do_make_title(self, title, fontsize=16, fontweight='bold'): """ Make the plot title. :param str title: the text to display as the title. :keyword int fontsize: the size of the font to use for the title. :keyword str fontweight: the weight of the font to use for the title. :return: matplotlib text instance representing the title. :rtype: :class:`matplotlib.text.Text` """ return self.axis.set_title( title, { 'fontsize': fontsize, 'fontweight': fontweight, 'verticalalignment': 'baseline', 'horizontalalignment': 'center' })
class plot: """Note, currently, this cant be used interactively - because Gtk has to be running....""" def __init__(self, window=None, startGtk=0, dims=2): self.dims = dims self.data = numpy.zeros((10, 10), numpy.float32) self.data[:] = numpy.arange(10).astype(numpy.float32) self.deactivatefn = None #this can be set by the caller, eg to turn off buttons... self.win = gtk.Window() self.win.connect("destroy", self.quit) self.win.set_default_size(400, 400) self.win.set_title("Window") self.cmap = colour.gray self.vbox = gtk.VBox() self.interpolation = "nearest" #see pylab documantation for others. self.win.add(self.vbox) self.vbox.connect("button_press_event", self.buttonPress) self.fig = Figure(figsize=(5, 4), dpi=50) self.ax = self.fig.add_subplot(111) self.fig.subplots_adjust(right=0.99, left=0.08, bottom=0.05, top=0.99) #self.ax.imshow(self.data,interpolation=self.interpolation) #print type(fig),dir(ax),dir(fig) self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea self.vbox.pack_start(self.canvas) self.toolbar = NavigationToolbar(self.canvas, self.win) self.vbox.pack_start(self.toolbar, False, False) self.mytoolbar = myToolbar(plotfn=self.plot) #self.toolbar.save_figure=self.mytoolbar.mysave self.vbox.pack_start(self.mytoolbar.toolbar, False, False) self.win.show_all() self.toolbar.hide() self.mytoolbar.toolbar.hide() self.active = 1 #will be set to zero once quit or window closed. self.toolbarVisible = 0 self.startedGtk = 0 self.update = 0 #self.plot() if startGtk == 1 and gtk.main_level() == 0: self.startedGtk = 1 thread.start_new_thread(gtk.main, ()) def quit(self, w=None, data=None): if self.deactivatefn != None: d = self.deactivatefn self.deactivatefn = None d() self.active = 0 self.win.hide() if self.startedGtk: gtk.main_quit() def newPalette(self, palette): if palette[-3:] == ".gp": palette = palette[:-3] if palette in colour.datad.keys(): self.cmap = getattr(colour, palette) else: print "Palette %s not regocnised" % str(palette) #self.plot() def newInterpolation(self, interp): if interp not in [ "bicubic", "bilinear", "blackman100", "blackman256", "blackman64", "nearest", "sinc144", "sinc64", "spline16", "spline36" ]: print "Interpolation %s not recognised" % str(interp) else: self.interpolation = interp #self.plot() def buttonPress(self, w, e, data=None): """If the user right clicks, we show or hide the toolbar...""" if e.button == 3: if self.toolbarVisible: self.toolbar.hide() self.mytoolbar.toolbar.hide() self.toolbarVisible = 0 else: self.toolbar.show() self.mytoolbar.toolbar.show() self.toolbarVisible = 1 return True def queuePlot(self, axis): """puts a request to plot in the idle loop... (gives the rest of the gui a chance to update before plotting) """ #print type(axis),self.data.shape #if type(axis)!=type(None): # print axis.shape if self.update: if hasattr(self.ax.xaxis, "callbacks"): if hasattr(self.ax.xaxis.callbacks, "signals") and hasattr( self.ax.xaxis.callbacks, "callbacks"): self.ax.xaxis.callbacks.callbacks = dict([ (s, dict()) for s in self.ax.xaxis.callbacks.signals ]) #needed to fix a bug! self.ax.yaxis.callbacks.callbacks = dict([ (s, dict()) for s in self.ax.yaxis.callbacks.signals ]) #needed to fix a bug! self.ax.clear() freeze, logscale, data, scale, aspect = self.mytoolbar.prepare( self.data, dim=self.dims) if len(data.shape) == 1 or self.dims == 1: #1D if len(data.shape) == 1: if freeze == 0: if type(axis) == type( None) or axis.shape[0] != data.shape[0]: axis = numpy.arange(data.shape[0]) + 1 if logscale: try: axis = numpy.log10(axis) except: print "Cannot take log" #self.fig.axis([axis[0],axis[-1],scale[0],scale[1]]) #print dir(self.ax) #print dir(self.ax.axis) #print self.ax.get_position() #self.ax.cla() #self.ax.axis([-1,1,-1,1]) #self.ax.autoscale_view() try: #older installations don't have this (e.g. cray) self.ax.set_aspect("auto") except: pass self.ax.plot(axis, data) else: #use first row of data for the x axis... #axis=data[0] #freeze,logscale,data,scale=self.mytoolbar.prepare(self.data,dim=1) if freeze == 0: if type(axis) == type( None) or axis.shape[0] != data.shape[-1]: axis = numpy.arange(data.shape[0]) + 1 if logscale: try: axis = numpy.log10(axis) except: print "Cannot take log" #self.fig.axis([axis[0],axis[-1],scale[0],scale[1]]) for i in range(data.shape[0]): self.ax.plot(axis, data[i]) else: #2D if len(data.shape) != 2: #force to 2d data = numpy.reshape(data, (reduce( lambda x, y: x * y, data.shape[:-1]), data.shape[-1])) #freeze,logscale,data,scale=self.mytoolbar.prepare(self.data) if freeze == 0: self.ax.imshow(data, interpolation=self.interpolation, cmap=self.cmap, vmin=scale[0], vmax=scale[1], origin="lower", aspect=("auto" if aspect else "equal")) if freeze == 0: try: self.ax.draw() except: pass #self.ax.update() self.canvas.draw() #self.canvas.queue_draw() self.update = 0 return False def plot(self, data=None, copy=0, axis=None): """Plot new data... axis may be specified if 1d... """ if self.active == 0: self.active = 1 self.win.show() #if type(data)==numpy.ndarray: # data=Numeric.array(data) if type(data) != type(None): if copy: self.data = data.copy().astype("d") else: if data.dtype.char == "d": self.data = data else: self.data = data.astype("d") else: #data==None? pass #if type(self.data)==numpy.ndarray: # self.data=Numeric.array(self.data) self.update = 1 #print "plot" #print type(axis) #if type(axis)!=type(None): # print axis.shape gobject.idle_add(self.queuePlot, axis) ## self.ax.clear() ## if len(self.data.shape)==1 or self.dims==1: ## #1D ## if len(self.data.shape)==1: ## freeze,logscale,data,scale=self.mytoolbar.prepare(self.data,dim=1) ## if freeze==0: ## if type(axis)==type(None) or axis.shape[0]!=data.shape[0]: ## axis=Numeric.arange(data.shape[0])+1 ## if logscale: ## try: ## axis=Numeric.log10(axis) ## except: ## print "Cannot take log" ## #self.fig.axis([axis[0],axis[-1],scale[0],scale[1]]) ## self.ax.plot(axis,data) ## else:#use first row of data for the x axis... ## #axis=data[0] ## freeze,logscale,data,scale=self.mytoolbar.prepare(self.data,dim=1) ## if freeze==0: ## if type(axis)==type(None) or axis.shape[0]!=data.shape[-1]: ## axis=Numeric.arange(data.shape[0])+1 ## if logscale: ## try: ## axis=Numeric.log10(axis) ## except: ## print "Cannot take log" ## #self.fig.axis([axis[0],axis[-1],scale[0],scale[1]]) ## for i in range(data.shape[0]): ## self.ax.plot(axis,data[i]) ## else:#2D ## if len(self.data.shape)!=2:#force to 2d ## self.data=Numeric.reshape(self.data,(reduce(lambda x,y:x*y,self.data.shape[:-1]),self.data.shape[-1])) ## freeze,logscale,data,scale=self.mytoolbar.prepare(self.data) ## if freeze==0: ## self.ax.imshow(data,interpolation=self.interpolation,cmap=self.cmap,vmin=scale[0],vmax=scale[1]) ## if freeze==0: ## try: ## self.ax.draw() ## except: ## pass ## #self.ax.update() ## self.canvas.draw() ## #self.canvas.queue_draw() return True
class XYDialog: def __init__(self, title, parent, syn2d, numX, numY): # create buttons self.loadButton = gtk.Button(label="Load", stock=gtk.STOCK_OPEN) self.saveButton = gtk.Button(label="Save and Use", stock=gtk.STOCK_SAVE) self.useButton = gtk.Button(label="Use") self.revertButton = gtk.Button(label="Revert") self.clearButton = gtk.Button(label="Clear") self.cancelButton = gtk.Button(label="Cancel") # create matplotlib figure self.figure = Figure() self.canvas = FigureCanvas(self.figure) self.mp = DataPointChooser(self.figure, self, numX, numY) self.syn2d = syn2d self.parent = parent self.sourcesFile, self.receiversFile = self.syn2d.getDataFiles() print "Loading initial data from: " + self.sourcesFile + " , " + self.receiversFile sources = self.loadXYFile(self.sourcesFile) receivers = self.loadXYFile(self.receiversFile, True) # create GTK dialog self.dialog = gtk.Dialog(title=title, parent=parent, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) self.dialog.set_default_size(500, 400) self.vBox = self.dialog.vbox # setup matplotlib events self.canvas.mpl_connect('button_press_event', self.mp.on_click) # pack buttons self.buttonBox = gtk.HBox(homogeneous=True, spacing=5) self.buttonBox.pack_start(self.loadButton, expand=False) self.buttonBox.pack_start(self.saveButton, expand=False) self.buttonBox.pack_start(self.useButton, expand=False) self.buttonBox.pack_start(self.revertButton, expand=False) self.buttonBox.pack_start(self.clearButton, expand=False) self.buttonBox.pack_end(self.cancelButton, expand=False) # connect buttons self.use = False self.loadButton.connect("clicked", self.loadHandler) self.saveButton.connect("clicked", self.saveHandler) self.useButton.connect("clicked", self.useHandler) self.revertButton.connect("clicked", self.revertHandler) self.clearButton.connect("clicked", self.clearHandler) self.cancelButton.connect("clicked", self.cancelHandler) self.label = gtk.Label( "Mouse Buttons: L-Add Station, M-Delete Point, R-Add Source") # pack and show dialog self.vBox.pack_start(self.canvas, expand=True) self.vBox.pack_start(gtk.HSeparator(), expand=False) self.vBox.pack_start(self.label, expand=False) self.vBox.pack_end(self.buttonBox, expand=False) self.mp.setOriginalData(sources, receivers) self.mp.reset_data() self.dialog.show_all() def redraw(self): self.canvas.draw() def loadHandler(self, widget): chooser = gtk.FileChooserDialog(title="Select DIRECTORY Containing 'sources.txt' and 'receivers.txt' Files", \ parent=self.parent, \ action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, \ buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, \ gtk.STOCK_OPEN, gtk.RESPONSE_OK)) response = chooser.run() if (response == gtk.RESPONSE_OK): dir = chooser.get_filename() sourceName = dir + os.sep + "sources.txt" if (os.path.exists(sourceName)): sources = self.loadXYFile(sourceName) else: sources = [] receiverName = dir + os.sep + "receivers.txt" if (os.path.exists(receiverName)): receivers = self.loadXYFile(receiverName) else: receivers = [] self.mp.setOriginalData(sources, receivers) self.mp.reset_data() chooser.destroy() def loadXYFile(self, file, skipDuplicates=False): if not os.path.exists(file): return [] x, y = self.syn2d.loadXYFile(file) data = [] for i in range(0, len(x)): if skipDuplicates: skip = False for point in data: if point[0] == x[i] and point[1] == y[i]: skip = True break if skip: continue data.append((x[i], y[i])) return data def writeXYFile(self, file, data): fp = open(file, "w") for point in data: fp.write(str(point[0]) + " " + str(point[1]) + "\n") fp.close() def saveHandler(self, widget): chooser = gtk.FileChooserDialog(title="Select DIRECTORY to save 'sources.txt' and 'receivers.txt' Files", \ parent=self.parent, \ action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, \ buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, \ gtk.STOCK_OPEN, gtk.RESPONSE_OK)) response = chooser.run() if (response == gtk.RESPONSE_OK): dir = chooser.get_filename() self.writeDataFiles(dir) chooser.destroy() def writeDataFiles(self, dir): if not dir.endswith(os.sep): dir += os.sep sourceName = dir + "sources.txt" self.writeXYFile(sourceName, self.mp.getSources()) receiverName = dir + "receivers.txt" self.writeXYFile(receiverName, self.mp.getReceivers()) return (sourceName, receiverName) def useHandler(self, widget): self.sourcesFile, self.receiversFile = self.writeDataFiles( self.syn2d.getWorkingDir()) self.use = True self.exit() def clearHandler(self, widget): self.mp.setOriginalData([], []) self.mp.reset_data() def getSourcesFile(self): return self.sourcesFile def getReceiversFile(self): return self.receiversFile def wasUseSelected(self): print "USE? " + str(self.use) return self.use def revertHandler(self, widget): self.mp.reset_data() def cancelHandler(self, widget): self.use = False self.exit() def updateButtons(self, sources, receivers): self.useButton.set_sensitive(len(sources) > 0 and len(receivers) > 0) def exit(self): self.dialog.hide()
class ResultViewer(gtk.Window): def __init__(self,modelf,paramsf,resultsf,auto_update = 1): super(ResultViewer, self).__init__() self.model_file = modelf if modelf!=None else "model.json" self.param_file = paramsf self.results_file = resultsf self.connect("destroy", gtk.main_quit) self.set_size_request(800, 600) self.set_position(gtk.WIN_POS_CENTER) self.vbox = gtk.VBox() self.time = gtk.SpinButton() self.time.set_numeric(True) self.time.set_increments(1,1) self.time.connect('value-changed', self.on_time_changed) self.vbox.pack_start(self.time,False) self.hbox = gtk.HPaned() self.add(self.hbox) self.graph = xdot.DotWidget() self.hbox.pack1(self.graph,True) self.set_focus(self.graph) self.graph.connect('clicked', self.on_url_clicked) self.current_node = None self.hbox.pack2(self.vbox,True) self.hbox.set_position(400) self.init_chart() if auto_update==1: import gobject gobject.timeout_add(500, self.update) self.update() else: self.reload() self.show_all() def reload(self): modelins = get_in_stream(self.model_file or "model.json") parameterins = get_in_stream(self.param_file or "prm.json") resultins = get_in_stream(self.results_file or "res.json") self.model = json.load(modelins) self.params = json.load(parameterins) self.results = json.load(resultins) modelins.close() parameterins.close() resultins.close() self.set_title(self.model["properties"]["name"]) self.time.set_range(0,len(self.results)-1) dotcode = generate_dot(self.model) self.graph.set_dotcode(dotcode, filename='<stdin>') self.draw_chart() dotcode = generate_dot(self.model) self.graph.set_dotcode(dotcode, filename='<stdin>') self.graph.zoom_to_fit() def update(self): import os import subprocess if not hasattr(self, "last_mtime"): self.last_mtime = None current_mtime = os.stat(self.model_file).st_mtime if current_mtime != self.last_mtime: self.last_mtime = current_mtime subprocess.call("make") self.reload() return True def init_chart(self): self.figure = Figure(figsize=(6,4), dpi=72) self.axis = self.figure.add_subplot(111) self.axis.grid(True) self.canvas = FigureCanvasGTK(self.figure) # a gtk.DrawingArea self.canvas.show() self.vbox.pack_start(self.canvas, True, True) def on_url_clicked(self, widget, url, event): self.current_node = url self.draw_chart() return True def on_time_changed(self,widget): self.draw_chart() def draw_chart(self): self.axis.clear() if not (self.current_node is None): setup_node_states(self.model["nodes"]) for n in self.model["nodes"]: if n["id"] == self.current_node: node = n break left = map(lambda x: x[0],n["states"]) width = map(lambda x: x[1] - x[0],n["states"]) self.axis.set_xlabel('') self.axis.set_ylabel('Probability') self.axis.grid(True) self.axis.set_title("{1} ({0})".format(self.current_node,n["label"])) values = self.results[int(self.time.get_value())][self.current_node] self.axis.bar(left, values, width, color='b') self.axis.set_xlim(left[0],left[-1]+width[-1]) # self.axis.set_ylim(0,1) self.canvas.draw()
class GuiTourneyGraphViewer: def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes" : True, "Sites" : True, "Games" : False, "Currencies" : True, "Limits" : False, "LimitSep" : False, "LimitType" : False, "Type" : False, "UseType" : 'tour', "Seats" : False, "SeatSep" : False, "Dates" : True, "GraphOpsTour" : True, "Groups" : False, "Button1" : True, "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() #update the graph at entry (simulate a "Refresh Graph" click) gobject.GObject.emit (self.filters.Button1, "clicked"); def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() currencies = self.filters.getCurrencies() # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, datesXAbs) = self.getData(playerids, sitenos, currencies) print _("Graph generated in: %s") %(time() - starttime) currencyLabel = ','.join(['%s' % key for key in currencies.keys()]) #Set axis labels and grid overlay properites self.ax.set_ylabel(currencyLabel, fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) self.ax.plot(green, color='green', label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + '(' + currencyLabel + '): %.2f' % green[-1]) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user else: self.ax.set_title(_("Tournament Results")) useDates = True #nothing to draw if (len(green) == 0): return #Get the dates of tourneys #if first tourney has no date, get the most ancient date and assume it's his one if datesXAbs[0] is None: i = 1 while i < len(datesXAbs) and type(datesXAbs[i]) is None: i = i+1 if i == len(datesXAbs): print "Wow wow wow : no dates in your whole tourneys" useDates = False else: datesXAbs[0] = datesXAbs[i] #now convert date to dateTime format if useDates: for i in range(0, len(datesXAbs)): if datesXAbs[i] is None: datesXAbs[i] = datesXAbs[i-1] else: datesXAbs[i] = datetime.datetime.strptime(datesXAbs[i], "%Y-%m-%d %H:%M:%S") datesXAbs[i] = datesXAbs[i].strftime('%d/%m') mycolor='red' if green[0]>0: mycolor='green' self.ax.plot([0,1], [0,green[0]], color=mycolor, label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + '(' + currencyLabel + '): %.2f' % green[-1]) for i in range(1, len(green)): final=green[i]-green[i-1] mycolor='red' if (green[i]>0): mycolor='green' self.ax.plot([i,i+1], [green[i-1],green[i]], color=mycolor) if (i % (len(green)/5) == 0): gain="" if (green[i]==0): gain="=" else: if (green[i]>0): gain="+" gain += str(green[i]) self.ax.annotate(gain, xy=(i, 0), color=mycolor, xycoords=('data', 'axes fraction'), xytext=(0, 18), textcoords='offset points', va='top', ha='left') if useDates: self.ax.annotate(datesXAbs[i], xy=(i, 0), xycoords=('data', 'axes fraction'), xytext=(0, -18), textcoords='offset points', va='top', ha='left') #~self.ax.axhline(0, color='black', lw=2) legend = self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) #end of def showClicked def getData(self, names, sites, currencies): tmp = self.sql.query['tourneyGraph'] # print "DEBUG: getData. :" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) currencytest = str(tuple()) #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) currencytest = str(tuple(currencies)) currencytest = currencytest.replace(",)",")") currencytest = currencytest.replace("u'","'") currencytest = "AND tt.currency in %s" % currencytest tmp = tmp.replace("<currency_test>", currencytest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace(",)", ")") # print "DEBUG: sql query:", tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None) green = map(lambda x:float(x[1]), winnings) datesXAbs = map(lambda x:x[8], winnings) #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) #blueline = cumsum(blue) #redline = cumsum(red) return (greenline/100, datesXAbs) def exportGraph (self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog(title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OK,gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename(self.exportFile) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename() + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class ManipulateGUI(object): """woot""" def __init__(self,parameterDict, liveUpdate=False): # 'public' vars self.parameterDict = parameterDict self.liveUpdate = liveUpdate self.nParams = len(parameterDict) #Set the Glade file and build from it self.gladefile = findFileInPythonPath('manipulateGUI.glade') self.builder = gtk.Builder() self.builder.add_from_file(self.gladefile) #Grab some key gui handles self.window = self.builder.get_object('manipulateWindow') self.window.connect('destroy', gtk.main_quit) self.paramBox = self.builder.get_object("paramBox") # loop over the parameterDict and... # ... label for every fixed parameter # ... slider for every variable # ... dropdown for non-fixed parameter self.widgetList = [] self.adjustmentList = [] self.comboboxList = [] for arg, params in self.parameterDict.iteritems(): hbox = gtk.HBox(False, 0) label = gtk.Label(arg+':') label.show() hbox.pack_start(label, True, True) if params['type'] == 'fixed': #add a new GTKlabel self.widgetList.append(gtk.Label(params['value'])) elif params['type'] == 'range': # a horizatonal scale bar # self.adjustmentList.append(gtk.Adjustment(value=params['value'], lower=params['lower'], upper=params['upper'], step_incr=params['step'], page_incr=params['page_inc'],page_size=params['page_size'])) self.adjustmentList.append(gtk.Adjustment(value=params['value'], lower=params['lower'], upper=params['upper'], step_incr=params['step'])) self.widgetList.append(gtk.HScale(adjustment=self.adjustmentList[-1])) self.widgetList[-1].connect('change-value', self.on_param_change_range_value) elif params['type'] == 'list': # build combobox from list self.widgetList.append(gtk.combo_box_new_text()) for item in params['options']: self.widgetList[-1].append_text(item) self.widgetList[-1].set_active(0) self.widgetList[-1].connect('changed', self.on_param_change_list_value) self.widgetList[-1].argName = arg hbox.pack_start(child=self.widgetList[-1], expand=True, fill=True) self.paramBox.pack_start(child=hbox, expand=True, fill=True) # self.paramBox.pack_start(child=self.widgetList[-1], expand=True, fill=True) # setup matplotlib in the matplotlibWindow self.figure = Figure(figsize=(10,4), dpi=72) self.inputAxis = self.figure.add_subplot(1,2,1) self.outputAxis = self.figure.add_subplot(1,2,2) self.canvas = FigureCanvasGTK(self.figure) # a gtk.DrawingArea self.canvas.show() self.canvas.draw() self.plotView = self.builder.get_object("mplBox") self.plotView.pack_start(self.canvas, True, True) # connect and init data self.builder.connect_signals(self) self.window.show_all() # button and checkbox callbacks def on_return_clicked(self, widget): # overload in subclass pass def on_refresh_clicked(self, widget): self.eval_function() def on_param_change_range_value(self, widget, event, paramValue): self.parameterDict[widget.argName]['value'] = paramValue if self.liveUpdate: self.eval_function() def on_param_change_list_value(self, widget): self.parameterDict[widget.argName]['value'] = widget.get_active() if self.liveUpdate: self.eval_function() def eval_function(self): # overload in subclass # should use self.parameterDict somehow... (particular for each subclass) # self.function(arg1 = self.parameterDict['arg1']['value'] , ... ) pass
class Backend( backend.Backend ): def init(self): # line_cache: key = id(Curve), value=mpl line object self.line_cache = dict() self.layer_to_axes = dict() self.axes_to_layer = dict() self.layers_cache = list() # copy of self.plot.layers def connect(self): logger.debug("Opening matplotlib session.") self.figure = Figure(dpi=100, facecolor="white") # figsize=(5,4), dpi=100) self.canvas = FigureCanvas(self.figure) self.canvas.show() self.line_cache.clear() self.layer_to_axes.clear() self.axes_to_layer.clear() backend.Backend.connect(self) logger.debug("Init finished") def disconnect(self): logger.debug("Closing matplotlib session.") if not self.canvas is None: self.canvas.destroy() self.canvas = None if not self.figure is None: self.figure = None backend.Backend.disconnect(self) #---------------------------------------------------------------------- def arrange(self, rows=1, cols=1): layers = self.plot.layers n = len(layers) if n > (rows*cols): rows = int((rows*cols) / n) + 1 cols = rows * n #raise ValueError("Not enough rows and cols for all layers!") self.figure.clear() self.figure.axes = [] self.layer_to_axes.clear() self.axes_to_layer.clear() self.layers_cache = list() j = 1 for layer in layers: print "Setting up layer", layer axes = self.figure.add_subplot("%d%d%d" % (rows,cols,j)) self.layer_to_axes[layer] = axes self.axes_to_layer[axes] = layer self.layers_cache.append(layer) j += 1 def draw_layer(self, layer, group_info): ax = self.layer_to_axes[layer] print "DRAWING AXES ", ax ax.lines = [] line_cache = [] line_count = 0 last_cx = -1 # Default values come in two flavors: # group-wise and single default values group_colors = uwrap.get(layer, 'group_colors') group_styles = uwrap.get(layer, 'group_styles') group_markers = uwrap.get(layer, 'group_markers') #default_color = 'r' default_color = None default_style = 'solid' default_marker = 'None' #:layer.visible if uwrap.get(layer, 'visible') is False: return #:layer.title title = uwrap.get(layer, 'title', None) if title is not None: ax.set_title(title) #:layer.grid grid = uwrap.get(layer, 'grid') ax.grid(grid) #:layer.lines for line in layer.lines: data_to_plot = [] #:line.visible if uwrap.get(line, 'visible') is False: if line in ax.lines: ax.lines.remove(line) continue ds = self.get_line_source(line) table = self.get_table(ds) cx, cy = self.get_column_indices(line) xdata, ydata = self.get_table_data(table, cx, cy) #:line.row_first #:line.row_last def limit_data(data, start, end): try: return data[start:end] except IndexError: backend.BackendError("Index range '%s'out of bounds!" % (start,end) ) start, end = line.row_first, line.row_last xdata = limit_data(xdata, start, end) ydata = limit_data(ydata, start, end) #:line.style global linestyle_mappings default = default_style or group_styles[line_count % len(group_styles)] style = uwrap.get(line, 'style', default) style = linestyle_mappings[style] #:line.marker global linemarker_mappings default = default_marker or group_markers[line_count % len(group_markers)] marker = uwrap.get(line, 'marker', default) marker = linemarker_mappings[marker] #:line.width width = uwrap.get(line, 'width') #:line.color default = default_color or group_colors[line_count % len(group_colors)] color = uwrap.get(line, 'color', default) #--- PLOT LINE --- l, = ax.plot( xdata, ydata, linewidth=width, linestyle=style, marker=marker, color=color) line_cache.append(l) label = self.get_line_label(line, table=table, cy=cy) l.set_label(label) line_count += 1 # # # # additional lines # # # p = len(xdata) # if p > 2: p = p/2 # atpoint = xdata[max(p-1,0)] # print "Printing vertical line at ", atpoint # ax.axvline(atpoint) #:layer.legend legend = uwrap.get(layer, 'legend') if legend is not None and line_count > 0: visible = uwrap.get(legend, 'visible') if visible is True: #:legend.label:TODO label = uwrap.get(legend, 'visible') if label is not None: pass #:legend.border:OK # (see below but keep it here!) border = uwrap.get(legend, 'border') #:legend.position TODO position = uwrap.get(legend, 'position', 'best') if position == 'at position': position = (uwrap.get(legend, 'x'), uwrap.get(legend, 'y')) # create legend entries from line labels labels = [l.get_label() for l in line_cache] legend = ax.legend(line_cache, labels, loc=position) legend.draw_frame(border) else: ax.legend_ = None else: ax.legend_ = None #:layer.axes for (key, axis) in layer.axes.iteritems(): #:axis.label #:axis.scale #:axis.start #:axis.end label = uwrap.get(axis, 'label') scale = uwrap.get(axis, 'scale') start = uwrap.get(axis, 'start') end = uwrap.get(axis, 'end') print "START = %s, END = %s" % (str(start), str(end)) if key == 'x': set_label = ax.set_xlabel set_scale = ax.set_xscale set_start = (lambda l: ax.set_xlim(xmin=l)) set_end = (lambda l: ax.set_xlim(xmax=l)) elif key == 'y': set_label = ax.set_ylabel set_scale = ax.set_yscale set_start = (lambda l: ax.set_ylim(ymin=l)) set_end = (lambda l: ax.set_ylim(ymax=l)) else: raise RuntimeError("Invalid axis key '%s'" % key) if label is not None: set_label(label) if scale is not None: set_scale(scale) if start is not None: set_start(start) if end is not None: set_end(end) def draw(self): logger.debug("Matplotlib: draw()") self.check_connection() # plot all curves together in one plot legend_list = [] # for legend later on curve_count = 0 # if self.plot.layers != self.layers_cache: self.arrange() for layer in self.plot.layers: group_info = {} self.draw_layer(layer, group_info) self.canvas.draw()
class GuiGraphViewer (threading.Thread): def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes" : True, "Sites" : True, "Games" : True, "Limits" : True, "LimitSep" : True, "LimitType" : True, "Type" : False, "UseType" : 'ring', "Seats" : False, "SeatSep" : False, "Dates" : True, "Groups" : False, "Button1" : True, "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("Refresh _Graph") self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name("_Export to File") self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("***Error: ")+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) raise def generateGraph(self, widget, data): try: self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() games = self.filters.getGames() for i in ('show', 'none'): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return if not limits: print _("No limits found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, blue, red) = self.getRingProfitGraph(playerids, sitenos, limits, games) print _("Graph generated in: %s") %(time() - starttime) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Hands"), fontsize = 12) self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) self.ax.plot(green, color='green', label=_('Hands: %d\nProfit: $%.2f') %(len(green), green[-1])) self.ax.plot(blue, color='blue', label=_('Showdown: $%.2f') %(blue[-1])) self.ax.plot(red, color='red', label=_('Non-showdown: $%.2f') %(red[-1])) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user #print "No hands returned by graph query" else: self.ax.set_title(_("Profit graph for ring games")) #text = "Profit: $%.2f\nTotal Hands: %d" %(green[-1], len(green)) #self.ax.annotate(text, # xy=(10, -10), # xycoords='axes points', # horizontalalignment='left', verticalalignment='top', # fontsize=10) #Draw plot self.ax.plot(green, color='green', label=_('Hands: %d\nProfit: $%.2f') %(len(green), green[-1])) self.ax.plot(blue, color='blue', label=_('Showdown: $%.2f') %(blue[-1])) self.ax.plot(red, color='red', label=_('Non-showdown: $%.2f') %(red[-1])) if sys.version[0:3] == '2.5': self.ax.legend(loc='upper left', shadow=True, prop=FontProperties(size='smaller')) else: self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("***Error: ")+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) #end of def showClicked def getRingProfitGraph(self, names, sites, limits, games): tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] # print "DEBUG: getRingProfitGraph" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #nametest = nametest.replace("L", "") q = [] for m in self.filters.display.items(): if m[0] == 'Games' and m[1]: for n in games: if games[n]: q.append(n) if len(q) > 0: gametest = str(tuple(q)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)",")") gametest = gametest.replace("u'","'") gametest = "and gt.category in %s" % gametest else: gametest = "and gt.category IS NULL" tmp = tmp.replace("<game_test>", gametest) lims = [int(x) for x in limits if x.isdigit()] potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl'] nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] limittest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " # and ( (limit and bb in()) or (nolimit and bb in ()) ) if lims: blindtest = str(tuple(lims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'pl' and gt.bigBlind in " if potlims: blindtest = str(tuple(potlims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'nl' and gt.bigBlind in " if nolims: blindtest = str(tuple(nolims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) )' else: limittest = limittest + '(-1) ) )' if type == 'ring': limittest = limittest + " and gt.type = 'ring' " elif type == 'tour': limittest = limittest + " and gt.type = 'tour' " #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace("<limit_test>", limittest) tmp = tmp.replace(",)", ")") #print "DEBUG: sql query:" #print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None, None) green = map(lambda x:float(x[1]), winnings) blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) blueline = cumsum(blue) redline = cumsum(red) return (greenline/100, blueline/100, redline/100) #end of def getRingProfitGraph def exportGraph (self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog(title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OK,gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename(self.exportFile) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename() + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class Main: def delete(self, widget, event, data=None): return False def destroy(self, widget, data=None): self.thread.abort() gtk.main_quit() def update(self): if (self.queue.empty()): return True data = self.queue.get() self.lines1[0].set_data(data.time,data.setpoint) self.lines1[1].set_data(data.time,data.temperature) self.lines2[0].set_data(data.time,data.power) self.x1.relim() self.x2.relim() self.x1.autoscale_view() self.x2.autoscale_view() self.canvas.draw() return True def save_settings(self,widget,port): self.thread.wait(1) port.write("save\r") port.readline() self.thread.wait(0) return def set_settings(self,widget,port,P,I,D,T): self.thread.wait(1) port.write("set KP " + str(P.get_value_as_int()) + "\r") port.readline() port.write("set KI " + str(I.get_value_as_int()) + "\r") port.readline() port.write("set KD " + str(D.get_value_as_int()) + "\r") port.readline() port.write("set KT " + str(T.get_value_as_int()) + "\r") port.readline() self.thread.wait(0) return def get_settings(self,widget,port,P,I,D,T): self.thread.wait(1) port.write("eeprom\r") port.readline() words = port.readline().split() P.set_value( int(words[1]) ) words = port.readline().split() I.set_value( int(words[1]) ) words = port.readline().split() D.set_value( int(words[1]) ) words = port.readline().split() T.set_value( int(words[1]) ) port.readline() self.thread.wait(0) return def __init__(self): try: port = serial.Serial(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0.1) except serial.SerialException: sys.stderr.write("could not open port\n") sys.exit(1) port.write("log 1\r") port.readline() self.queue = Queue.Queue(1) self.thread = UpdateData(port,self.queue) self.thread.start() self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_default_size(800,600) self.window.set_title("PID logger") self.window.connect("delete_event",self.delete) self.window.connect("destroy",self.destroy) ## matlibplot self.figure = Figure(figsize=(5,4),dpi=100) self.x1 = self.figure.add_subplot(111) self.x2 = self.figure.add_subplot(111,sharex=self.x1,frameon=False) self.x1.set_xlabel('time') self.x1.set_ylabel('temperature') self.x1.set_title('temperature profile') self.x1.grid(True) self.x1.yaxis.tick_left() self.x2.yaxis.tick_right() self.canvas = FigureCanvasGTK(self.figure) self.canvas.draw() self.canvas.show() vbox = gtk.VBox(homogeneous=False, spacing=0) hbox0 = gtk.HBox() self.window.add(vbox) vbox.pack_start(self.canvas,expand=True,fill=True,padding=0) separator0 = gtk.HSeparator() vbox.pack_start(separator0, expand=False, fill=False, padding=10) #PID settings PAdjustment = gtk.Adjustment(value=0,lower=0,upper=32,step_incr=1,page_incr=1,page_size=0) IAdjustment = gtk.Adjustment(value=0,lower=0,upper=32,step_incr=1,page_incr=1,page_size=0) DAdjustment = gtk.Adjustment(value=0,lower=0,upper=32,step_incr=1,page_incr=1,page_size=0) TAdjustment = gtk.Adjustment(value=0,lower=0,upper=255,step_incr=1,page_incr=10,page_size=0) Plabel = gtk.Label("P") Ilabel = gtk.Label("I") Dlabel = gtk.Label("D") Tlabel = gtk.Label("PID threshold") Pspinbutton = gtk.SpinButton(adjustment=PAdjustment,digits=0) Ptooltips = gtk.Tooltips() Ptooltips.set_tip(Pspinbutton,"Proportional term of the PID function",tip_private=None) Ispinbutton = gtk.SpinButton(adjustment=IAdjustment,digits=0) Itooltips = gtk.Tooltips() Itooltips.set_tip(Ispinbutton,"Integral term of the PID function (in units of 1/100)",tip_private=None) Dspinbutton = gtk.SpinButton(adjustment=DAdjustment,digits=0) Dtooltips = gtk.Tooltips() Dtooltips.set_tip(Dspinbutton,"Derivative term of the PID function",tip_private=None) Tspinbutton = gtk.SpinButton(adjustment=TAdjustment,digits=0) Ttooltips = gtk.Tooltips() Ttooltips.set_tip(Tspinbutton,"Error threshold between PD and PID behaviour of the controller",tip_private=None) Pvbox = gtk.VBox() Pvbox.pack_start(Plabel) Pvbox.pack_start(Pspinbutton) Ivbox = gtk.VBox() Ivbox.pack_start(Ilabel) Ivbox.pack_start(Ispinbutton) Dvbox = gtk.VBox() Dvbox.pack_start(Dlabel) Dvbox.pack_start(Dspinbutton) Tvbox = gtk.VBox() Tvbox.pack_start(Tlabel) Tvbox.pack_start(Tspinbutton) hbox0.pack_start(Pvbox,expand=True,fill=False) hbox0.pack_start(Ivbox,expand=True,fill=False) hbox0.pack_start(Dvbox,expand=True,fill=False) hbox0.pack_start(Tvbox,expand=True,fill=False) vbox.pack_start(hbox0,expand=False,fill=False,padding=0) hbox1 = gtk.HBox() Setbutton = gtk.Button(label="Set settings",stock=None) Setbutton.connect("clicked",self.set_settings,port,Pspinbutton,Ispinbutton,Dspinbutton,Tspinbutton) Settooltips = gtk.Tooltips() Settooltips.set_tip(Setbutton,"Apply settings to the PID controller",tip_private=None) self.get_settings(self,port,Pspinbutton,Ispinbutton,Dspinbutton,Tspinbutton) Savebutton = gtk.Button(label="Save settings",stock=None) Savetooltips = gtk.Tooltips() Savetooltips.set_tip(Savebutton,"Store current settings in the eeprom of the controller",tip_private=None) Savebutton.connect("clicked",self.save_settings,port) Getbutton = gtk.Button(label="Get settings",stock=None) Gettooltips = gtk.Tooltips() Gettooltips.set_tip(Getbutton,"Get PID settings from eeprom of the controller",tip_private=None) Getbutton.connect("clicked",self.get_settings,port,PAdjustment,IAdjustment,DAdjustment,TAdjustment) hbox1.pack_start(Getbutton,expand=True,fill=False,padding=10) hbox1.pack_start(Setbutton,expand=True,fill=False,padding=10) hbox1.pack_start(Savebutton,expand=True,fill=False,padding=10) separator1 = gtk.HSeparator() vbox.pack_start(separator1,expand=False,fill=False,padding=10) vbox.pack_start(hbox1,expand=False,fill=False,padding=0) self.lines1 = self.x1.plot([0,0],[0,0],'r-',[0,0],[0,0],'b-') self.lines2 = self.x2.plot([0,0],[0,0],'g-') self.window.show_all() def main(self): source_id = gobject.idle_add(self.update) gtk.main()
class AnalyzerGTK: def __init__(self): # Set the Glade file self.gladefile = "analyzer.glade" self.wTree = gtk.glade.XML(self.gladefile) # Get the Main Window and connect the "destroy" event self.window = self.wTree.get_widget("MainWindow") if (self.window): self.window.connect("destroy", gtk.main_quit) # Hook up the signals dic = { "on_menuOpen_activate": self.menuOpen_activate, "on_menuQuit_activate": self.menuQuit_activate, "on_menuAbout_activate": self.menuAbout_activate, "on_MainWindow_destroy": gtk.main_quit, "on_NetworkList_row_activated": self.plotRows, "on_btnPlotAnalysis_clicked": self.plotAnalysis, "on_checkShowTemplate_clicked": self.showTemplate, "on_btnClearFilter_clicked": self.clearFilter, "on_tbClear_clicked": self.clearPlot } self.wTree.signal_autoconnect(dic) # Create a LogFile object to handle data self.logfile = LogFile() # Create an empty plot window for tuning curves self.figureDT = Figure(figsize=(6,4), dpi=72) self.axisDT = self.figureDT.add_subplot(111) self.axisDT.set_xlabel('Duration') self.axisDT.set_ylabel('Mean Number of Spikes') self.axisDT.grid(True) self.canvasDT = FigureCanvasGTK(self.figureDT) # a gtk.DrawingArea self.canvasDT.show() self.graphviewDT = self.wTree.get_widget("vboxTuning") self.graphviewDT.pack_end(self.canvasDT) self.maxSpikes = 1 self.showTemplate = False # Create an empty plot window for analysis self.figureAN = Figure(dpi=72) self.axisAN = self.figureAN.add_subplot(111) self.canvasAN = FigureCanvasGTK(self.figureAN) self.canvasAN.show() self.graphviewAN = self.wTree.get_widget("vboxAnalysis") self.graphviewAN.pack_end(self.canvasAN) # Setup the analyze window self.cbXAxis = gtk.combo_box_new_text() self.cbYAxis = gtk.combo_box_new_text() self.cbZAxis = gtk.combo_box_new_text() self.cbXAxis.show() self.cbYAxis.show() self.cbZAxis.show() self.hboxAnalyze = self.wTree.get_widget("hboxAnalyze") labelX = gtk.Label("X-Axis") labelX.show() labelY = gtk.Label("Y-Axis") labelY.show() labelZ = gtk.Label("Z-Axis") labelZ.show() self.hboxAnalyze.pack_start(labelX) self.hboxAnalyze.pack_start(self.cbXAxis) self.hboxAnalyze.pack_start(labelY) self.hboxAnalyze.pack_start(self.cbYAxis) self.hboxAnalyze.pack_start(labelZ) self.hboxAnalyze.pack_start(self.cbZAxis) def plotAnalysis(self, widget): Xvar = self.cbXAxis.get_active_text() Yvar = self.cbYAxis.get_active_text() Zvar = self.cbZAxis.get_active_text() if Xvar == None or Yvar == None or Zvar == None: return if Zvar == "None": XvarIndex = self.logfile.params().index(Xvar)+1 YvarIndex = self.logfile.params().index(Yvar)+1 rowiter = self.treemodelsorted.get_iter_first() values = defaultdict(list) while rowiter != None: X = self.treemodelsorted.get_value(rowiter,XvarIndex) Y = self.treemodelsorted.get_value(rowiter,YvarIndex) values[float(X)].append(float(Y)) rowiter = self.treemodelsorted.iter_next(rowiter) X = [] Y = [] for k in sorted(values.keys()): X.append(k) Y.append(mean(values[k])) self.axisAN.cla() self.figureAN.clf() self.axisAN = self.figureAN.add_subplot(111) self.axisAN.plot(X,Y, 'k', linewidth=4) self.axisAN.set_xlabel(Xvar) self.axisAN.set_ylabel(Yvar) self.canvasAN.draw() else: XvarIndex = self.logfile.params().index(Xvar)+1 YvarIndex = self.logfile.params().index(Yvar)+1 ZvarIndex = self.logfile.params().index(Zvar)+1 rowiter = self.treemodelsorted.get_iter_first() values = {} Ykeys = [] while rowiter != None: X = self.treemodelsorted.get_value(rowiter,XvarIndex) Y = self.treemodelsorted.get_value(rowiter,YvarIndex) Z = self.treemodelsorted.get_value(rowiter,ZvarIndex) Ykeys.append(Y) values.setdefault(X,defaultdict(list))[Y].append(Z) rowiter = self.treemodelsorted.iter_next(rowiter) Ykeys = unique(Ykeys) XY = [] for k in sorted(values.keys()): tmp = [] for k2 in sorted(Ykeys): if values[k].has_key(k2): tmp.append(mean(values[k][k2])) else: tmp.append(0) XY.append(tmp) Z = array(XY) self.axisAN.cla() self.figureAN.clf() self.axisAN = self.figureAN.add_subplot(111) im = NonUniformImage(self.axisAN, interpolation='nearest', extent=(min(values.keys()),max(values.keys()),min(Ykeys),max(Ykeys))) im.set_data(values.keys(), Ykeys, Z.transpose()) self.axisAN.images.append(im) self.axisAN.set_xlim(min(values.keys()),max(values.keys())) self.axisAN.set_ylim(min(Ykeys),max(Ykeys)) self.axisAN.set_xlabel(Xvar) self.axisAN.set_ylabel(Yvar) self.axisAN.set_title(Zvar) self.figureAN.colorbar(im) self.canvasAN.draw() def showTemplate(self, widget): self.showTemplate = widget.get_active() def plotRows(self, widget, path, column): (model, pathlist) = self.treeview.get_selection().get_selected_rows() for p in pathlist: treeiter = self.treemodelsorted.get_iter(p) X = self.logfile.getdurs() Y = self.logfile.getresults(self.treemodelsorted.get_value(treeiter,0)) tmp = Y[:] tmp.append(self.maxSpikes) self.maxSpikes = max(tmp) self.axisDT.plot(X, Y, linewidth=2) self.axisDT.set_ylim( (0, self.maxSpikes+0.1) ) if self.showTemplate: Y = self.logfile.getresults(0) tmp = Y[:] tmp.append(self.maxSpikes) self.maxSpikes = max(tmp) self.axisDT.plot(X, Y, 'k', linewidth=3) self.axisDT.set_ylim( (0, self.maxSpikes+0.1) ) self.canvasDT.draw() def clearPlot(self, widget): self.maxSpikes = 1 self.axisDT.cla() self.axisDT.set_xlabel('Duration') self.axisDT.set_ylabel('Mean Number of Spikes') self.axisDT.grid(True) self.canvasDT.draw() def clearFilter(self, widget): for i in range(len(self.filtercboxes)): self.filtercboxes[i].set_active(0) def applyFilter(self,model,iter): show = True for i in range(len(self.filtercboxes)): f = self.filtercboxes[i].get_active_text() show = show and (f == "All" or f == str(self.liststore.get_value(iter,i+1))) return show def updateFilter(self, widget): self.treemodelfilter = self.liststore.filter_new(root=None) self.treemodelfilter.set_visible_func(self.applyFilter) self.treemodelsorted = gtk.TreeModelSort(self.treemodelfilter) self.treeview.set_model(self.treemodelsorted) def menuOpen_activate(self, widget): chooser = gtk.FileChooserDialog(title="Open Log File", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) response = chooser.run() if response == gtk.RESPONSE_OK: filename = chooser.get_filename() if self.logfile.open(filename): # Data file opened # Populate the ListStore for our table self.liststore = gtk.ListStore(int, *([float] *len(self.logfile.params()))) self.treeview = self.wTree.get_widget("NetworkList") self.treeview.set_model(self.liststore) self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) # Remove any old columns for c in self.treeview.get_columns(): self.treeview.remove_column(c) self.cbXAxis.get_model().clear() self.cbYAxis.get_model().clear() self.cbZAxis.get_model().clear() self.cbZAxis.append_text("None") # Add columns to filter table and fill analysis dropdowns self.filtertable = self.wTree.get_widget("FilterTable") self.filtertable.resize(len(self.logfile.params()), 2) textrenderer = gtk.CellRendererText() col = 1 self.filtercboxes = [] for p in self.logfile.params(): column = gtk.TreeViewColumn(p, textrenderer, text=col) column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) column.set_resizable(True) column.set_clickable(True) column.set_sort_column_id(col) self.treeview.append_column(column) label = gtk.Label(p) label.set_justify(gtk.JUSTIFY_RIGHT) label.show() cbox = gtk.combo_box_new_text() self.filtercboxes.append(cbox) self.filtercboxes[-1].show() self.filtercboxes[-1].connect("changed", self.updateFilter) self.filtertable.attach(label, 0, 1, col-1, col) self.filtertable.attach(cbox, 1, 2, col-1, col) self.cbXAxis.append_text(p) self.cbYAxis.append_text(p) self.cbZAxis.append_text(p) col = col + 1 # Add data to table for n in self.logfile.networkdefs(): itt = self.liststore.append(n) # Add data to columns tmp = [] for i in range(len(self.logfile.params())): tmp.append([]) for n in self.logfile.networkdefs(): for i in range(1,len(self.logfile.params())+1): tmp[i-1].append(n[i]) for i in range(len(tmp)): tmp[i] = unique(tmp[i]) tmp[i].sort() self.filtercboxes[i].append_text("All") self.filtercboxes[i].set_active(0) for n in tmp[i]: self.filtercboxes[i].append_text(str(n)) # Setup the filtered sorted liststores self.treemodelfilter = self.liststore.filter_new(root=None) self.treemodelfilter.set_visible_func(self.applyFilter) self.treemodelsorted = gtk.TreeModelSort(self.treemodelfilter) self.treeview.set_model(self.treemodelsorted) # Destroy the file chooser chooser.destroy() def menuAbout_activate(self, widget): aboutDialog = self.wTree.get_widget("AboutDialog") response = aboutDialog.run() if response == gtk.RESPONSE_CANCEL: aboutDialog.hide() def menuQuit_activate(self, widget): gtk.main_quit()
class Graphtemp(object): def __init__(self,tipo): self.cuento = 0 self.magico = 800 self.app = None self.tipo = tipo self.data = np.array([]) self.fechas = np.array([]) self.caldera = np.array([]) self.dpi = 100 self.f = Figure((3.0, 3.0), dpi=self.dpi) self.f.subplots_adjust(hspace=1, left=.05, right = .99, top=.92 ) self.a = self.f.add_subplot(111) self.a.set_axis_bgcolor('white') setp(self.a.get_xticklabels(), fontsize=8) setp(self.a.get_yticklabels(), fontsize=8) self.plot_data= self.a.plot( self.data, linewidth= 1.4, color= tipo.color, )[0] self.plot_data2 = self.a.plot( np.array([]), linestyle= "--", linewidth= 1.4, color= "g", )[0] self.plot_data3 = self.a.plot( np.array([]), linestyle= "--", linewidth= 1.4, color= "r", )[0] self.a.grid(True) # self.a.set_xlabel('Time') self.a.set_ylabel(tipo.formal) self.canvas = FigureCanvas(self.f) @inlineCallbacks def on_redraw_timer(self): from twisted.internet import reactor self.tiempo = time.time() if not self.app.is_stoped: # if paused do not add data, but still redraw the plot # (to respond to scale modifications, grid change, etc.) # #print "They have called me!" numero = self.tipo.actual if numero: self.data = np.append (self.data, float(numero))# .append(float()) acá le agrega le dato self.fechas = np.append(self.fechas, self.cuento) self.caldera = np.append(self.caldera, 40 if self.app.caldera.actual else 20) self.cuento += 1 if self.fechas.size > self.magico: self.data = np.delete(self.data, 0) self.fechas = np.delete(self.fechas, 0) self.caldera = np.delete(self.caldera, 0) xmax = self.fechas.max() if self.fechas.max() > self.magico else self.magico xmin = xmax - self.magico #ymin = round(self.data.min(), 0) - 1 ymin = -.5 ymax = 42#round(self.data.max(), 0) + 1 self.a.set_xbound(lower=xmin, upper=xmax) self.a.set_ybound(lower=ymin, upper=ymax) self.plot_data2.set_xdata(np.array([0, xmax])) yield self.plot_data2.set_ydata(np.array([self.tipo.medio]*2)) #self.fechas = np.append(self.fechas, datetime()) yield setp(self.a.get_xticklabels(), visible=True) yield #print np.arange(self.data.size) == self.fechas self.plot_data.set_xdata(self.fechas) self.plot_data.set_ydata(self.data) yield self.plot_data3.set_ydata(self.caldera) self.plot_data3.set_xdata(self.fechas) yield d = Deferred() reactor.callLater(.1, d.callback, None) yield d self.canvas.draw() yield else: d = Deferred() reactor.callLater(.1, d.callback, None) yield d reactor.callLater(time.time() - self.tiempo -.1, self.on_redraw_timer)
class Backend( backend.Backend ): def init(self): self.layer_to_axes = {} self.axes_to_layer = {} self.layers_cache = [] # copy of self.plot.layers self.layer_signals = {} self.line_caches = {} self.omaps = {} def connect(self): logger.debug("Opening matplotlib session.") self.figure = Figure(dpi=100, facecolor="white") # figsize=(5,4), dpi=100) self.canvas = FigureCanvas(self.figure) self.canvas.show() self.line_caches = {} self.layer_to_axes.clear() self.axes_to_layer.clear() backend.Backend.connect(self) logger.debug("Init finished") def set(self, project,plot): backend.Backend.set(self, project, plot) if self.project is not None: # TODO: connect to notify::layers of Plot pass def disconnect(self): logger.debug("Closing matplotlib session.") if not self.canvas is None: self.canvas.destroy() self.canvas = None if not self.figure is None: self.figure = None backend.Backend.disconnect(self) #---------------------------------------------------------------------- def arrange(self, rows=1, cols=1): layers = self.plot.layers n = len(layers) if n > (rows*cols): rows = int((rows*cols) / n) + 1 cols = rows * n #raise ValueError("Not enough rows and cols for all layers!") self.figure.clear() self.figure.axes = [] self.layer_to_axes.clear() self.axes_to_layer.clear() self.layers_cache = [] for signal_list in self.layer_signals.itervalues(): for signal in signal_list: Signals.disconnect(signal) self.layer_signals = {} j = 1 for layer in layers: print "Setting up layer", layer axes = self.figure.add_subplot("%d%d%d" % (rows,cols,j)) self.layer_to_axes[layer] = axes self.axes_to_layer[axes] = layer self.layers_cache.append(layer) print "Connecting to notify of ", layer self.layer_signals[layer] = \ [Signals.connect(layer, 'notify', self.on_update_layer), Signals.connect(layer, 'notify::labels', self.on_update_labels)] j += 1 def draw(self): self.check_connection() logger.debug("Matplotlib: draw()") if self.plot.layers != self.layers_cache: self.arrange() self.omaps = {} for layer in self.plot.layers: self.update_layer(layer) self.draw_canvas() def draw_canvas(self): self.canvas.draw() #---------------------------------------------------------------------- # Layer # def on_update_layer(self, sender, updateinfo={}): # updateinfo is ignored self.update_layer(sender) self.canvas.draw() def update_layer(self, layer, updateinfo={}): # updateinfo is ignored self.omaps[layer] = {} self.line_caches[layer] = {} axes = self.layer_to_axes[layer] axes.lines = [] line_cache = self.line_caches[layer] = [] #:layer.lines:OK for line in layer.lines: self.update_line(line, layer, axes=axes) #:layer.axes for (key, axis) in layer.axes.iteritems(): #:axis.label #:axis.scale #:axis.start #:axis.end label = axis.label scale = axis.scale start = axis.start end = axis.end logger.debug("start = %s; end = %s" % (start, end)) if key == 'x': set_label = axes.set_xlabel set_scale = axes.set_xscale set_start = (lambda l: axes.set_xlim(xmin=l)) set_end = (lambda l: axes.set_xlim(xmax=l)) elif key == 'y': set_label = axes.set_ylabel set_scale = axes.set_yscale set_start = (lambda l: axes.set_ylim(ymin=l)) set_end = (lambda l: axes.set_ylim(ymax=l)) else: raise RuntimeError("Invalid axis key '%s'" % key) if label is not None: set_label(label) if scale is not None: set_scale(scale) if start is not None: set_start(start) if end is not None: set_end(end) #:layer.visible if layer.visible is False: return # TODO #:layer.title title = layer.title if title is not None: axes.set_title(title) #:layer.grid axes.grid(layer.grid) #:layer.legend:OK self.update_legend(layer) #:layer.labels:OK axes.texts = [] for label in layer.labels: self.update_textlabel(label, layer) #---------------------------------------------------------------------- # Line # def update_line(self, line, layer, axes=None, updateinfo={}): # updateinfo is ignored axes = axes or self.layer_to_axes[layer] omap = self.omaps[layer] line_cache = self.line_caches[layer] data_to_plot = [] #:line.visible if line.visible is False: if line in axes.lines: axes.lines.remove(line) line_cache.remove(line) omap[line] = None return ds = self.get_line_source(line) table = self.get_table(ds) cx, cy = self.get_column_indices(line) xdata, ydata = self.get_table_data(table, cx, cy) #:line.row_first #:line.row_last def limit_data(data, start, end): try: return data[start:end] except IndexError: backend.BackendError("Index range '%s'out of bounds!" % (start,end) ) start, end = line.row_first, line.row_last xdata = limit_data(xdata, start, end) ydata = limit_data(ydata, start, end) #:line.style global linestyle_mappings style = linestyle_mappings[line.style] #:line.marker global linemarker_mappings marker = linemarker_mappings[line.marker] #:line.width:OK #:line.color N = len(layer.group_colors) index = layer.lines.index(line) color = line.rget('color', layer.group_colors[index%N]) #--- PLOT LINE --- l, = axes.plot( xdata, ydata, linewidth=line.width, linestyle=style, marker=marker, color=color) line_cache.append(l) omap[line] = l label = self.get_line_label(line, table=table, cy=cy) if label is not None: l.set_label(label) #---------------------------------------------------------------------- # TextLabel # def on_update_labels(self, layer, updateinfo={}): # updateinfo is ignored # clear existing labels and their corresponding mappings axes = self.layer_to_axes[layer] axes.texts = [] for label in layer.labels: try: self.omaps[layer].pop(label) except KeyError: pass # create new labels for label in layer.labels: self.update_textlabel(label, layer) self.canvas.draw() def update_textlabel(self, label, layer, axes=None, updateinfo={}): # updateinfo is ignored axes = axes or self.layer_to_axes[layer] kwargs = self.label_kwargs(axes, label) if kwargs is None: return transform = kwargs.pop('transform') try: mpl_label = self.omaps[layer][label] except KeyError: mpl_label = Text(**kwargs) self.omaps[layer][label] = mpl_label axes.texts.append(mpl_label) else: mpl_label.update(kwargs) mpl_label.set_transform(transform) def label_kwargs(self, axes, label): if label.x is None or label.y is None: logger.info("Label coordinates contains empty value. Skipped.") return None if label.system == 0: transform = axes.transData elif label.system == 1: transform = axes.transAxes elif label.system == 2: transform = self.figure.transFigure elif label.system == 3: transform = matplotlib.transforms.identity_transform() return {'x': label.x, 'y': label.y, 'text' : label.text, 'horizontalalignment': 'center', 'verticalalignment' : 'center', 'transform' : transform} #---------------------------------------------------------------------- # Legend # def update_legend(self, layer, axes=None, updateinfo={}): # updateinfo is ignored axes = axes or self.layer_to_axes[layer] legend = layer.legend if legend is None: if axes.legend_ is not None: axes.legend_ = None self.omaps[layer][legend] = None else: kw = self.legend_kwargs(legend, layer) border = kw.pop('border') visible = kw.pop('visible') handles, labels = kw.pop('handles'), kw.pop('labels') _legend = axes.legend(handles, labels, **kw) self.omaps[layer][legend] = _legend _legend.draw_frame(border) _legend.set_visible(visible) self.omaps[layer][legend] = axes.legend_ def legend_kwargs(self, legend, layer): """ 'border' must be popped and set via legend.draw_frame 'visible' must be popped and set via set_visible """ visible = legend.visible #:legend.label:TODO label = legend.label if label is not None: pass #:legend.border:OK # (see below but keep it here!) border = legend.border #:legend.position TODO position = legend.position if position == 'at position': position = (legend.x, legend.y) # create legend entries from line labels line_cache = self.line_caches[layer] labels = [l.get_label() for l in line_cache] rv = {'handles' : line_cache, 'labels' : labels, 'loc' : position, 'border' : border, 'visible' : visible} print "LEGEND" print rv return rv
class GuiBankrollGraphViewer(threading.Thread): def __init__(self, settings, db, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.settings = settings self.db = db self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) view = None filters_display = { "Heroes": True, "Sites": True, "Games": False, "Limits": False, "LimitSep": False, "LimitType": False, "Type": False, "UseType": 'tour', "Seats": False, "SeatSep": False, "Dates": True, "Groups": False, "Button1": True, "Button2": True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) numeric_const_pattern = r""" [-+]? # optional sign (?: (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc | (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc ) # followed by optional exponent part if desired (?: [Ee] [+-]? \d+ ) ? """ self.rx = re.compile(numeric_const_pattern, re.VERBOSE) self.mainHBox = gtk.HBox(False, 0) self.mainHBox.show() self.leftPanelBox = self.filters.get_vbox() #add a button to modify transferts ButtonTransfert = gtk.Button(_("ButtonTransfert")) ButtonTransfert.set_label(_("_Modify Transferts")) ButtonTransfert.connect("clicked", self.transfertsWindow, "clicked") ButtonTransfert.set_sensitive(True) self.filters.mainVBox.pack_start(ButtonTransfert, False) ButtonTransfert.show() self.hpane = gtk.HPaned() self.hpane.pack1(self.leftPanelBox) self.mainHBox.add(self.hpane) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None #update the graph at entry (simulate a «Refresh Graph» click) gobject.GObject.emit(self.filters.Button1, "clicked") self.db.rollback() #endinit def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, dates, transfersAmount, transfersDate, transferType) = self.getData(playerids, sitenos) print _("Graph generated in: %s") % (time() - starttime) #Set axis labels and grid overlay properites self.ax.set_ylabel("$", fontsize=12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) self.ax.plot(green, color='green', label=_('Bankroll') + _('Profit') + ': $%.2f' % green[-1]) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user else: self.ax.set_title(_("Bankroll Results")) useDates = True #nothing to draw if (len(green) == 0): return #Get the dates of the action (transfert / cg hand / tourney) #if it has no date, get the most ancient date and assume it's its one if dates[0] is None: i = 1 while i < len(dates) and type(dates[i]) is None: i = i + 1 if i == len(dates): print "Wow wow wow : no dates in your whole database" useDates = False else: dates[0] = dates[i] #now, convert date to dateTime format if useDates: for i in range(0, len(dates)): if dates[i] is None: dates[i] = dates[i - 1] #~else: #~dates[i] = datetime.datetime.strptime(dates[i], "%Y-%m-%d %H:%M:%S") for i in range(0, len(green) - 1): beneficeSinceStart = green[i + 1] - self.totalTransfer( dates[i + 1], transfersAmount, transfersDate) mycolor = self.color(transferType[i + 1], beneficeSinceStart) self.ax.plot([i, i + 1], [green[i], green[i + 1]], color=mycolor) #show date and gain only 5 times on X axis if (i % (len(green) / 5) == 1): #the gain since start at this time if (mycolor == 'cyan'): mycolor = 'green' self.ax.annotate('%.2f' % beneficeSinceStart, xy=(i, 0), color=mycolor, xycoords=('data', 'axes fraction'), xytext=(0, 18), textcoords='offset points', va='top', ha='left') #and show the date too if enabled if useDates: dateMMDD = datetime.datetime.strptime( dates[i], "%Y-%m-%d %H:%M:%S").strftime('%d/%m') self.ax.annotate(dateMMDD, xy=(i, 0), xycoords=('data', 'axes fraction'), xytext=(0, -18), textcoords='offset points', va='top', ha='left') #plot the last one and show the top corner legend i = len(green) - 1 bankroll = float(green[i]) profit = bankroll if len(transfersAmount) > 0: profit -= transfersAmount[len(transfersAmount) - 1] self.ax.plot([i, i + 1], [green[i], green[i]], color=self.color(transferType[i], beneficeSinceStart), label=_('Bankroll') + ': \$%.2f' % bankroll + '\n' + _('Profit') + ': \$%.2f' % profit) legend = self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #end of def showClicked #return total cash from transfer until «date» def totalTransfer(self, date, amounts, transfersDate): #~print transferts if len(amounts) == 0 or (date < transfersDate[0]): return 0 i = 0 while (i < len(amounts) - 1 and date > transfersDate[i]): i = i + 1 return amounts[i] def color(self, typ, gain): # 0:play, 1:transfert if typ == 1: return 'cyan' elif gain < 0: return 'red' else: return 'green' def getData(self, names, sites): print "DEBUG: args are :" print names print sites tmp = self.rightRequest('getAllPrintIdSite', names, sites) tmp2 = self.rightRequest('getAllTransfer', names, sites) print "DEBUG: sql query:" print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.cursor.execute(tmp2) transfers = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return None green = map(lambda x: float(x[0]), winnings) dates = map(lambda x: x[1], winnings) typeOf = map(lambda x: x[2], winnings) transferAmounts = map(lambda x: x[0], transfers) transferDates = map(lambda x: x[1], transfers) #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) #blueline = cumsum(blue) #redline = cumsum(red) transferAmounts = cumsum(transferAmounts) return (greenline / 100., dates, transferAmounts, transferDates, typeOf) def exportGraph(self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog( title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename( self.exportFile ) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename( ) + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy() #end of def exportGraph def transfertsWindow(self, widget, data): #if the window is already launched, put it in front if not self.settings['global_lock'].acquire( wait=False, source="GuiBankrollGraphViewer"): return #create the window … #first, check if there is at least one player on database, else quit if (len(self.filters.getHeroes()) == 0): print "No site/hero found, abort" return self.transferWindow = gtk.Window(gtk.WINDOW_TOPLEVEL) self.transferWindow.set_title("Transferts Management") self.transferWindow.set_position(gtk.WIN_POS_CENTER) self.transferWindow.set_transient_for(self.parent) self.transferWindow.connect("destroy", self.release) vbox = gtk.VBox(False, 0) self.transferWindow.add(vbox) ##### ##### #«new transfert» part hboxAdd = gtk.HBox(True, 0) vbox.pack_start(hboxAdd) #calendar cal = gtk.Calendar() vboxSelection = gtk.VBox(False, 0) #hour selection hourLabel = gtk.Label(_('Select hour:')) hourLabel.set_alignment(xalign=0.0, yalign=0.5) hboxHour = gtk.HBox(False, 0) timeHourPicker = gtk.SpinButton(None, 0, 0) timeHourPicker.set_increments(1, 6) timeHourPicker.set_range(0, 23) timeHourPicker.set_value(datetime.datetime.now().hour) # current hour timeMinPicker = gtk.SpinButton(None, 0, 0) timeMinPicker.set_increments(1, 10) timeMinPicker.set_range(0, 59) timeMinPicker.set_value(datetime.datetime.now().minute) # current hour #site/hero selection IDLabel = gtk.Label(_('Site - hero:')) IDLabel.set_alignment(xalign=0.0, yalign=0.5) IDSelection = gtk.combo_box_new_text() for site, hero in self.filters.getHeroes().items(): IDSelection.append_text(site + " - " + hero) IDSelection.set_active(0) #amount of virement amountLabel = gtk.Label(_('Tranfert amount ($):')) amountLabel.set_alignment(xalign=0.0, yalign=0.5) amountEntry = gtk.Entry() amountEntry.set_text('10.00') amountEntry.connect('changed', self.on_changed, 'changed') #button add buttonAdd = gtk.ToolButton(gtk.STOCK_ADD) buttonAdd.connect('clicked', self.newTransfer, 'clicked', cal, timeHourPicker, timeMinPicker, IDSelection, amountEntry) buttonAdd.connect('clicked', self.destroyWindow) hboxAdd.pack_start(cal, 0) hboxAdd.pack_start(vboxSelection, 0) vboxSelection.pack_start(hourLabel, 0) vboxSelection.pack_start(hboxHour, 0) hboxHour.pack_start(timeHourPicker, 0) hboxHour.pack_start(timeMinPicker, 0) vboxSelection.pack_start(IDLabel, 0) vboxSelection.pack_start(IDSelection, 0) vboxSelection.pack_start(amountLabel, 0) vboxSelection.pack_start(amountEntry, 0) vboxSelection.pack_start(buttonAdd, -1) #end of "new transfert" part ##### #### #### #start of "delete transfert" part hboxDelete = gtk.HBox(False, 0) vbox.pack_start(hboxDelete) #tab to create vboxTab = gtk.VBox(False, 0) self.createTab(vboxTab) buttonDelete = gtk.ToolButton(gtk.STOCK_DELETE) buttonDelete.connect('clicked', self.deleteTransfer, 'clicked') hboxDelete.pack_start(vboxTab, 1) hboxDelete.pack_start(buttonDelete, 1) #end of "delete transfert" part #### self.transferWindow.show_all() return #end of def transfertsWindow def release(self, widget, data=None): self.settings['global_lock'].release() self.transferWindow.destroy() return def on_changed(self, widget, data): #~text = widget.get_text().strip() #~widget.set_text(''.join([i for i in text if i in '0123456789'])) entry_text = widget.get_text() newtext = self.rx.findall(entry_text) if len(newtext): widget.set_text(newtext[0]) else: widget.set_text("") def destroyWindow(self, widget): self.transferWindow.destroy() return def newTransfer(self, widget, data, cal, timeHourPicker, timeMinPicker, IDSelection, amountEntry): year, month, day = cal.get_date() month = month + 1 # because gtk gives it between 0 and 11 ?! hour = timeHourPicker.get_value() minute = timeMinPicker.get_value() (site, separator, hero) = IDSelection.get_active_text().partition(' - ') transfer = float(amountEntry.get_text()) now = datetime.datetime(year, month, day, int(hour), int(minute), 0) #get siteID from siteName (table "sites") self.db.cursor.execute('SELECT id from sites where name LIKE "' + site + '"') siteID = self.db.cursor.fetchall()[0][0] self.db.rollback() #get heroID from heroName and siteID (table "players") self.db.cursor.execute('select id from players where name LIKE "' + hero + '" and siteId = ' + str(siteID)) heroID = self.db.cursor.fetchall()[0][0] self.db.rollback() #insert it in the table now query = "INSERT INTO BankrollsManagement(siteId, playerId, transfer, startTime) VALUES (?, ?, ?, ?)" #~print "DEBUG:\n%s" % query self.db.cursor.execute(query, (siteID, heroID, int(transfer * 100), now)) self.db.commit() self.db.rollback() #update the graph gobject.GObject.emit(self.filters.Button1, "clicked") def deleteTransfer(self, widget, data): #get the active line of the array selected = self.view.get_cursor()[0] #if no row selected, abort if selected is None: return #else, retrieve the line ( /!\ rowNumber != Id from the table ), rowNumber = selected[0] line = self.liststore[0][rowNumber] id = line[0] #then delete it from table and refresh graph self.db.cursor.execute('DELETE FROM BankrollsManagement WHERE id=' + str(id)) self.db.commit() self.db.rollback() #destroy the window self.destroyWindow(widget) gobject.GObject.emit(self.filters.Button1, "clicked") def createTab(self, vbox): cols_to_show = [ ["id", False, _("ID"), 0.0, "%s", "str"], ["siteName", True, _("Site"), 0.0, "%s", "str"] # true not allowed for this line (set in code) , ["playerName", True, _("Name"), 0.8, "%s", "str"] # true not allowed for this line (set in code) , ["amount", True, _("Amount"), 0.0, "%0.2f", "str"], ["date", True, _("Date"), 0.0, "%s", "str"] ] self.liststore = [] self.liststore.append(gtk.ListStore(*([str] * len(cols_to_show)))) self.view = gtk.TreeView(model=self.liststore[0]) self.view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) #vbox.pack_start(view, expand=False, padding=3) vbox.add(self.view) textcell = gtk.CellRendererText() textcell50 = gtk.CellRendererText() textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) listcols = [] listcols.append([]) # Create header row eg column: ("game", True, "Game", 0.0, "%s") for i, col in enumerate(cols_to_show): listcols[0].append(gtk.TreeViewColumn(col[_COL_HEADING])) self.view.append_column(listcols[0][i]) if col[_COL_FORMAT] == '%s': if col[_COL_XALIGN] == 0.0: listcols[0][i].pack_start(textcell, expand=True) listcols[0][i].add_attribute(textcell, 'text', i) cellrend = textcell else: listcols[0][i].pack_start(textcell50, expand=True) listcols[0][i].add_attribute(textcell50, 'text', i) cellrend = textcell50 listcols[0][i].set_expand(True) else: listcols[0][i].pack_start(numcell, expand=True) listcols[0][i].add_attribute(numcell, 'text', i) listcols[0][i].set_expand(True) cellrend = numcell query = self.sql.query['getAllTransferInformations'] #~print "DEBUG:\n%s" % query self.db.cursor.execute(query) result = self.db.cursor.fetchall() #~print "result of the big query in addGrid:",result colnames = [desc[0] for desc in self.db.cursor.description] #~for i in range(0, len(tab)) rows = len(result) # +1 for title row counter = 0 row = 0 sqlrow = 0 while sqlrow < rows: treerow = [] for col, column in enumerate(cols_to_show): if column[_COL_ALIAS] in colnames: if column[_COL_ALIAS] == 'amount': #convert $ cents to $ value = result[sqlrow][colnames.index( column[_COL_ALIAS])] / 100. else: value = result[sqlrow][colnames.index( column[_COL_ALIAS])] else: value = 111 if value != None and value != -999: treerow.append(column[_COL_FORMAT] % value) else: treerow.append(' ') #print "addGrid, just before end of big for. grid:",grid,"treerow:",treerow iter = self.liststore[0].append(treerow) sqlrow += 1 row += 1 vbox.show_all() def rightRequest(self, request, names, sites): tmp = self.sql.query[request] print "DEBUG: getData. :" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace(",)", ")") return tmp
class GuiGraphViewer(threading.Thread): def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes": True, "Sites": True, "Games": True, "Currencies": True, "Limits": True, "LimitSep": True, "LimitType": True, "Type": False, "UseType": 'ring', "Seats": False, "SeatSep": False, "Dates": True, "GraphOps": True, "Groups": False, "Button1": True, "Button2": True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.exportFile = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() games = self.filters.getGames() currencies = self.filters.getCurrencies() graphops = self.filters.getGraphOps() names = "" for i in ('show', 'none'): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) names = names + "\n" + _hname + " on " + site if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return if not limits: print _("No limits found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, blue, red, orange) = self.getRingProfitGraph(playerids, sitenos, limits, games, currencies, graphops['dspin']) print _("Graph generated in: %s") % (time() - starttime) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Hands")) # SET LABEL FOR X AXIS self.ax.set_ylabel(graphops['dspin']) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) self.ax.plot(green, color='green', label=_('Hands') + ': %d\n' % len(green) + _('Profit') + ': %.2f' % green[-1]) self.ax.plot(blue, color='blue', label=_('Showdown') + ': $%.2f' % (blue[-1])) self.ax.plot(red, color='red', label=_('Non-showdown') + ': $%.2f' % (red[-1])) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() else: self.ax.set_title((_("Profit graph for ring games") + names)) #Draw plot self.ax.plot(green, color='green', label=_('Hands') + ': %d\n' % len(green) + _('Profit') + ': (%s): %.2f' % (graphops['dspin'], green[-1])) if graphops['showdown'] == 'ON': self.ax.plot(blue, color='blue', label=_('Showdown') + ' (%s): %.2f' % (graphops['dspin'], blue[-1])) if graphops['nonshowdown'] == 'ON': self.ax.plot(red, color='red', label=_('Non-showdown') + ' (%s): %.2f' % (graphops['dspin'], red[-1])) if graphops['ev'] == 'ON': self.ax.plot(orange, color='orange', label=_('All-in EV') + ' (%s): %.2f' % (graphops['dspin'], orange[-1])) if sys.version[0:3] == '2.5': self.ax.legend(loc='upper left', shadow=True, prop=FontProperties(size='smaller')) else: self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) #end of def showClicked def getRingProfitGraph(self, names, sites, limits, games, currencies, units): # tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] # print "DEBUG: getRingProfitGraph" if units == '$': tmp = self.sql.query['getRingProfitAllHandsPlayerIdSiteInDollars'] elif units == 'BB': tmp = self.sql.query['getRingProfitAllHandsPlayerIdSiteInBB'] start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #nametest = nametest.replace("L", "") q = [] for m in self.filters.display.items(): if m[0] == 'Games' and m[1]: for n in games: if games[n]: q.append(n) if len(q) > 0: gametest = str(tuple(q)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)", ")") gametest = gametest.replace("u'", "'") gametest = "and gt.category in %s" % gametest else: gametest = "and gt.category IS NULL" tmp = tmp.replace("<game_test>", gametest) limittest = self.filters.get_limits_where_clause(limits) q = [] for n in currencies: if currencies[n]: q.append(n) currencytest = str(tuple(q)) currencytest = currencytest.replace(",)", ")") currencytest = currencytest.replace("u'", "'") currencytest = "AND gt.currency in %s" % currencytest if type == 'ring': limittest = limittest + " and gt.type = 'ring' " elif type == 'tour': limittest = limittest + " and gt.type = 'tour' " #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace("<limit_test>", limittest) tmp = tmp.replace("<currency_test>", currencytest) tmp = tmp.replace(",)", ")") #print "DEBUG: sql query:" #print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None, None) green = map(lambda x: float(x[1]), winnings) blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) orange = map(lambda x: float(x[3]), winnings) greenline = cumsum(green) blueline = cumsum(blue) redline = cumsum(red) orangeline = cumsum(orange) return (greenline / 100, blueline / 100, redline / 100, orangeline / 100) #end of def getRingProfitGraph def exportGraph(self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. png_filter = gtk.FileFilter() png_filter.add_pattern('*.png') dia_chooser = gtk.FileChooserDialog( title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dia_chooser.set_filter(png_filter) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) if self.exportFile is not None: dia_chooser.set_filename( self.exportFile ) # use previously chosen export path as default else: dia_chooser.set_current_name('fpdbgraph.png') response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return self.exportFile = dia_chooser.get_filename() dia_chooser.destroy() self.fig.savefig(self.exportFile, format="png") # Display info box to confirm graph created. diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class GuiGraphViewer (threading.Thread): def __init__(self, querylist, config, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes" : True, "Sites" : True, "Games" : True, "Limits" : True, "LimitSep" : True, "LimitType" : True, "Type" : False, "UseType" : 'ring', "Seats" : False, "SeatSep" : False, "Dates" : True, "Groups" : False, "Button1" : True, "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("Refresh _Graph") self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name("_Export to File") self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) raise def generateGraph(self, widget, data): try: self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() games = self.filters.getGames() for i in ('show', 'none'): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print "No sites selected - defaulting to PokerStars" self.db.rollback() return if not playerids: print "No player ids found" self.db.rollback() return if not limits: print "No limits found" self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, blue, red) = self.getRingProfitGraph(playerids, sitenos, limits, games) print "Graph generated in: %s" %(time() - starttime) #Set axis labels and grid overlay properites self.ax.set_xlabel("Hands", fontsize = 12) self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title("No Data for Player(s) Found") green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) self.ax.plot(green, color='green', label='Hands: %d\nProfit: $%.2f' %(len(green), green[-1])) self.ax.plot(blue, color='blue', label='Showdown: $%.2f' %(blue[-1])) self.ax.plot(red, color='red', label='Non-showdown: $%.2f' %(red[-1])) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user #print "No hands returned by graph query" else: self.ax.set_title("Profit graph for ring games") #text = "Profit: $%.2f\nTotal Hands: %d" %(green[-1], len(green)) #self.ax.annotate(text, # xy=(10, -10), # xycoords='axes points', # horizontalalignment='left', verticalalignment='top', # fontsize=10) #Draw plot self.ax.plot(green, color='green', label='Hands: %d\nProfit: $%.2f' %(len(green), green[-1])) self.ax.plot(blue, color='blue', label='Showdown: $%.2f' %(blue[-1])) self.ax.plot(red, color='red', label='Non-showdown: $%.2f' %(red[-1])) if sys.version[0:3] == '2.5': self.ax.legend(loc='best', shadow=True, prop=FontProperties(size='smaller')) else: self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) #end of def showClicked def getRingProfitGraph(self, names, sites, limits, games): tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] # print "DEBUG: getRingProfitGraph" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #nametest = nametest.replace("L", "") q = [] for m in self.filters.display.items(): if m[0] == 'Games' and m[1]: for n in games: if games[n]: q.append(n) if len(q) > 0: gametest = str(tuple(q)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)",")") gametest = gametest.replace("u'","'") gametest = "and gt.category in %s" % gametest else: gametest = "and gt.category IS NULL" tmp = tmp.replace("<game_test>", gametest) lims = [int(x) for x in limits if x.isdigit()] potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl'] nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] limittest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " # and ( (limit and bb in()) or (nolimit and bb in ()) ) if lims: blindtest = str(tuple(lims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'pl' and gt.bigBlind in " if potlims: blindtest = str(tuple(potlims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) ' else: limittest = limittest + '(-1) ) ' limittest = limittest + " or (gt.limitType = 'nl' and gt.bigBlind in " if nolims: blindtest = str(tuple(nolims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") limittest = limittest + blindtest + ' ) )' else: limittest = limittest + '(-1) ) )' if type == 'ring': limittest = limittest + " and gt.type = 'ring' " elif type == 'tour': limittest = limittest + " and gt.type = 'tour' " #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace("<limit_test>", limittest) tmp = tmp.replace(",)", ")") #print "DEBUG: sql query:" #print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return (None, None, None) green = map(lambda x:float(x[1]), winnings) blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) blueline = cumsum(blue) redline = cumsum(red) return (greenline/100, blueline/100, redline/100) #end of def getRingProfitGraph def exportGraph (self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) #TODO: Suggest path and filename to start with response = dia_chooser.run() if response == gtk.RESPONSE_OK: self.exportDir = dia_chooser.get_filename() print "DEBUG: self.exportDir = %s" %(self.exportDir) elif response == gtk.RESPONSE_CANCEL: print 'Closed, no graph exported' dia_chooser.destroy() #TODO: Check to see if file exists #NOTE: Dangerous - will happily overwrite any file we have write access too #TODO: This asks for a directory but will take a filename and overwrite it. self.fig.savefig(self.exportDir, format="png")
class GuiSessionViewer: def __init__(self, config, querylist, mainwin, owner, debug=True): self.debug = debug self.conf = config self.sql = querylist self.window = mainwin self.owner = owner self.liststore = None self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 self.fig = None self.canvas = None self.ax = None self.graphBox = None # create new db connection to avoid conflicts with other threads self.db = Database.Database(self.conf, sql=self.sql) self.cursor = self.db.cursor settings = {} settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_default_paths()) # text used on screen stored here so that it can be configured self.filterText = {'handhead':_('Hand Breakdown for all levels listed above')} filters_display = { "Heroes" : True, "Sites" : True, "Games" : True, "Currencies": True, "Limits" : True, "LimitSep" : True, "LimitType" : True, "Type" : False, "Seats" : True, "SeatSep" : False, "Dates" : True, "Groups" : False, "GroupsAll" : False, "Button1" : True, "Button2" : False } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("_Refresh") self.filters.registerButton1Callback(self.refreshStats) # ToDo: store in config # ToDo: create popup to adjust column config # columns to display, keys match column name returned by sql, values in tuple are: # is column displayed, column heading, xalignment, formatting self.columns = [ (1.0, "SID" ) , (1.0, "Hands" ) , (0.5, "Start" ) , (0.5, "End" ) , (1.0, "Rate" ) , (1.0, "Open" ) , (1.0, "Close" ) , (1.0, "Low" ) , (1.0, "High" ) , (1.0, "Range" ) , (1.0, "Profit") ] self.detailFilters = [] # the data used to enhance the sql select self.main_hbox = gtk.HPaned() self.stats_frame = gtk.Frame() self.stats_frame.show() main_vbox = gtk.VPaned() main_vbox.show() self.graphBox = gtk.VBox(False, 0) self.graphBox.set_size_request(400,400) self.graphBox.show() self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) self.main_hbox.pack1(self.filters.get_vbox()) self.main_hbox.pack2(main_vbox) main_vbox.pack1(self.graphBox) main_vbox.pack2(self.stats_frame) self.main_hbox.show() # make sure Hand column is not displayed #[x for x in self.columns if x[0] == 'hand'][0][1] = False # if DEBUG == False: # warning_string = _("Session Viewer is proof of concept code only, and contains many bugs.\n") # warning_string += _("Feel free to use the viewer, but there is no guarantee that the data is accurate.\n") # warning_string += _("If you are interested in developing the code further please contact us via the usual channels.\n") # warning_string += _("Thank you") # self.warning_box(warning_string) def warning_box(self, str, diatitle=_("FPDB WARNING")): diaWarning = gtk.Dialog(title=diatitle, parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) label = gtk.Label(str) diaWarning.vbox.add(label) label.show() response = diaWarning.run() diaWarning.destroy() return response def get_vbox(self): """returns the vbox of this thread""" return self.main_hbox def refreshStats(self, widget, data): try: self.stats_vbox.destroy() except AttributeError: pass self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) self.fillStatsFrame(self.stats_vbox) def fillStatsFrame(self, vbox): sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() games = self.filters.getGames() currencies = self.filters.getCurrencies() limits = self.filters.getLimits() seats = self.filters.getSeats() sitenos = [] playerids = [] for i in ('show', 'none'): if i in limits: limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(result) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") sitenos = [2] if not games: print _("No games found") return if not currencies: print _("No currencies found") return if not playerids: print _("No player ids found") return if not limits: print _("No limits found") return self.createStatsPane(vbox, playerids, sitenos, games, currencies, limits, seats) def createStatsPane(self, vbox, playerids, sitenos, games, currencies, limits, seats): starttime = time() (results, quotes) = self.generateDatasets(playerids, sitenos, games, currencies, limits, seats) if DEBUG: for x in quotes: print "start %s\tend %s \thigh %s\tlow %s" % (x[1], x[2], x[3], x[4]) self.generateGraph(quotes) heading = gtk.Label(self.filterText['handhead']) heading.show() vbox.pack_start(heading, expand=False, padding=3) # Scrolled window for detailed table (display by hand) swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.show() vbox.pack_start(swin, expand=True, padding=3) vbox1 = gtk.VBox(False, 0) vbox1.show() swin.add_with_viewport(vbox1) self.addTable(vbox1, results) self.db.rollback() print _("Stats page displayed in %4.2f seconds") % (time() - starttime) #end def fillStatsFrame(self, vbox): def generateDatasets(self, playerids, sitenos, games, currencies, limits, seats): if (DEBUG): print "DEBUG: Starting generateDatasets" THRESHOLD = 1800 # Min # of secs between consecutive hands before being considered a new session PADDING = 5 # Additional time in minutes to add to a session, session startup, shutdown etc # Get a list of timestamps and profits q = self.sql.query['sessionStats'] start_date, end_date = self.filters.getDates() q = q.replace("<datestest>", " BETWEEN '" + start_date + "' AND '" + end_date + "'") l = [] for m in self.filters.display.items(): if m[0] == 'Games' and m[1]: for n in games: if games[n]: l.append(n) if len(l) > 0: gametest = str(tuple(l)) gametest = gametest.replace("L", "") gametest = gametest.replace(",)",")") gametest = gametest.replace("u'","'") gametest = "AND gt.category in %s" % gametest else: gametest = "AND gt.category IS NULL" q = q.replace("<game_test>", gametest) limittest = self.filters.get_limits_where_clause(limits) q = q.replace("<limit_test>", limittest) l = [] for n in currencies: if currencies[n]: l.append(n) currencytest = str(tuple(l)) currencytest = currencytest.replace(",)",")") currencytest = currencytest.replace("u'","'") currencytest = "AND gt.currency in %s" % currencytest q = q.replace("<currency_test>", currencytest) if seats: q = q.replace('<seats_test>', 'AND h.seats BETWEEN ' + str(seats['from']) + ' AND ' + str(seats['to'])) else: q = q.replace('<seats_test>', 'AND h.seats BETWEEN 0 AND 100') nametest = str(tuple(playerids)) nametest = nametest.replace("L", "") nametest = nametest.replace(",)",")") q = q.replace("<player_test>", nametest) q = q.replace("<ampersand_s>", "%s") if DEBUG: hands = [ ( u'10000', 10), ( u'10000', 20), ( u'10000', 30), ( u'20000', -10), ( u'20000', -20), ( u'20000', -30), ( u'30000', 40), ( u'40000', 0), ( u'50000', -40), ( u'60000', 10), ( u'60000', 30), ( u'60000', -20), ( u'70000', -20), ( u'70000', 10), ( u'70000', 30), ( u'80000', -10), ( u'80000', -30), ( u'80000', 20), ( u'90000', 20), ( u'90000', -10), ( u'90000', -30), (u'100000', 30), (u'100000', -50), (u'100000', 30), (u'110000', -20), (u'110000', 50), (u'110000', -20), (u'120000', -30), (u'120000', 50), (u'120000', -30), (u'130000', 20), (u'130000', -50), (u'130000', 20), (u'140000', 40), (u'140000', -40), (u'150000', -40), (u'150000', 40), (u'160000', -40), (u'160000', 80), (u'160000', -40), ] else: self.db.cursor.execute(q) hands = self.db.cursor.fetchall() #fixme - nasty hack to ensure that the hands.insert() works # for mysql data. mysql returns tuples which can't be inserted # into so convert explicity to list. hands = list(hands) if (not hands): return ([], []) hands.insert(0, (hands[0][0], 0)) # Take that list and create an array of the time between hands times = map(lambda x:long(x[0]), hands) profits = map(lambda x:float(x[1]), hands) #print "DEBUG: times : %s" % times #print "DEBUG: profits: %s" % profits #print "DEBUG: len(times) %s" %(len(times)) diffs = diff(times) # This array is the difference in starttime between consecutive hands diffs2 = append(diffs,THRESHOLD + 1) # Append an additional session to the end of the diffs, so the next line # includes an index into the last 'session' index = nonzero(diffs2 > THRESHOLD) # This array represents the indexes into 'times' for start/end times of sessions # times[index[0][0]] is the end of the first session, #print "DEBUG: len(index[0]) %s" %(len(index[0])) if len(index[0]) > 0: #print "DEBUG: index[0][0] %s" %(index[0][0]) #print "DEBUG: index %s" %(index) pass else: index = [[0]] #print "DEBUG: index %s" %(index) #print "DEBUG: index[0][0] %s" %(index[0][0]) pass first_idx = 1 quotes = [] results = [] cum_sum = cumsum(profits) / 100 sid = 1 total_hands = 0 total_time = 0 global_open = None global_lwm = None global_hwm = None self.times = [] # Take all results and format them into a list for feeding into gui model. #print "DEBUG: range(len(index[0]): %s" % range(len(index[0])) for i in range(len(index[0])): last_idx = index[0][i] hds = last_idx - first_idx + 1 # Number of hands in session if hds > 0: stime = strftime("%d/%m/%Y %H:%M", localtime(times[first_idx])) # Formatted start time etime = strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])) # Formatted end time self.times.append((times[first_idx], times[last_idx])) minutesplayed = (times[last_idx] - times[first_idx])/60 minutesplayed = minutesplayed + PADDING if minutesplayed == 0: minutesplayed = 1 hph = hds*60/minutesplayed # Hands per hour end_idx = last_idx+1 won = sum(profits[first_idx:end_idx])/100.0 #print "DEBUG: profits[%s:%s]: %s" % (first_idx, end_idx, profits[first_idx:end_idx]) hwm = max(cum_sum[first_idx-1:end_idx]) # include the opening balance, lwm = min(cum_sum[first_idx-1:end_idx]) # before we win/lose first hand open = (sum(profits[:first_idx]))/100 close = (sum(profits[:end_idx]))/100 #print "DEBUG: range: (%s, %s) - (min, max): (%s, %s) - (open,close): (%s, %s)" %(first_idx, end_idx, lwm, hwm, open, close) total_hands = total_hands + hds total_time = total_time + minutesplayed if (global_lwm == None or global_lwm > lwm): global_lwm = lwm if (global_hwm == None or global_hwm < hwm): global_hwm = hwm if (global_open == None): global_open = open global_stime = stime results.append([sid, hds, stime, etime, hph, "%.2f" % open, "%.2f" % close, "%.2f" % lwm, "%.2f" % hwm, "%.2f" % (hwm - lwm), "%.2f" % won]) quotes.append((sid, open, close, hwm, lwm)) #print "DEBUG: Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won) first_idx = end_idx sid = sid+1 else: print "hds <= 0" global_close = close global_etime = etime results.append([''] * 11) results.append([_("all"), total_hands, global_stime, global_etime, total_hands * 60 / total_time, "%.2f" % global_open, "%.2f" % global_close, "%.2f" % global_lwm, "%.2f" % global_hwm, "%.2f" % (global_hwm - global_lwm), "%.2f" % (global_close - global_open)]) return (results, quotes) def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig is not None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("Error:")+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) raise def generateGraph(self, quotes): self.clearGraphData() #print "DEBUG:" #print "\tquotes = %s" % quotes #for i in range(len(highs)): # print "DEBUG: (%s, %s, %s, %s)" %(lows[i], opens[i], closes[i], highs[i]) # print "DEBUG: diffs h/l: %s o/c: %s" %(lows[i] - highs[i], opens[i] - closes[i]) self.ax = self.fig.add_subplot(111) self.ax.set_title(_("Session candlestick graph")) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Sessions"), fontsize = 12) self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) candlestick(self.ax, quotes, width=0.50, colordown='r', colorup='g', alpha=1.00) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() def addTable(self, vbox, results): row = 0 sqlrow = 0 colxalign,colheading = range(2) self.liststore = gtk.ListStore(*([str] * len(self.columns))) for row in results: iter = self.liststore.append(row) view = gtk.TreeView(model=self.liststore) view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) vbox.add(view) print view.connect("row-activated", self.row_activated) cell05 = gtk.CellRendererText() cell05.set_property('xalign', 0.5) cell10 = gtk.CellRendererText() cell10.set_property('xalign', 1.0) listcols = [] # Create header row eg column: ("game", True, "Game", 0.0, "%s") for col, column in enumerate(self.columns): treeviewcolumn = gtk.TreeViewColumn(column[colheading]) listcols.append(treeviewcolumn) treeviewcolumn.set_alignment(column[colxalign]) view.append_column(listcols[col]) if (column[colxalign] == 0.5): cell = cell05 else: cell = cell10 listcols[col].pack_start(cell, expand=True) listcols[col].add_attribute(cell, 'text', col) listcols[col].set_expand(True) vbox.show_all() def row_activated(self, view, path, column): if path[0] < len(self.times): replayer = None for tabobject in self.owner.threads: if isinstance(tabobject, GuiHandViewer.GuiHandViewer): replayer = tabobject self.owner.tab_hand_viewer(None) break if replayer is None: self.owner.tab_hand_viewer(None) for tabobject in self.owner.threads: if isinstance(tabobject, GuiHandViewer.GuiHandViewer): replayer = tabobject break # added the timezone offset ('+00:00') to make the db query work. Otherwise the hands # at the edges of the date range are not included. A better solution may be possible. # Optionally the end date in the call below, which is a Long gets a '+1'. reformat = lambda t: strftime("%Y-%m-%d %H:%M:%S+00:00", gmtime(t)) handids = replayer.get_hand_ids_from_date_range(reformat(self.times[path[0]][0]), reformat(self.times[path[0]][1]), save_date = True) replayer.reload_hands(handids)
class GuiSessionViewer(threading.Thread): def __init__(self, config, querylist, mainwin, debug=True): self.debug = debug self.conf = config self.sql = querylist self.liststore = None self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 self.fig = None self.canvas = None self.ax = None self.graphBox = None # create new db connection to avoid conflicts with other threads self.db = Database.Database(self.conf, sql=self.sql) self.cursor = self.db.cursor settings = {} settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_default_paths()) # text used on screen stored here so that it can be configured self.filterText = { 'handhead': _('Hand Breakdown for all levels listed above') } filters_display = { "Heroes": True, "Sites": True, "Games": False, "Limits": False, "LimitSep": False, "LimitType": False, "Type": True, "Seats": False, "SeatSep": False, "Dates": True, "Groups": False, "GroupsAll": False, "Button1": True, "Button2": False } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name("_Refresh") self.filters.registerButton1Callback(self.refreshStats) # ToDo: store in config # ToDo: create popup to adjust column config # columns to display, keys match column name returned by sql, values in tuple are: # is column displayed, column heading, xalignment, formatting self.columns = [ ("sid", True, "SID", 0.0, "%s"), ("hand", False, "Hand", 0.0, "%s" ) # true not allowed for this line , ("n", True, "Hds", 1.0, "%d"), ("start", True, "Start", 1.0, "%d"), ("end", True, "End", 1.0, "%d"), ("hph", True, "Hands/h", 1.0, "%d"), ("profit", True, "Profit", 1.0, "%s") #, ("avgseats", True, "Seats", 1.0, "%3.1f") #, ("vpip", True, "VPIP", 1.0, "%3.1f") #, ("pfr", True, "PFR", 1.0, "%3.1f") #, ("pf3", True, "PF3", 1.0, "%3.1f") #, ("steals", True, "Steals", 1.0, "%3.1f") #, ("saw_f", True, "Saw_F", 1.0, "%3.1f") #, ("sawsd", True, "SawSD", 1.0, "%3.1f") #, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f") #, ("wmsd", True, "W$SD", 1.0, "%3.1f") #, ("flafq", True, "FlAFq", 1.0, "%3.1f") #, ("tuafq", True, "TuAFq", 1.0, "%3.1f") #, ("rvafq", True, "RvAFq", 1.0, "%3.1f") #, ("pofafq", False, "PoFAFq", 1.0, "%3.1f") #, ("net", True, "Net($)", 1.0, "%6.2f") #, ("bbper100", True, "BB/100", 1.0, "%4.2f") #, ("rake", True, "Rake($)", 1.0, "%6.2f") #, ("variance", True, "Variance", 1.0, "%5.2f") ] self.stats_frame = None self.stats_vbox = None self.detailFilters = [] # the data used to enhance the sql select #self.main_hbox = gtk.HBox(False, 0) #self.main_hbox.show() self.main_hbox = gtk.HPaned() self.stats_frame = gtk.Frame() self.stats_frame.show() self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) # self.fillStatsFrame(self.stats_vbox) #self.main_hbox.pack_start(self.filters.get_vbox()) #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) self.main_hbox.pack1(self.filters.get_vbox()) self.main_hbox.pack2(self.stats_frame) self.main_hbox.show() # make sure Hand column is not displayed #[x for x in self.columns if x[0] == 'hand'][0][1] = False def get_vbox(self): """returns the vbox of this thread""" return self.main_hbox def refreshStats(self, widget, data): try: self.stats_vbox.destroy() except AttributeError: pass self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) self.fillStatsFrame(self.stats_vbox) def fillStatsFrame(self, vbox): sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() seats = self.filters.getSeats() sitenos = [] playerids = [] # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _q = self.sql.query['getPlayerId'] _name = Charset.to_utf8(heroes[site]) #print 'DEBUG(_name) :: %s' % _name self.cursor.execute(_q, (_name, )) # arg = tuple result = self.db.cursor.fetchall() if len(result) == 1: playerids.append(result[0][0]) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") sitenos = [2] if not playerids: print _("No player ids found") return if not limits: print _("No limits found") return self.createStatsPane(vbox, playerids, sitenos, limits, seats) def createStatsPane(self, vbox, playerids, sitenos, limits, seats): starttime = time() (results, opens, closes, highs, lows) = self.generateDatasets(playerids, sitenos, limits, seats) self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.generateGraph(opens, closes, highs, lows) vbox.pack_start(self.graphBox) # Separator sep = gtk.HSeparator() vbox.pack_start(sep, expand=False, padding=3) sep.show_now() vbox.show_now() heading = gtk.Label(self.filterText['handhead']) heading.show() vbox.pack_start(heading, expand=False, padding=3) # Scrolled window for detailed table (display by hand) swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.show() vbox.pack_start(swin, expand=True, padding=3) vbox1 = gtk.VBox(False, 0) vbox1.show() swin.add_with_viewport(vbox1) self.addTable(vbox1, results) self.db.rollback() print _("Stats page displayed in %4.2f seconds") % (time() - starttime) #end def fillStatsFrame(self, vbox): def generateDatasets(self, playerids, sitenos, limits, seats): THRESHOLD = 1800 # Minimum number of seconds between consecutive hands before being considered a new session PADDING = 5 # Additional time in minutes to add to a session, session startup, shutdown etc (FiXME: user configurable) # Get a list of all handids and their timestampts #FIXME: Query still need to filter on blind levels q = self.sql.query['sessionStats'] start_date, end_date = self.filters.getDates() q = q.replace("<datestest>", " between '" + start_date + "' and '" + end_date + "'") nametest = str(tuple(playerids)) nametest = nametest.replace("L", "") nametest = nametest.replace(",)", ")") q = q.replace("<player_test>", nametest) q = q.replace("<ampersand_s>", "%s") self.db.cursor.execute(q) hands = self.db.cursor.fetchall() # Take that list and create an array of the time between hands times = map(lambda x: long(x[0]), hands) handids = map(lambda x: int(x[1]), hands) winnings = map(lambda x: float(x[4]), hands) #print "DEBUG: len(times) %s" %(len(times)) diffs = diff( times ) # This array is the difference in starttime between consecutive hands diffs2 = append( diffs, THRESHOLD + 1 ) # Append an additional session to the end of the diffs, so the next line # includes an index into the last 'session' index = nonzero( diffs2 > THRESHOLD ) # This array represents the indexes into 'times' for start/end times of sessions # times[index[0][0]] is the end of the first session, #print "DEBUG: len(index[0]) %s" %(len(index[0])) if len(index[0]) > 0: #print "DEBUG: index[0][0] %s" %(index[0][0]) #print "DEBUG: index %s" %(index) pass else: index = [[0]] #print "DEBUG: index %s" %(index) #print "DEBUG: index[0][0] %s" %(index[0][0]) pass total = 0 first_idx = 0 lowidx = 0 uppidx = 0 opens = [] closes = [] highs = [] lows = [] results = [] cum_sum = cumsum(winnings) cum_sum = cum_sum / 100 sid = 1 # Take all results and format them into a list for feeding into gui model. for i in range(len(index[0])): hds = index[0][i] - first_idx + 1 # Number of hands in session if hds > 0: stime = strftime("%d/%m/%Y %H:%M", localtime( times[first_idx])) # Formatted start time etime = strftime("%d/%m/%Y %H:%M", localtime( times[index[0][i]])) # Formatted end time minutesplayed = (times[index[0][i]] - times[first_idx]) / 60 if minutesplayed == 0: minutesplayed = 1 minutesplayed = minutesplayed + PADDING hph = hds * 60 / minutesplayed # Hands per hour won = sum(winnings[first_idx:index[0][i]]) / 100.0 hwm = max(cum_sum[first_idx:index[0][i]]) lwm = min(cum_sum[first_idx:index[0][i]]) open = (sum(winnings[:first_idx])) / 100 close = (sum(winnings[:index[0][i]])) / 100 #print "DEBUG: range: (%s, %s) - (min, max): (%s, %s) - (open,close): (%s, %s)" %(first_idx, index[0][i], lwm, hwm, open, close) results.append([sid, hds, stime, etime, hph, won]) opens.append(open) closes.append(close) highs.append(hwm) lows.append(lwm) #print "DEBUG: Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won) total = total + (index[0][i] - first_idx) first_idx = index[0][i] + 1 sid = sid + 1 else: print "hds <= 0" return (results, opens, closes, highs, lows) def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig is not None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("***Error: ") + err[2] + "(" + str(err[1]) + "): " + str( sys.exc_info()[1]) raise def generateGraph(self, opens, closes, highs, lows): self.clearGraphData() # print "DEBUG:" # print "highs = %s" % highs # print "lows = %s" % lows # print "opens = %s" % opens # print "closes = %s" % closes # print "len(highs): %s == len(lows): %s" %(len(highs), len(lows)) # print "len(opens): %s == len(closes): %s" %(len(opens), len(closes)) # # for i in range(len(highs)): # print "DEBUG: (%s, %s, %s, %s)" %(lows[i], opens[i], closes[i], highs[i]) # print "DEBUG: diffs h/l: %s o/c: %s" %(lows[i] - highs[i], opens[i] - closes[i]) self.ax = self.fig.add_subplot(111) self.ax.set_title(_("Session candlestick graph")) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Sessions"), fontsize=12) self.ax.set_ylabel("$", fontsize=12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) candlestick2(self.ax, opens, closes, highs, lows, width=0.50, colordown='r', colorup='g', alpha=1.00) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() def addTable(self, vbox, results): row = 0 sqlrow = 0 colalias, colshow, colheading, colxalign, colformat = 0, 1, 2, 3, 4 # pre-fetch some constant values: cols_to_show = [x for x in self.columns if x[colshow]] self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) for row in results: iter = self.liststore.append(row) view = gtk.TreeView(model=self.liststore) view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) vbox.add(view) textcell = gtk.CellRendererText() textcell50 = gtk.CellRendererText() textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) listcols = [] # Create header row eg column: ("game", True, "Game", 0.0, "%s") for col, column in enumerate(cols_to_show): s = column[colheading] listcols.append(gtk.TreeViewColumn(s)) view.append_column(listcols[col]) if column[colformat] == '%s': if column[colxalign] == 0.0: listcols[col].pack_start(textcell, expand=True) listcols[col].add_attribute(textcell, 'text', col) else: listcols[col].pack_start(textcell50, expand=True) listcols[col].add_attribute(textcell50, 'text', col) listcols[col].set_expand(True) else: listcols[col].pack_start(numcell, expand=True) listcols[col].add_attribute(numcell, 'text', col) listcols[col].set_expand(True) vbox.show_all()
class GuiApp(object): def __init__(self): global unit self.filename=None self.gladefile="/usr/share/freeceilo-%s/ceilo.glade" % VERSION self.wTree=gtk.glade.XML(self.gladefile) self.window=self.wTree.get_widget("FreeCeilo") self.window.maximize() self.threadvar=0 #--------buttons---------# self.Open=self.wTree.get_widget("Open") #-----cloud detection plotting button------# self.first_cld_plot_button=self.wTree.get_widget("first_cld_plot_button") self.second_cld_plot_button=self.wTree.get_widget("second_cld_plot_button") self.third_cld_plot_button=self.wTree.get_widget("third_cld_plot_button") self.cld_det_clear_button=self.wTree.get_widget("cld_det_clear_button") self.cld_det_save_button=self.wTree.get_widget("cld_det_save_button") #------------ gnuplot buttons -------------# self.gnu_plot_button=self.wTree.get_widget("gnu_plot_button") self.gnuplot_zoom_in_button=self.wTree.get_widget("gnuplot_zoom_in_button") self.gnuplot_zoom_normal_button=self.wTree.get_widget("gnuplot_zoom_normal_button") self.gnuplot_zoom_out_button=self.wTree.get_widget("gnuplot_zoom_out_button") self.gnuplot_save_button=self.wTree.get_widget("gnuplot_save_button") #--------buttons ends---------# #------progressbars------# self.progressbar1=self.wTree.get_widget("progressbar1") self.progressbar2=self.wTree.get_widget("progressbar2") self.progressbar1.set_fraction(0.0) self.datpbar=0 self.progressbar2.set_fraction(0.0) self.gnupbar=0 self.timer = gobject.timeout_add (100, progress_timeout, self) #------gnu plot image normal size ----# self.imagex= 810 self.imagey=540 self.gnuplot_image=self.wTree.get_widget("gnuplot_image") self.x=0 #--------Handles events dic={"on_FreeCeilo_destroy":self.quitprogram,"on_Open_activate":self.openfile, "on_first_cld_plot_button_clicked":self.firstcloudshow,"on_second_cld_plot_button_clicked" :self.secondcloudshow,"on_third_cld_plot_button_clicked":self.thirdcloudshow, "on_zoom_in_x_activate":self.clouddetectzoominx,"on_zoom_out_x_activate": self.clouddetectzoomoutx,"on_zoom_in_y_activate":self.clouddetectzoominy, "on_zoom_out_y_activate":self.clouddetectzoomouty,"on_cld_det_save_button_clicked" :self.clouddetectsave,"on_cld_det_clear_button_clicked":self.clouddetectgraphclear, "on_gnu_plot_button_clicked":self.gnuplotshow,"on_gnuplot_zoom_in_button_clicked": self.gnuplotzoomin,"on_gnuplot_zoom_normal_button_clicked":self.gnuplotzoomnormal, "on_gnuplot_zoom_out_button_clicked":self.gnuplotzoomout,"on_gnuplot_save_button_clicked" :self.gnuplotsave} self.wTree.signal_autoconnect(dic) #-----plotting area for graphs self.graphview = self.wTree.get_widget("hbox7") #-----plotting area for cloud detection graph self.figure1 = Figure(figsize=(12,6),facecolor='y',dpi=55,edgecolor='black',linewidth=5.0) self.axis1 = self.figure1.add_subplot(111) self.axis1.grid(True) self.canvas1 = FigureCanvasGTK(self.figure1) self.graphview.pack_start(self.canvas1,True,True) self.canvas1.show() self.axis1.set_autoscale_on(True) #-----plotting area for back scatterprofile self.figure2 = Figure(figsize=(12,6),facecolor='y', dpi=55,edgecolor='black',linewidth=4.0) self.axis2 = self.figure2.add_subplot(111) self.axis2.grid(True) self.canvas2 = FigureCanvasGTK(self.figure2) self.graphview.pack_end(self.canvas2,True,True) self.canvas2.show() ################################# File Openning and dataprocessing ####################### def openfile(self,obj): self.filename=None if(self.threadvar==1): os.system("killall -9 gnuplot > /dev/zero") self.datpbar=0 self.gnupbar=0 self.datadecode_thread_obj.stop() dialog = gtk.FileChooserDialog("Open..", None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) dialog.set_default_response(gtk.RESPONSE_OK) filter = gtk.FileFilter() filter.set_name("DATA FILES") filter.add_pattern("*.dat") dialog.add_filter(filter) response=dialog.run() if response == gtk.RESPONSE_OK: self.filename=dialog.get_filename() self.window.set_title("Free Ceilo 0.1 ( "+self.filename+")") self.datpbar=1 self.gnupbar=1 #---- Threads for datadecoding ,gnuplot ---# cond=Condition() self.datadecode_thread_obj=datadecodethreadlib.DataDecodeThread(cond,self.filename) self.datadecode_thread_obj.start() self.threadvar=1 #---- Threads ends ------# elif response == gtk.RESPONSE_CANCEL: print 'Closed, no files selected' dialog.destroy() ###############################Cloud Detection Graph Plotting ####################### ##### Show the first cloud ###### def firstcloudshow(self,obj): self.x= getstatusobj.getdataobj() self.axis1.set_xlabel('Time') self.axis1.set_ylabel('Altitude'+","+unit) self.axis1.set_title('Cloud Detection Graph') self.axis1.grid(True) self.axis1.plot_date(self.x.lwst_time_index,self.x.lwst_cldbse_hgt,'b.',label=" First") self.axis1.xaxis.set_major_locator(MaxNLocator(6)) ####-------for rotating the xticks------------------########### #labels = self.axis1.get_xticklabels() #setp(labels, rotation=90, fontsize=10) self.axis1.legend() self.canvas1.draw() ##### Show the second cloud ###### def secondcloudshow(self,obj): self.x= getstatusobj.getdataobj() self.axis1.set_xlabel('Time') self.axis1.set_ylabel('Altitude'+","+unit) self.axis1.set_title('Cloud Detection Graph') self.axis1.grid(True) self.axis1.plot_date(self.x.sec_time_index,self.x.sec_lwst_cldbse_hgt,'g.',label="Second") self.axis1.xaxis.set_major_locator(MaxNLocator(6)) self.axis1.legend() self.canvas1.draw() ##### Show the third cloud ###### def thirdcloudshow(self,obj): self.x= getstatusobj.getdataobj() self.axis1.set_xlabel('Time') self.axis1.set_ylabel('Altitude'+","+unit) self.axis1.set_title('Cloud Detection Graph') self.axis1.grid(True) self.axis1.plot_date(self.x.higst_time_index,self.x.higst_cldbse_hgt,'r.',label="Third") self.axis1.xaxis.set_major_locator(MaxNLocator(6)) self.axis1.legend() self.canvas1.draw() ####---------Zooming the figure--------######## def clouddetectzoominx(self,obj): self.axis1.zoomx(2) self.canvas1.draw() def clouddetectzoomoutx(self,obj): self.axis1.zoomx(-2) self.canvas1.draw() def clouddetectzoominy(self,obj): self.axis1.zoomy(2) self.canvas1.draw() def clouddetectzoomouty(self,obj): self.axis1.zoomy(-2) self.canvas1.draw() def clouddetectgraphclear(self,obj): self.axis1.clear() self.axis1.set_xlabel('Time') self.axis1.set_ylabel('Altitude'+","+unit) self.axis1.set_title('Cloud Detection Graph') self.axis1.grid(True) self.canvas1.draw() #############SAVE CLD DETECT######### def clouddetectsave(self,obj): dialog= gtk.FileChooserDialog("Save Cloud Detection Graph ", None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) dialog.set_default_response(gtk.RESPONSE_OK) dialog.set_current_folder(os.curdir) dialog.set_do_overwrite_confirmation(True) filter = gtk.FileFilter() filter.set_name("image(png) file") filter.add_pattern("*.png") dialog.add_filter(filter) response=dialog.run() if response == gtk.RESPONSE_OK: plotimagefile=dialog.get_filename() dialog.destroy() #self.canvas1.print_figure1(plotimagefile, 72,'w', 'w','portrait') self.figure1.savefig(plotimagefile) self.canvas1.destroy() self.canvas1 = FigureCanvasGTK(self.figure1) self.graphview.pack_start(self.canvas1, True,True) self.canvas1.show() elif response == gtk.RESPONSE_CANCEL: print "no file is selected" dialog.destroy() self.canvas1.show() ##########################################GNUPLOT#################################### #--------- The Functions to display Gnuplot image and zoom the images and save------# def gnuplotshow(self,obj): self.gnuplot_image.set_from_file("/tmp/FreeCeilo/.gnu.png") #----Functions to zoom the images----# def gnuplotzoomin(self,obj): self.imagex+=30 self.imagey+=20 self.gnuplotzoom() def gnuplotzoomout(self,obj): self.imagex-=30 self.imagey-=20 self.gnuplotzoom() def gnuplotzoomnormal(self,obj): self.imagex=810 self.imagey=540 self.gnuplotzoom() def gnuplotzoom(self): self.gnuplot_image.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file("/tmp/FreeCeilo/.gnu.png").scale_simple(self.imagex,self.imagey,gtk.gdk.INTERP_BILINEAR)) #---Function to save the gnuplot image in another location using save as option---# def gnuplotsave(self,obj): dialog= gtk.FileChooserDialog("Save as..", None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) dialog.set_default_response(gtk.RESPONSE_OK) dialog.set_do_overwrite_confirmation(True) filter = gtk.FileFilter() filter.set_name("image(png) file") filter.add_pattern("*.png") dialog.add_filter(filter) response=dialog.run() if response == gtk.RESPONSE_OK: plotimagefile=dialog.get_filename() if not plotimagefile.endswith(".png"): k=plotimagefile.find(".") plotimagefile=plotimagefile[0:k] plotimagefile=plotimagefile.__add__(".png") shutil.copyfile("/tmp/FreeCeilo/.gnu.png",plotimagefile) elif response == gtk.RESPONSE_CANCEL: print "no file is selected" dialog.destroy() ################################# Quit the Program ######################### def quitprogram(self,obj): self.id1=os.getpid() gobject.source_remove(self.timer) self.timer = 0 gtk.main_quit() os.system("killall -9 gnuplot > /dev/zero") os.popen("kill -9 "+str(self.id1)) sys.exit(0)
class GuiTourneyGraphViewer(threading.Thread): def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes": True, "Sites": True, "Games": False, "Limits": False, "LimitSep": False, "LimitType": False, "Type": False, "UseType": 'tour', "Seats": False, "SeatSep": False, "Dates": True, "Groups": False, "Button1": True, "Button2": True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display=filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5, 4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() green = self.getData(playerids, sitenos) print _("Graph generated in: %s") % (time() - starttime) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Tournaments"), fontsize=12) self.ax.set_ylabel("$", fontsize=12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000. ]) self.ax.plot(green, color='green', label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + ': $%.2f' % green[-1]) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user else: self.ax.set_title(_("Tournament Results")) #Draw plot self.ax.plot(green, color='green', label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + ': $%.2f' % green[-1]) legend = self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) #end of def showClicked def getData(self, names, sites): tmp = self.sql.query['tourneyResults'] print "DEBUG: getData" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace(",)", ")") print "DEBUG: sql query:" print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return None green = map(lambda x: float(x[1]), winnings) #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) #blueline = cumsum(blue) #redline = cumsum(red) return (greenline / 100) def exportGraph(self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog( title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename( self.exportFile ) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename( ) + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class Graphtemp(object): def __init__(self, tipo): self.cuento = 0 self.magico = 800 self.app = None self.tipo = tipo self.data = np.array([]) self.fechas = np.array([]) self.caldera = np.array([]) self.dpi = 100 self.f = Figure((3.0, 3.0), dpi=self.dpi) self.f.subplots_adjust(hspace=1, left=.05, right=.99, top=.92) self.a = self.f.add_subplot(111) self.a.set_axis_bgcolor('white') setp(self.a.get_xticklabels(), fontsize=8) setp(self.a.get_yticklabels(), fontsize=8) self.plot_data = self.a.plot( self.data, linewidth=1.4, color=tipo.color, )[0] self.plot_data2 = self.a.plot( np.array([]), linestyle="--", linewidth=1.4, color="g", )[0] self.plot_data3 = self.a.plot( np.array([]), linestyle="--", linewidth=1.4, color="r", )[0] self.a.grid(True) # self.a.set_xlabel('Time') self.a.set_ylabel(tipo.formal) self.canvas = FigureCanvas(self.f) @inlineCallbacks def on_redraw_timer(self): from twisted.internet import reactor self.tiempo = time.time() if not self.app.is_stoped: # if paused do not add data, but still redraw the plot # (to respond to scale modifications, grid change, etc.) # #print "They have called me!" numero = self.tipo.actual if numero: self.data = np.append( self.data, float(numero)) # .append(float()) acá le agrega le dato self.fechas = np.append(self.fechas, self.cuento) self.caldera = np.append(self.caldera, 40 if self.app.caldera.actual else 20) self.cuento += 1 if self.fechas.size > self.magico: self.data = np.delete(self.data, 0) self.fechas = np.delete(self.fechas, 0) self.caldera = np.delete(self.caldera, 0) xmax = self.fechas.max( ) if self.fechas.max() > self.magico else self.magico xmin = xmax - self.magico #ymin = round(self.data.min(), 0) - 1 ymin = -.5 ymax = 42 #round(self.data.max(), 0) + 1 self.a.set_xbound(lower=xmin, upper=xmax) self.a.set_ybound(lower=ymin, upper=ymax) self.plot_data2.set_xdata(np.array([0, xmax])) yield self.plot_data2.set_ydata(np.array([self.tipo.medio] * 2)) #self.fechas = np.append(self.fechas, datetime()) yield setp(self.a.get_xticklabels(), visible=True) yield #print np.arange(self.data.size) == self.fechas self.plot_data.set_xdata(self.fechas) self.plot_data.set_ydata(self.data) yield self.plot_data3.set_ydata(self.caldera) self.plot_data3.set_xdata(self.fechas) yield d = Deferred() reactor.callLater(.1, d.callback, None) yield d self.canvas.draw() yield else: d = Deferred() reactor.callLater(.1, d.callback, None) yield d reactor.callLater(time.time() - self.tiempo - .1, self.on_redraw_timer)
class MatPlotLibPlotter(Plotter): def __init__(self, module, mainWindow, preferredWidth, preferredHeight, startWithImage=True): """ Constructor for a Mat Plot Lib Plotter. module - the module using this plotter mainWindow - the MainWindow displaying this plotter preferredWidth - the default width of the plotter preferredHeight - the default height of the plotter startWithImage - a boolean, that if true will display the module's baseimage at first, and switch to the matplotlib plotter when setBaseImageVisible(False) or drawFigure() is called. """ # call the superclass constructor Plotter.__init__(self, module, mainWindow) self.preferredWidth = preferredWidth self.preferredHeight = preferredHeight self.imageVisible = startWithImage path = mainWindow.getPath() if (self.module.baseimage): self.imageFile = self.module.directory + os.sep + self.module.baseimage else: self.imageFile = path + os.sep + "img" + os.sep + "seatree.jpg" self.figure = matplotlib.figure.Figure() self.axis = self.figure.add_subplot(111) self.canvas = FigureCanvas(self.figure) self.bgColor = "white" self.colorMap = matplotlib.cm.Spectral self.evenAspectRatio = True self.climMin = None self.climMax = None self.image = None self.contourLines = False self.contourFills = False self.colorBarOrientation = VERTICAL self.gui = mplSettingsPanel.MPLSettingsPanel(self) def tellModuleToReplot(self): self.module.updatePlot() def setColorMapByName(self, name, reversed=False, updateGUI=True): self.colorMap = matplotlib.cm.get_cmap(name=name) #print "Reversing? " + str(reversed) # set our custom 'seatree_reversed' flag try: dummy = self.colorMap.seatree_reversed # print "it has a reversed flag!" except: # print "it doesn't have a reversed flag!" self.colorMap.seatree_reversed = False # print "Reversing? " + str(reversed) # print "Already Reversed? " + str(self.colorMap.seatree_reversed) if reversed != self.colorMap.seatree_reversed: # print "lets flip it!" self.colorMap = self.reverseColormap(self.colorMap) self.colorMap.seatree_reversed = reversed if updateGUI: self.gui.loadOptionsFromPlotter() def reverseColormap(self, cm): # print cm._segmentdata['red'] #self.__reverseCMComponent(cm, 'red') #self.__reverseCMComponent(cm, 'blue') #self.__reverseCMComponent(cm, 'green') segData = dict() numSegs = len(cm._segmentdata['red']) reds = [] for i in range(numSegs): index = numSegs - i - 1 val = self.colorMap._segmentdata['red'][index] reds.append((1 - val[0], val[1], val[2])) numSegs = len(cm._segmentdata['blue']) blues = [] for i in range(numSegs): index = numSegs - i - 1 val = self.colorMap._segmentdata['blue'][index] blues.append((1 - val[0], val[1], val[2])) numSegs = len(cm._segmentdata['green']) greens = [] for i in range(numSegs): index = numSegs - i - 1 val = self.colorMap._segmentdata['green'][index] greens.append((1 - val[0], val[1], val[2])) segData = {'red': reds, 'blue': blues, 'green': greens} newCM = matplotlib.colors.LinearSegmentedColormap(cm.name,segData,1024) #cm._segmentdata = segData # print newCM._segmentdata['red'] newCM.seatree_reversed = True return newCM def isColorMapReversed(self, colorMap=None): if colorMap == None: colorMap = self.colorMap try: if self.colorMap.seatree_reversed: return True except: self.colorMap.seatree_reversed = False return False return False def setColorMap(self, colorMap): self.colorMap = colorMap self.gui.loadOptionsFromPlotter() def getColorMap(self): return self.colorMap def setColorbarOrientation(self, orientation): self.colorBarOrientation = orientation def applyColorLimits(self): if self.image != None: try: self.image.set_clim(self.climMin, self.climMax) except: pass def setColorLimits(self, min, max, updateGUI=True): self.climMin = min self.climMax = max if updateGUI: self.gui.loadOptionsFromPlotter() def getColorLimits(self): return (self.climMin, self.climMax) def setContourFills(self, contour): """ Plot XYZ data contoured. contour - boolean that, if true, will contour plots """ self.contourFills = contour def getContourFills(self): return self.contourFills def setContourLines(self, contour): """ Plot XYZ data contour lines drawn. contour - boolean that, if true, will contour plots """ self.contourLines = contour def getContourLines(self): return self.contourLines def setBaseImageVisible(self, visible): if (self.imageVisible != visible): self.imageVisible = visible self.mainWindow.loadPlotter(self) def drawFigure(self, applyAspect=True): self.setBaseImageVisible(False) if applyAspect: self.applyAspectRatio() self.applyColorLimits() self.canvas.draw() self.mainWindow.setSaveActive(True) def addTextLabel(self, x, y, text, **kwargs): self.figure.text(x, y, text, **kwargs) def setAxis(self, axis): self.axis = axis def getAxis(self): return self.axis def getFigure(self): return self.figure def getLastImage(self): return self.image def clearFigure(self, subplot=111): self.figure.clear() if subplot != None: self.axis = self.figure.add_subplot(subplot) self.mainWindow.setSaveActive(False) def getAxisLimits(self, axis=None): """ Returns the limits of the axis as a tuple with this format: [xmin, xmax, ymin, ymax] """ if axis == None: axis = self.axis x = axis.get_xlim() y = axis.get_ylim() return (x[0], x[1], y[0], y[1]) def limitAxis(self, minX, maxX, minY, maxY): self.minX = minX self.maxX = maxX self.minY = minY self.maxY = maxY self.axis.set_xlim(minX,maxX) self.axis.set_ylim(minY,maxY) def applyAspectRatio(self, axis=None): if axis == None: axis = self.axis if self.evenAspectRatio: axis._aspect = 'equal' axis.apply_aspect() else: axis._aspect = 'auto' axis.apply_aspect() def setAspectRatioEven(self, even): self.evenAspectRatio = even def plotXYZFromFile(self, xyzFile, numX, numY, title="", colorBar=False): """ Load data from a file and call plotXYZData """ a = matplotlib.mlab.load(xyzFile) n = numX # determine square size m = numY # determine geometry xmin, xmax = min(a[:,0]), max(a[:,0]) ymin, ymax = min(a[:,1]), max(a[:,1]) range = [ xmin , xmax, ymin, ymax ]; # assign three columns to vectors x=a[:,0].reshape(n,m) y=a[:,1].reshape(n,m) z=a[:,2].reshape(n,m) self.plotXYZData(x,y,z,title,colorBar,range); def plotXYZFromSquareDataFile(self, xyzFile, title="", colorBar=False): """ Load data from a file assuming that the data is given on an n by n "square" set of points and call plotXYZData """ a = matplotlib.mlab.load(xyzFile) n = int(math.sqrt(a.shape[0])) # determine square size m = n # determine geometry xmin, xmax = min(a[:,0]), max(a[:,0]) ymin, ymax = min(a[:,1]), max(a[:,1]) range = [ xmin , xmax, ymin, ymax ]; # assign three columns to vectors x=a[:,0].reshape(n,m) y=a[:,1].reshape(n,m) z=a[:,2].reshape(n,m) self.plotXYZData(x,y,z,title,colorBar,range); def plotXYZData(self, x, y, z, title="", colorBar=False, range=None): """ Plot xyz data in vectors x y z if range is set, will expect four entry vector with limiting range for plot sorted as [xmin, xmax,ymin,ymax] """ if self.contourFills: self.image = self.axis.contourf(x, y, z, cmap=self.colorMap, shading='flat', extend='both') else: self.image = self.axis.pcolor(x, y, z, cmap=self.colorMap, shading='flat') if self.contourLines: self.axis.contour(x, y, z, colors='black', linewidths=1, shading='flat', extend='both') if range != None: self.limitAxis(range[0],range[1],range[2],range[3]); if (colorBar): self.figure.colorbar(self.image, orientation=self.colorBarOrientation) if (title): self.axis.set_title(title) def plotRegularXYZData(self, data, title="", colorBar=False): """ Plot xyz data in vectors x y z if range is set, will expect four entry vector with limiting range for plot sorted as [xmin, xmax,ymin,ymax] """ self.image = self.axis.imshow(data, cmap=self.colorMap) if self.contourLines: self.axis.contour(x, y, z, colors='black', linewidths=1, shading='flat', extend='both') if (colorBar): self.figure.colorbar(self.image, orientation=self.colorBarOrientation) if (title): self.axis.set_title(title) def addLine(self, xdata, ydata, color='b', **kwargs): """ Add a line to the current axis xdata - numpy array of x values ydata - numpy array of y values color - color of the line """ line = matplotlib.lines.Line2D(xdata,ydata,color=color,**kwargs) self.axis.add_line(line) def addArrow(self, x, y, dx, dy, width=1.0): arrow = matplotlib.patches.Arrow(x, y, dx, dy, width=width) self.axis.add_patch(arrow) def plotScatterData(self, x, y, type=None, color='b', colorMap=None, colorBar=False, size=30, globalWidth=0.2, linewidths=None, setAsImage=True): if type == None: type = CIRCLE if globalWidth != None and not linewidths: linewidths = [] for i in range(0, len(x)): linewidths.append(globalWidth) image = self.axis.scatter(x, y, s=size, c=color, cmap=colorMap, marker=type, linewidths=linewidths) if setAsImage: self.image = image if (colorBar): self.figure.colorbar(self.image) def loadXYFile(self, file): fp = open(file, "r") lines = fp.readlines() x = [] y = [] for line in lines: line = line.strip(" \t\n") if line.startswith("#"): continue lineSplit = line.split() if len(lineSplit) < 2: continue x.append(float(lineSplit[0])) y.append(float(lineSplit[1])) return x, y def plotPolygon(self, polygon, arrows=False, fill=False): poly = matplotlib.patches.Polygon(polygon, fill=fill) self.axis.add_patch(poly) def loadGMTPolygonFile(self, polyFile): fp = open(polyFile, "r") lines = fp.readlines() fp.close() return self.loadGMTPolygons(lines) def loadGMTPolygons(self, lines): polys = [] curPoly = [] for line in lines: line = line.strip(" \t\n") if line.startswith(">"): if len(curPoly) > 0: #print curPoly polys.append(curPoly) curPoly = [] continue lineSplit = line.split() if len(lineSplit) < 2: print "bad polygon line parse!" continue poly = [] poly.append(float(lineSplit[0])) poly.append(float(lineSplit[1])) #print "added: " + str(poly) curPoly.append(poly) return polys def getMainWidget(self): if (self.imageVisible): # return the Image Event Box for this image plotter self.image = gtk.Image() self.image.show() self.imageBuffer = 7 self.imageEB = gtk.EventBox() self.imageEB.add(self.image) self.imageEB.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.bgColor)) self.imageEB.set_size_request(self.preferredWidth + self.imageBuffer, self.preferredHeight + self.imageBuffer) self.image.set_from_file(self.imageFile) self.mainWindow.setSaveActive(False) self.imageEB.show_all() return self.imageEB else: self.plotBuffer = 10 self.canvas.show_all() self.canvas.set_size_request(self.preferredWidth + self.plotBuffer, self.preferredHeight + self.plotBuffer) return self.canvas def getBottomPanel(self): return self.gui def getSaveTypes(self): saveTypes = [] saveTypes.append(["png", "PNG Image"]) #saveTypes.append(["ps", "PostScript Plot"]) # should work, but doesn't for some reason saveTypes.append(["pdf", "Portable Document Format"]) saveTypes.append(["svg", "Scalable Vector Graphics Format"]) return saveTypes def savePlot(self, typeExtension, fileName): # don't check for save types since we only have 1 if self.figure != None: self.figure.savefig(fileName, format=typeExtension) """ For some reason matplotlib doesn't reset the pixmap to the on screen one after rendering, so you can't replot anything until an event like a resize happens. This will get around that limitation """ self.canvas._renderer_init() self.canvas._pixmap = gtk.gdk.Pixmap (self.canvas.window, self.canvas._pixmap_width, self.canvas._pixmap_height) self.canvas._renderer.set_pixmap (self.canvas._pixmap) return True return False def displayImage(self, imageFile, default=False): """ This method will display an image file imageFile - the file name of the image to be displayed """ self.imageFile = imageFile self.image.set_from_file(self.imageFile) if not default: self.mainWindow.setSaveActive(True)
class GuiBankrollGraphViewer (threading.Thread): def __init__(self, settings, db, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.settings = settings self.db = db self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) view = None filters_display = { "Heroes" : True, "Sites" : True, "Games" : False, "Limits" : False, "LimitSep" : False, "LimitType" : False, "Type" : False, "UseType" : 'tour', "Seats" : False, "SeatSep" : False, "Dates" : True, "Groups" : False, "Button1" : True, "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) numeric_const_pattern = r""" [-+]? # optional sign (?: (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc | (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc ) # followed by optional exponent part if desired (?: [Ee] [+-]? \d+ ) ? """ self.rx = re.compile(numeric_const_pattern, re.VERBOSE) self.mainHBox = gtk.HBox(False, 0) self.mainHBox.show() self.leftPanelBox = self.filters.get_vbox() #add a button to modify transferts ButtonTransfert=gtk.Button(_("ButtonTransfert")) ButtonTransfert.set_label(_("_Modify Transferts")) ButtonTransfert.connect("clicked", self.transfertsWindow, "clicked") ButtonTransfert.set_sensitive(True) self.filters.mainVBox.pack_start(ButtonTransfert, False) ButtonTransfert.show() self.hpane = gtk.HPaned() self.hpane.pack1(self.leftPanelBox) self.mainHBox.add(self.hpane) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None #update the graph at entry (simulate a «Refresh Graph» click) gobject.GObject.emit (self.filters.Button1, "clicked"); self.db.rollback() #endinit def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() (green, dates, transfersAmount, transfersDate, transferType) = self.getData(playerids, sitenos) print _("Graph generated in: %s") %(time() - starttime) #Set axis labels and grid overlay properites self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) self.ax.plot(green, color='green', label=_('Bankroll') + _('Profit') + ': $%.2f' % green[-1]) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user else: self.ax.set_title(_("Bankroll Results")) useDates = True #nothing to draw if (len(green) == 0): return #Get the dates of the action (transfert / cg hand / tourney) #if it has no date, get the most ancient date and assume it's its one if dates[0] is None: i = 1 while i < len(dates) and type(dates[i]) is None: i = i+1 if i == len(dates): print "Wow wow wow : no dates in your whole database" useDates = False else: dates[0] = dates[i] #now, convert date to dateTime format if useDates: for i in range(0, len(dates)): if dates[i] is None: dates[i] = dates[i-1] #~else: #~dates[i] = datetime.datetime.strptime(dates[i], "%Y-%m-%d %H:%M:%S") for i in range(0, len(green)-1): beneficeSinceStart=green[i+1]-self.totalTransfer(dates[i+1], transfersAmount, transfersDate) mycolor = self.color(transferType[i+1], beneficeSinceStart) self.ax.plot([i,i+1], [green[i],green[i+1]], color=mycolor) #show date and gain only 5 times on X axis if (i % (len(green)/5) == 1): #the gain since start at this time if (mycolor=='cyan'): mycolor='green' self.ax.annotate('%.2f' % beneficeSinceStart, xy=(i, 0), color=mycolor, xycoords=('data', 'axes fraction'), xytext=(0, 18), textcoords='offset points', va='top', ha='left') #and show the date too if enabled if useDates: dateMMDD=datetime.datetime.strptime(dates[i], "%Y-%m-%d %H:%M:%S").strftime('%d/%m') self.ax.annotate(dateMMDD, xy=(i, 0), xycoords=('data', 'axes fraction'), xytext=(0, -18), textcoords='offset points', va='top', ha='left') #plot the last one and show the top corner legend i = len(green)-1 bankroll = float(green[i]) profit = bankroll if len(transfersAmount)>0: profit -= transfersAmount[len(transfersAmount)-1] self.ax.plot([i,i+1], [green[i],green[i]], color=self.color(transferType[i], beneficeSinceStart), label=_('Bankroll') + ': \$%.2f' % bankroll + '\n' + _('Profit') + ': \$%.2f' % profit) legend = self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #end of def showClicked #return total cash from transfer until «date» def totalTransfer(self, date, amounts, transfersDate): #~print transferts if len(amounts) == 0 or (date < transfersDate[0]): return 0 i=0 while (i < len(amounts)-1 and date > transfersDate[i]): i = i + 1 return amounts[i] def color(self, typ, gain): # 0:play, 1:transfert if typ == 1: return 'cyan' elif gain < 0: return 'red' else: return 'green' def getData(self, names, sites): print "DEBUG: args are :" print names print sites tmp = self.rightRequest('getAllPrintIdSite', names, sites) tmp2 = self.rightRequest('getAllTransfer', names, sites) print "DEBUG: sql query:" print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.cursor.execute(tmp2) transfers = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return None green = map(lambda x:float(x[0]), winnings) dates = map(lambda x:x[1], winnings) typeOf = map(lambda x:x[2], winnings) transferAmounts = map(lambda x:x[0], transfers) transferDates = map(lambda x:x[1], transfers) #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) #blueline = cumsum(blue) #redline = cumsum(red) transferAmounts = cumsum(transferAmounts) return (greenline/100., dates, transferAmounts, transferDates, typeOf) def exportGraph (self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog(title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OK,gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename(self.exportFile) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename() + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy() #end of def exportGraph def transfertsWindow (self, widget, data) : #if the window is already launched, put it in front if not self.settings['global_lock'].acquire(wait=False, source="GuiBankrollGraphViewer"): return #create the window … #first, check if there is at least one player on database, else quit if (len(self.filters.getHeroes()) == 0): print "No site/hero found, abort" return self.transferWindow = gtk.Window(gtk.WINDOW_TOPLEVEL) self.transferWindow.set_title("Transferts Management") self.transferWindow.set_position(gtk.WIN_POS_CENTER) self.transferWindow.set_transient_for(self.parent) self.transferWindow.connect("destroy", self.release) vbox = gtk.VBox(False, 0) self.transferWindow.add(vbox) ##### ##### #«new transfert» part hboxAdd = gtk.HBox(True, 0) vbox.pack_start(hboxAdd) #calendar cal = gtk.Calendar() vboxSelection = gtk.VBox(False, 0) #hour selection hourLabel = gtk.Label(_('Select hour:')) hourLabel.set_alignment(xalign=0.0, yalign=0.5) hboxHour = gtk.HBox(False, 0) timeHourPicker = gtk.SpinButton(None, 0, 0); timeHourPicker.set_increments(1, 6) timeHourPicker.set_range(0, 23) timeHourPicker.set_value(datetime.datetime.now().hour) # current hour timeMinPicker = gtk.SpinButton(None, 0, 0); timeMinPicker.set_increments(1, 10) timeMinPicker.set_range(0, 59) timeMinPicker.set_value(datetime.datetime.now().minute) # current hour #site/hero selection IDLabel = gtk.Label(_('Site - hero:')) IDLabel.set_alignment(xalign=0.0, yalign=0.5) IDSelection = gtk.combo_box_new_text() for site, hero in self.filters.getHeroes().items(): IDSelection.append_text(site + " - " + hero) IDSelection.set_active(0) #amount of virement amountLabel = gtk.Label(_('Tranfert amount ($):')) amountLabel.set_alignment(xalign=0.0, yalign=0.5) amountEntry = gtk.Entry() amountEntry.set_text('10.00') amountEntry.connect('changed', self.on_changed, 'changed') #button add buttonAdd = gtk.ToolButton(gtk.STOCK_ADD) buttonAdd.connect('clicked', self.newTransfer, 'clicked', cal, timeHourPicker, timeMinPicker, IDSelection, amountEntry) buttonAdd.connect('clicked', self.destroyWindow) hboxAdd.pack_start(cal, 0) hboxAdd.pack_start(vboxSelection, 0) vboxSelection.pack_start(hourLabel, 0) vboxSelection.pack_start(hboxHour, 0) hboxHour.pack_start(timeHourPicker, 0) hboxHour.pack_start(timeMinPicker, 0) vboxSelection.pack_start(IDLabel, 0) vboxSelection.pack_start(IDSelection, 0) vboxSelection.pack_start(amountLabel, 0) vboxSelection.pack_start(amountEntry, 0) vboxSelection.pack_start(buttonAdd, -1) #end of "new transfert" part ##### #### #### #start of "delete transfert" part hboxDelete = gtk.HBox(False, 0) vbox.pack_start(hboxDelete) #tab to create vboxTab = gtk.VBox(False, 0) self.createTab(vboxTab) buttonDelete = gtk.ToolButton(gtk.STOCK_DELETE) buttonDelete.connect('clicked', self.deleteTransfer, 'clicked') hboxDelete.pack_start(vboxTab, 1) hboxDelete.pack_start(buttonDelete, 1) #end of "delete transfert" part #### self.transferWindow.show_all() return #end of def transfertsWindow def release(self, widget, data=None): self.settings['global_lock'].release() self.transferWindow.destroy() return def on_changed(self, widget, data): #~text = widget.get_text().strip() #~widget.set_text(''.join([i for i in text if i in '0123456789'])) entry_text = widget.get_text() newtext = self.rx.findall(entry_text) if len(newtext) : widget.set_text(newtext[0]) else: widget.set_text("") def destroyWindow(self, widget): self.transferWindow.destroy() return def newTransfer(self, widget, data, cal, timeHourPicker, timeMinPicker, IDSelection, amountEntry): year, month, day = cal.get_date() month = month + 1 # because gtk gives it between 0 and 11 ?! hour = timeHourPicker.get_value() minute = timeMinPicker.get_value() (site, separator, hero) = IDSelection.get_active_text().partition(' - ') transfer = float(amountEntry.get_text()) now = datetime.datetime(year, month, day, int(hour), int(minute), 0) #get siteID from siteName (table "sites") self.db.cursor.execute('SELECT id from sites where name LIKE "' + site + '"') siteID = self.db.cursor.fetchall()[0][0] self.db.rollback() #get heroID from heroName and siteID (table "players") self.db.cursor.execute('select id from players where name LIKE "' + hero + '" and siteId = ' + str(siteID)) heroID = self.db.cursor.fetchall()[0][0] self.db.rollback() #insert it in the table now query = "INSERT INTO BankrollsManagement(siteId, playerId, transfer, startTime) VALUES (?, ?, ?, ?)" #~print "DEBUG:\n%s" % query self.db.cursor.execute(query, (siteID, heroID, int(transfer*100), now)) self.db.commit() self.db.rollback() #update the graph gobject.GObject.emit (self.filters.Button1, "clicked"); def deleteTransfer(self, widget, data): #get the active line of the array selected = self.view.get_cursor()[0] #if no row selected, abort if selected is None: return #else, retrieve the line ( /!\ rowNumber != Id from the table ), rowNumber = selected[0] line = self.liststore[0][rowNumber] id = line[0] #then delete it from table and refresh graph self.db.cursor.execute('DELETE FROM BankrollsManagement WHERE id=' + str(id)) self.db.commit() self.db.rollback() #destroy the window self.destroyWindow(widget) gobject.GObject.emit (self.filters.Button1, "clicked"); def createTab(self, vbox) : cols_to_show = [ ["id", False, _("ID"), 0.0, "%s", "str"] , ["siteName", True, _("Site"), 0.0, "%s", "str"] # true not allowed for this line (set in code) , ["playerName", True, _("Name"), 0.8, "%s", "str"] # true not allowed for this line (set in code) , ["amount", True, _("Amount"), 0.0, "%0.2f", "str"] , ["date", True, _("Date"), 0.0, "%s", "str"]] self.liststore=[] self.liststore.append( gtk.ListStore(*([str] * len(cols_to_show))) ) self.view = gtk.TreeView(model=self.liststore[0]) self.view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) #vbox.pack_start(view, expand=False, padding=3) vbox.add(self.view) textcell = gtk.CellRendererText() textcell50 = gtk.CellRendererText() textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) listcols = [] listcols.append( [] ) # Create header row eg column: ("game", True, "Game", 0.0, "%s") for i, col in enumerate(cols_to_show): listcols[0].append(gtk.TreeViewColumn(col[_COL_HEADING])) self.view.append_column(listcols[0][i]) if col[_COL_FORMAT] == '%s': if col[_COL_XALIGN] == 0.0: listcols[0][i].pack_start(textcell, expand=True) listcols[0][i].add_attribute(textcell, 'text', i) cellrend = textcell else: listcols[0][i].pack_start(textcell50, expand=True) listcols[0][i].add_attribute(textcell50, 'text', i) cellrend = textcell50 listcols[0][i].set_expand(True) else: listcols[0][i].pack_start(numcell, expand=True) listcols[0][i].add_attribute(numcell, 'text', i) listcols[0][i].set_expand(True) cellrend = numcell query = self.sql.query['getAllTransferInformations'] #~print "DEBUG:\n%s" % query self.db.cursor.execute(query) result = self.db.cursor.fetchall() #~print "result of the big query in addGrid:",result colnames = [desc[0] for desc in self.db.cursor.description] #~for i in range(0, len(tab)) rows = len(result) # +1 for title row counter = 0 row = 0 sqlrow = 0 while sqlrow < rows: treerow = [] for col,column in enumerate(cols_to_show): if column[_COL_ALIAS] in colnames: if column[_COL_ALIAS] == 'amount': #convert $ cents to $ value = result[sqlrow][colnames.index(column[_COL_ALIAS])]/100. else: value = result[sqlrow][colnames.index(column[_COL_ALIAS])] else: value = 111 if value != None and value != -999: treerow.append(column[_COL_FORMAT] % value) else: treerow.append(' ') #print "addGrid, just before end of big for. grid:",grid,"treerow:",treerow iter = self.liststore[0].append(treerow) sqlrow += 1 row += 1 vbox.show_all() def rightRequest(self, request, names, sites): tmp = self.sql.query[request] print "DEBUG: getData. :" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace(",)", ")") return tmp
class GuiSessionViewer (threading.Thread): def __init__(self, config, querylist, mainwin, debug=True): self.debug = debug self.conf = config self.sql = querylist self.liststore = None self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 self.fig = None self.canvas = None self.ax = None self.graphBox = None # create new db connection to avoid conflicts with other threads self.db = Database.Database(self.conf, sql=self.sql) self.cursor = self.db.cursor settings = {} settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_default_paths()) # text used on screen stored here so that it can be configured self.filterText = {'handhead':_('Hand Breakdown for all levels listed above')} filters_display = { "Heroes" : True, "Sites" : True, "Games" : False, "Limits" : False, "LimitSep" : False, "LimitType" : False, "Type" : True, "Seats" : False, "SeatSep" : False, "Dates" : True, "Groups" : False, "GroupsAll" : False, "Button1" : True, "Button2" : False } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("_Refresh") self.filters.registerButton1Callback(self.refreshStats) # ToDo: store in config # ToDo: create popup to adjust column config # columns to display, keys match column name returned by sql, values in tuple are: # is column displayed, column heading, xalignment, formatting self.columns = [ ("sid", True, "SID", 0.0, "%s") , ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line , ("n", True, "Hds", 1.0, "%d") , ("start", True, "Start", 1.0, "%d") , ("end", True, "End", 1.0, "%d") , ("hph", True, "Hands/h", 1.0, "%d") , ("profit", True, "Profit", 1.0, "%s") #, ("avgseats", True, "Seats", 1.0, "%3.1f") #, ("vpip", True, "VPIP", 1.0, "%3.1f") #, ("pfr", True, "PFR", 1.0, "%3.1f") #, ("pf3", True, "PF3", 1.0, "%3.1f") #, ("steals", True, "Steals", 1.0, "%3.1f") #, ("saw_f", True, "Saw_F", 1.0, "%3.1f") #, ("sawsd", True, "SawSD", 1.0, "%3.1f") #, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f") #, ("wmsd", True, "W$SD", 1.0, "%3.1f") #, ("flafq", True, "FlAFq", 1.0, "%3.1f") #, ("tuafq", True, "TuAFq", 1.0, "%3.1f") #, ("rvafq", True, "RvAFq", 1.0, "%3.1f") #, ("pofafq", False, "PoFAFq", 1.0, "%3.1f") #, ("net", True, "Net($)", 1.0, "%6.2f") #, ("bbper100", True, "BB/100", 1.0, "%4.2f") #, ("rake", True, "Rake($)", 1.0, "%6.2f") #, ("variance", True, "Variance", 1.0, "%5.2f") ] self.stats_frame = None self.stats_vbox = None self.detailFilters = [] # the data used to enhance the sql select #self.main_hbox = gtk.HBox(False, 0) #self.main_hbox.show() self.main_hbox = gtk.HPaned() self.stats_frame = gtk.Frame() self.stats_frame.show() self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) # self.fillStatsFrame(self.stats_vbox) #self.main_hbox.pack_start(self.filters.get_vbox()) #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) self.main_hbox.pack1(self.filters.get_vbox()) self.main_hbox.pack2(self.stats_frame) self.main_hbox.show() # make sure Hand column is not displayed #[x for x in self.columns if x[0] == 'hand'][0][1] = False def get_vbox(self): """returns the vbox of this thread""" return self.main_hbox def refreshStats(self, widget, data): try: self.stats_vbox.destroy() except AttributeError: pass self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(self.stats_vbox) self.fillStatsFrame(self.stats_vbox) def fillStatsFrame(self, vbox): sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() seats = self.filters.getSeats() sitenos = [] playerids = [] # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _q = self.sql.query['getPlayerId'] _name = Charset.to_utf8(heroes[site]) #print 'DEBUG(_name) :: %s' % _name self.cursor.execute(_q, (_name,)) # arg = tuple result = self.db.cursor.fetchall() if len(result) == 1: playerids.append(result[0][0]) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") sitenos = [2] if not playerids: print _("No player ids found") return if not limits: print _("No limits found") return self.createStatsPane(vbox, playerids, sitenos, limits, seats) def createStatsPane(self, vbox, playerids, sitenos, limits, seats): starttime = time() (results, opens, closes, highs, lows) = self.generateDatasets(playerids, sitenos, limits, seats) self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.generateGraph(opens, closes, highs, lows) vbox.pack_start(self.graphBox) # Separator sep = gtk.HSeparator() vbox.pack_start(sep, expand=False, padding=3) sep.show_now() vbox.show_now() heading = gtk.Label(self.filterText['handhead']) heading.show() vbox.pack_start(heading, expand=False, padding=3) # Scrolled window for detailed table (display by hand) swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.show() vbox.pack_start(swin, expand=True, padding=3) vbox1 = gtk.VBox(False, 0) vbox1.show() swin.add_with_viewport(vbox1) self.addTable(vbox1, results) self.db.rollback() print _("Stats page displayed in %4.2f seconds") % (time() - starttime) #end def fillStatsFrame(self, vbox): def generateDatasets(self, playerids, sitenos, limits, seats): THRESHOLD = 1800 # Minimum number of seconds between consecutive hands before being considered a new session PADDING = 5 # Additional time in minutes to add to a session, session startup, shutdown etc (FiXME: user configurable) # Get a list of all handids and their timestampts #FIXME: Query still need to filter on blind levels q = self.sql.query['sessionStats'] start_date, end_date = self.filters.getDates() q = q.replace("<datestest>", " between '" + start_date + "' and '" + end_date + "'") nametest = str(tuple(playerids)) nametest = nametest.replace("L", "") nametest = nametest.replace(",)",")") q = q.replace("<player_test>", nametest) q = q.replace("<ampersand_s>", "%s") self.db.cursor.execute(q) hands = self.db.cursor.fetchall() # Take that list and create an array of the time between hands times = map(lambda x:long(x[0]), hands) handids = map(lambda x:int(x[1]), hands) winnings = map(lambda x:float(x[4]), hands) #print "DEBUG: len(times) %s" %(len(times)) diffs = diff(times) # This array is the difference in starttime between consecutive hands diffs2 = append(diffs,THRESHOLD + 1) # Append an additional session to the end of the diffs, so the next line # includes an index into the last 'session' index = nonzero(diffs2 > THRESHOLD) # This array represents the indexes into 'times' for start/end times of sessions # times[index[0][0]] is the end of the first session, #print "DEBUG: len(index[0]) %s" %(len(index[0])) if len(index[0]) > 0: #print "DEBUG: index[0][0] %s" %(index[0][0]) #print "DEBUG: index %s" %(index) pass else: index = [[0]] #print "DEBUG: index %s" %(index) #print "DEBUG: index[0][0] %s" %(index[0][0]) pass total = 0 first_idx = 0 lowidx = 0 uppidx = 0 opens = [] closes = [] highs = [] lows = [] results = [] cum_sum = cumsum(winnings) cum_sum = cum_sum/100 sid = 1 # Take all results and format them into a list for feeding into gui model. for i in range(len(index[0])): hds = index[0][i] - first_idx + 1 # Number of hands in session if hds > 0: stime = strftime("%d/%m/%Y %H:%M", localtime(times[first_idx])) # Formatted start time etime = strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])) # Formatted end time minutesplayed = (times[index[0][i]] - times[first_idx])/60 if minutesplayed == 0: minutesplayed = 1 minutesplayed = minutesplayed + PADDING hph = hds*60/minutesplayed # Hands per hour won = sum(winnings[first_idx:index[0][i]])/100.0 hwm = max(cum_sum[first_idx:index[0][i]]) lwm = min(cum_sum[first_idx:index[0][i]]) open = (sum(winnings[:first_idx]))/100 close = (sum(winnings[:index[0][i]]))/100 #print "DEBUG: range: (%s, %s) - (min, max): (%s, %s) - (open,close): (%s, %s)" %(first_idx, index[0][i], lwm, hwm, open, close) results.append([sid, hds, stime, etime, hph, won]) opens.append(open) closes.append(close) highs.append(hwm) lows.append(lwm) #print "DEBUG: Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won) total = total + (index[0][i] - first_idx) first_idx = index[0][i] + 1 sid = sid+1 else: print "hds <= 0" return (results, opens, closes, highs, lows) def clearGraphData(self): try: try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig is not None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea except: err = traceback.extract_tb(sys.exc_info()[2])[-1] print _("***Error: ")+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) raise def generateGraph(self, opens, closes, highs, lows): self.clearGraphData() # print "DEBUG:" # print "highs = %s" % highs # print "lows = %s" % lows # print "opens = %s" % opens # print "closes = %s" % closes # print "len(highs): %s == len(lows): %s" %(len(highs), len(lows)) # print "len(opens): %s == len(closes): %s" %(len(opens), len(closes)) # # for i in range(len(highs)): # print "DEBUG: (%s, %s, %s, %s)" %(lows[i], opens[i], closes[i], highs[i]) # print "DEBUG: diffs h/l: %s o/c: %s" %(lows[i] - highs[i], opens[i] - closes[i]) self.ax = self.fig.add_subplot(111) self.ax.set_title(_("Session candlestick graph")) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Sessions"), fontsize = 12) self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) candlestick2(self.ax, opens, closes, highs, lows, width=0.50, colordown='r', colorup='g', alpha=1.00) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() def addTable(self, vbox, results): row = 0 sqlrow = 0 colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4 # pre-fetch some constant values: cols_to_show = [x for x in self.columns if x[colshow]] self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) for row in results: iter = self.liststore.append(row) view = gtk.TreeView(model=self.liststore) view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) vbox.add(view) textcell = gtk.CellRendererText() textcell50 = gtk.CellRendererText() textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) listcols = [] # Create header row eg column: ("game", True, "Game", 0.0, "%s") for col, column in enumerate(cols_to_show): s = column[colheading] listcols.append(gtk.TreeViewColumn(s)) view.append_column(listcols[col]) if column[colformat] == '%s': if column[colxalign] == 0.0: listcols[col].pack_start(textcell, expand=True) listcols[col].add_attribute(textcell, 'text', col) else: listcols[col].pack_start(textcell50, expand=True) listcols[col].add_attribute(textcell50, 'text', col) listcols[col].set_expand(True) else: listcols[col].pack_start(numcell, expand=True) listcols[col].add_attribute(numcell, 'text', col) listcols[col].set_expand(True) vbox.show_all()
class GuiTourneyGraphViewer (threading.Thread): def __init__(self, querylist, config, parent, debug=True): """Constructor for GraphViewer""" self.sql = querylist self.conf = config self.debug = debug self.parent = parent #print "start of GraphViewer constructor" self.db = Database.Database(self.conf, sql=self.sql) filters_display = { "Heroes" : True, "Sites" : True, "Games" : False, "Limits" : False, "LimitSep" : False, "LimitType" : False, "Type" : False, "UseType" : 'tour', "Seats" : False, "SeatSep" : False, "Dates" : True, "Groups" : False, "Button1" : True, "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name(_("Refresh _Graph")) self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name(_("_Export to File")) self.filters.registerButton2Callback(self.exportGraph) 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) # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax self.graphBox = gtk.VBox(False, 0) self.graphBox.show() self.hpane.pack2(self.graphBox) self.hpane.show() self.fig = None #self.exportButton.set_sensitive(False) self.canvas = None self.db.rollback() def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox #end def get_vbox def clearGraphData(self): try: if self.canvas: self.graphBox.remove(self.canvas) except: pass if self.fig != None: self.fig.clear() self.fig = Figure(figsize=(5,4), dpi=100) if self.canvas is not None: self.canvas.destroy() self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea def generateGraph(self, widget, data): self.clearGraphData() sitenos = [] playerids = [] sites = self.filters.getSites() heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: playerids.append(int(result)) if not sitenos: #Should probably pop up here. print _("No sites selected - defaulting to PokerStars") self.db.rollback() return if not playerids: print _("No player ids found") self.db.rollback() return #Set graph properties self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() green = self.getData(playerids, sitenos) print _("Graph generated in: %s") %(time() - starttime) #Set axis labels and grid overlay properites self.ax.set_xlabel(_("Tournaments"), fontsize = 12) self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) if green == None or green == []: self.ax.set_title(_("No Data for Player(s) Found")) green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 500., 500., 500., 500., 500., 500., 500., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 0., 0., 0., 0., 0., 0., 125., 250., 375., 500., 625., 750., 875., 1000., 875., 750., 625., 500., 375., 250., 125., 0., 0., 0., 0., 500., 1000., 900., 800., 700., 600., 500., 400., 300., 200., 100., 0., 500., 1000., 1000.]) self.ax.plot(green, color='green', label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + ': $%.2f' % green[-1]) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #TODO: Do something useful like alert user else: self.ax.set_title(_("Tournament Results")) #Draw plot self.ax.plot(green, color='green', label=_('Tournaments') + ': %d\n' % len(green) + _('Profit') + ': $%.2f' % green[-1]) legend = self.ax.legend(loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) legend.draggable(True) self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() #self.exportButton.set_sensitive(True) #end of def showClicked def getData(self, names, sites): tmp = self.sql.query['tourneyResults'] print "DEBUG: getData" start_date, end_date = self.filters.getDates() #Buggered if I can find a way to do this 'nicely' take a list of integers and longs # and turn it into a tuple readale by sql. # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("<player_test>", nametest) tmp = tmp.replace("<site_test>", sitetest) tmp = tmp.replace("<startdate_test>", start_date) tmp = tmp.replace("<enddate_test>", end_date) tmp = tmp.replace(",)", ")") print "DEBUG: sql query:" print tmp self.db.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() self.db.rollback() if len(winnings) == 0: return None green = map(lambda x:float(x[1]), winnings) #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings) greenline = cumsum(green) #blueline = cumsum(blue) #redline = cumsum(red) return (greenline/100) def exportGraph (self, widget, data): if self.fig is None: return # Might want to disable export button until something has been generated. dia_chooser = gtk.FileChooserDialog(title=_("Please choose the directory you wish to export to:"), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OK,gtk.RESPONSE_OK)) dia_chooser.set_destroy_with_parent(True) dia_chooser.set_transient_for(self.parent) try: dia_chooser.set_filename(self.exportFile) # use previously chosen export path as default except: pass response = dia_chooser.run() if response <> gtk.RESPONSE_OK: print _('Closed, no graph exported') dia_chooser.destroy() return # generate a unique filename for export now = datetime.now() now_formatted = now.strftime("%Y%m%d%H%M%S") self.exportFile = dia_chooser.get_filename() + "/fpdb" + now_formatted + ".png" dia_chooser.destroy() #print "DEBUG: self.exportFile = %s" %(self.exportFile) self.fig.savefig(self.exportFile, format="png") #display info box to confirm graph created diainfo = gtk.MessageDialog(parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=_("Graph created")) diainfo.format_secondary_text(self.exportFile) diainfo.run() diainfo.destroy()
class ComponentChooser(gtk.Window): """Used for selecting components to use for remixing""" components = None # Contains the components to choose from data = None # Contains the original data choose_mask = [] def __init__(self, _components = None, _data=None): self.components = _components self.data = _data gtk.Window.__init__(self,gtk.WINDOW_TOPLEVEL) self.set_default_size(700,500) self.set_title("Choose components to use...") self.connect("delete_event", self.delete_event) self.connect("destroy", self.delete_event) self.vbox = gtk.VBox() self.add(self.vbox) self.f = Figure(figsize=(5,4), dpi=100, subplotpars=SubplotParams(left=0.06, top=0.95, right=0.97, bottom=0.1)) self.setupSubplots() self.plotAll() self.canvas = FigureCanvas(self.f) self.canvas.mpl_connect('button_press_event', self.cb_canvas) self.vbox.pack_start(self.canvas) self.toolbar = NavigationToolbar( self.canvas, self.window ) self.vbox.pack_start(self.toolbar, False, False) self.show_all() def delete_event(self, widget, event, data=None): self.hide() return True def cb_canvas(self, event): """Called when the figure is clicked.""" #print event.button if event.button == 3: for i,a in enumerate(self.axs): if event.inaxes == a: #print "Subplot %i geclickt" % i self.choose_mask[i] = bool(True-self.choose_mask[i]) #print self.choose_mask[i] #a.grid() a.cla() #TODO: plotte... a.plot(self.components[:,i]) # if not self.choose_mask[i]: a.text(numpy.array(a.get_xlim()).mean(),numpy.array(a.get_ylim()).mean(),"X",horizontalalignment='center',verticalalignment='center',fontsize=20,color="red") self.canvas.draw() def setupSubplots(self): self.f.clear() try: ncols = 4 nrows = self.components.shape[1]/ncols +1 self.axs = [] self.choose_mask = [] for i in range(self.components.shape[1]): self.axs.append(self.f.add_subplot(nrows,ncols,i+1)) self.choose_mask.append(True) except Exception, e: print "Error while setting up subplots", e
class XYDialog: def __init__(self, title, parent, filename, tmpn, type): # create matplotlib figure self.figure = Figure() self.canvas = FigureCanvas(self.figure) self.mp = ManipulateXYData(filename, type, self.figure, self, tmpn=tmpn) # create GTK dialog self.dialog = gtk.Dialog(title=title, parent=parent, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) self.dialog.set_default_size(500, 400) self.vBox = self.dialog.vbox # setup matplotlib events self.canvas.mpl_connect('button_press_event', self.mp.on_click) self.canvas.mpl_connect('button_release_event', self.mp.on_release) # create buttons self.buttonBox = gtk.HBox(homogeneous=True, spacing=5) self.saveButton = gtk.Button(label="Save and Use", stock=gtk.STOCK_SAVE) self.useButton = gtk.Button(label="Use") self.revertButton = gtk.Button(label="Revert") self.cancelButton = gtk.Button(label="Cancel") # pack buttons self.buttonBox.pack_start(self.saveButton, expand=False) self.buttonBox.pack_start(self.useButton, expand=False) self.buttonBox.pack_start(self.revertButton, expand=False) self.buttonBox.pack_end(self.cancelButton, expand=False) # connect buttons self.use = False self.save = False self.saveButton.connect("clicked", self.saveHandler) self.useButton.connect("clicked", self.useHandler) self.revertButton.connect("clicked", self.revertHandler) self.cancelButton.connect("clicked", self.cancelHandler) # pack and show dialog self.vBox.pack_start(self.canvas, expand=True) self.vBox.pack_start(gtk.HSeparator(), expand=False) self.vBox.pack_end(self.buttonBox, expand=False) self.dialog.show_all() def redraw(self): self.canvas.draw() def saveHandler(self, widget): self.mp.save_to_file() self.save = True self.exit() def useHandler(self, widget): self.mp.save_to_file() self.use = True self.save = False self.exit() def revertHandler(self, widget): self.mp.reset_data() def cancelHandler(self, widget): self.save = False self.use = False self.exit() def exit(self): self.dialog.hide() #self.dialog.response(0)
class Backend( backend.Backend ): def init(self): self.layer_to_axes = {} self.axes_to_layer = {} self.layers_cache = [] # copy of self.plot.layers self.layer_signals = {} self.line_caches = {} self.omaps = {} def connect(self): logger.debug("Opening matplotlib session.") self.figure = Figure(dpi=100, facecolor="white") # figsize=(5,4), dpi=100) self.canvas = FigureCanvas(self.figure) self.canvas.show() self.line_caches = {} self.layer_to_axes.clear() self.axes_to_layer.clear() backend.Backend.connect(self) logger.debug("Init finished") def set(self, project,plot): backend.Backend.set(self, project, plot) if self.project is not None: # TODO: connect to notify::layers of Plot pass def disconnect(self): logger.debug("Closing matplotlib session.") if not self.canvas is None: self.canvas.destroy() self.canvas = None if not self.figure is None: self.figure = None backend.Backend.disconnect(self) #---------------------------------------------------------------------- def arrange(self, rows=1, cols=1): layers = self.plot.layers n = len(layers) if n > (rows*cols): rows = int((rows*cols) / n) + 1 cols = rows * n #raise ValueError("Not enough rows and cols for all layers!") self.figure.clear() self.figure.axes = [] self.layer_to_axes.clear() self.axes_to_layer.clear() self.layers_cache = [] for signal_list in self.layer_signals.itervalues(): for signal in signal_list: Signals.disconnect(signal) self.layer_signals = {} j = 1 for layer in layers: print "Setting up layer", layer axes = self.figure.add_subplot("%d%d%d" % (rows,cols,j)) self.layer_to_axes[layer] = axes self.axes_to_layer[axes] = layer self.layers_cache.append(layer) print "Connecting to notify of ", layer self.layer_signals[layer] = \ [Signals.connect(layer, 'notify', self.on_update_sobject), Signals.connect(layer, 'notify::labels', self.on_update_labels)] j += 1 def draw_layer(self, layer, group_info): ax = self.layer_to_axes[layer] logger.info ("drawing layer %s (axes %s)" % (layer, ax)) omap = self.omaps[layer] ax.lines = [] line_cache = self.line_caches[layer] = [] last_cx = -1 # Default values come in two flavors: # group-wise and single default values group_styles = uwrap.get(layer, 'group_styles') group_markers = uwrap.get(layer, 'group_markers') # def update_layer(self, layer): #:layer.axes for (key, axis) in layer.axes.iteritems(): #:axis.label #:axis.scale #:axis.start #:axis.end label = uwrap.get(axis, 'label') scale = uwrap.get(axis, 'scale') start = uwrap.get(axis, 'start') end = uwrap.get(axis, 'end') print "START = %s, END = %s" % (str(start), str(end)) if key == 'x': set_label = ax.set_xlabel set_scale = ax.set_xscale set_start = (lambda l: ax.set_xlim(xmin=l)) set_end = (lambda l: ax.set_xlim(xmax=l)) elif key == 'y': set_label = ax.set_ylabel set_scale = ax.set_yscale set_start = (lambda l: ax.set_ylim(ymin=l)) set_end = (lambda l: ax.set_ylim(ymax=l)) else: raise RuntimeError("Invalid axis key '%s'" % key) if label is not None: set_label(label) if scale is not None: set_scale(scale) if start is not None: set_start(start) if end is not None: set_end(end) #:layer.visible if uwrap.get(layer, 'visible') is False: return # TODO #:layer.title title = uwrap.get(layer, 'title', None) if title is not None: ax.set_title(title) # TODO #:layer.grid grid = uwrap.get(layer, 'grid') ax.grid(grid) #:layer.lines:OK for line in layer.lines: self.update_line(line, layer, axes=ax) #:layer.legend:OK self.update_legend(layer.legend, layer) #:layer.labels:OK ax.texts = [] for label in layer.labels: self.update_textlabel(label, layer) def draw(self): self.check_connection() logger.debug("Matplotlib: draw()") if self.plot.layers != self.layers_cache: self.arrange() self.omaps = {} for layer in self.plot.layers: self.omaps[layer] = {} self.line_caches[layer] = {} group_info = {} self.draw_layer(layer, group_info) self.canvas.draw() #---------------------------------------------------------------------- def on_update_sobject(self, sender, sobject, **kwargs): logger.debug("Updating %s with args %s" % (sobject,str(kwargs))) return try: if isinstance(sobject, objects.TextLabel): self.update_textlabel(sobject, sender, **kwargs) elif isinstance(sobject, objects.Legend): self.update_legend(sobject, **kwargs) elif isinstance(sobject, objects.Line): self.update_line(sobject, **kwargs) self.update_legend(sobject, **kwargs) except Exception, msg: logger.info("Exception raised during sobject update.") logger.info(str(inspect.trace())) else: