1. Python Programming 101

This first chapter introduces Python to readers that have never programmed in Python before. It also provides instructions for installing Python on your computer. The Wing Integrated Development Environment (IDE) is recommended for students learning to program. Wing IDE 101 is the free version of the IDE for educational use. The chapter goes on to introduce classes, objects, and object-oriented programming by developing a drawing application using the Tkinter graphical user interface library.

An understanding of object-oriented programming concepts presented in this chapter is necessary before reading the rest of the text. The PyList datatype presented in this chapter is also used in subsequent chapters including chapter 2. Even if the reader has worked through an introductory text or has a basic understanding of Python, there are concepts from this first chapter that are used throughout the text.

1.1. The Dog Class

You can download the Dog class by clicking here.

 1class Dog:
 2    # This is the constructor for the class. It is called whenever a Dog
 3    # object is created. The reference called "self" is created by Python
 4    # and made to point to the space for the newly created object. Python
 5    # does this automatically for us but we have to have "self" as the first
 6    # parameter to the __init__ method (i.e. the constructor). 
 7    def __init__(self, name, month, day, year, speakText):
 8        self.name = name
 9        self.month = month
10        self.day = day
11        self.year = year
12        self.speakText = speakText
13        
14    # This is an accessor method that returns the speakText stored in the
15    # object. Notice that "self" is a parameter. Every method has "self" as its
16    # first parameter. The "self" parameter is a reference to the current 
17    # object. The current object appears on the left hand side of the dot (i.e.
18    # the .) when the method is called. 
19    def speak(self):
20        return self.speakText
21    
22    # Here is an accessor method to get the name
23    def getName(self):
24        return self.name
25    
26    # This is another accessor method that uses the birthday information to 
27    # return a string representing the date.
28    def birthDate(self):
29        return str(self.month) + "/" + str(self.day) + "/" + str(self.year)
30    
31    # This is a mutator method that changes the speakText of the Dog object.
32    def changeBark(self,bark):
33        self.speakText = bark
34        
35    # When creating the new puppy we don't know it's birthday. Pick the 
36    # first dog's birthday plus one year. The speakText will be the 
37    # concatenation of both dog's text. The dog on the left side of the + 
38    # operator is the object referenced by the "self" parameter. The 
39    # "otherDog" parameter is the dog on the right side of the + operator. 
40    def __add__(self,otherDog):
41        return Dog("Puppy of " + self.name + " and " + otherDog.name, \
42                   self.month, self.day, self.year + 1, \
43                   self.speakText + otherDog.speakText)
44  
45def main():      
46    boyDog = Dog("Mesa", 5, 15, 2004, "WOOOOF")
47    girlDog = Dog("Sequoia", 5, 6, 2004, "barkbark")
48    print(boyDog.speak())
49    print(girlDog.speak())
50    print(boyDog.birthDate())
51    print(girlDog.birthDate())
52    boyDog.changeBark("woofywoofy")
53    print(boyDog.speak())
54    puppy = boyDog + girlDog
55    print(puppy.speak())
56    print(puppy.getName())
57    print(puppy.birthDate())
58    
59if __name__ == "__main__":
60    main()

1.2. The Variable Length Records Draw Program

You can download the draw program with variable length records by clicking this link.

  1# The imports include turtle graphics and tkinter modules. 
  2# The colorchooser and filedialog modules let the user
  3# pick a color and a filename.
  4import turtle
  5import tkinter
  6import tkinter.colorchooser
  7import tkinter.filedialog
  8
  9# The following classes define the different commands that 
 10# are supported by the drawing application. 
 11class GoToCommand:
 12    def __init__(self,x,y,width=1,color="black"):
 13        self.x = x
 14        self.y = y
 15        self.width = width
 16        self.color = color
 17        
 18    # The draw method for each command draws the command
 19    # using the given turtle
 20    def draw(self,turtle):
 21        turtle.width(self.width)
 22        turtle.pencolor(self.color)
 23        turtle.goto(self.x,self.y)
 24        
 25    # The __str__ method is a special method that is called
 26    # when a command is converted to a string. The string
 27    # version of the command is how it appears in the graphics
 28    # file format. 
 29    def __str__(self):
 30        return "goto\n" + str(self.x) + "\n" + str(self.y) + "\n" + str(self.width) \
 31               + "\n" + self.color 
 32        
 33class CircleCommand:
 34    def __init__(self,radius, width=1,color="black"):
 35        self.radius = radius
 36        self.width = width
 37        self.color = color
 38        
 39    def draw(self,turtle):
 40        turtle.width(self.width)
 41        turtle.pencolor(self.color)
 42        turtle.circle(self.radius)
 43        
 44    def __str__(self):
 45        return "circle\n" + str(self.radius) + "\n" + str(self.width) + "\n" + self.color
 46        
 47class BeginFillCommand:
 48    def __init__(self,color):
 49        self.color = color
 50        
 51    def draw(self,turtle):
 52        turtle.fillcolor(self.color)
 53        turtle.begin_fill()
 54        
 55    def __str__(self):
 56        return "beginfill\n" + self.color
 57        
 58class EndFillCommand:
 59    def __init__(self):
 60        pass
 61    
 62    def draw(self,turtle):
 63        turtle.end_fill()
 64        
 65    def __str__(self):
 66        return "endfill"
 67        
 68class PenUpCommand:
 69    def __init__(self):
 70        pass
 71    
 72    def draw(self,turtle):
 73        turtle.penup()
 74        
 75    def __str__(self):
 76        return "penup"
 77        
 78class PenDownCommand:
 79    def __init__(self):
 80        pass
 81    
 82    def draw(self,turtle):
 83        turtle.pendown()
 84        
 85    def __str__(self):
 86        return "pendown"
 87
 88# This is the container class for a graphics sequence. It is meant
 89# to hold the graphics commands sequence. 
 90class PyList:
 91    def __init__(self):
 92        self.gcList = []
 93        
 94    # The append method is used to add commands to the sequence.
 95    def append(self,item):
 96        self.gcList = self.gcList + [item]
 97        
 98    # This method is used by the undo function. It slices the sequence
 99    # to remove the last item
100    def removeLast(self):
101        self.gcList = self.gcList[:-1]
102       
103    # This special method is called when iterating over the sequence.
104    # Each time yield is called another element of the sequence is returned
105    # to the iterator (i.e. the for loop that called this.)
106    def __iter__(self):
107        for c in self.gcList:
108            yield c
109    
110    # This is called when the len function is called on the sequence.        
111    def __len__(self):
112        return len(self.gcList)
113       
114# This class defines the drawing application. The following line says that
115# the DrawingApplication class inherits from the Frame class. This means
116class DrawingApplication(tkinter.Frame):
117    def __init__(self, master=None):
118        super().__init__(master)
119        self.pack()
120        self.buildWindow()    
121        self.graphicsCommands = PyList()
122
123    # This method is called to load a file containing graphics commands
124    # and add them to the self.graphicsCommands sequence.
125    def loadSequence(self,file):
126        command = file.readline().strip() 
127        
128        while command != "":
129            
130            if command == "goto":
131                x = float(file.readline())
132                y = float(file.readline())
133                width = float(file.readline())
134                color = file.readline().strip()
135                cmd = GoToCommand(x,y,width,color)
136    
137            elif command == "circle":
138                radius = float(file.readline())
139                width = int(file.readline())
140                color = file.readline().strip()
141                cmd = CircleCommand(radius,width,color)
142    
143            elif command == "beginfill":
144                color = file.readline().strip()
145                cmd = BeginFillCommand(color)
146    
147            elif command == "endfill":
148                cmd = EndFillCommand()
149                
150            elif command == "penup":
151                cmd = PenUpCommand()
152                
153            elif command == "pendown":
154                cmd = PenDownCommand()
155            else:
156                raise RuntimeError("Unknown Command: " + command) 
157    
158            self.graphicsCommands.append(cmd)
159            command = file.readline().strip()
160        
161    # This method is called to create all the widgets, place them in the GUI,
162    # and define the event handlers for the application.
163    def buildWindow(self):
164        
165        # The master is the root window. The title is set as below. 
166        self.master.title("Draw")
167        
168        # Here is how to create a menu bar. The tearoff=0 means that menus
169        # can't be separated from the window which is a feature of tkinter.
170        bar = tkinter.Menu(self.master)
171        fileMenu = tkinter.Menu(bar,tearoff=0)
172        
173        # This code is called by the "New" menu item below when it is selected.
174        # The same applies for loadFile, addToFile, and saveFile below. The 
175        # "Exit" menu item below calls quit on the "master" or root window. 
176        def newWindow():
177            # This sets up the turtle to be ready for a new picture to be 
178            # drawn. It also sets the sequence back to empty. It is necessary
179            # for the graphicsCommands sequence to be in the object (i.e. 
180            # self.graphicsCommands) because otherwise the statement:
181            # graphicsCommands = PyList()
182            # would make this variable a local variable in the newWindow 
183            # method. If it were local, it would not be set anymore once the
184            # newWindow method returned.
185            theTurtle.clear()
186            theTurtle.penup()
187            theTurtle.goto(0,0)
188            theTurtle.pendown()  
189            screen.update()
190            screen.listen()
191            self.graphicsCommands = PyList()
192            
193        fileMenu.add_command(label="New",command=newWindow)
194            
195        def loadFile():
196
197            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
198            file = open(filename, "r")
199            
200            newWindow()
201            
202            # This re-initializes the sequence for the new picture. 
203            self.graphicsCommands = PyList()
204            
205            # calling loadSequence will read the graphics commands from the file.
206            self.loadSequence(file)
207               
208            for cmd in self.graphicsCommands:
209                cmd.draw(theTurtle)
210                
211            # This line is necessary to update the window after the picture is drawn.
212            screen.update()
213            
214            
215        fileMenu.add_command(label="Load...",command=loadFile)
216        
217        def addToFile():
218            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
219            file = open(filename, "r")
220            
221            theTurtle.penup()
222            theTurtle.goto(0,0)
223            theTurtle.pendown()
224            theTurtle.pencolor("#000000")
225            theTurtle.fillcolor("#000000")
226            cmd = PenUpCommand()
227            self.graphicsCommands.append(cmd)
228            cmd = GoToCommand(0,0,1,"#000000")
229            self.graphicsCommands.append(cmd)
230            cmd = PenDownCommand()
231            self.graphicsCommands.append(cmd)
232            screen.update()
233            self.loadSequence(file)
234               
235            for cmd in self.graphicsCommands:
236                cmd.draw(theTurtle)
237                
238            screen.update()            
239        
240        fileMenu.add_command(label="Load Into...",command=addToFile)
241        
242        def saveFile():
243            filename = tkinter.filedialog.asksaveasfilename(title="Save Picture As...")
244            file = open(filename, "w")
245            
246            for cmd in self.graphicsCommands:
247                file.write(str(cmd)+"\n")
248                
249            file.close()
250            
251        fileMenu.add_command(label="Save As...",command=saveFile)
252        
253
254        fileMenu.add_command(label="Exit",command=self.master.quit)
255        
256        bar.add_cascade(label="File",menu=fileMenu)
257        
258        # This tells the root window to display the newly created menu bar.
259        self.master.config(menu=bar)    
260        
261        # Here several widgets are created. The canvas is the drawing area on 
262        # the left side of the window. 
263        canvas = tkinter.Canvas(self,width=600,height=600)
264        canvas.pack(side=tkinter.LEFT)
265        
266        # By creating a RawTurtle, we can have the turtle draw on this canvas. 
267        # Otherwise, a RawTurtle and a Turtle are exactly the same.
268        theTurtle = turtle.RawTurtle(canvas)
269        
270        # This makes the shape of the turtle a circle. 
271        theTurtle.shape("circle")
272        screen = theTurtle.getscreen()
273        
274        # This causes the application to not update the screen unless 
275        # screen.update() is called. This is necessary for the ondrag event
276        # handler below. Without it, the program bombs after dragging the 
277        # turtle around for a while.
278        screen.tracer(0)
279    
280        # This is the area on the right side of the window where all the 
281        # buttons, labels, and entry boxes are located. The pad creates some empty 
282        # space around the side. The side puts the sideBar on the right side of the 
283        # this frame. The fill tells it to fill in all space available on the right
284        # side. 
285        sideBar = tkinter.Frame(self,padx=5,pady=5)
286        sideBar.pack(side=tkinter.RIGHT, fill=tkinter.BOTH)
287        
288        # This is a label widget. Packing it puts it at the top of the sidebar.
289        pointLabel = tkinter.Label(sideBar,text="Width")
290        pointLabel.pack()
291        
292        # This entry widget allows the user to pick a width for their lines. 
293        # With the widthSize variable below you can write widthSize.get() to get
294        # the contents of the entry widget and widthSize.set(val) to set the value
295        # of the entry widget to val. Initially the widthSize is set to 1. str(1) is needed because
296        # the entry widget must be given a string. 
297        widthSize = tkinter.StringVar()
298        widthEntry = tkinter.Entry(sideBar,textvariable=widthSize)
299        widthEntry.pack()
300        widthSize.set(str(1))
301        
302        radiusLabel = tkinter.Label(sideBar,text="Radius")
303        radiusLabel.pack()
304        radiusSize = tkinter.StringVar()
305        radiusEntry = tkinter.Entry(sideBar,textvariable=radiusSize)
306        radiusSize.set(str(10))
307        radiusEntry.pack()
308        
309        # A button widget calls an event handler when it is pressed. The circleHandler
310        # function below is the event handler when the Draw Circle button is pressed. 
311        def circleHandler():
312            # When drawing, a command is created and then the command is drawn by calling
313            # the draw method. Adding the command to the graphicsCommands sequence means the
314            # application will remember the picture. 
315            cmd = CircleCommand(int(radiusSize.get()), int(widthSize.get()), penColor.get())
316            cmd.draw(theTurtle)
317            self.graphicsCommands.append(cmd)
318            
319            # These two lines are needed to update the screen and to put the focus back
320            # in the drawing canvas. This is necessary because when pressing "u" to undo,
321            # the screen must have focus to receive the key press. 
322            screen.update()
323            screen.listen()
324        
325        # This creates the button widget in the sideBar. The fill=tkinter.BOTH causes the button
326        # to expand to fill the entire width of the sideBar.
327        circleButton = tkinter.Button(sideBar, text = "Draw Circle", command=circleHandler)
328        circleButton.pack(fill=tkinter.BOTH)             
329
330        # The color mode 255 below allows colors to be specified in RGB form (i.e. Red/
331        # Green/Blue). The mode allows the Red value to be set by a two digit hexadecimal
332        # number ranging from 00-FF. The same applies for Blue and Green values. The 
333        # color choosers below return a string representing the selected color and a slice
334        # is taken to extract the #RRGGBB hexadecimal string that the color choosers return.
335        screen.colormode(255)
336        penLabel = tkinter.Label(sideBar,text="Pen Color")
337        penLabel.pack()
338        penColor = tkinter.StringVar()
339        penEntry = tkinter.Entry(sideBar,textvariable=penColor)
340        penEntry.pack()
341        # This is the color black.
342        penColor.set("#000000")  
343        
344        def getPenColor():
345            color = tkinter.colorchooser.askcolor()
346            if color != None:
347                penColor.set(str(color)[-9:-2])
348            
349        penColorButton = tkinter.Button(sideBar, text = "Pick Pen Color", command=getPenColor)
350        penColorButton.pack(fill=tkinter.BOTH)           
351            
352        fillLabel = tkinter.Label(sideBar,text="Fill Color")
353        fillLabel.pack()
354        fillColor = tkinter.StringVar()
355        fillEntry = tkinter.Entry(sideBar,textvariable=fillColor)
356        fillEntry.pack()
357        fillColor.set("#000000")     
358        
359        def getFillColor():
360            color = tkinter.colorchooser.askcolor()
361            if color != None:    
362                fillColor.set(str(color)[-9:-2])       
363   
364        fillColorButton = tkinter.Button(sideBar, text = "Pick Fill Color", command=getFillColor)
365        fillColorButton.pack(fill=tkinter.BOTH) 
366
367
368        def beginFillHandler():
369            cmd = BeginFillCommand(fillColor.get())
370            cmd.draw(theTurtle)
371            self.graphicsCommands.append(cmd)
372            
373        beginFillButton = tkinter.Button(sideBar, text = "Begin Fill", command=beginFillHandler)
374        beginFillButton.pack(fill=tkinter.BOTH) 
375        
376        def endFillHandler():
377            cmd = EndFillCommand()
378            cmd.draw(theTurtle)
379            self.graphicsCommands.append(cmd)
380            
381        endFillButton = tkinter.Button(sideBar, text = "End Fill", command=endFillHandler)
382        endFillButton.pack(fill=tkinter.BOTH) 
383 
384        penLabel = tkinter.Label(sideBar,text="Pen Is Down")
385        penLabel.pack()
386        
387        def penUpHandler():
388            cmd = PenUpCommand()
389            cmd.draw(theTurtle)
390            penLabel.configure(text="Pen Is Up")
391            self.graphicsCommands.append(cmd)
392
393        penUpButton = tkinter.Button(sideBar, text = "Pen Up", command=penUpHandler)
394        penUpButton.pack(fill=tkinter.BOTH) 
395       
396        def penDownHandler():
397            cmd = PenDownCommand()
398            cmd.draw(theTurtle)
399            penLabel.configure(text="Pen Is Down")
400            self.graphicsCommands.append(cmd)
401
402        penDownButton = tkinter.Button(sideBar, text = "Pen Down", command=penDownHandler)
403        penDownButton.pack(fill=tkinter.BOTH)          
404
405        # Here is another event handler. This one handles mouse clicks on the screen.
406        def clickHandler(x,y): 
407            # When a mouse click occurs, get the widthSize entry value and set the width of the 
408            # pen to the widthSize value. The int(widthSize.get()) is needed because
409            # the width is an integer, but the entry widget stores it as a string.
410            cmd = GoToCommand(x,y,int(widthSize.get()),penColor.get())
411            cmd.draw(theTurtle)
412            self.graphicsCommands.append(cmd)          
413            screen.update()
414            screen.listen()
415           
416        # Here is how we tie the clickHandler to mouse clicks.
417        screen.onclick(clickHandler)  
418        
419        def dragHandler(x,y):
420            cmd = GoToCommand(x,y,int(widthSize.get()),penColor.get())
421            cmd.draw(theTurtle)
422            self.graphicsCommands.append(cmd)  
423            screen.update()
424            screen.listen()
425            
426        theTurtle.ondrag(dragHandler)
427        
428        # the undoHandler undoes the last command by removing it from the 
429        # sequence and then redrawing the entire picture. 
430        def undoHandler():
431            if len(self.graphicsCommands) > 0:
432                self.graphicsCommands.removeLast()
433                theTurtle.clear()
434                theTurtle.penup()
435                theTurtle.goto(0,0)
436                theTurtle.pendown()
437                for cmd in self.graphicsCommands:
438                    cmd.draw(theTurtle)
439                screen.update()
440                screen.listen()
441                
442        screen.onkeypress(undoHandler, "u")
443        screen.listen()
444   
445# The main function in our GUI program is very simple. It creates the 
446# root window. Then it creates the DrawingApplication frame which creates 
447# all the widgets and has the logic for the event handlers. Calling mainloop
448# on the frames makes it start listening for events. The mainloop function will 
449# return when the application is exited. 
450def main():
451    root = tkinter.Tk()  
452    drawingApp = DrawingApplication(root)  
453
454    drawingApp.mainloop()
455    print("Program Execution Completed.")
456        
457if __name__ == "__main__":
458    main()

1.3. The Final XML Draw Program

You can download the final draw program with XML support from Appendix H of the text by clicking this link.

  1# The imports include turtle graphics and tkinter modules. 
  2# The colorchooser and filedialog modules let the user
  3# pick a color and a filename.
  4import turtle
  5import tkinter
  6import tkinter.colorchooser
  7import tkinter.filedialog
  8import xml.dom.minidom
  9
 10# The following classes define the different commands that 
 11# are supported by the drawing application. 
 12class GoToCommand:
 13    def __init__(self,x,y,width=1,color="black"):
 14        self.x = x
 15        self.y = y
 16        self.width = width
 17        self.color = color
 18        
 19    # The draw method for each command draws the command
 20    # using the given turtle
 21    def draw(self,turtle):
 22        turtle.width(self.width)
 23        turtle.pencolor(self.color)
 24        turtle.goto(self.x,self.y)
 25        
 26    # The __str__ method is a special method that is called
 27    # when a command is converted to a string. The string
 28    # version of the command is how it appears in the graphics
 29    # file format. 
 30    def __str__(self):
 31        return '<Command x="' + str(self.x) + '" y="' + str(self.y) + \
 32               '" width="' + str(self.width) \
 33               + '" color="' + self.color + '">GoTo</Command>' 
 34        
 35class CircleCommand:
 36    def __init__(self,radius, width=1,color="black"):
 37        self.radius = radius
 38        self.width = width
 39        self.color = color
 40        
 41    def draw(self,turtle):
 42        turtle.width(self.width)
 43        turtle.pencolor(self.color)
 44        turtle.circle(self.radius)
 45        
 46    def __str__(self):
 47        return '<Command radius="' + str(self.radius) + '" width="' + \
 48               str(self.width) + '" color="' + self.color + '">Circle</Command>'
 49        
 50class BeginFillCommand:
 51    def __init__(self,color):
 52        self.color = color
 53        
 54    def draw(self,turtle):
 55        turtle.fillcolor(self.color)
 56        turtle.begin_fill()
 57        
 58    def __str__(self):
 59        return '<Command color="' + self.color + '">BeginFill</Command>'
 60        
 61class EndFillCommand:
 62    def __init__(self):
 63        pass
 64    
 65    def draw(self,turtle):
 66        turtle.end_fill()
 67        
 68    def __str__(self):
 69        return "<Command>EndFill</Command>"
 70        
 71class PenUpCommand:
 72    def __init__(self):
 73        pass
 74    
 75    def draw(self,turtle):
 76        turtle.penup()
 77        
 78    def __str__(self):
 79        return "<Command>PenUp</Command>"
 80        
 81class PenDownCommand:
 82    def __init__(self):
 83        pass
 84    
 85    def draw(self,turtle):
 86        turtle.pendown()
 87        
 88    def __str__(self):
 89        return "<Command>PenDown</Command>"
 90
 91# This is the PyList container object. It is meant to hold a  
 92class PyList:
 93    def __init__(self):
 94        self.gcList = []
 95        
 96    # The append method is used to add commands to the sequence.
 97    def append(self,item):
 98        self.gcList = self.gcList + [item]
 99        
100    # This method is used by the undo function. It slices the sequence
101    # to remove the last item
102    def removeLast(self):
103        self.gcList = self.gcList[:-1]
104       
105    # This special method is called when iterating over the sequence.
106    # Each time yield is called another element of the sequence is returned
107    # to the iterator (i.e. the for loop that called this.)
108    def __iter__(self):
109        for c in self.gcList:
110            yield c
111    
112    # This is called when the len function is called on the sequence.        
113    def __len__(self):
114        return len(self.gcList)            
115        
116# This class defines the drawing application. The following line says that
117# the DrawingApplication class inherits from the Frame class. This means 
118# that a DrawingApplication is like a Frame object except for the code
119# written here which redefines/extends the behavior of a Frame. 
120class DrawingApplication(tkinter.Frame):
121    def __init__(self, master=None):
122        super().__init__(master)
123        self.pack()
124        self.buildWindow()    
125        self.graphicsCommands = PyList()
126 
127    # This method is called to create all the widgets, place them in the GUI,
128    # and define the event handlers for the application.
129    def buildWindow(self):
130        
131        # The master is the root window. The title is set as below. 
132        self.master.title("Draw")
133        
134        # Here is how to create a menu bar. The tearoff=0 means that menus
135        # can't be separated from the window which is a feature of tkinter.
136        bar = tkinter.Menu(self.master)
137        fileMenu = tkinter.Menu(bar,tearoff=0)
138        
139        # This code is called by the "New" menu item below when it is selected.
140        # The same applies for loadFile, addToFile, and saveFile below. The 
141        # "Exit" menu item below calls quit on the "master" or root window. 
142        def newWindow():
143            # This sets up the turtle to be ready for a new picture to be 
144            # drawn. It also sets the sequence back to empty. It is necessary
145            # for the graphicsCommands sequence to be in the object (i.e. 
146            # self.graphicsCommands) because otherwise the statement:
147            # graphicsCommands = PyList()
148            # would make this variable a local variable in the newWindow 
149            # method. If it were local, it would not be set anymore once the
150            # newWindow method returned.
151            theTurtle.clear()
152            theTurtle.penup()
153            theTurtle.goto(0,0)
154            theTurtle.pendown()  
155            screen.update()
156            screen.listen()
157            self.graphicsCommands = PyList()
158            
159        fileMenu.add_command(label="New",command=newWindow)
160
161        # The parse function adds the contents of an XML file to the sequence.
162        def parse(filename):
163            xmldoc = xml.dom.minidom.parse(filename)
164            
165            graphicsCommandsElement = xmldoc.getElementsByTagName("GraphicsCommands")[0]
166            
167            graphicsCommands = graphicsCommandsElement.getElementsByTagName("Command")
168            
169            for commandElement in graphicsCommands:
170                print(type(commandElement))
171                command = commandElement.firstChild.data.strip()
172                attr = commandElement.attributes
173                if command == "GoTo":
174                    x = float(attr["x"].value)
175                    y = float(attr["y"].value)
176                    width = float(attr["width"].value)
177                    color = attr["color"].value.strip()
178                    cmd = GoToCommand(x,y,width,color)
179        
180                elif command == "Circle":
181                    radius = float(attr["radius"].value)
182                    width = float(attr["width"].value)
183                    color = attr["color"].value.strip()
184                    cmd = CircleCommand(radius,width,color)
185        
186                elif command == "BeginFill":
187                    color = attr["color"].value.strip()
188                    cmd = BeginFillCommand(color)
189        
190                elif command == "EndFill":
191                    cmd = EndFillCommand()
192                    
193                elif command == "PenUp":
194                    cmd = PenUpCommand()
195                    
196                elif command == "PenDown":
197                    cmd = PenDownCommand()
198                else:
199                    raise RuntimeError("Unknown Command: " + command) 
200        
201                self.graphicsCommands.append(cmd)
202
203        def loadFile():
204
205            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
206            
207            newWindow()
208            
209            # This re-initializes the sequence for the new picture. 
210            self.graphicsCommands = PyList()
211            
212            # calling parse will read the graphics commands from the file.
213            parse(filename)
214               
215            for cmd in self.graphicsCommands:
216                cmd.draw(theTurtle)
217                
218            # This line is necessary to update the window after the picture is drawn.
219            screen.update()
220            
221            
222        fileMenu.add_command(label="Load...",command=loadFile)
223        
224        def addToFile():
225            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
226            
227            theTurtle.penup()
228            theTurtle.goto(0,0)
229            theTurtle.pendown()
230            theTurtle.pencolor("#000000")
231            theTurtle.fillcolor("#000000")
232            cmd = PenUpCommand()
233            self.graphicsCommands.append(cmd)
234            cmd = GoToCommand(0,0,1,"#000000")
235            self.graphicsCommands.append(cmd)
236            cmd = PenDownCommand()
237            self.graphicsCommands.append(cmd)
238            screen.update()
239            parse(filename)
240               
241            for cmd in self.graphicsCommands:
242                cmd.draw(theTurtle)
243                
244            screen.update()            
245        
246        fileMenu.add_command(label="Load Into...",command=addToFile)
247        
248        # The write function writes an XML file to the given filename
249        def write(filename):
250            file = open(filename, "w")
251            file.write('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n')
252            file.write('<GraphicsCommands>\n')
253            for cmd in self.graphicsCommands:
254                file.write('    '+str(cmd)+"\n")
255                
256            file.write('</GraphicsCommands>\n')
257                
258            file.close()  
259
260        def saveFile():
261            filename = tkinter.filedialog.asksaveasfilename(title="Save Picture As...")
262            write(filename)
263            
264        fileMenu.add_command(label="Save As...",command=saveFile)
265        
266
267        fileMenu.add_command(label="Exit",command=self.master.quit)
268        
269        bar.add_cascade(label="File",menu=fileMenu)
270        
271        # This tells the root window to display the newly created menu bar.
272        self.master.config(menu=bar)    
273        
274        # Here several widgets are created. The canvas is the drawing area on 
275        # the left side of the window. 
276        canvas = tkinter.Canvas(self,width=600,height=600)
277        canvas.pack(side=tkinter.LEFT)
278        
279        # By creating a RawTurtle, we can have the turtle draw on this canvas. 
280        # Otherwise, a RawTurtle and a Turtle are exactly the same.
281        theTurtle = turtle.RawTurtle(canvas)
282        
283        # This makes the shape of the turtle a circle. 
284        theTurtle.shape("circle")
285        screen = theTurtle.getscreen()
286        
287        # This causes the application to not update the screen unless 
288        # screen.update() is called. This is necessary for the ondrag event
289        # handler below. Without it, the program bombs after dragging the 
290        # turtle around for a while.
291        screen.tracer(0)
292    
293        # This is the area on the right side of the window where all the 
294        # buttons, labels, and entry boxes are located. The pad creates some empty 
295        # space around the side. The side puts the sideBar on the right side of the 
296        # this frame. The fill tells it to fill in all space available on the right
297        # side. 
298        sideBar = tkinter.Frame(self,padx=5,pady=5)
299        sideBar.pack(side=tkinter.RIGHT, fill=tkinter.BOTH)
300        
301        # This is a label widget. Packing it puts it at the top of the sidebar.
302        pointLabel = tkinter.Label(sideBar,text="Width")
303        pointLabel.pack()
304        
305        # This entry widget allows the user to pick a width for their lines. 
306        # With the widthSize variable below you can write widthSize.get() to get
307        # the contents of the entry widget and widthSize.set(val) to set the value
308        # of the entry widget to val. Initially the widthSize is set to 1. str(1) is 
309        # needed because the entry widget must be given a string. 
310        widthSize = tkinter.StringVar()
311        widthEntry = tkinter.Entry(sideBar,textvariable=widthSize)
312        widthEntry.pack()
313        widthSize.set(str(1))
314        
315        radiusLabel = tkinter.Label(sideBar,text="Radius")
316        radiusLabel.pack()
317        radiusSize = tkinter.StringVar()
318        radiusEntry = tkinter.Entry(sideBar,textvariable=radiusSize)
319        radiusSize.set(str(10))
320        radiusEntry.pack()
321        
322        # A button widget calls an event handler when it is pressed. The circleHandler
323        # function below is the event handler when the Draw Circle button is pressed. 
324        def circleHandler():
325            # When drawing, a command is created and then the command is drawn by calling
326            # the draw method. Adding the command to the graphicsCommands sequence means the
327            # application will remember the picture. 
328            cmd = CircleCommand(float(radiusSize.get()), float(widthSize.get()), penColor.get())
329            cmd.draw(theTurtle)
330            self.graphicsCommands.append(cmd)
331            
332            # These two lines are needed to update the screen and to put the focus back
333            # in the drawing canvas. This is necessary because when pressing "u" to undo,
334            # the screen must have focus to receive the key press. 
335            screen.update()
336            screen.listen()
337        
338        # This creates the button widget in the sideBar. The fill=tkinter.BOTH causes the button
339        # to expand to fill the entire width of the sideBar.
340        circleButton = tkinter.Button(sideBar, text = "Draw Circle", command=circleHandler)
341        circleButton.pack(fill=tkinter.BOTH)             
342
343        # The color mode 255 below allows colors to be specified in RGB form (i.e. Red/
344        # Green/Blue). The mode allows the Red value to be set by a two digit hexadecimal
345        # number ranging from 00-FF. The same applies for Blue and Green values. The 
346        # color choosers below return a string representing the selected color and a slice
347        # is taken to extract the #RRGGBB hexadecimal string that the color choosers return.
348        screen.colormode(255)
349        penLabel = tkinter.Label(sideBar,text="Pen Color")
350        penLabel.pack()
351        penColor = tkinter.StringVar()
352        penEntry = tkinter.Entry(sideBar,textvariable=penColor)
353        penEntry.pack()
354        # This is the color black.
355        penColor.set("#000000")  
356        
357        def getPenColor():
358            color = tkinter.colorchooser.askcolor()
359            if color != None:
360                penColor.set(str(color)[-9:-2])
361            
362        penColorButton = tkinter.Button(sideBar, text = "Pick Pen Color", command=getPenColor)
363        penColorButton.pack(fill=tkinter.BOTH)           
364            
365        fillLabel = tkinter.Label(sideBar,text="Fill Color")
366        fillLabel.pack()
367        fillColor = tkinter.StringVar()
368        fillEntry = tkinter.Entry(sideBar,textvariable=fillColor)
369        fillEntry.pack()
370        fillColor.set("#000000")     
371        
372        def getFillColor():
373            color = tkinter.colorchooser.askcolor()
374            if color != None:    
375                fillColor.set(str(color)[-9:-2])       
376   
377        fillColorButton = \
378            tkinter.Button(sideBar, text = "Pick Fill Color", command=getFillColor)
379        fillColorButton.pack(fill=tkinter.BOTH) 
380
381
382        def beginFillHandler():
383            cmd = BeginFillCommand(fillColor.get())
384            cmd.draw(theTurtle)
385            self.graphicsCommands.append(cmd)
386            
387        beginFillButton = tkinter.Button(sideBar, text = "Begin Fill", command=beginFillHandler)
388        beginFillButton.pack(fill=tkinter.BOTH) 
389        
390        def endFillHandler():
391            cmd = EndFillCommand()
392            cmd.draw(theTurtle)
393            self.graphicsCommands.append(cmd)
394            
395        endFillButton = tkinter.Button(sideBar, text = "End Fill", command=endFillHandler)
396        endFillButton.pack(fill=tkinter.BOTH) 
397 
398        penLabel = tkinter.Label(sideBar,text="Pen Is Down")
399        penLabel.pack()
400        
401        def penUpHandler():
402            cmd = PenUpCommand()
403            cmd.draw(theTurtle)
404            penLabel.configure(text="Pen Is Up")
405            self.graphicsCommands.append(cmd)
406
407        penUpButton = tkinter.Button(sideBar, text = "Pen Up", command=penUpHandler)
408        penUpButton.pack(fill=tkinter.BOTH) 
409       
410        def penDownHandler():
411            cmd = PenDownCommand()
412            cmd.draw(theTurtle)
413            penLabel.configure(text="Pen Is Down")
414            self.graphicsCommands.append(cmd)
415
416        penDownButton = tkinter.Button(sideBar, text = "Pen Down", command=penDownHandler)
417        penDownButton.pack(fill=tkinter.BOTH)          
418
419        # Here is another event handler. This one handles mouse clicks on the screen.
420        def clickHandler(x,y): 
421            # When a mouse click occurs, get the widthSize entry value and set the width of the 
422            # pen to the widthSize value. The float(widthSize.get()) is needed because
423            # the width is an integer, but the entry widget stores it as a string.
424            cmd = GoToCommand(x,y,float(widthSize.get()),penColor.get())
425            cmd.draw(theTurtle)
426            self.graphicsCommands.append(cmd)          
427            screen.update()
428            screen.listen()
429           
430        # Here is how we tie the clickHandler to mouse clicks.
431        screen.onclick(clickHandler)  
432        
433        def dragHandler(x,y):
434            cmd = GoToCommand(x,y,float(widthSize.get()),penColor.get())
435            cmd.draw(theTurtle)
436            self.graphicsCommands.append(cmd)  
437            screen.update()
438            screen.listen()
439            
440        theTurtle.ondrag(dragHandler)
441        
442        # the undoHandler undoes the last command by removing it from the 
443        # sequence and then redrawing the entire picture. 
444        def undoHandler():
445            if len(self.graphicsCommands) > 0:
446                self.graphicsCommands.removeLast()
447                theTurtle.clear()
448                theTurtle.penup()
449                theTurtle.goto(0,0)
450                theTurtle.pendown()
451                for cmd in self.graphicsCommands:
452                    cmd.draw(theTurtle)
453                screen.update()
454                screen.listen()
455                
456        screen.onkeypress(undoHandler, "u")
457        screen.listen()
458   
459# The main function in our GUI program is very simple. It creates the 
460# root window. Then it creates the DrawingApplication frame which creates 
461# all the widgets and has the logic for the event handlers. Calling mainloop
462# on the frames makes it start listening for events. The mainloop function will 
463# return when the application is exited. 
464def main():
465    root = tkinter.Tk()  
466    drawingApp = DrawingApplication(root)  
467
468    drawingApp.mainloop()
469    print("Program Execution Completed.")
470        
471if __name__ == "__main__":
472    main()

1.4. Figures from Text

../_images/wingide1.png

Fig. 1: The Wing IDE

../_images/xref.png

Fig. 2: A Reference and Object

../_images/dogobjects.png

Fig. 3: A Couple of Dog Objects

Method Defintion

Operator

Description

__add__(self,y)

x + y

The addition of two objects. The type of x determines which add operator is called.

__contains__(self,y)

y in x

When x is a collection you can test to see if y is in it.

__eq__(self,y)

x == y

Returns True or False depending on the values of x and y.

__ge__(self,y)

x >= y

Returns True or False depending on the values of x and y.

__getitem__(self,y)

x[y]

Returns the item at the yth position in x.

__gt__(self,y)

x > y

Returns True or False depending on the values of x and y.

__hash__(self)

hash(x)

Returns an integral value for x.

__int__(self)

int(x)

Returns an integer representation of x.

__iter__(self)

for v in x

Returns an iterator object for the sequence x.

__le__(self,y)

x <= y

Returns True or False depending on the values of x and y.

__len__(self)

len(x)

Returns the size of x where x has some length attribute.

__lt__(self,y)

x < y

Returns True or False depending on the values of x and y.

__mod__(self,y)

x % y

Returns the value of x modulo y. This is the remainder of x/y.

__mul__(self,y)

x * y

Returns the product of x and y.

__ne__(self,y)

x != y

Returns True or False depending on the values of x and y.

__neg__(self)

-x

Returns the unary negation of x.

__repr__(self)

repr(x)

Returns a string version of x suitable to be evaluated by the eval function.

__setitem__(self,i,y)

x[i] = y

Sets the item at the ith position in x to y.

__str__(self)

str(x)

Return a string representation of x suitable for user-level interaction.

__sub__(self,y)

x - y

The difference of two objects.

../_images/pixel.png

Fig. 4: Python Operator Magic Methods

../_images/blockindent.png

Fig. 5: Adjusting Indentation in Wing IDE 101

../_images/Draw.png

Fig. 6: The Draw Program

../_images/labelledGUI.png

Fig. 7: The Draw Program Layout