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
    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