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.

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

1.2. The Variable Length Records Draw Program

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# The imports include turtle graphics and tkinter modules. 
# The colorchooser and filedialog modules let the user
# pick a color and a filename.
import turtle
import tkinter
import tkinter.colorchooser
import tkinter.filedialog

# The following classes define the different commands that 
# are supported by the drawing application. 
class GoToCommand:
    def __init__(self,x,y,width=1,color="black"):
        self.x = x
        self.y = y
        self.width = width
        self.color = color
        
    # The draw method for each command draws the command
    # using the given turtle
    def draw(self,turtle):
        turtle.width(self.width)
        turtle.pencolor(self.color)
        turtle.goto(self.x,self.y)
        
    # The __str__ method is a special method that is called
    # when a command is converted to a string. The string
    # version of the command is how it appears in the graphics
    # file format. 
    def __str__(self):
        return "goto\n" + str(self.x) + "\n" + str(self.y) + "\n" + str(self.width) \
               + "\n" + self.color 
        
class CircleCommand:
    def __init__(self,radius, width=1,color="black"):
        self.radius = radius
        self.width = width
        self.color = color
        
    def draw(self,turtle):
        turtle.width(self.width)
        turtle.pencolor(self.color)
        turtle.circle(self.radius)
        
    def __str__(self):
        return "circle\n" + str(self.radius) + "\n" + str(self.width) + "\n" + self.color
        
class BeginFillCommand:
    def __init__(self,color):
        self.color = color
        
    def draw(self,turtle):
        turtle.fillcolor(self.color)
        turtle.begin_fill()
        
    def __str__(self):
        return "beginfill\n" + self.color
        
class EndFillCommand:
    def __init__(self):
        pass
    
    def draw(self,turtle):
        turtle.end_fill()
        
    def __str__(self):
        return "endfill"
        
class PenUpCommand:
    def __init__(self):
        pass
    
    def draw(self,turtle):
        turtle.penup()
        
    def __str__(self):
        return "penup"
        
class PenDownCommand:
    def __init__(self):
        pass
    
    def draw(self,turtle):
        turtle.pendown()
        
    def __str__(self):
        return "pendown"

# This is the container class for a graphics sequence. It is meant
# to hold the graphics commands sequence. 
class PyList:
    def __init__(self):
        self.gcList = []
        
    # The append method is used to add commands to the sequence.
    def append(self,item):
        self.gcList = self.gcList + [item]
        
    # This method is used by the undo function. It slices the sequence
    # to remove the last item
    def removeLast(self):
        self.gcList = self.gcList[:-1]
       
    # This special method is called when iterating over the sequence.
    # Each time yield is called another element of the sequence is returned
    # to the iterator (i.e. the for loop that called this.)
    def __iter__(self):
        for c in self.gcList:
            yield c
    
    # This is called when the len function is called on the sequence.        
    def __len__(self):
        return len(self.gcList)
       
# This class defines the drawing application. The following line says that
# the DrawingApplication class inherits from the Frame class. This means
class DrawingApplication(tkinter.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        self.buildWindow()    
        self.graphicsCommands = PyList()

    # This method is called to load a file containing graphics commands
    # and add them to the self.graphicsCommands sequence.
    def loadSequence(self,file):
        command = file.readline().strip() 
        
        while command != "":
            
            if command == "goto":
                x = float(file.readline())
                y = float(file.readline())
                width = float(file.readline())
                color = file.readline().strip()
                cmd = GoToCommand(x,y,width,color)
    
            elif command == "circle":
                radius = float(file.readline())
                width = int(file.readline())
                color = file.readline().strip()
                cmd = CircleCommand(radius,width,color)
    
            elif command == "beginfill":
                color = file.readline().strip()
                cmd = BeginFillCommand(color)
    
            elif command == "endfill":
                cmd = EndFillCommand()
                
            elif command == "penup":
                cmd = PenUpCommand()
                
            elif command == "pendown":
                cmd = PenDownCommand()
            else:
                raise RuntimeError("Unknown Command: " + command) 
    
            self.graphicsCommands.append(cmd)
            command = file.readline().strip()
        
    # This method is called to create all the widgets, place them in the GUI,
    # and define the event handlers for the application.
    def buildWindow(self):
        
        # The master is the root window. The title is set as below. 
        self.master.title("Draw")
        
        # Here is how to create a menu bar. The tearoff=0 means that menus
        # can't be separated from the window which is a feature of tkinter.
        bar = tkinter.Menu(self.master)
        fileMenu = tkinter.Menu(bar,tearoff=0)
        
        # This code is called by the "New" menu item below when it is selected.
        # The same applies for loadFile, addToFile, and saveFile below. The 
        # "Exit" menu item below calls quit on the "master" or root window. 
        def newWindow():
            # This sets up the turtle to be ready for a new picture to be 
            # drawn. It also sets the sequence back to empty. It is necessary
            # for the graphicsCommands sequence to be in the object (i.e. 
            # self.graphicsCommands) because otherwise the statement:
            # graphicsCommands = PyList()
            # would make this variable a local variable in the newWindow 
            # method. If it were local, it would not be set anymore once the
            # newWindow method returned.
            theTurtle.clear()
            theTurtle.penup()
            theTurtle.goto(0,0)
            theTurtle.pendown()  
            screen.update()
            screen.listen()
            self.graphicsCommands = PyList()
            
        fileMenu.add_command(label="New",command=newWindow)
            
        def loadFile():

            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
            file = open(filename, "r")
            
            newWindow()
            
            # This re-initializes the sequence for the new picture. 
            self.graphicsCommands = PyList()
            
            # calling loadSequence will read the graphics commands from the file.
            self.loadSequence(file)
               
            for cmd in self.graphicsCommands:
                cmd.draw(theTurtle)
                
            # This line is necessary to update the window after the picture is drawn.
            screen.update()
            
            
        fileMenu.add_command(label="Load...",command=loadFile)
        
        def addToFile():
            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
            file = open(filename, "r")
            
            theTurtle.penup()
            theTurtle.goto(0,0)
            theTurtle.pendown()
            theTurtle.pencolor("#000000")
            theTurtle.fillcolor("#000000")
            cmd = PenUpCommand()
            self.graphicsCommands.append(cmd)
            cmd = GoToCommand(0,0,1,"#000000")
            self.graphicsCommands.append(cmd)
            cmd = PenDownCommand()
            self.graphicsCommands.append(cmd)
            screen.update()
            self.loadSequence(file)
               
            for cmd in self.graphicsCommands:
                cmd.draw(theTurtle)
                
            screen.update()            
        
        fileMenu.add_command(label="Load Into...",command=addToFile)
        
        def saveFile():
            filename = tkinter.filedialog.asksaveasfilename(title="Save Picture As...")
            file = open(filename, "w")
            
            for cmd in self.graphicsCommands:
                file.write(str(cmd)+"\n")
                
            file.close()
            
        fileMenu.add_command(label="Save As...",command=saveFile)
        

        fileMenu.add_command(label="Exit",command=self.master.quit)
        
        bar.add_cascade(label="File",menu=fileMenu)
        
        # This tells the root window to display the newly created menu bar.
        self.master.config(menu=bar)    
        
        # Here several widgets are created. The canvas is the drawing area on 
        # the left side of the window. 
        canvas = tkinter.Canvas(self,width=600,height=600)
        canvas.pack(side=tkinter.LEFT)
        
        # By creating a RawTurtle, we can have the turtle draw on this canvas. 
        # Otherwise, a RawTurtle and a Turtle are exactly the same.
        theTurtle = turtle.RawTurtle(canvas)
        
        # This makes the shape of the turtle a circle. 
        theTurtle.shape("circle")
        screen = theTurtle.getscreen()
        
        # This causes the application to not update the screen unless 
        # screen.update() is called. This is necessary for the ondrag event
        # handler below. Without it, the program bombs after dragging the 
        # turtle around for a while.
        screen.tracer(0)
    
        # This is the area on the right side of the window where all the 
        # buttons, labels, and entry boxes are located. The pad creates some empty 
        # space around the side. The side puts the sideBar on the right side of the 
        # this frame. The fill tells it to fill in all space available on the right
        # side. 
        sideBar = tkinter.Frame(self,padx=5,pady=5)
        sideBar.pack(side=tkinter.RIGHT, fill=tkinter.BOTH)
        
        # This is a label widget. Packing it puts it at the top of the sidebar.
        pointLabel = tkinter.Label(sideBar,text="Width")
        pointLabel.pack()
        
        # This entry widget allows the user to pick a width for their lines. 
        # With the widthSize variable below you can write widthSize.get() to get
        # the contents of the entry widget and widthSize.set(val) to set the value
        # of the entry widget to val. Initially the widthSize is set to 1. str(1) is needed because
        # the entry widget must be given a string. 
        widthSize = tkinter.StringVar()
        widthEntry = tkinter.Entry(sideBar,textvariable=widthSize)
        widthEntry.pack()
        widthSize.set(str(1))
        
        radiusLabel = tkinter.Label(sideBar,text="Radius")
        radiusLabel.pack()
        radiusSize = tkinter.StringVar()
        radiusEntry = tkinter.Entry(sideBar,textvariable=radiusSize)
        radiusSize.set(str(10))
        radiusEntry.pack()
        
        # A button widget calls an event handler when it is pressed. The circleHandler
        # function below is the event handler when the Draw Circle button is pressed. 
        def circleHandler():
            # When drawing, a command is created and then the command is drawn by calling
            # the draw method. Adding the command to the graphicsCommands sequence means the
            # application will remember the picture. 
            cmd = CircleCommand(int(radiusSize.get()), int(widthSize.get()), penColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)
            
            # These two lines are needed to update the screen and to put the focus back
            # in the drawing canvas. This is necessary because when pressing "u" to undo,
            # the screen must have focus to receive the key press. 
            screen.update()
            screen.listen()
        
        # This creates the button widget in the sideBar. The fill=tkinter.BOTH causes the button
        # to expand to fill the entire width of the sideBar.
        circleButton = tkinter.Button(sideBar, text = "Draw Circle", command=circleHandler)
        circleButton.pack(fill=tkinter.BOTH)             

        # The color mode 255 below allows colors to be specified in RGB form (i.e. Red/
        # Green/Blue). The mode allows the Red value to be set by a two digit hexadecimal
        # number ranging from 00-FF. The same applies for Blue and Green values. The 
        # color choosers below return a string representing the selected color and a slice
        # is taken to extract the #RRGGBB hexadecimal string that the color choosers return.
        screen.colormode(255)
        penLabel = tkinter.Label(sideBar,text="Pen Color")
        penLabel.pack()
        penColor = tkinter.StringVar()
        penEntry = tkinter.Entry(sideBar,textvariable=penColor)
        penEntry.pack()
        # This is the color black.
        penColor.set("#000000")  
        
        def getPenColor():
            color = tkinter.colorchooser.askcolor()
            if color != None:
                penColor.set(str(color)[-9:-2])
            
        penColorButton = tkinter.Button(sideBar, text = "Pick Pen Color", command=getPenColor)
        penColorButton.pack(fill=tkinter.BOTH)           
            
        fillLabel = tkinter.Label(sideBar,text="Fill Color")
        fillLabel.pack()
        fillColor = tkinter.StringVar()
        fillEntry = tkinter.Entry(sideBar,textvariable=fillColor)
        fillEntry.pack()
        fillColor.set("#000000")     
        
        def getFillColor():
            color = tkinter.colorchooser.askcolor()
            if color != None:    
                fillColor.set(str(color)[-9:-2])       
   
        fillColorButton = tkinter.Button(sideBar, text = "Pick Fill Color", command=getFillColor)
        fillColorButton.pack(fill=tkinter.BOTH) 


        def beginFillHandler():
            cmd = BeginFillCommand(fillColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)
            
        beginFillButton = tkinter.Button(sideBar, text = "Begin Fill", command=beginFillHandler)
        beginFillButton.pack(fill=tkinter.BOTH) 
        
        def endFillHandler():
            cmd = EndFillCommand()
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)
            
        endFillButton = tkinter.Button(sideBar, text = "End Fill", command=endFillHandler)
        endFillButton.pack(fill=tkinter.BOTH) 
 
        penLabel = tkinter.Label(sideBar,text="Pen Is Down")
        penLabel.pack()
        
        def penUpHandler():
            cmd = PenUpCommand()
            cmd.draw(theTurtle)
            penLabel.configure(text="Pen Is Up")
            self.graphicsCommands.append(cmd)

        penUpButton = tkinter.Button(sideBar, text = "Pen Up", command=penUpHandler)
        penUpButton.pack(fill=tkinter.BOTH) 
       
        def penDownHandler():
            cmd = PenDownCommand()
            cmd.draw(theTurtle)
            penLabel.configure(text="Pen Is Down")
            self.graphicsCommands.append(cmd)

        penDownButton = tkinter.Button(sideBar, text = "Pen Down", command=penDownHandler)
        penDownButton.pack(fill=tkinter.BOTH)          

        # Here is another event handler. This one handles mouse clicks on the screen.
        def clickHandler(x,y): 
            # When a mouse click occurs, get the widthSize entry value and set the width of the 
            # pen to the widthSize value. The int(widthSize.get()) is needed because
            # the width is an integer, but the entry widget stores it as a string.
            cmd = GoToCommand(x,y,int(widthSize.get()),penColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)          
            screen.update()
            screen.listen()
           
        # Here is how we tie the clickHandler to mouse clicks.
        screen.onclick(clickHandler)  
        
        def dragHandler(x,y):
            cmd = GoToCommand(x,y,int(widthSize.get()),penColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)  
            screen.update()
            screen.listen()
            
        theTurtle.ondrag(dragHandler)
        
        # the undoHandler undoes the last command by removing it from the 
        # sequence and then redrawing the entire picture. 
        def undoHandler():
            if len(self.graphicsCommands) > 0:
                self.graphicsCommands.removeLast()
                theTurtle.clear()
                theTurtle.penup()
                theTurtle.goto(0,0)
                theTurtle.pendown()
                for cmd in self.graphicsCommands:
                    cmd.draw(theTurtle)
                screen.update()
                screen.listen()
                
        screen.onkeypress(undoHandler, "u")
        screen.listen()
   
# The main function in our GUI program is very simple. It creates the 
# root window. Then it creates the DrawingApplication frame which creates 
# all the widgets and has the logic for the event handlers. Calling mainloop
# on the frames makes it start listening for events. The mainloop function will 
# return when the application is exited. 
def main():
    root = tkinter.Tk()  
    drawingApp = DrawingApplication(root)  

    drawingApp.mainloop()
    print("Program Execution Completed.")
        
if __name__ == "__main__":
    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
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
# The imports include turtle graphics and tkinter modules. 
# The colorchooser and filedialog modules let the user
# pick a color and a filename.
import turtle
import tkinter
import tkinter.colorchooser
import tkinter.filedialog
import xml.dom.minidom

# The following classes define the different commands that 
# are supported by the drawing application. 
class GoToCommand:
    def __init__(self,x,y,width=1,color="black"):
        self.x = x
        self.y = y
        self.width = width
        self.color = color
        
    # The draw method for each command draws the command
    # using the given turtle
    def draw(self,turtle):
        turtle.width(self.width)
        turtle.pencolor(self.color)
        turtle.goto(self.x,self.y)
        
    # The __str__ method is a special method that is called
    # when a command is converted to a string. The string
    # version of the command is how it appears in the graphics
    # file format. 
    def __str__(self):
        return '<Command x="' + str(self.x) + '" y="' + str(self.y) + \
               '" width="' + str(self.width) \
               + '" color="' + self.color + '">GoTo</Command>' 
        
class CircleCommand:
    def __init__(self,radius, width=1,color="black"):
        self.radius = radius
        self.width = width
        self.color = color
        
    def draw(self,turtle):
        turtle.width(self.width)
        turtle.pencolor(self.color)
        turtle.circle(self.radius)
        
    def __str__(self):
        return '<Command radius="' + str(self.radius) + '" width="' + \
               str(self.width) + '" color="' + self.color + '">Circle</Command>'
        
class BeginFillCommand:
    def __init__(self,color):
        self.color = color
        
    def draw(self,turtle):
        turtle.fillcolor(self.color)
        turtle.begin_fill()
        
    def __str__(self):
        return '<Command color="' + self.color + '">BeginFill</Command>'
        
class EndFillCommand:
    def __init__(self):
        pass
    
    def draw(self,turtle):
        turtle.end_fill()
        
    def __str__(self):
        return "<Command>EndFill</Command>"
        
class PenUpCommand:
    def __init__(self):
        pass
    
    def draw(self,turtle):
        turtle.penup()
        
    def __str__(self):
        return "<Command>PenUp</Command>"
        
class PenDownCommand:
    def __init__(self):
        pass
    
    def draw(self,turtle):
        turtle.pendown()
        
    def __str__(self):
        return "<Command>PenDown</Command>"

# This is the PyList container object. It is meant to hold a  
class PyList:
    def __init__(self):
        self.gcList = []
        
    # The append method is used to add commands to the sequence.
    def append(self,item):
        self.gcList = self.gcList + [item]
        
    # This method is used by the undo function. It slices the sequence
    # to remove the last item
    def removeLast(self):
        self.gcList = self.gcList[:-1]
       
    # This special method is called when iterating over the sequence.
    # Each time yield is called another element of the sequence is returned
    # to the iterator (i.e. the for loop that called this.)
    def __iter__(self):
        for c in self.gcList:
            yield c
    
    # This is called when the len function is called on the sequence.        
    def __len__(self):
        return len(self.gcList)            
        
# This class defines the drawing application. The following line says that
# the DrawingApplication class inherits from the Frame class. This means 
# that a DrawingApplication is like a Frame object except for the code
# written here which redefines/extends the behavior of a Frame. 
class DrawingApplication(tkinter.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        self.buildWindow()    
        self.graphicsCommands = PyList()
 
    # This method is called to create all the widgets, place them in the GUI,
    # and define the event handlers for the application.
    def buildWindow(self):
        
        # The master is the root window. The title is set as below. 
        self.master.title("Draw")
        
        # Here is how to create a menu bar. The tearoff=0 means that menus
        # can't be separated from the window which is a feature of tkinter.
        bar = tkinter.Menu(self.master)
        fileMenu = tkinter.Menu(bar,tearoff=0)
        
        # This code is called by the "New" menu item below when it is selected.
        # The same applies for loadFile, addToFile, and saveFile below. The 
        # "Exit" menu item below calls quit on the "master" or root window. 
        def newWindow():
            # This sets up the turtle to be ready for a new picture to be 
            # drawn. It also sets the sequence back to empty. It is necessary
            # for the graphicsCommands sequence to be in the object (i.e. 
            # self.graphicsCommands) because otherwise the statement:
            # graphicsCommands = PyList()
            # would make this variable a local variable in the newWindow 
            # method. If it were local, it would not be set anymore once the
            # newWindow method returned.
            theTurtle.clear()
            theTurtle.penup()
            theTurtle.goto(0,0)
            theTurtle.pendown()  
            screen.update()
            screen.listen()
            self.graphicsCommands = PyList()
            
        fileMenu.add_command(label="New",command=newWindow)

        # The parse function adds the contents of an XML file to the sequence.
        def parse(filename):
            xmldoc = xml.dom.minidom.parse(filename)
            
            graphicsCommandsElement = xmldoc.getElementsByTagName("GraphicsCommands")[0]
            
            graphicsCommands = graphicsCommandsElement.getElementsByTagName("Command")
            
            for commandElement in graphicsCommands:
                print(type(commandElement))
                command = commandElement.firstChild.data.strip()
                attr = commandElement.attributes
                if command == "GoTo":
                    x = float(attr["x"].value)
                    y = float(attr["y"].value)
                    width = float(attr["width"].value)
                    color = attr["color"].value.strip()
                    cmd = GoToCommand(x,y,width,color)
        
                elif command == "Circle":
                    radius = float(attr["radius"].value)
                    width = float(attr["width"].value)
                    color = attr["color"].value.strip()
                    cmd = CircleCommand(radius,width,color)
        
                elif command == "BeginFill":
                    color = attr["color"].value.strip()
                    cmd = BeginFillCommand(color)
        
                elif command == "EndFill":
                    cmd = EndFillCommand()
                    
                elif command == "PenUp":
                    cmd = PenUpCommand()
                    
                elif command == "PenDown":
                    cmd = PenDownCommand()
                else:
                    raise RuntimeError("Unknown Command: " + command) 
        
                self.graphicsCommands.append(cmd)

        def loadFile():

            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
            
            newWindow()
            
            # This re-initializes the sequence for the new picture. 
            self.graphicsCommands = PyList()
            
            # calling parse will read the graphics commands from the file.
            parse(filename)
               
            for cmd in self.graphicsCommands:
                cmd.draw(theTurtle)
                
            # This line is necessary to update the window after the picture is drawn.
            screen.update()
            
            
        fileMenu.add_command(label="Load...",command=loadFile)
        
        def addToFile():
            filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")
            
            theTurtle.penup()
            theTurtle.goto(0,0)
            theTurtle.pendown()
            theTurtle.pencolor("#000000")
            theTurtle.fillcolor("#000000")
            cmd = PenUpCommand()
            self.graphicsCommands.append(cmd)
            cmd = GoToCommand(0,0,1,"#000000")
            self.graphicsCommands.append(cmd)
            cmd = PenDownCommand()
            self.graphicsCommands.append(cmd)
            screen.update()
            parse(filename)
               
            for cmd in self.graphicsCommands:
                cmd.draw(theTurtle)
                
            screen.update()            
        
        fileMenu.add_command(label="Load Into...",command=addToFile)
        
        # The write function writes an XML file to the given filename
        def write(filename):
            file = open(filename, "w")
            file.write('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n')
            file.write('<GraphicsCommands>\n')
            for cmd in self.graphicsCommands:
                file.write('    '+str(cmd)+"\n")
                
            file.write('</GraphicsCommands>\n')
                
            file.close()  

        def saveFile():
            filename = tkinter.filedialog.asksaveasfilename(title="Save Picture As...")
            write(filename)
            
        fileMenu.add_command(label="Save As...",command=saveFile)
        

        fileMenu.add_command(label="Exit",command=self.master.quit)
        
        bar.add_cascade(label="File",menu=fileMenu)
        
        # This tells the root window to display the newly created menu bar.
        self.master.config(menu=bar)    
        
        # Here several widgets are created. The canvas is the drawing area on 
        # the left side of the window. 
        canvas = tkinter.Canvas(self,width=600,height=600)
        canvas.pack(side=tkinter.LEFT)
        
        # By creating a RawTurtle, we can have the turtle draw on this canvas. 
        # Otherwise, a RawTurtle and a Turtle are exactly the same.
        theTurtle = turtle.RawTurtle(canvas)
        
        # This makes the shape of the turtle a circle. 
        theTurtle.shape("circle")
        screen = theTurtle.getscreen()
        
        # This causes the application to not update the screen unless 
        # screen.update() is called. This is necessary for the ondrag event
        # handler below. Without it, the program bombs after dragging the 
        # turtle around for a while.
        screen.tracer(0)
    
        # This is the area on the right side of the window where all the 
        # buttons, labels, and entry boxes are located. The pad creates some empty 
        # space around the side. The side puts the sideBar on the right side of the 
        # this frame. The fill tells it to fill in all space available on the right
        # side. 
        sideBar = tkinter.Frame(self,padx=5,pady=5)
        sideBar.pack(side=tkinter.RIGHT, fill=tkinter.BOTH)
        
        # This is a label widget. Packing it puts it at the top of the sidebar.
        pointLabel = tkinter.Label(sideBar,text="Width")
        pointLabel.pack()
        
        # This entry widget allows the user to pick a width for their lines. 
        # With the widthSize variable below you can write widthSize.get() to get
        # the contents of the entry widget and widthSize.set(val) to set the value
        # of the entry widget to val. Initially the widthSize is set to 1. str(1) is 
        # needed because the entry widget must be given a string. 
        widthSize = tkinter.StringVar()
        widthEntry = tkinter.Entry(sideBar,textvariable=widthSize)
        widthEntry.pack()
        widthSize.set(str(1))
        
        radiusLabel = tkinter.Label(sideBar,text="Radius")
        radiusLabel.pack()
        radiusSize = tkinter.StringVar()
        radiusEntry = tkinter.Entry(sideBar,textvariable=radiusSize)
        radiusSize.set(str(10))
        radiusEntry.pack()
        
        # A button widget calls an event handler when it is pressed. The circleHandler
        # function below is the event handler when the Draw Circle button is pressed. 
        def circleHandler():
            # When drawing, a command is created and then the command is drawn by calling
            # the draw method. Adding the command to the graphicsCommands sequence means the
            # application will remember the picture. 
            cmd = CircleCommand(float(radiusSize.get()), float(widthSize.get()), penColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)
            
            # These two lines are needed to update the screen and to put the focus back
            # in the drawing canvas. This is necessary because when pressing "u" to undo,
            # the screen must have focus to receive the key press. 
            screen.update()
            screen.listen()
        
        # This creates the button widget in the sideBar. The fill=tkinter.BOTH causes the button
        # to expand to fill the entire width of the sideBar.
        circleButton = tkinter.Button(sideBar, text = "Draw Circle", command=circleHandler)
        circleButton.pack(fill=tkinter.BOTH)             

        # The color mode 255 below allows colors to be specified in RGB form (i.e. Red/
        # Green/Blue). The mode allows the Red value to be set by a two digit hexadecimal
        # number ranging from 00-FF. The same applies for Blue and Green values. The 
        # color choosers below return a string representing the selected color and a slice
        # is taken to extract the #RRGGBB hexadecimal string that the color choosers return.
        screen.colormode(255)
        penLabel = tkinter.Label(sideBar,text="Pen Color")
        penLabel.pack()
        penColor = tkinter.StringVar()
        penEntry = tkinter.Entry(sideBar,textvariable=penColor)
        penEntry.pack()
        # This is the color black.
        penColor.set("#000000")  
        
        def getPenColor():
            color = tkinter.colorchooser.askcolor()
            if color != None:
                penColor.set(str(color)[-9:-2])
            
        penColorButton = tkinter.Button(sideBar, text = "Pick Pen Color", command=getPenColor)
        penColorButton.pack(fill=tkinter.BOTH)           
            
        fillLabel = tkinter.Label(sideBar,text="Fill Color")
        fillLabel.pack()
        fillColor = tkinter.StringVar()
        fillEntry = tkinter.Entry(sideBar,textvariable=fillColor)
        fillEntry.pack()
        fillColor.set("#000000")     
        
        def getFillColor():
            color = tkinter.colorchooser.askcolor()
            if color != None:    
                fillColor.set(str(color)[-9:-2])       
   
        fillColorButton = \
            tkinter.Button(sideBar, text = "Pick Fill Color", command=getFillColor)
        fillColorButton.pack(fill=tkinter.BOTH) 


        def beginFillHandler():
            cmd = BeginFillCommand(fillColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)
            
        beginFillButton = tkinter.Button(sideBar, text = "Begin Fill", command=beginFillHandler)
        beginFillButton.pack(fill=tkinter.BOTH) 
        
        def endFillHandler():
            cmd = EndFillCommand()
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)
            
        endFillButton = tkinter.Button(sideBar, text = "End Fill", command=endFillHandler)
        endFillButton.pack(fill=tkinter.BOTH) 
 
        penLabel = tkinter.Label(sideBar,text="Pen Is Down")
        penLabel.pack()
        
        def penUpHandler():
            cmd = PenUpCommand()
            cmd.draw(theTurtle)
            penLabel.configure(text="Pen Is Up")
            self.graphicsCommands.append(cmd)

        penUpButton = tkinter.Button(sideBar, text = "Pen Up", command=penUpHandler)
        penUpButton.pack(fill=tkinter.BOTH) 
       
        def penDownHandler():
            cmd = PenDownCommand()
            cmd.draw(theTurtle)
            penLabel.configure(text="Pen Is Down")
            self.graphicsCommands.append(cmd)

        penDownButton = tkinter.Button(sideBar, text = "Pen Down", command=penDownHandler)
        penDownButton.pack(fill=tkinter.BOTH)          

        # Here is another event handler. This one handles mouse clicks on the screen.
        def clickHandler(x,y): 
            # When a mouse click occurs, get the widthSize entry value and set the width of the 
            # pen to the widthSize value. The float(widthSize.get()) is needed because
            # the width is an integer, but the entry widget stores it as a string.
            cmd = GoToCommand(x,y,float(widthSize.get()),penColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)          
            screen.update()
            screen.listen()
           
        # Here is how we tie the clickHandler to mouse clicks.
        screen.onclick(clickHandler)  
        
        def dragHandler(x,y):
            cmd = GoToCommand(x,y,float(widthSize.get()),penColor.get())
            cmd.draw(theTurtle)
            self.graphicsCommands.append(cmd)  
            screen.update()
            screen.listen()
            
        theTurtle.ondrag(dragHandler)
        
        # the undoHandler undoes the last command by removing it from the 
        # sequence and then redrawing the entire picture. 
        def undoHandler():
            if len(self.graphicsCommands) > 0:
                self.graphicsCommands.removeLast()
                theTurtle.clear()
                theTurtle.penup()
                theTurtle.goto(0,0)
                theTurtle.pendown()
                for cmd in self.graphicsCommands:
                    cmd.draw(theTurtle)
                screen.update()
                screen.listen()
                
        screen.onkeypress(undoHandler, "u")
        screen.listen()
   
# The main function in our GUI program is very simple. It creates the 
# root window. Then it creates the DrawingApplication frame which creates 
# all the widgets and has the logic for the event handlers. Calling mainloop
# on the frames makes it start listening for events. The mainloop function will 
# return when the application is exited. 
def main():
    root = tkinter.Tk()  
    drawingApp = DrawingApplication(root)  

    drawingApp.mainloop()
    print("Program Execution Completed.")
        
if __name__ == "__main__":
    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