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

Source Code for Module SimPy.SimPlot

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