Package SimPy :: Module SimPlot
[hide private]
[frames] | no frames]

Source Code for Module SimPy.SimPlot

  1  #!/usr/bin/env python 
  2  # $Revision: 1.1.1.14 $ $Date: 2007/01/08 14:47:12 $ kgm 
  3  """ SimPlot 1.8  Provides basic plotting services based on Tk/Tkinter. 
  4   
  5  LICENSE: 
  6  Copyright (C) 2002, 2005,2006,2007  Klaus G. Muller, Tony Vignaux 
  7  mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz 
  8   
  9      This library is free software; you can redistribute it and/or 
 10      modify it under the terms of the GNU Lesser General Public 
 11      License as published by the Free Software Foundation; either 
 12      version 2.1 of the License, or (at your option) any later version. 
 13   
 14      This library is distributed in the hope that it will be useful, 
 15      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 16      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 17      Lesser General Public License for more details. 
 18   
 19      You should have received a copy of the GNU Lesser General Public 
 20      License along with this library; if not, write to the Free Software 
 21      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 22  END OF LICENSE 
 23   
 24  Derived from plotting package in Grayson's Tkinter book. 
 25  The idea to use this package came from Prof. Simon Frost 
 26  of U of California, San Diego who also strongly contributed 
 27  to the design and implementation of SimPlot. 
 28   
 29   
 30  Change history: 
 31   
 32      Nov 2, 2003 :  Combined utils.py (also from Grayson) with plotting package. 
 33      Nov 11, 2003: Made totally OO 
 34      Dec 16, 2003: Completion of SimPlot 1.4alpha 
 35      Feb 2004:       Release with SimPy 1.4 
 36      Aug 27, 2005: Added tests for empty point sets to plotXXX functions. 
 37   
 38  """ 
 39  __version__= '1.8 $Revision: 1.1.1.14 $ $Date: 2007/01/08 14:47:12 $' 
 40  from Tkinter import * 
 41  from Canvas import Line, CanvasText, Rectangle 
 42  from tkMessageBox import * 
 43  from tkSimpleDialog import askinteger,askstring,askfloat 
 44  from tkFileDialog import * 
 45  import string, math 
 46  from math import pi 
 47  from SimPy.Simulation import Monitor 
 48   
49 -def minCoordinate(clist):
50 if len(clist) < 2: return clist[0] 51 try: 52 x, y = clist[0] 53 for x1, y1 in clist[1:]: 54 if x1 <= x or y1 <= y: 55 x, y = x1, y1 56 except: 57 x, y = 0, 0 58 59 return x,y
60
61 -def maxCoordinate(clist):
62 if len(clist) < 2: return clist[0] 63 try: 64 x, y = clist[0] 65 for x1, y1 in clist[1:]: 66 if x1 >= x or y1 >= y: 67 x, y = x1, y1 68 except: 69 x, y = 0, 0 70 71 return x,y
72
73 -def minBound(clist):
74 x = 10000000 75 y = 10000000 76 for x1, y1 in clist: 77 if x1 < x: x = x1 78 if y1 < y: y = y1 79 return x,y
80
81 -def maxBound(clist):
82 x = -10000000 83 y = -10000000 84 for x1, y1 in clist: 85 if x1 > x: x = x1 86 if y1 > y: y = y1 87 return x,y
88
89 -class SimPlot(object):
90 - def __init__(self,root=Tk()):
91 self.root=root 92 pass
93
94 - def mainloop(self):
95 self.root.mainloop()
96
97 - def makeLine(self,points,**attr):
98 return GraphLine(points, **attr)
99 - def makeStep(self,points, **attr):
100 #convert data list to steps 101 step0=points[:] 102 step1=[[0,0]]*2*len(step0) 103 prev=[step0[0][0],0] 104 for x in range(len(step0)): 105 step1[2*x]=[step0[x][0],prev[1]] 106 step1[2*x+1]=step0[x] 107 prev=step0[x] 108 #draw the line 109 return self.makeLine(step1,smooth=False, **attr)
110
111 - def makeHistogram(self,points,**attr):
112 """Makes a histogram graph. 'points' must be a Histogram-like 113 object. 114 """ 115 #convert data list to bars 116 step0=points[:] 117 step1=[[0,0]]*3*len(step0) 118 prev=[step0[0][0],0] 119 for x in range(len(step0)): 120 step1[3*x]=[step0[x][0],prev[1]] 121 step1[3*x+1]=[step0[x][0],0.0] 122 step1[3*x+2]=step0[x] 123 prev=step0[x] 124 deltax=step0[1][0]-step0[0][0] 125 step1.append([prev[0]+deltax,prev[1]]) 126 step1.append([prev[0]+deltax,0]) 127 #make the line 128 return self.makeLine(step1,smooth=False, 129 xaxis=(step1[0][0],step1[-1][0]), 130 **attr)
131
132 - def makeSymbols(self,points,**attr):
133 return GraphSymbols(points,**attr)
134 - def makeBars(self,points,**attr):
135 return GraphBars(points,**attr)
136 - def makeGraphObjects(self,objects):
137 return GraphObjects(objects)
138 - def makeGraphBase(self, master, width, height, 139 background='white',title="",xtitle='',ytitle='', **kw):
140 return GraphBase(master, width, height, 141 background,title,xtitle,ytitle,**kw)
142
143 - def graphMenu(self,root,graph):
144 """To provide a File menu (postscript output, more to come) 145 to the plotxxxx plots""" 146 mainMenu=Menu(root) 147 root.config(menu=mainMenu) 148 def postscriptout(): 149 graph.postscr()
150 file=Menu(mainMenu) 151 file.add_command(label="Postscript",command=postscriptout) 152 mainMenu.add_cascade(label='File',menu=file,underline=0)
153
154 - def plotLine(self,points,windowsize=(500,300),title='',width=1,color='black', 155 smooth=0,background='white',xlab='x',ylab='y', 156 xaxis='automatic',yaxis='automatic'):
157 """Generates a line chart, with menu to save as Postscript file. 158 'points' can be a Monitor instance. 159 """ 160 if points!=[]: 161 root=Toplevel() 162 f=Frame(root) 163 try: #if it is like a Monitor, take xlab,ylab from it 164 ylab=points.ylab 165 xlab=points.tlab 166 if not title: title=points.name 167 except: 168 pass 169 line=self.makeLine(points, width=width,color=color,smooth=smooth) 170 gr=self.makeGraphObjects([line]) 171 graph = self.makeGraphBase(f, windowsize[0], windowsize[1], 172 title=title,xtitle=xlab, 173 ytitle=ylab,background=background) 174 graph.pack(side=LEFT, fill=BOTH, expand=YES) 175 graph.draw(gr,xaxis=xaxis, yaxis=yaxis) 176 #File menu 177 self.graphMenu(root,graph) 178 f.pack() 179 return graph 180 else: 181 print "SimPlot.plotline: dataset empty, no plot." 182 return None
183
184 - def plotStep(self,points,windowsize=(500,300),title='',width=1,color='black', 185 background='white',xlab='x',ylab='y', 186 xaxis='automatic',yaxis='automatic'):
187 """Generates a step chart, with menu to save as Postscript file. 188 'points' can be a Monitor instance. 189 """ 190 if points!=[]: 191 #convert data list to steps 192 step0=points[:] 193 step1=[[0,0]]*2*len(step0) 194 prev=[step0[0][0],0] 195 for x in range(len(step0)): 196 step1[2*x]=[step0[x][0],prev[1]] 197 step1[2*x+1]=step0[x] 198 prev=step0[x] 199 #treat monitor case 200 try: #if it is like a Monitor, take xlab,ylab from it 201 ylab=points.ylab 202 xlab=points.tlab 203 if not title: title=points.name 204 except: 205 pass 206 #draw the line 207 smooth=False 208 return self.plotLine(step1,windowsize,title,width,color, 209 smooth,background,xlab,ylab, 210 xaxis,yaxis) 211 else: 212 print "SimPlot.plotStep: dataset empty, no plot." 213 return None
214
215 - def plotHistogram(self,points,windowsize=(500,300),title='',width=1,color='black', 216 background='white',xlab='x',ylab='y', 217 xaxis='automatic',yaxis='automatic'):
218 """Makes a histogram plot. 'points' can be a Monitor instance. 219 """ 220 if points!=[]: 221 #convert data list to bars 222 step0=points[:] 223 step1=[[0,0]]*3*len(step0) 224 prev=[step0[0][0],0] 225 for x in range(len(step0)): 226 step1[3*x]=[step0[x][0],prev[1]] 227 step1[3*x+1]=[step0[x][0],0.0] 228 step1[3*x+2]=step0[x] 229 prev=step0[x] 230 deltax=step0[1][0]-step0[0][0] 231 step1.append([prev[0]+deltax,prev[1]]) 232 step1.append([prev[0]+deltax,0]) 233 #treat monitor case 234 try: #if it is like a Monitor, take xlab,ylab from it 235 ylab=points.ylab 236 xlab=points.tlab 237 if not title: title=points.name 238 except: 239 pass 240 #draw the line 241 smooth=False 242 return self.plotLine(step1,windowsize=windowsize,title=title,width=width, 243 color=color,smooth=smooth,background=background, 244 xlab=xlab,ylab=ylab,xaxis=(step1[0][0],step1[-1][0]), 245 yaxis=yaxis) 246 else: 247 print "SimPlot.plotHistogram: dataset empty, no plot." 248 return None
249
250 - def plotBars(self,points,windowsize=(500,300),title='',color='black', 251 width=1,size=3,fillcolor='black',fillstyle='', 252 outline='black',background='white',xlab='x',ylab='y', 253 xaxis='automatic',yaxis='automatic',anchor=0.0):
254 """Generates a bar chart, with menu to save as Postscript file. 255 'points' can be a Monitor instance. 256 """ 257 if points!=[]: 258 root=Toplevel() 259 f=Frame(root) 260 try: #if it is like a Monitor, take xlab,ylab from it 261 ylab=points.ylab 262 xlab=points.tlab 263 if not title: title=points.name 264 except: 265 pass 266 bars=self.makeBars(points, width=width,size=size,color=color, 267 fillcolor=fillcolor,fillstyle=fillstyle, 268 outline=outline,anchor=anchor) 269 gr=self.makeGraphObjects([bars]) 270 graph = self.makeGraphBase(f, windowsize[0],windowsize[1], 271 title=title,xtitle=xlab, 272 ytitle=ylab,background=background) 273 graph.pack(side=LEFT, fill=BOTH, expand=YES) 274 graph.draw(gr,xaxis=xaxis, yaxis=yaxis) 275 #File menu 276 self.graphMenu(root,graph) 277 f.pack() 278 return graph 279 else: 280 print "SimPlot.plotBars dataset empty, no plot." 281 return None
282
283 - def plotScatter(self,points,windowsize=(500,300),title='',width=1,color='black', 284 fillcolor='black',size=2,fillstyle='', 285 outline='black',marker='circle', 286 background='white',xlab='x',ylab='y', 287 xaxis='automatic',yaxis='automatic'):
288 if points!=[]: 289 root=Toplevel() 290 f=Frame(root) 291 try: #if it is like a Monitor, take xlab,ylab from it 292 ylab=points.ylab 293 xlab=points.tlab 294 if not title: title=points.name 295 except: 296 pass 297 scat=self.makeSymbols(points, width=width,color=color,size=size, 298 marker=marker,fillcolor=fillcolor, 299 fillstyle=fillstyle,outline=outline) 300 gr=self.makeGraphObjects([scat]) 301 graph = self.makeGraphBase(f, windowsize[0],windowsize[1], 302 title=title,xtitle=xlab, 303 ytitle=ylab,background=background) 304 graph.pack(side=LEFT, fill=BOTH, expand=YES) 305 graph.draw(gr,xaxis=xaxis, yaxis=yaxis) 306 #File menu 307 self.graphMenu(root,graph) 308 f.pack() 309 return graph 310 else: 311 print "SimPlot.plotScatter: dataset empty, no plot." 312 return None
313
314 - def mainloop(self):
315 self.root.mainloop()
316
317 -class GraphPoints:
318 - def __init__(self, points, attr):
319 self.points = points 320 self.scaled = self.points 321 self.attributes = {} 322 for name, value in self._attributes.items(): 323 try: 324 value = attr[name] 325 except KeyError: pass 326 self.attributes[name] = value
327
328 - def boundingBox(self):
329 return minBound(self.points), maxBound(self.points)
330
331 - def fitToScale(self, scale=(1,1), shift=(0,0)):
332 self.scaled = [] 333 for x,y in self.points: 334 self.scaled.append(((scale[0]*x)+shift[0],\ 335 (scale[1]*y)+shift[1])) 336 self.attributes.get('anchor', 0.0) 337 self.anchor = scale[1]*self.attributes.get('anchor', 0.0)+\ 338 shift[1]
339
340 -class GraphLine(GraphPoints):
341 - def __init__(self, points, **attr):
342 GraphPoints.__init__(self, points, attr)
343 344 _attributes = {'color': 'black', 345 'width': 1, 346 'smooth': 0, 347 'splinesteps': 12} 348
349 - def draw(self, canvas):
350 color = self.attributes['color'] 351 width = self.attributes['width'] 352 smooth = self.attributes['smooth'] 353 steps = self.attributes['splinesteps'] 354 arguments = (canvas,) 355 if smooth: 356 for i in range(len(self.points)): 357 x1, y1 = self.scaled[i] 358 arguments = arguments + (x1, y1) 359 else: 360 for i in range(len(self.points)-1): 361 x1, y1 = self.scaled[i] 362 x2, y2 = self.scaled[i+1] 363 arguments = arguments + (x1, y1, x2, y2) 364 apply(Line, arguments, {'fill': color, 'width': width, 365 'smooth': smooth, 'splinesteps':steps})
366
367 -class GraphSymbols(GraphPoints):
368 - def __init__(self, points, **attr):
369 GraphPoints.__init__(self, points, attr)
370 371 _attributes = {'color': 'black', 372 'width': 1, 373 'fillcolor': 'black', 374 'size': 2, 375 'fillstyle': '', 376 'outline': 'black', 377 'marker': 'circle'} 378
379 - def draw(self, canvas):
380 color = self.attributes['color'] 381 size = self.attributes['size'] 382 fillcolor = self.attributes['fillcolor'] 383 marker = self.attributes['marker'] 384 fillstyle = self.attributes['fillstyle'] 385 386 self._drawmarkers(canvas, self.scaled, marker, color, 387 fillstyle, fillcolor, size)
388
389 - def _drawmarkers(self, c, coords, marker='circle', color='black', 390 fillstyle='', fillcolor='', size=2):
391 l = [] 392 f = eval('self._' +marker) 393 for xc, yc in coords: 394 id = f(c, xc, yc, outline=color, size=size, 395 fill=fillcolor, fillstyle=fillstyle) 396 if type(id) is type(()): 397 for item in id: l.append(item) 398 else: 399 l.append(id) 400 return l
401
402 - def _circle(self, c, xc, yc, size=1, fill='', outline='black', 403 fillstyle=''):
404 id = c.create_oval(xc-0.5, yc-0.5, xc+0.5, yc+0.5, 405 fill=fill, outline=outline, 406 stipple=fillstyle) 407 c.scale(id, xc, yc, size*5, size*5) 408 return id
409
410 - def _dot(self, c, xc, yc, size=1, fill='', outline='black', 411 fillstyle=''):
412 id = c.create_oval(xc-0.5, yc-0.5, xc+0.5, yc+0.5, 413 fill=fill, outline=outline, 414 stipple=fillstyle) 415 c.scale(id, xc, yc, size*2.5, size*2.5) 416 return id
417
418 - def _square(self, c, xc, yc, size=1, fill='', outline='black', 419 fillstyle=''):
420 id = c.create_rectangle(xc-0.5, yc-0.5, xc+0.5, yc+0.5, 421 fill=fill, outline=outline, 422 stipple=fillstyle) 423 c.scale(id, xc, yc, size*5, size*5) 424 return id
425
426 - def _triangle(self, c, xc, yc, size=1, fill='', outline='black', 427 fillstyle=''):
428 id = c.create_polygon(-0.5, 0.288675134595, 429 0.5, 0.288675134595, 430 0.0, -0.577350269189, fill=fill, 431 outline=outline, stipple=fillstyle) 432 c.move(id, xc, yc) 433 c.scale(id, xc, yc, size*5, size*5) 434 return id
435
436 - def _triangle_down(self, c, xc, yc, size=1, fill='', 437 outline='black', fillstyle=''):
438 id = c.create_polygon(-0.5, -0.288675134595, 439 0.5, -0.288675134595, 440 0.0, 0.577350269189, fill=fill, 441 outline=outline, stipple=fillstyle) 442 c.move(id, xc, yc) 443 c.scale(id, xc, yc, size*5, size*5) 444 return id
445
446 - def _cross(self, c, xc, yc, size=1, fill='black', outline=None, 447 fillstyle=''):
448 if outline: fill=outline 449 id1 = c.create_line(xc-0.5, yc-0.5, xc+0.5, yc+0.5, 450 fill=fill) 451 id2 = c.create_line(xc-0.5, yc+0.5, xc+0.5, yc-0.5, 452 fill=fill) 453 c.scale(id1, xc, yc, size*5, size*5) 454 c.scale(id2, xc, yc, size*5, size*5) 455 return id1, id2
456
457 - def _plus(self, c, xc, yc, size=1, fill='black', outline=None, 458 fillstyle=''):
459 if outline: fill=outline 460 id1 = c.create_line(xc-0.5, yc, xc+0.5, yc, fill=fill) 461 id2 = c.create_line(xc, yc+0.5, xc, yc-0.5, fill=fill) 462 c.scale(id1, xc, yc, size*5, size*5) 463 c.scale(id2, xc, yc, size*5, size*5) 464 return id1, id2
465
466 -class GraphBars(GraphPoints):
467 - def __init__(self, points, **attr):
468 GraphPoints.__init__(self, points, attr)
469 470 _attributes = {'color': 'black', 471 'width': 1, 472 'fillcolor': 'black', 473 'size': 3, 474 'fillstyle': '', 475 'outline': 'black'} 476
477 - def draw(self, canvas):
478 color = self.attributes['color'] 479 width = self.attributes['width'] 480 fillstyle = self.attributes['fillstyle'] 481 outline = self.attributes['outline'] 482 spread = self.attributes['size'] 483 arguments = (canvas,) 484 p1, p2 = self.boundingBox() 485 for i in range(len(self.points)): 486 x1, y1 = self.scaled[i] 487 canvas.create_rectangle(x1-spread, y1, x1+spread, 488 self.anchor, fill=color, 489 width=width, outline=outline, 490 stipple=fillstyle)
491
492 -class GraphObjects:
493 - def __init__(self, objects):
494 self.objects = objects
495
496 - def boundingBox(self):
497 c1, c2 = self.objects[0].boundingBox() 498 for object in self.objects[1:]: 499 c1o, c2o = object.boundingBox() 500 c1 = minBound([c1, c1o]) 501 502 c2 = maxBound([c2, c2o]) 503 return c1, c2
504
505 - def fitToScale(self, scale=(1,1), shift=(0,0)):
506 for object in self.objects: 507 object.fitToScale(scale, shift)
508
509 - def draw(self, canvas):
510 for object in self.objects: 511 object.draw(canvas)
512
513 -class GraphBase(Frame):
514 - def __init__(self, master, width, height, 515 background='white',title="",xtitle='',ytitle='', **kw):
516 apply(Frame.__init__, (self, master), kw) 517 self.title=title 518 self.xtitle=xtitle 519 self.ytitle=ytitle 520 self.canvas = Canvas(self, width=width, height=height, 521 background=background) 522 self.canvas.pack(fill=BOTH, expand=YES) 523 border_w = self.canvas.winfo_reqwidth() - \ 524 string.atoi(self.canvas.cget('width')) 525 border_h = self.canvas.winfo_reqheight() - \ 526 string.atoi(self.canvas.cget('height')) 527 self.border = (border_w, border_h) 528 self.canvas.bind('<Configure>', self.configure) 529 self.plotarea_size = [None, None] 530 self._setsize() 531 self.last_drawn = None 532 self.font = ('Verdana', 10)
533
534 - def configure(self, event):
535 new_width = event.width-self.border[0] 536 new_height = event.height-self.border[1] 537 width = string.atoi(self.canvas.cget('width')) 538 height = string.atoi(self.canvas.cget('height')) 539 if new_width == width and new_height == height: 540 return 541 self.canvas.configure(width=new_width, height=new_height) 542 self._setsize() 543 self.clear() 544 self.replot()
545
546 - def bind(self, *args):
547 apply(self.canvas.bind, args)
548
549 - def _setsize(self):
550 self.width = string.atoi(self.canvas.cget('width')) 551 self.height = string.atoi(self.canvas.cget('height')) 552 #self.plotarea_size[0] = 0.90 * self.width 553 #self.plotarea_size[1] = 0.90 * -self.height 554 self.plotarea_size[0] = 0.90 * self.width 555 self.plotarea_size[1] = 0.90 * -self.height 556 xo = 0.5*(self.width-self.plotarea_size[0]) 557 yo = self.height-0.5*(self.height+self.plotarea_size[1]) 558 self.plotarea_origin = (xo, yo)
559
560 - def draw(self, graphics, xaxis = 'automatic', yaxis = 'automatic'):
561 562 self.last_drawn = (graphics, xaxis, yaxis) 563 p1, p2 = graphics.boundingBox() 564 xaxis = self._axisInterval(xaxis, p1[0], p2[0]) 565 yaxis = self._axisInterval(yaxis, p1[1], p2[1]) 566 text_width = [0., 0.] 567 text_height = [0., 0.] 568 if xaxis is not None: 569 p1 = xaxis[0], p1[1] 570 p2 = xaxis[1], p2[1] 571 xticks = self._ticks(xaxis[0], xaxis[1]) 572 bb = self._textBoundingBox(xticks[0][1]) 573 text_height[1] = bb[3]-bb[1] 574 text_width[0] = 0.5*(bb[2]-bb[0]) 575 bb = self._textBoundingBox(xticks[-1][1]) 576 text_width[1] = 0.5*(bb[2]-bb[0]) 577 else: 578 xticks = None 579 if yaxis is not None: 580 p1 = p1[0], yaxis[0] 581 p2 = p2[0], yaxis[1] 582 yticks = self._ticks(yaxis[0], yaxis[1]) 583 for y in yticks: 584 bb = self._textBoundingBox(y[1]) 585 w = bb[2]-bb[0] 586 text_width[0] = max(text_width[0], w) 587 h = 0.5*(bb[3]-bb[1]) 588 text_height[0] = h 589 text_height[1] = max(text_height[1], h) 590 else: 591 yticks = None 592 text1 = [text_width[0], -text_height[1]] 593 text2 = [text_width[1], -text_height[0]] 594 scale = ((self.plotarea_size[0]-text1[0]-text2[0]) / \ 595 (p2[0]-p1[0]), 596 (self.plotarea_size[1]-text1[1]-text2[1]) / \ 597 (p2[1]-p1[1])) 598 shift = ((-p1[0]*scale[0]) + self.plotarea_origin[0] + \ 599 text1[0], 600 (-p1[1]*scale[1]) + self.plotarea_origin[1] + \ 601 text1[1]) 602 self._drawAxes(self.canvas, xaxis, yaxis, p1, p2, 603 scale, shift, xticks, yticks) 604 graphics.fitToScale(scale, shift) 605 graphics.draw(self.canvas)
606
607 - def _axisInterval(self, spec, lower, upper):
608 if spec is None: 609 return None 610 if spec == 'minimal': 611 if lower == upper: 612 return lower-0.5, upper+0.5 613 else: 614 return lower, upper 615 if spec == 'automatic': 616 range = upper-lower 617 if range == 0.: 618 return lower-0.5, upper+0.5 619 log = math.log10(range) 620 power = math.floor(log) 621 fraction = log-power 622 if fraction <= 0.05: 623 power = power-1 624 grid = 10.**power 625 lower = lower - lower % grid 626 mod = upper % grid 627 if mod != 0: 628 upper = upper - mod + grid 629 return lower, upper 630 if type(spec) == type(()): 631 lower, upper = spec 632 if lower <= upper: 633 return lower, upper 634 else: 635 return upper, lower 636 raise ValueError, str(spec) + ': illegal axis specification'
637
638 - def _drawAxes(self, canvas, xaxis, yaxis, 639 bb1, bb2, scale, shift, xticks, yticks):
640 dict = {'anchor': N, 'fill': 'black'} 641 if self.font is not None: 642 dict['font'] = self.font 643 if xaxis is not None: 644 #draw x-axis 645 lower, upper = xaxis 646 text = 1 647 once=1 648 for y, d in [(bb1[1], -3), (bb2[1], 3)]: 649 #d=.5 of tick-length 650 p1 = (scale[0]*lower)+shift[0], (scale[1]*y)+shift[1] 651 if once: pp1=p1 652 p2 = (scale[0]*upper)+shift[0], (scale[1]*y)+shift[1] 653 if once: pp2=p2 654 once = 0 655 Line(self.canvas, p1[0], p1[1], p2[0], p2[1], 656 fill = 'black', width = 1) 657 if xticks: 658 for x, label in xticks: 659 p = (scale[0]*x)+shift[0], \ 660 (scale[1]*y)+shift[1] 661 Line(self.canvas, p[0], p[1], p[0], p[1]+d, 662 fill = 'black', width = 1) 663 if text: 664 dict['text'] = label 665 apply(CanvasText, (self.canvas, p[0], 666 p[1]+2), dict) ##KGM 14 Aug 03 667 text = 0 668 #write x-axis title 669 CanvasText(self.canvas,(pp2[0]-pp1[0])/2.+pp1[0],pp1[1]+22,text=self.xtitle) 670 #write graph title 671 CanvasText(self.canvas,(pp2[0]-pp1[0])/2.+pp1[0],7,text=self.title) 672 dict['anchor'] = E 673 if yaxis is not None: 674 #draw y-axis 675 lower, upper = yaxis 676 text = 1 677 once = 1 678 for x, d in [(bb1[0], -3), (bb2[0], 3)]: 679 p1 = (scale[0]*x)+shift[0], (scale[1]*lower)+shift[1] 680 p2 = (scale[0]*x)+shift[0], (scale[1]*upper)+shift[1] 681 if once: pp1=p1 ;pp2=p2 682 once = 0 683 Line(self.canvas, p1[0], p1[1], p2[0], p2[1], 684 fill = 'black', width = 1) 685 if yticks: 686 for y, label in yticks: 687 p = (scale[0]*x)+shift[0], \ 688 (scale[1]*y)+shift[1] 689 Line(self.canvas, p[0], p[1], p[0]-d, p[1], 690 fill = 'black', width = 1) 691 if text: 692 dict['text'] = label 693 apply(CanvasText,(self.canvas, 694 p[0]-4,p[1]+2), dict) 695 text = 0 696 #write y-axis title 697 CanvasText(self.canvas,pp2[0],pp2[1] - 10,text=self.ytitle)
698
699 - def _ticks(self, lower, upper):
700 ideal = (upper-lower)/7. 701 log = math.log10(ideal) 702 power = math.floor(log) 703 fraction = log-power 704 factor = 1. 705 error = fraction 706 for f, lf in self._multiples: 707 e = math.fabs(fraction-lf) 708 if e < error: 709 error = e 710 factor = f 711 grid = factor * 10.**power 712 if power > 3 or power < -3: 713 format = '%+7.0e' 714 elif power >= 0: 715 digits = max(1, int(power)) 716 format = '%' + `digits`+'.0f' 717 else: 718 digits = -int(power) 719 format = '%'+`digits+2`+'.'+`digits`+'f' 720 ticks = [] 721 t = -grid*math.floor(-lower/grid) 722 while t <= upper and len(ticks) < 200: 723 ticks.append((t, format % (t,))) 724 t = t + grid 725 return ticks
726 727 _multiples = [(2., math.log10(2.)), (5., math.log10(5.))] 728
729 - def _textBoundingBox(self, text):
730 bg = self.canvas.cget('background') 731 dict = {'anchor': NW, 'text': text, 'fill': bg} 732 if self.font is not None: 733 dict['font'] = self.font 734 item = apply(CanvasText, (self.canvas, 0., 0.), dict) 735 bb = self.canvas.bbox(item) 736 self.canvas.delete(item) 737 return bb
738
739 - def replot(self):
740 if self.last_drawn is not None: 741 apply(self.draw, self.last_drawn)
742
743 - def clear(self):
744 self.canvas.delete('all')
745
746 - def postscr(self,filename=None):
747 """Write to Postscript file given by 'filename'. If none provided, 748 ask user. 749 """ 750 from tkFileDialog import asksaveasfilename 751 if not filename: 752 filename=asksaveasfilename() 753 if filename: 754 if not filename[-3:] == '.ps': 755 filename+=".ps" 756 self.canvas.postscript(width=self.width,height=self.height,file=filename)
757
758 -class TextBox(Frame):
759 - def __init__(self, master, width, height, 760 background='white',boxtext='', **kw):
761 apply(Frame.__init__, (self, master), kw) 762 self.width=width 763 self.height=height 764 self.canvas=Canvas(self, width=width, height=height, 765 background=background) 766 self.canvas.pack(fill=BOTH, expand=YES)
767 #CanvasText(self.canvas,text=boxtext) 768
769 - def postscr(self):
770 #select output file 771 #from tkFileDialog import asksaveasfilename 772 filename=asksaveasfilename() 773 if filename: 774 if not filename[-3:] == '.ps': 775 filename+=".ps" 776 self.canvas.postscript(width=self.width,height=self.height,file=filename)
777 778 if __name__ == '__main__': 779 print "SimPlot.py %s"%__version__ 780 root=Tk() 781 plt=SimPlot() 782 root.title('SimPlot example - First frame') 783 784 root1 = Tk() 785 root1.title('SimPlot example - Second frame') 786 787 """PARAMETER DEFAULTS: 788 GraphBase 789 --------- 790 background='white', 791 title="", 792 xtitle='', 793 ytitle='' 794 795 GraphBase.draw 796 -------------- 797 xaxis = 'automatic', 798 yaxis = 'automatic') 799 800 GraphLine 801 --------- 802 color: 'black', 803 width: 1, 804 smooth: 0, 805 splinesteps: 12 806 807 GraphSymbols: 808 ------------- 809 color: 'black', 810 width: 1, 811 fillcolor: 'black', 812 size: 2, 813 fillstyle: '', 814 outline: 'black', 815 marker: 'circle'} 816 817 GraphBars 818 --------- 819 color: 'black', 820 width: 1, 821 fillcolor: 'black', 822 size: 3, 823 fillstyle: '', 824 outline: 'black' 825 """ 826 # Plot 1 -- smooth line + filled bars 827 di = 5.0*pi/40. 828 data = [] 829 for i in range(40): 830 data.append((float(i)*di, 831 (math.sin(float(i)*di)-math.cos(float(i)*di)))) 832 line1 = plt.makeLine(data, color='black', width=1, 833 smooth=1) 834 line1a = plt.makeBars(data[1:], color='blue', fillstyle='gray25', 835 anchor=0.0) 836 837 838 graphObject=plt.makeGraphObjects([line1a,line1]) 839 #Second panel -- Narrow bars 840 line2 = plt.makeBars([(0,0),(1,145),(2,-90),(3,147),(4,22),(5,31), 841 (6,77),(7,125),(8,220),(9,550),(10,560),(11,0)], 842 outline='green',color='red', size=7) 843 844 845 graphObject2=plt.makeGraphObjects([line2]) 846 847 # Third plot -- Smooth line and unsmoothed line 848 line3 = plt.makeLine([(1,145+100),(2,151+100),(3,147+100),(4,22+100),(5,31+100), 849 (6,77+100),(7,125+100),(8,220+100),(9,550+100),(10,560+100)], 850 color='blue', width=2, smooth=1) 851 line3a = plt.makeLine([(1,145),(2,151),(3,147),(4,22),(5,31), 852 (6,77),(7,125),(8,220),(9,550),(10,560)], 853 color='green', width=2, smooth=0) 854 line3b = plt.makeStep([(1,145+100),(2,151+100),(3,147+100),(4,22+100),(5,31+100), 855 (6,77+100),(7,125+100),(8,220+100),(9,550+100),(10,560+100)], 856 color='red', width=2) 857 858 graphObject3 = plt.makeGraphObjects([line3, line3a, line3b]) 859 860 # Fourth plot -- lines with all available symbols with different 861 # outline colors/fill colors/sizes 862 863 line4 = plt.makeSymbols([(1,100),(2,100),(3,100),(4,100),(5,100), 864 (6,100),(7,100),(8,100),(9,100),(10,100)], 865 color='black',fillcolor='red', width=2,marker='triangle') 866 line5 = plt.makeSymbols([(1,200),(2,200),(3,200),(4,200),(5,200), 867 (6,200),(7,200),(8,200),(9,200),(10,200)], 868 color='red', width=2,marker='circle') 869 line6 = plt.makeSymbols([(1,300),(2,300),(3,300),(4,300),(5,300), 870 (6,300),(7,300),(8,300),(9,300),(10,300)], 871 color='green', width=2,marker='dot') 872 line7 = plt.makeSymbols([(1,400),(2,400),(3,400),(4,400),(5,400), 873 (6,400),(7,400),(8,400),(9,400),(10,400)], 874 color='blue', fillcolor='white', 875 size=2, width=2,marker='square') 876 line8 = plt.makeSymbols([(1,500),(2,500),(3,500),(4,500),(5,500), 877 (6,500),(7,500),(8,500),(9,500),(10,500)], 878 color='yellow', width=2,marker='triangle') 879 line9 = plt.makeSymbols([(1,600),(2,600),(3,600),(4,600),(5,600), 880 (6,600),(7,600),(8,600),(9,600),(10,600)], 881 color='magenta', width=2,marker='cross') 882 line10 = plt.makeSymbols([(1,700),(2,700),(3,700),(4,700),(5,700), 883 (6,700),(7,700),(8,700),(9,700),(10,700)], 884 color='brown', width=2,marker='plus') 885 line11 = plt.makeSymbols([(1,800),(2,800),(3,800),(4,800),(5,800), 886 (6,800),(7,800),(8,800),(9,800),(10,800)], 887 color='black', fillcolor='orange', 888 width=2,marker='triangle_down') 889 890 891 graphObject4 = GraphObjects([line4, line5, line6, line7, line8, 892 line9, line10, line11]) 893 894 # Two panels 895 f1 = Frame(root) 896 f2 = Frame(root1) 897 898 graph={} 899 # Plots 1 and 2 in panel f1, side by side 900 graph[1]= plt.makeGraphBase(f1, 500, 300, title="Plot 1: 1 makeLine call, 1 makeBars call", 901 xtitle='the x-axis',ytitle='the y-axis') 902 graph[1].pack(side=LEFT, fill=BOTH, expand=YES) 903 graph[1].draw(graphObject,xaxis='minimal', yaxis='minimal') 904 905 graph[2] = plt.makeGraphBase(f1, 500, 300,title="Plot 2: 1 makeBars call", 906 xtitle='time',ytitle='pulse [volt]') 907 # Set side-by-side plots 908 graph[2].pack(side=LEFT, fill=BOTH, expand=YES) 909 graph[2].draw(graphObject2, 'minimal', 'automatic') 910 911 # Pack panel 1 to make it visible 912 f1.pack() 913 914 # Plots 2 and 3 in panel f2, one under the other 915 graph[3] = plt.makeGraphBase(f2, 500, 300, 916 title="Plot 3: 2 makeLine call (smooth, not smooth); 1 makeStep call") 917 graph[3].pack(side=TOP, fill=BOTH, expand=YES) 918 graph[3].draw(graphObject3) 919 920 graph[4] = plt.makeGraphBase(f2, 500, 300, border=3,title="Plot 4: 8 makeSymbols calls") 921 # Set one-over-other configuration of plots 922 graph[4].pack(side=TOP, fill=BOTH, expand=YES) 923 graph[4].draw(graphObject4) 924 925 # Pack panel 2 to make it visible 926 f2.pack() 927 928 # Save graph[1] to Postscript file (user selects filename) 929 graph[1].postscr() 930 931 # end plotting stuff 932 933 #### Very Important -- get Tk going by starting event loop 934 plt.mainloop() 935