Package tkintertable :: Module Plot
[hide private]
[frames] | no frames]

Source Code for Module tkintertable.Plot

  1  #!/usr/bin/env python 
  2  """ 
  3      Module for basic plotting inside the TableCanvas. Uses matplotlib. 
  4      Created August 2008 
  5      Copyright (C) Damien Farrell 
  6   
  7      This program is free software; you can redistribute it and/or 
  8      modify it under the terms of the GNU General Public License 
  9      as published by the Free Software Foundation; either version 2 
 10      of the License, or (at your option) any later version. 
 11   
 12      This program is distributed in the hope that it will be useful, 
 13      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 15      GNU General Public License for more details. 
 16   
 17      You should have received a copy of the GNU General Public License 
 18      along with this program; if not, write to the Free Software 
 19      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 20  """ 
 21   
 22  import sys, os 
 23  import copy 
 24  from Tkinter import * 
 25  from math import * 
 26  try: 
 27      import numpy 
 28  except: 
 29      print 'you need numpy to do statistics' 
 30   
 31  import matplotlib 
 32  matplotlib.use('TkAgg') 
 33  from matplotlib.font_manager import FontProperties 
 34  import pylab 
 35   
 36   
37 -class pylabPlotter(object):
38 """An interface to matplotlib for general plotting and stats, using tk backend""" 39 40 colors = ['#0049B4','#C90B11','#437C17','#AFC7C7','#E9AB17','#7F525D','#F6358A', 41 '#52D017','#FFFC17','#F76541','#F62217' ] 42 linestyles = ['-','--'] 43 shapes = ['o','-','--',':','.' ,'p','^','<','s','+','x','D','1','4','h'] 44 legend_positions = ['best', 'upper left','upper center','upper right', 45 'center left','center','center right' 46 'lower left','lower center','lower right'] 47 48 graphtypes = ['XY', 'hist', 'bar', 'pie'] 49 fonts = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] 50 51
52 - def __init__(self):
53 #Setup variables 54 self.shape = 'o' 55 self.grid = 0 56 self.xscale = 0 57 self.yscale = 0 58 self.showlegend = 0 59 self.legendloc = 'best' 60 self.legendlines = [] 61 self.legendnames = [] 62 self.graphtype = 'XY' 63 self.datacolors = self.colors 64 self.dpi = 300 65 self.linewidth = 1.5 66 self.font = 'sans-serif' 67 self.fontsize = 12 68 try: 69 self.setupPlotVars() 70 except: 71 print 'no tk running' 72 self.currdata = None 73 #self.format = None #data format 74 self.plottitle = '' 75 self.plotxlabel = '' 76 self.plotylabel = '' 77 return
78
79 - def plotXY(self, x, y, title='', xlabel=None, ylabel=None, shape=None, 80 clr=None, lw=1):
81 """Do x-y plot of 2 lists""" 82 if shape == None: 83 shape = self.shape 84 if clr == None: 85 clr = 'b' 86 if self.xscale == 1: 87 if self.yscale == 1: 88 line, = pylab.loglog(x, y, shape, color=clr, linewidth=lw) 89 else: 90 line, = pylab.semilogx(x, y, shape, color=clr, linewidth=lw) 91 elif self.yscale == 1: 92 line, = pylab.semilogy(x, y, shape, color=clr, linewidth=lw) 93 else: 94 line, = pylab.plot(x, y, shape, color=clr, linewidth=lw) 95 return line
96
97 - def doHistogram(self, data, bins=10):
98 """Do a pylab histogram of 1 or more lists""" 99 if len(data) == 1: 100 ydim=1 101 else: 102 ydim=2 103 dim=int(ceil(len(data)/2.0)) 104 i=1 105 #fig = pylab.figure() 106 for r in data: 107 if len(r)==0: 108 continue 109 ax = pylab.subplot(ydim,dim,i) 110 print r 111 for j in range(len(r)): 112 r[j] = float(r[j]) 113 pylab.hist(r,bins=bins) 114 i=i+1 115 return ax
116
117 - def doBarChart(self, x, y, clr):
118 """Do a pylab bar chart""" 119 #xloc = range(len(x)) 120 for i in range(len(x)): 121 x[i] = float(x[i]);y[i] = float(y[i]) 122 plotfig = pylab.bar(x, y, color=clr, alpha=0.6) 123 124 return plotfig
125
126 - def doPieChart(self, data):
127 """Do a pylab bar chart""" 128 if len(data) == 1: 129 ydim=1 130 else: 131 ydim=2 132 dim=int(ceil(len(data)/2.0)) 133 i=1 134 for r in data: 135 if len(r)==0: 136 continue 137 fig = pylab.subplot(ydim,dim,i) 138 print r 139 for j in range(len(r)): 140 r[j] = float(r[j]) 141 pylab.pie(r) 142 i=i+1 143 144 return
145
146 - def setData(self, data):
147 """Set the current plot data, useful for re-plotting without re-calling 148 explicit functions from the parent""" 149 150 self.currdata = data 151 return
152
153 - def hasData(self):
154 """Is there some plot data?""" 155 if self.currdata != None and len(self.currdata) > 0: 156 return True 157 else: 158 return False
159
160 - def setDataSeries(self, names=None, start=1):
161 """Set the series names, for use in legend""" 162 self.dataseriesvars=[] 163 for i in range(start,len(names)): 164 s=StringVar() 165 s.set(names[i]) 166 self.dataseriesvars.append(s) 167 #print self.dataseriesvars 168 return
169
170 - def setFormat(self, format):
171 """Set current data format of currdata""" 172 self.format = format 173 return
174
175 - def plotCurrent(self, data=None, graphtype='bar', show=True, guiopts=False,title=None):
176 """Re-do the plot with the current options and data""" 177 if guiopts == True: 178 self.applyOptions() 179 if title != None: 180 self.settitle(title) 181 self.clear() 182 currfig = pylab.figure(1) 183 184 if data == None: 185 try: 186 data = self.currdata 187 except: 188 print 'no data to plot' 189 return 190 else: 191 self.setData(data) 192 193 seriesnames = [] 194 legendlines = [] 195 for d in self.dataseriesvars: 196 seriesnames.append(d.get()) 197 198 self.graphtype = graphtype 199 #do an X-Y plot, with the first list as X xals 200 if self.graphtype == 'bar' or len(data) == 1: 201 i=0 202 pdata = copy.deepcopy(data) 203 if len(pdata)>1: 204 x = pdata[0] 205 pdata.remove(x) 206 for y in pdata: 207 if i >= len(self.colors): 208 i = 0 209 c = self.colors[i] 210 self.doBarChart(x, y, clr=c) 211 i+=1 212 else: 213 y = pdata[0] 214 x = range(len(y)) 215 self.doBarChart(x, y, clr='b') 216 217 elif self.graphtype == 'XY': 218 pdata = copy.deepcopy(data) 219 x = pdata[0] 220 pdata.remove(x) 221 i=0 222 for y in pdata: 223 if i >= len(self.colors): 224 i = 0 225 c = self.colors[i] 226 line = self.plotXY(x, y, clr=c, lw=self.linewidth) 227 legendlines.append(line) 228 i+=1 229 230 elif self.graphtype == 'hist': 231 self.doHistogram(data) 232 elif self.graphtype == 'pie': 233 self.doPieChart(data) 234 235 pylab.title(self.plottitle) 236 pylab.xlabel(self.plotxlabel) 237 pylab.ylabel(self.plotylabel) 238 #create legend data 239 if self.showlegend == 1: 240 print legendlines 241 pylab.legend(legendlines,seriesnames, 242 loc=self.legendloc) 243 if self.grid == 1: 244 pylab.grid(True) 245 246 if show == True: 247 self.show() 248 return currfig
249
250 - def clear(self):
251 """clear plot""" 252 pylab.clf() 253 self.legendlines = [] 254 self.legendnames = [] 255 return
256
257 - def show(self):
258 pylab.show() 259 return
260
261 - def saveCurrent(self, filename=None):
262 import tkFileDialog, os 263 filename=tkFileDialog.asksaveasfilename(parent=self.plotprefswin, 264 defaultextension='.png', 265 filetypes=[("Png file","*.png"), 266 ("All files","*.*")]) 267 if not filename: 268 return 269 fig = self.plotCurrent(show=False) 270 fig.savefig(filename, dpi=self.dpi) 271 return
272
273 - def setTitle(self, title=None):
274 self.plottitle = title
275
276 - def setxlabel(self, label=None):
277 self.plotxlabel = label
278
279 - def setylabel(self, label=None):
280 self.plotylabel = label
281
282 - def setOptions(self, shape=None, grid=None, xscale=None, yscale=None, 283 showlegend=None, legendloc=None, linewidth=None, 284 graphtype=None, font=None, fontsize=None):
285 """Set the options before plotting""" 286 if shape != None: 287 self.shape = shape 288 if grid != None: 289 self.grid = grid 290 if xscale != None: 291 self.xscale = xscale 292 if yscale != None: 293 self.yscale = yscale 294 if showlegend != None: 295 self.showlegend = showlegend 296 if legendloc != None: 297 self.legendloc = legendloc 298 if linewidth != None: 299 self.linewidth = linewidth 300 if graphtype !=None: 301 self.graphtype = graphtype 302 if font != None: 303 self.font = font 304 if fontsize != None: 305 self.fontsize = fontsize 306 pylab.rc("font", family=self.font, size=self.fontsize) 307 return
308
309 - def setupPlotVars(self):
310 """Plot Vars """ 311 self.pltgrid = IntVar() 312 self.pltlegend = IntVar() 313 self.pltsymbol = StringVar() 314 self.pltsymbol.set(self.shape) 315 self.legendlocvar = StringVar() 316 self.legendlocvar.set(self.legendloc) 317 self.xscalevar = IntVar() 318 self.yscalevar = IntVar() 319 self.xscalevar.set(0) 320 self.yscalevar.set(0) 321 self.graphtypevar = StringVar() 322 self.graphtypevar.set(self.graphtype) 323 self.linewidthvar = DoubleVar() 324 self.linewidthvar.set(self.linewidth) 325 self.fontvar = StringVar() 326 self.fontvar.set(self.font) 327 self.fontsizevar = DoubleVar() 328 self.fontsizevar.set(self.fontsize) 329 #plot specific 330 self.plottitlevar = StringVar() 331 self.plottitlevar.set('') 332 self.plotxlabelvar = StringVar() 333 self.plotxlabelvar.set('') 334 self.plotylabelvar = StringVar() 335 self.plotylabelvar.set('') 336 self.dataseriesvars=[] 337 return
338 339
340 - def applyOptions(self):
341 """Apply the gui option vars to the plotter options""" 342 self.setOptions(shape=self.pltsymbol.get(), grid=self.pltgrid.get(), 343 xscale=self.xscalevar.get(), yscale=self.yscalevar.get(), 344 showlegend = self.pltlegend.get(), 345 legendloc = self.legendlocvar.get(), 346 linewidth = self.linewidthvar.get(), 347 graphtype = self.graphtypevar.get(), 348 font = self.fontvar.get(), 349 fontsize = self.fontsizevar.get()) 350 self.setTitle(self.plottitlevar.get()) 351 self.setxlabel(self.plotxlabelvar.get()) 352 self.setylabel(self.plotylabelvar.get()) 353 return
354
355 - def plotSetup(self, data=None):
356 """Plot options dialog""" 357 358 if data != None: 359 self.setData(data) 360 self.plotprefswin=Toplevel() 361 self.plotprefswin.geometry('+300+450') 362 self.plotprefswin.title('Plot Preferences') 363 row=0 364 frame1=LabelFrame(self.plotprefswin, text='General') 365 frame1.grid(row=row,column=0,sticky='news',padx=2,pady=2) 366 def close_prefsdialog(): 367 self.plotprefswin.destroy()
368 369 def choosecolor(x): 370 """Choose color for data series""" 371 d=x[0] 372 c=x[1] 373 print 'passed', 'd',d, 'c',c 374 import tkColorChooser 375 colour,colour_string = tkColorChooser.askcolor(c,parent=self.plotprefswin) 376 if colour != None: 377 self.datacolors[d] = str(colour_string) 378 cbuttons[d].configure(bg=colour_string) 379 380 return
381 382 Checkbutton(frame1, text="Grid lines", variable=self.pltgrid, 383 onvalue=1, offvalue=0).grid(row=0,column=0, columnspan=2, sticky='news') 384 Checkbutton(frame1, text="Legend", variable=self.pltlegend, 385 onvalue=1, offvalue=0).grid(row=1,column=0, columnspan=2, sticky='news') 386 387 Label(frame1,text='Symbol:').grid(row=2,column=0,padx=2,pady=2) 388 symbolbutton = Menubutton(frame1,textvariable=self.pltsymbol, 389 relief=GROOVE, width=16, bg='lightblue') 390 symbol_menu = Menu(symbolbutton, tearoff=0) 391 symbolbutton['menu'] = symbol_menu 392 for text in self.shapes: 393 symbol_menu.add_radiobutton(label=text, 394 variable=self.pltsymbol, 395 value=text, 396 indicatoron=1) 397 symbolbutton.grid(row=2,column=1, sticky='news',padx=2,pady=2) 398 row=row+1 399 400 Label(frame1,text='Legend pos:').grid(row=3,column=0,padx=2,pady=2) 401 legendposbutton = Menubutton(frame1,textvariable=self.legendlocvar, 402 relief=GROOVE, width=16, bg='lightblue') 403 legendpos_menu = Menu(legendposbutton, tearoff=0) 404 legendposbutton['menu'] = legendpos_menu 405 i=0 406 for p in self.legend_positions: 407 legendpos_menu.add_radiobutton(label=p, 408 variable=self.legendlocvar, 409 value=p, 410 indicatoron=1) 411 i+=1 412 legendposbutton.grid(row=3,column=1, sticky='news',padx=2,pady=2) 413 414 Label(frame1,text='Font:').grid(row=4,column=0,padx=2,pady=2) 415 fontbutton = Menubutton(frame1,textvariable=self.fontvar, 416 relief=GROOVE, width=16, bg='lightblue') 417 font_menu = Menu(fontbutton, tearoff=0) 418 fontbutton['menu'] = font_menu 419 for f in self.fonts: 420 font_menu.add_radiobutton(label=f, 421 variable=self.fontvar, 422 value=f, 423 indicatoron=1) 424 fontbutton.grid(row=4,column=1, sticky='news',padx=2,pady=2) 425 row=row+1 426 Label(frame1,text='Font size:').grid(row=5,column=0,padx=2,pady=2) 427 Scale(frame1,from_=8,to=26,resolution=0.5,orient='horizontal', 428 relief=GROOVE,variable=self.fontsizevar).grid(row=5,column=1,padx=2,pady=2) 429 430 Label(frame1,text='linewidth:').grid(row=6,column=0,padx=2,pady=2) 431 Scale(frame1,from_=1,to=10,resolution=0.5,orient='horizontal', 432 relief=GROOVE,variable=self.linewidthvar).grid(row=6,column=1,padx=2,pady=2) 433 row=0 434 scalesframe = LabelFrame(self.plotprefswin, text="Axes Scales") 435 scales={0:'norm',1:'log'} 436 for i in range(0,2): 437 Radiobutton(scalesframe,text='x-'+scales[i],variable=self.xscalevar, 438 value=i).grid(row=0,column=i,pady=2) 439 Radiobutton(scalesframe,text='y-'+scales[i],variable=self.yscalevar, 440 value=i).grid(row=1,column=i,pady=2) 441 scalesframe.grid(row=row,column=1,sticky='news',padx=2,pady=2) 442 443 row=row+1 444 frame=LabelFrame(self.plotprefswin, text='Graph type') 445 frame.grid(row=row,column=0,columnspan=2,sticky='news',padx=2,pady=2) 446 for i in range(len(self.graphtypes)): 447 Radiobutton(frame,text=self.graphtypes[i],variable=self.graphtypevar, 448 value=self.graphtypes[i]).grid(row=0,column=i,pady=2) 449 450 row=row+1 451 labelsframe = LabelFrame(self.plotprefswin,text='Labels') 452 labelsframe.grid(row=row,column=0,columnspan=2,sticky='news',padx=2,pady=2) 453 Label(labelsframe,text='Title:').grid(row=0,column=0,padx=2,pady=2) 454 Entry(labelsframe,textvariable=self.plottitlevar,bg='white',relief=GROOVE).grid(row=0,column=1,padx=2,pady=2) 455 Label(labelsframe,text='X-axis label:').grid(row=1,column=0,padx=2,pady=2) 456 Entry(labelsframe,textvariable=self.plotxlabelvar,bg='white',relief=GROOVE).grid(row=1,column=1,padx=2,pady=2) 457 Label(labelsframe,text='Y-axis label:').grid(row=2,column=0,padx=2,pady=2) 458 Entry(labelsframe,textvariable=self.plotylabelvar,bg='white',relief=GROOVE).grid(row=2,column=1,padx=2,pady=2) 459 print self.currdata 460 if self.currdata != None: 461 #print self.dataseriesvars 462 row=row+1 463 seriesframe = LabelFrame(self.plotprefswin, text="Data Series Labels") 464 seriesframe.grid(row=row,column=0,columnspan=2,sticky='news',padx=2,pady=2) 465 #self.dataseriesvars=[] 466 if len(self.dataseriesvars) == 0: 467 self.setDataSeries(range(len(self.currdata))) 468 r=1 469 sr=1 470 cl=0 471 for s in self.dataseriesvars: 472 Label(seriesframe,text='Series '+str(r)).grid(row=r,column=cl,padx=2,pady=2) 473 Entry(seriesframe,textvariable=s,bg='white', 474 relief=GROOVE).grid(row=r,column=cl+1,padx=2,pady=2) 475 r+=1 476 if r > 8: 477 r=1 478 cl+=2 479 row=row+1 480 cbuttons = {} 481 frame = LabelFrame(self.plotprefswin, text="Dataset Colors") 482 r=1 483 cl=0 484 sr=1 485 ci=0 486 for d in range(len(self.dataseriesvars)): 487 if d >= len(self.datacolors): 488 self.datacolors.append(self.colors[ci]) 489 ci+=1 490 c = self.datacolors[d] 491 action = lambda x =(d,c): choosecolor(x) 492 cbuttons[d]=Button(frame,text='Series '+str(sr),bg=c,command=action) 493 cbuttons[d].grid(row=r,column=cl,sticky='news',padx=2,pady=2) 494 r+=1 495 sr+=1 496 if r > 8: 497 r=1 498 cl+=1 499 frame.grid(row=row,column=0,columnspan=2,sticky='news',padx=2,pady=2) 500 501 row=row+1 502 frame=Frame(self.plotprefswin) 503 frame.grid(row=row,column=0,columnspan=2,sticky='news',padx=2,pady=2) 504 replotb = Button(frame, text="Replot", 505 command=lambda:self.plotCurrent(graphtype=self.graphtype,guiopts=True), 506 relief=GROOVE, bg='#99ccff') 507 replotb.pack(side=LEFT,fill=X,padx=2,pady=2) 508 b = Button(frame, text="Apply", command=self.applyOptions, relief=GROOVE, bg='#99ccff') 509 b.pack(side=LEFT,fill=X,padx=2,pady=2) 510 b = Button(frame, text="Save", command=self.saveCurrent, relief=GROOVE, bg='#99ccff') 511 b.pack(side=LEFT,fill=X,padx=2,pady=2) 512 c=Button(frame,text='Close', command=close_prefsdialog, relief=GROOVE, bg='#99ccff') 513 c.pack(side=LEFT,fill=X,padx=2,pady=2) 514 if self.currdata == None: 515 replotb.configure(state=DISABLED) 516 517 self.plotprefswin.focus_set() 518 self.plotprefswin.grab_set() 519 520 return 521