Creating two button methods Python
Question: I am creating a program in Python that creates a shape based on user input. I
need to create two functions to create buttons using [Zeller's
graphic.py](http://mcsp.wartburg.edu/zelle/python/graphics.py) file. One
button needs to say Quit and the second needs to say Process. Here is what i
have so far but as you can see, they are not in defined functions:

    
    
    #create Quit button
    quitButton = Text(Point(70,73), "Quit")
    quitButton.draw(w)
    Rectangle(Point(45, 50), Point(95,97)).draw(w)
    
    #create Process button
    enterButton = Text(Point(145,73), "Process")
    enterButton.draw(w)
    Rectangle(Point(120, 48), Point(170,98)).draw(w)
    

Here is a description of the necessary methods

  * `createButton(text, pt1button, pt2button, win)` creates a rectangle with corner points `pt1button` and `pt2button` with centered text in window win
  * `clickedButton(button, clickPt)` returns true/false if `clickPt` is in button.

I tried to create the function and received the following error.

Here is my function:

    
    
    def createButton(text, pt1button, pt2button, win):
        button = Text(Point(pt1button, pt2button), text)
        button.draw(win)
    

Here is where I called the function:

    
    
    createButton("Process",145,73,win) 
    createButton("Quit",70,73,win) 
    

Here is the error that was thrown:

    
    
    Traceback (most recent call last):
    File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/project4FINAL.p‌​y", line 77, in <module> main()
    File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/project4FINAL.p‌​y", line 27, in main buttonCreate("Process", 145,73, win)
    NameError: global name 'win' is not defined 
    

Any suggestions?


Answer: So from looking at the code it looks like you want to create a callback
function for each button and then assign each to the canvas via the
`setMouseHandler` of method of `GraphWin`.

So from the example given in the API:

    
    
    from graphics import *
    
    def example_callback():
        print "I love unicorns!" 
    
    def main():
        # win is a Canvas with a setMouseHandler method
        win = GraphWin("My Circle", 100, 100)
        c = Circle(Point(50,50), 10)
        c.draw(win)
    
        #Add a callback to the canvas
        c.cavas.setMouseHandler(example_callback)
        # or win.setMouseHandler(example_callback)
    
        win.getMouse() # Pause to view result
        win.close()    # Close window when done
    
    main()
    

Unless you have bounds checking in you callbacks (to see which shape on the
canvas the mouse is inside), you should only have one canvas per drawn shape.

An example following the use of a `createButton` function:

    
    
    def createButton(text, pt1button, pt2button, win):
        button = Text(Point(pt1button, pt2button), text)
        button.draw(win)
        return button
    
    def _callback(pt):
       print "I love unicorns!"
       print
       print "Mouse clicked at x=%d, y=%d"%(pt.x,pt.y)
       print
    
    def test():
       win = GraphWin()
       win.setCoords(0,0,100,100)
       quitButton = createButton("Quit",70,73,win)
       Rectangle(Point(45, 50), Point(95,97)).draw(win)
       win.setMouseHandler(_callback)
       while True:
          win.getMouse()
       win.close()
    

Below is a complete example using a new Button object:

    
    
    from graphics import *
    
    class Button(object):
        def __init__(self, text, text_pos, rect_pos, win, callback):
            self.win = win
            self.text = Text(text_pos, text)
            self.text.draw(self.win)
    
            # the limits of the button will be defined by the rectangle                                                              
            self.coords = [rect_pos[0].x,rect_pos[0].y,rect_pos[1].x,rect_pos[1].y]
            self.rect = Rectangle(*rect_pos)
            self.rect.draw(self.win)
    
            # set the buttons callback
            self.callback = callback
    
        def _is_inside(self,click):
            limits = self.coords
            return  (limits[0] < click.x < limits[2]) and (limits[1] < click.y < limits[3])
    
    class MyWindow(object):
        def __init__(self,coords=(0,0,100,100)):
            self.win = GraphWin()
            self.win.setCoords(*coords)
    
            # a list of all possible buttons
            self.buttons = []
    
            # register the master callback function
            self.win.setMouseHandler(self._callback)
            self._quit = False
    
            #create a quit and confess button with a custom create method
            self.create_button("Quit",(Point(10,10),Point(40,40)),Point(20,20),self.quit)
            self.create_button("Confess",(Point(50,50),Point(80,80)),Point(60,60),self.confess)
    
        def create_button(self,text,coords,text_coords,callback):
            button = Button(text,text_coords,coords,self.win,callback)
            # create a button and add it to our list of buttons
            self.buttons.append(button)
    
        def confess(self,point):
            print
            print "I love unicorns!"
            print
    
        def quit(self,point):
            self._quit = True
            self.win.close()
    
        # This function is called for any click on the canvas
        def _callback(self,point):
            # Need to do a coordinate transform here to get the click in the coordinates of the button
            x,y = self.win.trans.world(point.x,point.y)
            point = Point(x,y)
            print "Clicked at x=%d, y=%d"%(point.x,point.y)
    
            # cycle through buttons and execute all for which the clicked point lies inside their rectangle
            for button in self.buttons:
                if button._is_inside(point):
                    button.callback(point)
    
        # a loop to keep getting mouse input
        def run(self):
            while not self._quit:
                try:
                    self.win.getMouse()
                except GraphicsError:
                    break
    
    if __name__ == "__main__":
        x = MyWindow()
        x.run()
    

