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

# Source Code for Module SimPy.SimPlot

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