title: Python tkinter: plot function with canvas, HOMEWORK
body: <p>An assignment for uni asks me to use tkinter to create a GUI program that takes a function, color, start point, end point and how many steps to take between (line drawn between each step).</p>
  
  <p>I have had a decent go at it. I have got the entire layout and frame finished (basically) I am finding a few things a little hard. </p>
  
  <p>we have been given this support code.</p>
  
  <pre><code>from Tkinter import *
  import tkMessageBox
  from tkColorChooser import askcolor
  from math import *
  
  
  class FunctionError(Exception):
      """A simple function error exception produced by the make_function 
      function for invalid function definitions.
      """
      pass
  
  def make_function(text):
      """Take a string representing a function in x and return the corresponding
      function.
  
      The FunctionError exception is thrown if text does not represent a valid
      function.
  
      make_function(string) -&gt; (float -&gt; float)
      """
  
      try:
          exec 'f = lambda x: ' + text
          1+f(2.0)       ## test to see if there are any errors in the definition
      except ZeroDivisionError:  ## ignore zero division errors
          pass
      except:
          raise FunctionError()
      return f
  
  
  
  
  class FunctionIterator(object):
      """An iterator object that is intended to be used to produce (x,y)
      pairs of a function 
  
      Constructor: FunctionIterator(function, startx, endx, steps)
      function is the function being iterated over
      startx is the x value to start the iteration
      endx is the x value at the end of the iteration
      steps is the number of iterations.
      Assumes if a division by zero occurs at a given x it won't occur 
      'close to' x
  
      Example:
      Assume square is a function defined to square a number - i.e. 
      square(x) = x*x
      Then 
      &gt;&gt;&gt; list(FunctionIterator(square, 0.0, 5.0, 5))
      [(0.0, 0.0), (1.0, 1.0), (2.0, 4.0), (3.0, 9.0), (4.0, 16.0), (5.0, 25.0)]
  
      and
      for x,y in FunctionIterator(square, 0.0, 5.0, 5): print x,y
  
      produces output
  
      0.0 0.0
      1.0 1.0
      2.0 4.0
      3.0 9.0
      4.0 16.0
      5.0 25.0
  
      NOTE: for functions that are undefined at a given x (e.g. log(-1))
      the y value returned is set to 10000000
      """
  
      def __init__(self, f, startx, endx, steps):
          self._f = f
          self._startx = startx
          self._endx = endx
          self._delta = (endx-startx)/steps
  
  
      def __iter__(self):
          self._x = self._startx
          self._zero = False
          return self
  
      def next(self):
          if self._x &gt; self._endx + self._delta/2:
              raise StopIteration
          else:
              x = self._x
              try:
                  y = self._f(x)
                  self._x += self._delta
                  return (x, y)
              except ZeroDivisionError:
                  if x == self._startx:
                      x += self._delta/2
                      y = self._f(x)
                      self._x += self._delta
                      return (x, y)
                  elif self._zero:
                      x += self._delta/2
                      y = self._f(x)
                      self._x += self._delta
                      self._zero = False
                      return (x, y)
                  else:
                      x -= self._delta/2
                      y = self._f(x)
                      self._zero = True
                      return (x, y)
              except:
                  self._x += self._delta
                  return (x, 10000000)
  
  
  
  class WorldScreen(object):
      """The WorldScreen class is used to convert between real coodinates
      and screen coordinates.
  
      Constructor: WorldScreen(x1, y1, x2, y2, width, height)
      (x1, y1) : The bottom left in real coords
      (x2, y2) : The top right in real coords
      width : the width of the screen (canvas)
      height: the height of the screen (canvas)
  
      NOTE: On a canvas the y increases are you move down the canvas
      so a 'flip' occurs between real y values and screen values - see 
      example below
  
      """
  
      def __init__(self, x1, y1, x2, y2, width, height):
          self.x = x1
          self.y = y1
          self.xscale = (x2-x1)/width
          self.yscale = (y2-y1)/height
          self.height = height
  
      def world2screen(self, x, y):
          """Return the screen (canvas) coordinates given real coordinates.
  
          If the calculated screen y coord is outside the screen then the
          appropriate 'edge value' is returned.
  
          world2screen(float, float) -&gt; (float, float)
  
          Example:
          &gt;&gt;&gt; ws = World_Screen(0,0,10,10, 100.0, 100.0)
          &gt;&gt;&gt; ws.world2screen(5, 5)
          (50.0, 50.0)
          &gt;&gt;&gt; ws.world2screen(10, 10)
          (100.0, 0.0)
          Note that in this case when y is 10 in real coordinates (i.e.
          at the top then the y coord in the canvas is 0 (i.e. at the top)
          """
          wy = self.height - (y - self.y)/self.yscale
          """
          if wy &lt; 0:
              wy = 0
          elif wy &gt; self.height:
              wy = self.height
          """
          return ((x-self.x)/self.xscale, wy)
  
      def screen2world(self, x, y):
          """Return the real coordinates given screen (canvas)coordinates.
  
          screen2world(float, float) -&gt; (float, float)
  
          Example:
          &gt;&gt;&gt; ws = World_Screen(0,0,10,10, 100.0, 100.0)
          &gt;&gt;&gt; ws.screen2world(50,50)
          (5.0, 5.0)
          &gt;&gt;&gt; ws.screen2world(100,0)
          (10.0, 10.0)
          """
  
          return (x*self.xscale + self.x, (self.height - y)*self.yscale + self.y) 
  </code></pre>
  
  <p>This is my code</p>
  
  <pre><code>class PointFrame(Frame):
      """a simple application to allow a user ti enter an
      expression and evaluate it
      """
      def __init__(self, master):
          """
          a a=simple expression evaluator
          """
          Frame.__init__(self, master)
          Label(self, text='Last Point Clicked: ').pack(side=LEFT)
          Label(self, text='').pack(side=LEFT)
          Label(self, text='Cursor Point: ').pack(side=LEFT)
          Label(self, text='').pack(side=LEFT)
  
  
  class CanvasApp(object):
      def __init__(self, master):
          master.title('canvas')
  
          self._canvas = Canvas(master, bg='white', width=500, height=500)
          self._canvas.pack(side=TOP, expand=True, fill=BOTH)
  
  
  class FunctionFrame(Frame):
      """a simple application to allow a user ti enter an
      expressio
      n and evaluate it
      """
      def __init__(self, master):
          """
          a a=simple expression evaluator
          """
          Frame.__init__(self, master, relief=SUNKEN, bg='#A5A5A5', pady=3)
          Label(self, text='Function in x: ', bg='#A5A5A5').pack(side=LEFT)
          function = Entry(self, width=35).pack(side=LEFT, padx=2)
          Button(self, text='Select', command=self.select).pack(side=RIGHT, padx=4)
          colour = Entry(self, width=15).pack(side=RIGHT)
          Label(self, text='Function Colour: ', bg='#A5A5A5').pack(side=RIGHT, padx=2)
  
      def select(self):
          (rgb, hx)= askcolor()
  
  
  class PlotFrame(Frame):
      """a simple application to allow a user ti enter an
      expression and evaluate it
      """
      def __init__(self, master):
          """
          a a=simple expression evaluator
          """
          entry_width = 10
          Frame.__init__(self, master, bg='#A5A5A5', pady=3)
          Label(self, text='Plot Settings', bg='#A5A5A5').pack(side=LEFT)
          Label(self, text='Start X:', bg='#A5A5A5').pack(side=LEFT)
          start_x = Entry(self, width=entry_width).pack(side=LEFT)
          Label(self, text='End X:', bg='#A5A5A5').pack(side=LEFT)
          end_x = Entry(self, width=entry_width).pack(side=LEFT)
          Label(self, text='Start Y:', bg='#A5A5A5').pack(side=LEFT)
          start_y = Entry(self, width=entry_width).pack(side=LEFT)
          Label(self, text='End Y:', bg='#A5A5A5').pack(side=LEFT)
          end_y = Entry(self, width=entry_width).pack(side=LEFT)
          Label(self, text='Steps: ', bg='#A5A5A5').pack(side=LEFT)
          steps = Entry(self, width=entry_width).pack(side=LEFT)
  
  
  class ButtonFrame(Frame):
      """a simple application to allow a user ti enter an
      expression and evaluate it
      """
      def __init__(self, master):
          """
          a a=simple expression evaluator
          """
          Frame.__init__(self, master, bg='#CECEF6')
          Button(self, text='Add Function', command=self.add_function).pack(side=LEFT)
          Button(self, text='Redraw All', command=self.redraw_all).pack(side=LEFT)
          Button(self, text='Remove Last Function', command=self.remove_last).pack(side=LEFT)
          Button(self, text='Remove All Functions', command=self.remove_all).pack(side=LEFT)
          Button(self, text='Exit', command=self.exit_app).pack(side=LEFT)
  
  
      def add_function(self):
          make_function(function)
  
  
      def redraw_all(self):
          pass
  
      def remove_last(self):
          pass
  
      def remove_all(self):
          pass
  
      def exit_app(self):
          self.master.destroy
  
  
  class PlotApp(object):
      def __init__(self, master):
          master.title('Assignment 2')
          PointFrame(master).pack(side=TOP, expand=True, fill=BOTH, pady=2)
          self._canvas = Canvas(master, bg='white', width=500, height=500).pack(side=TOP, expand=True, fill=BOTH, padx=8)
          FunctionFrame(master).pack(side=TOP, expand=True, fill=BOTH, pady=4)
          separator = Frame(height=18, bd=1)
          separator.pack(fill=X)
          PlotFrame(master).pack(side=TOP, expand=True, fill=BOTH, pady=4)
          separator = Frame(height=10, bd=1)
          separator.pack(fill=X)
          ButtonFrame(master).pack(side=TOP, pady=5)
  </code></pre>
  
  <p>The things im having problems with;</p>
  
  <ol>
  <li><p>How can i use the make_function function in the support code to check if the function given in my entry (function) is a function. Sorry for saying function so much.</p></li>
  <li><p>my first class - pointframe is meant o show the last clicked point in the canval and current curser point in real world coordinates. I am meant to use the worldscreen function in the support to convert into real world cords. How can i do this?</p></li>
  <li><p>the select button uses tkcolorchooser to pick a color. how can i make that color come up in the color entry next to it?</p></li>
  <li><p>how can i use the color, function, start and end point and steps to draw a function. using the functioniterator in the support code? then how can i draw over it with a new one, change color, delete all and redraw all?</p></li>
  </ol>
  
  <p>I realize this is alot. i am not asking for someone to just do it for me. I am just after helps, hints and anything that can help me get closer.</p>
  
  <p>Thank you</p>