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¶
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. |