Menu
Home
Log in / Register
 
Home arrow Computer Science arrow Data Structures and Algorithms with Python
< Prev   CONTENTS   Next >

1.14.1 A GUI Drawing Application

1 # This class defines the drawing application. The following line says that

2 # the DrawingApplication class inherits from the Frame class. This means

3 # that a DrawingApplication is like a Frame object except for the code

4 # written here which redefines/extends the behavior of a Frame.

5 class DrawingApplication(tkinter.Frame):

6 def init (self, master=None):

7 super(). init (master)

8 self.pack()

9 self.buildWindow()

10 self.graphicsCommands = PyList()

11

12 # This method is called to create all the widgets, place them in the GUI,

13 # and define the event handlers for the application.

14 def buildWindow(self):

15

16 # The master is the root window. The title is set as below.

17 self.master.title("Draw")

18

19 # Here is how to create a menu bar. The tearoff=0 means that menus

20 # can't be separated from the window which is a feature of tkinter.

21 bar = tkinter.Menu(self.master)

22 fileMenu = tkinter.Menu(bar,tearoff=0)

23

24 # This code is called by the "New" menu item below when it is selected.

25 # The same applies for loadFile, addToFile, and saveFile below. The

26 # "Exit" menu item below calls quit on the "master" or root window.

27 def newWindow():

28 # This sets up the turtle to be ready for a new picture to be

29 # drawn. It also sets the sequence back to empty. It is necessary

30 # for the graphicsCommands sequence to be in the object (i.e.

31 # self.graphicsCommands) because otherwise the statement:

32 # graphicsCommands = PyList()

33 # would make this variable a local variable in the newWindow

34 # method. If it were local, it would not be set anymore once the

35 # newWindow method returned.

36 theTurtle.clear()

37 theTurtle.penup()

38 theTurtle.goto(0,0)

39 theTurtle.pendown()

40 screen.update()

41 screen.listen()

42 self.graphicsCommands = PyList()

43

44 fileMenu.add_command(label="New",command=newWindow)

45

46 # The parse function adds the contents of an XML file to the sequence.

47 def parse(filename):

48 xmldoc = xml.dom.minidom.parse(filename)

49

50 graphicsCommandsElement = xmldoc.getElementsByTagName("GraphicsCommands")[0]

51

52 graphicsCommands = graphicsCommandsElement.getElementsByTagName("Command")

53

54 for commandElement in graphicsCommands:

55 print(type(commandElement))

56 command = commandElement.firstChild.data.strip()

57 attr = commandElement.attributes

58 if command == "GoTo":

59 x = float(attr["x"].value)

60 y = float(attr["y"].value)

61 width = float(attr["width"].value)

62 color = attr["color"].value.strip()

63 cmd = GoToCommand(x,y,width,color)

64

65 elif command == "Circle":

66 radius = float(attr["radius"].value)

67 width = float(attr["width"].value)

68 color = attr["color"].value.strip()

69 cmd = CircleCommand(radius,width,color)

70

71 elif command == "BeginFill":

72 color = attr["color"].value.strip()

73 cmd = BeginFillCommand(color)

74

75 elif command == "EndFill":

76 cmd = EndFillCommand()

77

78 elif command == "PenUp":

79 cmd = PenUpCommand()

80

81 elif command == "PenDown":

82 cmd = PenDownCommand()

83 else:

84 raise RuntimeError("Unknown Command: " + command)

85

86 self.graphicsCommands.append(cmd)

87

88 def loadFile():

89

90 filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")

91

92 newWindow()

93

94 # This re-initializes the sequence for the new picture.

95 self.graphicsCommands = PyList()

96

97 # calling parse will read the graphics commands from the file.

98 parse(filename)

99

100 for cmd in self.graphicsCommands:

101 cmd.draw(theTurtle)

102

103 # This line is necessary to update the window after the picture is drawn.

104 screen.update()

105

106

107 fileMenu.add_command(label="Load...",command=loadFile)

108

109 def addToFile():

110 filename = tkinter.filedialog.askopenfilename(title="Select a Graphics File")

111

112 theTurtle.penup()

113 theTurtle.goto(0,0)

114 theTurtle.pendown()

115 theTurtle.pencolor("#000000")

116 theTurtle.fillcolor("#000000")

117 cmd = PenUpCommand()

118 self.graphicsCommands.append(cmd)

119 cmd = GoToCommand(0,0,1,"#000000")

120 self.graphicsCommands.append(cmd)

121 cmd = PenDownCommand()

122 self.graphicsCommands.append(cmd)

123 screen.update()

124 parse(filename)

125

126 for cmd in self.graphicsCommands:

127 cmd.draw(theTurtle)

128

129 screen.update()

130

131 fileMenu.add_command(label="Load Into...",command=addToFile)

132

133 # The write function writes an XML file to the given filename

134 def write(filename):

135 file = open(filename, "w")

136 file.write('<?xml version="1.0" encoding="UTF-8" standalone="no" ?> ')

137 file.write('<GraphicsCommands> ')

138 for cmd in self.graphicsCommands:

139 file.write(' '+str(cmd)+" ")

140

141 file.write('</GraphicsCommands> ')

142

143 file.close()

144

145 def saveFile():

146 filename = tkinter.filedialog.asksaveasfilename(title="Save Picture As...")

147 write(filename)

148

149 fileMenu.add_command(label="Save As...",command=saveFile)

150

151

152 fileMenu.add_command(label="Exit",command=self.master.quit)

153

154 bar.add_cascade(label="File",menu=fileMenu)

155

156 # This tells the root window to display the newly created menu bar.

157 self.master.config(menu=bar)

158

159 # Here several widgets are created. The canvas is the drawing area on

160 # the left side of the window.

161 canvas = tkinter.Canvas(self,width=600,height=600)

162 canvas.pack(side=tkinter.LEFT)

163

164 # By creating a RawTurtle, we can have the turtle draw on this canvas.

165 # Otherwise, a RawTurtle and a Turtle are exactly the same.

166 theTurtle = turtle.RawTurtle(canvas)

167

168 # This makes the shape of the turtle a circle.

169 theTurtle.shape("circle")

170 screen = theTurtle.getscreen()

171

172 # This causes the application to not update the screen unless

173 # screen.update() is called. This is necessary for the ondrag event

174 # handler below. Without it, the program bombs after dragging the

175 # turtle around for a while.

176 screen.tracer(0)

177

178 # This is the area on the right side of the window where all the

179 # buttons, labels, and entry boxes are located. The pad creates some empty

180 # space around the side. The side puts the sideBar on the right side of the

181 # this frame. The fill tells it to fill in all space available on the right

182 # side.

183 sideBar = tkinter.Frame(self,padx=5,pady=5)

184 sideBar.pack(side=tkinter.RIGHT, fill=tkinter.BOTH)

185

186 # This is a label widget. Packing it puts it at the top of the sidebar.

187 pointLabel = tkinter.Label(sideBar,text="Width")

188 pointLabel.pack()

189

190 # This entry widget allows the user to pick a width for their lines.

191 # With the widthSize variable below you can write widthSize.get() to get

192 # the contents of the entry widget and widthSize.set(val) to set the value

193 # of the entry widget to val. Initially the widthSize is set to 1. str(1) is

194 # needed because the entry widget must be given a string.

195 widthSize = tkinter.StringVar()

196 widthEntry = tkinter.Entry(sideBar,textvariable=widthSize)

197 widthEntry.pack()

198 widthSize.set(str(1))

199

200 radiusLabel = tkinter.Label(sideBar,text="Radius")

201 radiusLabel.pack()

202 radiusSize = tkinter.StringVar()

203 radiusEntry = tkinter.Entry(sideBar,textvariable=radiusSize)

204 radiusSize.set(str(10))

205 radiusEntry.pack()

206

207 # A button widget calls an event handler when it is pressed. The circleHandler

208 # function below is the event handler when the Draw Circle button is pressed.

209 def circleHandler():

210 # When drawing, a command is created and then the command is drawn by calling

211 # the draw method. Adding the command to the graphicsCommands sequence means the

212 # application will remember the picture.

213 cmd = CircleCommand(float(radiusSize.get()), float(widthSize.get()), penColor.get())

214 cmd.draw(theTurtle)

215 self.graphicsCommands.append(cmd)

216

217 # These two lines are needed to update the screen and to put the focus back

218 # in the drawing canvas. This is necessary because when pressing "u" to undo,

219 # the screen must have focus to receive the key press.

220 screen.update()

221 screen.listen()

222

223 # This creates the button widget in the sideBar. The fill=tkinter.BOTH causes the button

224 # to expand to fill the entire width of the sideBar.

225 circleButton = tkinter.Button(sideBar, text = "Draw Circle", command=circleHandler)

226 circleButton.pack(fill=tkinter.BOTH)

227

228 # The color mode 255 below allows colors to be specified in RGB form (i.e. Red/

229 # Green/Blue). The mode allows the Red value to be set by a two digit hexadecimal

230 # number ranging from 00-FF. The same applies for Blue and Green values. The

231 # color choosers below return a string representing the selected color and a slice

232 # is taken to extract the #RRGGBB hexadecimal string that the color choosers return.

233 screen.colormode(255)

234 penLabel = tkinter.Label(sideBar,text="Pen Color")

235 penLabel.pack()

236 penColor = tkinter.StringVar()

237 penEntry = tkinter.Entry(sideBar,textvariable=penColor)

238 penEntry.pack()

239 # This is the color black.

240 penColor.set("#000000")

241

242 def getPenColor():

243 color = tkinter.colorchooser.askcolor()

244 if color != None:

245 penColor.set(str(color)[-9:-2])

246

247 penColorButton = tkinter.Button(sideBar, text = "Pick Pen Color", command=getPenColor)

248 penColorButton.pack(fill=tkinter.BOTH)

249

250 fillLabel = tkinter.Label(sideBar,text="Fill Color")

251 fillLabel.pack()

252 fillColor = tkinter.StringVar()

253 fillEntry = tkinter.Entry(sideBar,textvariable=fillColor)

254 fillEntry.pack()

255 fillColor.set("#000000")

256

257 def getFillColor():

258 color = tkinter.colorchooser.askcolor()

259 if color != None:

260 fillColor.set(str(color)[-9:-2])

261

262 fillColorButton =

263 tkinter.Button(sideBar, text = "Pick Fill Color", command=getFillColor)

264 fillColorButton.pack(fill=tkinter.BOTH)

265

266

267 def beginFillHandler():

268 cmd = BeginFillCommand(fillColor.get())

269 cmd.draw(theTurtle)

270 self.graphicsCommands.append(cmd)

271

272 beginFillButton = tkinter.Button(sideBar, text = "Begin Fill", command=beginFillHandler)

273 beginFillButton.pack(fill=tkinter.BOTH)

274

275 def endFillHandler():

276 cmd = EndFillCommand()

277 cmd.draw(theTurtle)

278 self.graphicsCommands.append(cmd)

279

280 endFillButton = tkinter.Button(sideBar, text = "End Fill", command=endFillHandler)

281 endFillButton.pack(fill=tkinter.BOTH)

282

283 penLabel = tkinter.Label(sideBar,text="Pen Is Down")

284 penLabel.pack()

285

286 def penUpHandler():

287 cmd = PenUpCommand()

288 cmd.draw(theTurtle)

289 penLabel.configure(text="Pen Is Up")

290 self.graphicsCommands.append(cmd)

291

292 penUpButton = tkinter.Button(sideBar, text = "Pen Up", command=penUpHandler)

293 penUpButton.pack(fill=tkinter.BOTH)

294

295 def penDownHandler():

296 cmd = PenDownCommand()

297 cmd.draw(theTurtle)

298 penLabel.configure(text="Pen Is Down")

299 self.graphicsCommands.append(cmd)

300

301 penDownButton = tkinter.Button(sideBar, text = "Pen Down", command=penDownHandler)

302 penDownButton.pack(fill=tkinter.BOTH)

303

304 # Here is another event handler. This one handles mouse clicks on the screen.

305 def clickHandler(x,y):

306 # When a mouse click occurs, get the widthSize entry value and set the width of the

307 # pen to the widthSize value. The float(widthSize.get()) is needed because

308 # the width is a float, but the entry widget stores it as a string.

309 cmd = GoToCommand(x,y,float(widthSize.get()),penColor.get())

310 cmd.draw(theTurtle)

311 self.graphicsCommands.append(cmd)

312 screen.update()

313 screen.listen()

314

315 # Here is how we tie the clickHandler to mouse clicks.

316 screen.onclick(clickHandler)

317

318 def dragHandler(x,y):

319 cmd = GoToCommand(x,y,float(widthSize.get()),penColor.get())

320 cmd.draw(theTurtle)

321 self.graphicsCommands.append(cmd)

322 screen.update()

323 screen.listen()

324

325 theTurtle.ondrag(dragHandler)

326

327 # the undoHandler undoes the last command by removing it from the

328 # sequence and then redrawing the entire picture.

329 def undoHandler():

330 if len(self.graphicsCommands) > 0:

331 self.graphicsCommands.removeLast()

332 theTurtle.clear()

333 theTurtle.penup()

334 theTurtle.goto(0,0)

335 theTurtle.pendown()

336 for cmd in self.graphicsCommands:

337 cmd.draw(theTurtle)

338 screen.update()

339 screen.listen()

340

341 screen.onkeypress(undoHandler, "u")

342 screen.listen()

343

344 # The main function in our GUI program is very simple. It creates the

345 # root window. Then it creates the DrawingApplication frame which creates

346 # all the widgets and has the logic for the event handlers. Calling mainloop

347 # on the frames makes it start listening for events. The mainloop function will

348 # return when the application is exited.

349 def main():

350 root = tkinter.Tk()

351 drawingApp = DrawingApplication(root)

352

353 drawingApp.mainloop()

354 print("Program Execution Completed.")

355

356 if name == " main ":

357 main()

 
Found a mistake? Please highlight the word and press Shift + Enter  
< Prev   CONTENTS   Next >
 
Subjects
Accounting
Business & Finance
Communication
Computer Science
Economics
Education
Engineering
Environment
Geography
Health
History
Language & Literature
Law
Management
Marketing
Philosophy
Political science
Psychology
Religion
Sociology
Travel