text: #!/usr/bin/env python
  # $Revision: 1.1.1.14 $ $Date: 2007/01/08 14:47:12 $ kgm
  """ SimPlot 1.8  Provides basic plotting services based on Tk/Tkinter.
  
  LICENSE:
  Copyright (C) 2002, 2005,2006,2007  Klaus G. Muller, Tony Vignaux
  mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz
  
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public
      License as published by the Free Software Foundation; either
      version 2.1 of the License, or (at your option) any later version.
  
      This library is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.
  
      You should have received a copy of the GNU Lesser General Public
      License along with this library; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  END OF LICENSE
  
  Derived from plotting package in Grayson's Tkinter book.
  The idea to use this package came from Prof. Simon Frost
  of U of California, San Diego who also strongly contributed
  to the design and implementation of SimPlot.
  
  
  Change history:
  
      Nov 2, 2003 :  Combined utils.py (also from Grayson) with plotting package.
      Nov 11, 2003: Made totally OO
      Dec 16, 2003: Completion of SimPlot 1.4alpha
      Feb 2004:       Release with SimPy 1.4
      Aug 27, 2005: Added tests for empty point sets to plotXXX functions.
  
  """
  __version__= '1.8 $Revision: 1.1.1.14 $ $Date: 2007/01/08 14:47:12 $'
  from Tkinter import *
  from Canvas import Line, CanvasText, Rectangle
  from tkMessageBox import *
  from tkSimpleDialog import askinteger,askstring,askfloat
  from tkFileDialog import *
  import string, math
  from math import pi
  from SimPy.Simulation import Monitor
  
  def minCoordinate(clist):
      if len(clist) < 2: return clist[0]
      try:
          x,  y  = clist[0]
          for x1, y1 in clist[1:]:
              if x1 <= x or y1 <= y:
                  x, y = x1, y1
      except:
          x, y = 0, 0
  
      return x,y
  
  def maxCoordinate(clist):
      if len(clist) < 2: return clist[0]
      try:
          x,  y  = clist[0]
          for x1, y1 in clist[1:]:
              if x1 >= x or y1 >= y:
                  x, y = x1, y1
      except:
          x, y = 0, 0
  
      return x,y
  
  def minBound(clist):
      x = 10000000
      y = 10000000
      for x1, y1 in clist:
          if x1 < x: x = x1
          if y1 < y: y = y1        
      return x,y
  
  def maxBound(clist):
      x = -10000000
      y = -10000000
      for x1, y1 in clist:
          if x1 > x: x = x1
          if y1 > y: y = y1        
      return x,y
  
  class SimPlot(object):
      def __init__(self,root=Tk()):
          self.root=root
          pass
  
      def mainloop(self):
          self.root.mainloop()
  
      def makeLine(self,points,**attr):
          return GraphLine(points, **attr)
      def makeStep(self,points, **attr):
          #convert data list to steps
          step0=points[:]
          step1=[[0,0]]*2*len(step0)
          prev=[step0[0][0],0]
          for x in range(len(step0)):
              step1[2*x]=[step0[x][0],prev[1]]
              step1[2*x+1]=step0[x]
              prev=step0[x]
          #draw the line
          return self.makeLine(step1,smooth=False, **attr)
      
      def makeHistogram(self,points,**attr):
          """Makes a histogram graph. 'points' must be a Histogram-like
          object.
          """
          #convert data list to bars
          step0=points[:]
          step1=[[0,0]]*3*len(step0)
          prev=[step0[0][0],0]
          for x in range(len(step0)):
              step1[3*x]=[step0[x][0],prev[1]]
              step1[3*x+1]=[step0[x][0],0.0]
              step1[3*x+2]=step0[x]
              prev=step0[x]
          deltax=step0[1][0]-step0[0][0]
          step1.append([prev[0]+deltax,prev[1]])
          step1.append([prev[0]+deltax,0])
          #make the line
          return self.makeLine(step1,smooth=False,
                               xaxis=(step1[0][0],step1[-1][0]),
                               **attr)        
      
      def makeSymbols(self,points,**attr):
          return GraphSymbols(points,**attr)
      def makeBars(self,points,**attr):
          return GraphBars(points,**attr)
      def makeGraphObjects(self,objects):
          return GraphObjects(objects)
      def makeGraphBase(self, master, width, height,
                   background='white',title="",xtitle='',ytitle='', **kw):
          return GraphBase(master, width, height,
                   background,title,xtitle,ytitle,**kw)
  
      def graphMenu(self,root,graph):
          """To provide a File menu (postscript output, more to come)
          to the plotxxxx plots"""
          mainMenu=Menu(root)
          root.config(menu=mainMenu)
          def postscriptout():
              graph.postscr()
          file=Menu(mainMenu)
          file.add_command(label="Postscript",command=postscriptout)
          mainMenu.add_cascade(label='File',menu=file,underline=0)
          
      def plotLine(self,points,windowsize=(500,300),title='',width=1,color='black',
                   smooth=0,background='white',xlab='x',ylab='y',
                   xaxis='automatic',yaxis='automatic'):
          """Generates a line chart, with menu to save as Postscript file.
          'points' can be a Monitor instance.
          """
          if points!=[]:
              root=Toplevel()
              f=Frame(root)
              try: #if it is like a Monitor, take xlab,ylab from it
                  ylab=points.ylab
                  xlab=points.tlab
                  if not title: title=points.name 
              except:
                  pass
              line=self.makeLine(points, width=width,color=color,smooth=smooth)
              gr=self.makeGraphObjects([line])
              graph = self.makeGraphBase(f, windowsize[0], windowsize[1],
                                     title=title,xtitle=xlab,
                                     ytitle=ylab,background=background)
              graph.pack(side=LEFT, fill=BOTH, expand=YES)
              graph.draw(gr,xaxis=xaxis, yaxis=yaxis)
              #File menu
              self.graphMenu(root,graph)
              f.pack()
              return graph
          else:
              print "SimPlot.plotline: dataset empty, no plot."
              return None
      
      def plotStep(self,points,windowsize=(500,300),title='',width=1,color='black',
                   background='white',xlab='x',ylab='y',
                   xaxis='automatic',yaxis='automatic'):
          """Generates a step chart, with menu to save as Postscript file.
          'points' can be a Monitor instance.
          """
          if points!=[]:
              #convert data list to steps
              step0=points[:]
              step1=[[0,0]]*2*len(step0)
              prev=[step0[0][0],0]
              for x in range(len(step0)):
                  step1[2*x]=[step0[x][0],prev[1]]
                  step1[2*x+1]=step0[x]
                  prev=step0[x]
              #treat monitor case
              try: #if it is like a Monitor, take xlab,ylab from it
                  ylab=points.ylab
                  xlab=points.tlab
                  if not title: title=points.name 
              except:
                  pass
              #draw the line
              smooth=False
              return self.plotLine(step1,windowsize,title,width,color,
                   smooth,background,xlab,ylab,
                   xaxis,yaxis)
          else:
              print "SimPlot.plotStep: dataset empty, no plot."
              return None
  
      def plotHistogram(self,points,windowsize=(500,300),title='',width=1,color='black',
                   background='white',xlab='x',ylab='y',
                   xaxis='automatic',yaxis='automatic'):
          """Makes a histogram plot. 'points' can be a Monitor  instance.
          """
          if points!=[]:
              #convert data list to bars
              step0=points[:]
              step1=[[0,0]]*3*len(step0)
              prev=[step0[0][0],0]
              for x in range(len(step0)):
                  step1[3*x]=[step0[x][0],prev[1]]
                  step1[3*x+1]=[step0[x][0],0.0]
                  step1[3*x+2]=step0[x]
                  prev=step0[x]
              deltax=step0[1][0]-step0[0][0]
              step1.append([prev[0]+deltax,prev[1]])
              step1.append([prev[0]+deltax,0])
              #treat monitor case
              try: #if it is like a Monitor, take xlab,ylab from it
                  ylab=points.ylab
                  xlab=points.tlab
                  if not title: title=points.name 
              except:
                  pass
              #draw the line
              smooth=False
              return self.plotLine(step1,windowsize=windowsize,title=title,width=width,
                               color=color,smooth=smooth,background=background,
                               xlab=xlab,ylab=ylab,xaxis=(step1[0][0],step1[-1][0]),
                               yaxis=yaxis)
          else:
              print "SimPlot.plotHistogram: dataset empty, no plot."
              return None
      
      def plotBars(self,points,windowsize=(500,300),title='',color='black',
                   width=1,size=3,fillcolor='black',fillstyle='',
                   outline='black',background='white',xlab='x',ylab='y',
                   xaxis='automatic',yaxis='automatic',anchor=0.0):
          """Generates a bar chart, with menu to save as Postscript file.
          'points' can be a Monitor instance.
          """
          if points!=[]:
              root=Toplevel()
              f=Frame(root)
              try: #if it is like a Monitor, take xlab,ylab from it
                  ylab=points.ylab
                  xlab=points.tlab
                  if not title: title=points.name 
              except:
                  pass
              bars=self.makeBars(points, width=width,size=size,color=color,
                             fillcolor=fillcolor,fillstyle=fillstyle,
                             outline=outline,anchor=anchor)
              gr=self.makeGraphObjects([bars])
              graph = self.makeGraphBase(f, windowsize[0],windowsize[1],
                                     title=title,xtitle=xlab,
                                     ytitle=ylab,background=background)
              graph.pack(side=LEFT, fill=BOTH, expand=YES)
              graph.draw(gr,xaxis=xaxis, yaxis=yaxis)
              #File menu
              self.graphMenu(root,graph)
              f.pack()
              return graph
          else:
              print "SimPlot.plotBars dataset empty, no plot."
              return None
  
      def plotScatter(self,points,windowsize=(500,300),title='',width=1,color='black',
                      fillcolor='black',size=2,fillstyle='',
                      outline='black',marker='circle',
                      background='white',xlab='x',ylab='y',
                      xaxis='automatic',yaxis='automatic'):
          if points!=[]:
              root=Toplevel()
              f=Frame(root)
              try: #if it is like a Monitor, take xlab,ylab from it
                  ylab=points.ylab
                  xlab=points.tlab
                  if not title: title=points.name 
              except:
                  pass
              scat=self.makeSymbols(points, width=width,color=color,size=size,
                                marker=marker,fillcolor=fillcolor,
                                fillstyle=fillstyle,outline=outline)
              gr=self.makeGraphObjects([scat])
              graph = self.makeGraphBase(f, windowsize[0],windowsize[1],
                                     title=title,xtitle=xlab,
                                     ytitle=ylab,background=background)
              graph.pack(side=LEFT, fill=BOTH, expand=YES)
              graph.draw(gr,xaxis=xaxis, yaxis=yaxis)
              #File menu
              self.graphMenu(root,graph)
              f.pack()
              return graph
          else:
              print "SimPlot.plotScatter: dataset empty, no plot."
              return None
  
      def mainloop(self):
          self.root.mainloop()
      
  class GraphPoints:
      def __init__(self, points, attr):
          self.points = points
          self.scaled = self.points
          self.attributes = {}
          for name, value in self._attributes.items():
              try:
                  value = attr[name]
              except KeyError: pass
              self.attributes[name] = value
  
      def boundingBox(self):
          return minBound(self.points),  maxBound(self.points)
  
      def fitToScale(self, scale=(1,1), shift=(0,0)):
          self.scaled = []
          for x,y in self.points:
              self.scaled.append(((scale[0]*x)+shift[0],\
                                 (scale[1]*y)+shift[1]))
              self.attributes.get('anchor', 0.0)
          self.anchor = scale[1]*self.attributes.get('anchor', 0.0)+\
                        shift[1]
  
  class GraphLine(GraphPoints):
      def __init__(self, points, **attr):
          GraphPoints.__init__(self, points, attr)
  
      _attributes = {'color':       'black',
                     'width':        1,
                     'smooth':       0,
                     'splinesteps': 12}
  
      def draw(self, canvas):
          color  = self.attributes['color']
          width  = self.attributes['width']
          smooth = self.attributes['smooth']
          steps  = self.attributes['splinesteps']
          arguments = (canvas,)
          if smooth:
              for i in range(len(self.points)):
                  x1, y1 = self.scaled[i]
                  arguments = arguments + (x1, y1)
          else:
              for i in range(len(self.points)-1):
                  x1, y1 = self.scaled[i]
                  x2, y2 = self.scaled[i+1]
                  arguments = arguments + (x1, y1, x2, y2)
          apply(Line, arguments, {'fill': color, 'width': width,
                                  'smooth': smooth, 'splinesteps':steps})
  
  class GraphSymbols(GraphPoints):
      def __init__(self, points, **attr):
          GraphPoints.__init__(self, points, attr)
  
      _attributes = {'color': 'black',
                     'width': 1,
                     'fillcolor': 'black',
                     'size': 2,
                     'fillstyle': '',
                     'outline': 'black',
                     'marker': 'circle'}
  
      def draw(self, canvas):
          color     = self.attributes['color']
          size      = self.attributes['size']
          fillcolor = self.attributes['fillcolor']
          marker    = self.attributes['marker']
          fillstyle = self.attributes['fillstyle']
  
          self._drawmarkers(canvas, self.scaled, marker, color,
                            fillstyle, fillcolor, size)
  
      def _drawmarkers(self, c, coords, marker='circle', color='black',
                       fillstyle='', fillcolor='', size=2):
          l = []
          f = eval('self._' +marker)
          for xc, yc in coords:
              id = f(c, xc, yc, outline=color, size=size,
                     fill=fillcolor, fillstyle=fillstyle)
              if type(id) is type(()):
                  for item in id: l.append(item)
              else:
                  l.append(id)
          return l
      
      def _circle(self, c, xc, yc, size=1, fill='', outline='black',
                  fillstyle=''):
          id = c.create_oval(xc-0.5, yc-0.5, xc+0.5, yc+0.5, 
                             fill=fill, outline=outline,
                             stipple=fillstyle)
          c.scale(id, xc, yc, size*5, size*5)
          return id
  
      def _dot(self, c, xc, yc, size=1, fill='', outline='black',
               fillstyle=''):
          id = c.create_oval(xc-0.5, yc-0.5, xc+0.5, yc+0.5, 
                             fill=fill, outline=outline,
                             stipple=fillstyle)
          c.scale(id, xc, yc, size*2.5, size*2.5)
          return id
  
      def _square(self, c, xc, yc, size=1, fill='', outline='black',
                  fillstyle=''):
          id = c.create_rectangle(xc-0.5, yc-0.5, xc+0.5, yc+0.5,
                                  fill=fill, outline=outline,
                                  stipple=fillstyle)
          c.scale(id, xc, yc, size*5, size*5)
          return id
      
      def _triangle(self, c, xc, yc, size=1, fill='', outline='black',
                    fillstyle=''):
          id = c.create_polygon(-0.5, 0.288675134595,
                                0.5, 0.288675134595,
                                0.0, -0.577350269189, fill=fill,
                                outline=outline, stipple=fillstyle)
          c.move(id, xc, yc)
          c.scale(id, xc, yc, size*5, size*5)
          return id
  
      def _triangle_down(self, c, xc, yc, size=1, fill='',
                         outline='black', fillstyle=''):
          id = c.create_polygon(-0.5, -0.288675134595,
                                0.5, -0.288675134595,
                                0.0, 0.577350269189, fill=fill,
                                outline=outline, stipple=fillstyle)
          c.move(id, xc, yc)
          c.scale(id, xc, yc, size*5, size*5)
          return id
  
      def _cross(self, c, xc, yc, size=1, fill='black', outline=None,
                 fillstyle=''):
          if outline: fill=outline
          id1 = c.create_line(xc-0.5, yc-0.5, xc+0.5, yc+0.5,
                              fill=fill)
          id2 = c.create_line(xc-0.5, yc+0.5, xc+0.5, yc-0.5,
                              fill=fill)
          c.scale(id1, xc, yc, size*5, size*5)
          c.scale(id2, xc, yc, size*5, size*5)
          return id1, id2
  
      def _plus(self, c, xc, yc, size=1, fill='black', outline=None,
                fillstyle=''):
          if outline: fill=outline
          id1 = c.create_line(xc-0.5, yc, xc+0.5, yc, fill=fill)
          id2 = c.create_line(xc, yc+0.5, xc, yc-0.5, fill=fill)
          c.scale(id1, xc, yc, size*5, size*5)
          c.scale(id2, xc, yc, size*5, size*5)
          return id1, id2
  
  class GraphBars(GraphPoints):
      def __init__(self, points, **attr):
          GraphPoints.__init__(self, points, attr)
  
      _attributes = {'color': 'black',
                     'width': 1,
                     'fillcolor': 'black',
                     'size': 3,
                     'fillstyle': '', 
                     'outline': 'black'}
  
      def draw(self, canvas):
          color     = self.attributes['color']
          width     = self.attributes['width']
          fillstyle = self.attributes['fillstyle']
          outline   = self.attributes['outline']
          spread    = self.attributes['size']
          arguments = (canvas,)
          p1, p2    = self.boundingBox()
          for i in range(len(self.points)):
              x1, y1 = self.scaled[i]
              canvas.create_rectangle(x1-spread, y1, x1+spread,
                                      self.anchor, fill=color,
                                      width=width, outline=outline,
                                      stipple=fillstyle)
  
  class GraphObjects:
      def __init__(self, objects):
          self.objects = objects
  
      def boundingBox(self):
          c1, c2 = self.objects[0].boundingBox()
          for object in self.objects[1:]:
              c1o, c2o = object.boundingBox()
              c1 = minBound([c1, c1o])
  
              c2 = maxBound([c2, c2o])
          return c1, c2
  
      def fitToScale(self, scale=(1,1), shift=(0,0)):
          for object in self.objects:
              object.fitToScale(scale, shift)
  
      def draw(self, canvas):
          for object in self.objects:
              object.draw(canvas)
  
  class GraphBase(Frame):
      def __init__(self, master, width, height,
                   background='white',title="",xtitle='',ytitle='', **kw):
          apply(Frame.__init__, (self, master), kw)
          self.title=title                                        
          self.xtitle=xtitle
          self.ytitle=ytitle
          self.canvas = Canvas(self, width=width, height=height,
                               background=background)
          self.canvas.pack(fill=BOTH, expand=YES)
          border_w = self.canvas.winfo_reqwidth() - \
                     string.atoi(self.canvas.cget('width'))
          border_h = self.canvas.winfo_reqheight() - \
                     string.atoi(self.canvas.cget('height'))
          self.border = (border_w, border_h)
          self.canvas.bind('<Configure>', self.configure)
          self.plotarea_size = [None, None]
          self._setsize()
          self.last_drawn = None
          self.font = ('Verdana', 10)
  
      def configure(self, event):
          new_width = event.width-self.border[0]
          new_height = event.height-self.border[1]
          width = string.atoi(self.canvas.cget('width'))
          height = string.atoi(self.canvas.cget('height'))
          if new_width == width and new_height == height:
              return
          self.canvas.configure(width=new_width, height=new_height)
          self._setsize()
          self.clear()
          self.replot()
  
      def bind(self, *args):
          apply(self.canvas.bind, args)
  
      def _setsize(self):
          self.width = string.atoi(self.canvas.cget('width'))
          self.height = string.atoi(self.canvas.cget('height'))
          #self.plotarea_size[0] = 0.90 * self.width
          #self.plotarea_size[1] = 0.90 * -self.height
          self.plotarea_size[0] = 0.90 * self.width
          self.plotarea_size[1] = 0.90 * -self.height      
          xo = 0.5*(self.width-self.plotarea_size[0])
          yo = self.height-0.5*(self.height+self.plotarea_size[1])
          self.plotarea_origin = (xo, yo)
          
      def draw(self, graphics, xaxis = 'automatic', yaxis = 'automatic'):
          
          self.last_drawn = (graphics, xaxis, yaxis)
          p1, p2 = graphics.boundingBox()
          xaxis = self._axisInterval(xaxis, p1[0], p2[0])
          yaxis = self._axisInterval(yaxis, p1[1], p2[1])
          text_width = [0., 0.]
          text_height = [0., 0.]
          if xaxis is not None:
              p1 = xaxis[0], p1[1]
              p2 = xaxis[1], p2[1]
              xticks = self._ticks(xaxis[0], xaxis[1])
              bb = self._textBoundingBox(xticks[0][1])
              text_height[1] = bb[3]-bb[1]
              text_width[0] = 0.5*(bb[2]-bb[0])
              bb = self._textBoundingBox(xticks[-1][1])
              text_width[1] = 0.5*(bb[2]-bb[0])
          else:
              xticks = None
          if yaxis is not None:
              p1 = p1[0], yaxis[0]
              p2 = p2[0], yaxis[1]
              yticks = self._ticks(yaxis[0], yaxis[1])
              for y in yticks:
                  bb = self._textBoundingBox(y[1])
                  w = bb[2]-bb[0]
                  text_width[0] = max(text_width[0], w)
              h = 0.5*(bb[3]-bb[1])
              text_height[0] = h
              text_height[1] = max(text_height[1], h)
          else:
              yticks = None
          text1 = [text_width[0], -text_height[1]]
          text2 = [text_width[1], -text_height[0]]
          scale = ((self.plotarea_size[0]-text1[0]-text2[0]) / \
                   (p2[0]-p1[0]),
                   (self.plotarea_size[1]-text1[1]-text2[1]) / \
                   (p2[1]-p1[1]))
          shift = ((-p1[0]*scale[0]) + self.plotarea_origin[0] + \
                   text1[0],
                   (-p1[1]*scale[1]) + self.plotarea_origin[1] + \
                   text1[1])
          self._drawAxes(self.canvas, xaxis, yaxis, p1, p2,
                          scale, shift, xticks, yticks)
          graphics.fitToScale(scale, shift)
          graphics.draw(self.canvas)
  
      def _axisInterval(self, spec, lower, upper):
          if spec is None:
              return None
          if spec == 'minimal':
              if lower == upper:
                  return lower-0.5, upper+0.5
              else:
                  return lower, upper
          if spec == 'automatic':
              range = upper-lower
              if range == 0.:
                  return lower-0.5, upper+0.5
              log = math.log10(range)
              power = math.floor(log)
              fraction = log-power
              if fraction <= 0.05:
                  power = power-1
              grid = 10.**power
              lower = lower - lower % grid
              mod = upper % grid
              if mod != 0:
                  upper = upper - mod + grid
              return lower, upper
          if type(spec) == type(()):
              lower, upper = spec
              if lower <= upper:
                  return lower, upper
              else:
                  return upper, lower
          raise ValueError, str(spec) + ': illegal axis specification'
  
      def _drawAxes(self, canvas, xaxis, yaxis,
                    bb1, bb2, scale, shift, xticks, yticks):
          dict = {'anchor': N, 'fill': 'black'}
          if self.font is not None:
              dict['font'] = self.font
          if xaxis is not None:
          #draw x-axis
              lower, upper = xaxis
              text = 1
              once=1
              for y, d in [(bb1[1], -3), (bb2[1], 3)]:
              #d=.5 of tick-length
                  p1 = (scale[0]*lower)+shift[0], (scale[1]*y)+shift[1]
                  if once: pp1=p1
                  p2 = (scale[0]*upper)+shift[0], (scale[1]*y)+shift[1]
                  if once: pp2=p2
                  once = 0
                  Line(self.canvas, p1[0], p1[1], p2[0], p2[1],
                       fill = 'black', width = 1)
                  if xticks:
                      for x, label in xticks:
                          p = (scale[0]*x)+shift[0], \
                              (scale[1]*y)+shift[1]
                          Line(self.canvas, p[0], p[1], p[0], p[1]+d,
                               fill = 'black', width = 1)
                          if text:
                              dict['text'] = label
                              apply(CanvasText, (self.canvas, p[0],
                                                 p[1]+2), dict)    ##KGM 14 Aug 03
                  text = 0
              #write x-axis title
              CanvasText(self.canvas,(pp2[0]-pp1[0])/2.+pp1[0],pp1[1]+22,text=self.xtitle)
          #write graph title
          CanvasText(self.canvas,(pp2[0]-pp1[0])/2.+pp1[0],7,text=self.title) 
          dict['anchor'] = E
          if yaxis is not None:
              #draw y-axis
              lower, upper = yaxis
              text = 1
              once = 1
              for x, d in [(bb1[0], -3), (bb2[0], 3)]:
                  p1 = (scale[0]*x)+shift[0], (scale[1]*lower)+shift[1]
                  p2 = (scale[0]*x)+shift[0], (scale[1]*upper)+shift[1]
                  if once: pp1=p1 ;pp2=p2
                  once = 0
                  Line(self.canvas, p1[0], p1[1], p2[0], p2[1],
                       fill = 'black', width = 1)
                  if yticks:
                      for y, label in yticks:
                          p = (scale[0]*x)+shift[0], \
                              (scale[1]*y)+shift[1]
                          Line(self.canvas, p[0], p[1], p[0]-d, p[1],
                               fill = 'black', width = 1)
                          if text:
                              dict['text'] = label
                              apply(CanvasText,(self.canvas,
                                                p[0]-4,p[1]+2), dict)
                  text = 0
              #write y-axis title
              CanvasText(self.canvas,pp2[0],pp2[1] - 10,text=self.ytitle)
  
      def _ticks(self, lower, upper):
          ideal = (upper-lower)/7.
          log = math.log10(ideal)
          power = math.floor(log)
          fraction = log-power
          factor = 1.
          error = fraction
          for f, lf in self._multiples:
              e = math.fabs(fraction-lf)
              if e < error:
                  error = e
                  factor = f
          grid = factor * 10.**power
          if power > 3 or power < -3:
              format = '%+7.0e'
          elif power >= 0:
              digits = max(1, int(power))
              format = '%' + `digits`+'.0f'
          else:
              digits = -int(power)
              format = '%'+`digits+2`+'.'+`digits`+'f'
          ticks = []
          t = -grid*math.floor(-lower/grid)
          while t <= upper and len(ticks) < 200:
              ticks.append((t, format % (t,)))
              t = t + grid
          return ticks
  
      _multiples = [(2., math.log10(2.)), (5., math.log10(5.))]
  
      def _textBoundingBox(self, text):
          bg = self.canvas.cget('background')
          dict = {'anchor': NW, 'text': text, 'fill': bg}
          if self.font is not None:
              dict['font'] = self.font
          item = apply(CanvasText, (self.canvas, 0., 0.), dict)
          bb = self.canvas.bbox(item)
          self.canvas.delete(item)
          return bb
  
      def replot(self):
          if self.last_drawn is not None:
              apply(self.draw, self.last_drawn)
  
      def clear(self):
          self.canvas.delete('all')
  
      def postscr(self,filename=None):
          """Write to Postscript file given by 'filename'. If none provided,
          ask user.
          """
          from tkFileDialog import asksaveasfilename
          if not filename:
              filename=asksaveasfilename()
          if filename:
              if not filename[-3:] == '.ps':
                  filename+=".ps"
              self.canvas.postscript(width=self.width,height=self.height,file=filename)
  
  class TextBox(Frame):
      def __init__(self, master, width, height,
                   background='white',boxtext='', **kw):
          apply(Frame.__init__, (self, master), kw)
          self.width=width
          self.height=height
          self.canvas=Canvas(self, width=width, height=height,
                             background=background)
          self.canvas.pack(fill=BOTH, expand=YES)
          #CanvasText(self.canvas,text=boxtext)
  
      def postscr(self):
          #select output file
          #from tkFileDialog import asksaveasfilename
          filename=asksaveasfilename()
          if filename:
              if not filename[-3:] == '.ps':
                  filename+=".ps"
              self.canvas.postscript(width=self.width,height=self.height,file=filename)
          
  if __name__ == '__main__':
      print "SimPlot.py %s"%__version__
      root=Tk()
      plt=SimPlot()
      root.title('SimPlot example - First frame')
  
      root1 = Tk()
      root1.title('SimPlot example - Second frame')
      
      """PARAMETER DEFAULTS:
      GraphBase
      ---------
      background='white',
      title="",
      xtitle='',
      ytitle=''
  
      GraphBase.draw
      --------------
      xaxis = 'automatic',
      yaxis = 'automatic')
      
      GraphLine
      ---------
      color:       'black',
      width:        1,
      smooth:       0,
      splinesteps: 12
  
      GraphSymbols:
      -------------
      color: 'black',
      width: 1,
      fillcolor: 'black',
      size: 2,
      fillstyle: '',
      outline: 'black',
      marker: 'circle'}
  
      GraphBars
      ---------
      color: 'black',
      width: 1,
      fillcolor: 'black',
      size: 3,
      fillstyle: '', 
      outline: 'black'
      """
      # Plot 1 -- smooth line + filled bars
      di = 5.0*pi/40.
      data = []
      for i in range(40):
          data.append((float(i)*di,
                       (math.sin(float(i)*di)-math.cos(float(i)*di))))
      line1  = plt.makeLine(data, color='black', width=1,
                        smooth=1)
      line1a = plt.makeBars(data[1:], color='blue', fillstyle='gray25',
                        anchor=0.0)
      
  
      graphObject=plt.makeGraphObjects([line1a,line1])
      #Second panel -- Narrow bars
      line2 = plt.makeBars([(0,0),(1,145),(2,-90),(3,147),(4,22),(5,31),
                          (6,77),(7,125),(8,220),(9,550),(10,560),(11,0)],
                         outline='green',color='red', size=7)
  
  
      graphObject2=plt.makeGraphObjects([line2])
  
      # Third plot -- Smooth line and unsmoothed line 
      line3 = plt.makeLine([(1,145+100),(2,151+100),(3,147+100),(4,22+100),(5,31+100),
                          (6,77+100),(7,125+100),(8,220+100),(9,550+100),(10,560+100)],
                         color='blue', width=2, smooth=1)
      line3a = plt.makeLine([(1,145),(2,151),(3,147),(4,22),(5,31),
                          (6,77),(7,125),(8,220),(9,550),(10,560)],
                         color='green', width=2, smooth=0)
      line3b = plt.makeStep([(1,145+100),(2,151+100),(3,147+100),(4,22+100),(5,31+100),
                          (6,77+100),(7,125+100),(8,220+100),(9,550+100),(10,560+100)],
                         color='red', width=2)
      
      graphObject3 = plt.makeGraphObjects([line3, line3a, line3b])
      
      # Fourth plot -- lines with all available symbols with different
      #                outline colors/fill colors/sizes
      
      line4 = plt.makeSymbols([(1,100),(2,100),(3,100),(4,100),(5,100),
                          (6,100),(7,100),(8,100),(9,100),(10,100)],
                         color='black',fillcolor='red', width=2,marker='triangle')
      line5 = plt.makeSymbols([(1,200),(2,200),(3,200),(4,200),(5,200),
                          (6,200),(7,200),(8,200),(9,200),(10,200)],
                         color='red', width=2,marker='circle')
      line6 = plt.makeSymbols([(1,300),(2,300),(3,300),(4,300),(5,300),
                          (6,300),(7,300),(8,300),(9,300),(10,300)],
                         color='green', width=2,marker='dot')
      line7 = plt.makeSymbols([(1,400),(2,400),(3,400),(4,400),(5,400),
                          (6,400),(7,400),(8,400),(9,400),(10,400)],
                         color='blue', fillcolor='white',
                           size=2, width=2,marker='square')
      line8 = plt.makeSymbols([(1,500),(2,500),(3,500),(4,500),(5,500),
                          (6,500),(7,500),(8,500),(9,500),(10,500)],
                         color='yellow', width=2,marker='triangle')
      line9 = plt.makeSymbols([(1,600),(2,600),(3,600),(4,600),(5,600),
                          (6,600),(7,600),(8,600),(9,600),(10,600)],
                         color='magenta', width=2,marker='cross')
      line10 = plt.makeSymbols([(1,700),(2,700),(3,700),(4,700),(5,700),
                          (6,700),(7,700),(8,700),(9,700),(10,700)],
                         color='brown', width=2,marker='plus')
      line11 = plt.makeSymbols([(1,800),(2,800),(3,800),(4,800),(5,800),
                          (6,800),(7,800),(8,800),(9,800),(10,800)],
                         color='black', fillcolor='orange',
                            width=2,marker='triangle_down')
    
  
      graphObject4 = GraphObjects([line4, line5, line6, line7, line8,
                                   line9, line10, line11])
  
      # Two panels
      f1 = Frame(root)
      f2 = Frame(root1)
   
      graph={}
      # Plots 1 and 2 in panel f1, side by side
      graph[1]= plt.makeGraphBase(f1, 500, 300, title="Plot 1: 1 makeLine call, 1 makeBars call",
                           xtitle='the x-axis',ytitle='the y-axis')
      graph[1].pack(side=LEFT, fill=BOTH, expand=YES)
      graph[1].draw(graphObject,xaxis='minimal', yaxis='minimal')
      
      graph[2]  = plt.makeGraphBase(f1, 500, 300,title="Plot 2: 1 makeBars call",
                          xtitle='time',ytitle='pulse [volt]')
      # Set side-by-side plots
      graph[2].pack(side=LEFT, fill=BOTH, expand=YES)
      graph[2].draw(graphObject2, 'minimal', 'automatic')
         
      # Pack panel 1 to make it visible
      f1.pack()
      
      # Plots 2 and 3 in panel f2, one under the other
      graph[3]  = plt.makeGraphBase(f2, 500, 300,
                          title="Plot 3: 2 makeLine call (smooth, not smooth); 1 makeStep call")
      graph[3].pack(side=TOP, fill=BOTH, expand=YES)
      graph[3].draw(graphObject3)
      
      graph[4]  = plt.makeGraphBase(f2, 500, 300, border=3,title="Plot 4: 8 makeSymbols calls")
      # Set one-over-other configuration of plots
      graph[4].pack(side=TOP, fill=BOTH, expand=YES)
      graph[4].draw(graphObject4)
  
      # Pack panel 2 to make it visible
      f2.pack()
  
      # Save graph[1] to Postscript file (user selects filename)
      graph[1].postscr()
          
      # end plotting stuff
  
      #### Very Important -- get Tk going by starting event loop
      plt.mainloop()
  