blob: 295f9f4fc6dbd12c363738a283c0707a0fd68e2d [file] [log] [blame]
Guido van Rossumb241b671998-12-04 16:42:46 +00001# LogoMation-like turtle graphics
2
Georg Brandle3a25832006-05-17 14:56:04 +00003"""
4Turtle graphics is a popular way for introducing programming to
5kids. It was part of the original Logo programming language developed
6by Wally Feurzeig and Seymour Papert in 1966.
7
8Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it
9the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
10the direction it is facing, drawing a line as it moves. Give it the
11command turtle.left(25), and it rotates in-place 25 degrees clockwise.
12
13By combining together these and similar commands, intricate shapes and
14pictures can easily be drawn.
15"""
16
Guido van Rossumb241b671998-12-04 16:42:46 +000017from math import * # Also for export
Georg Brandlf2944602007-08-10 17:29:51 +000018from time import sleep
Guido van Rossumb241b671998-12-04 16:42:46 +000019import Tkinter
Guido van Rossumfd2ede22002-09-23 16:55:05 +000020
Georg Brandle3a25832006-05-17 14:56:04 +000021speeds = ['fastest', 'fast', 'normal', 'slow', 'slowest']
22
Martin v. Löwis4b6ea792000-10-01 17:52:01 +000023class Error(Exception):
24 pass
Guido van Rossumb241b671998-12-04 16:42:46 +000025
26class RawPen:
27
28 def __init__(self, canvas):
29 self._canvas = canvas
30 self._items = []
31 self._tracing = 1
Guido van Rossum3c7a25a2001-08-09 16:42:07 +000032 self._arrow = 0
Georg Brandle3a25832006-05-17 14:56:04 +000033 self._delay = 10 # default delay for drawing
Martin v. Löwis2b88f632006-07-03 10:19:49 +000034 self._angle = 0.0
Guido van Rossumb241b671998-12-04 16:42:46 +000035 self.degrees()
36 self.reset()
37
38 def degrees(self, fullcircle=360.0):
Georg Brandle3a25832006-05-17 14:56:04 +000039 """ Set angle measurement units to degrees.
40
41 Example:
42 >>> turtle.degrees()
43 """
Martin v. Löwis2b88f632006-07-03 10:19:49 +000044 # Don't try to change _angle if it is 0, because
45 # _fullcircle might not be set, yet
46 if self._angle:
47 self._angle = (self._angle / self._fullcircle) * fullcircle
Guido van Rossumb241b671998-12-04 16:42:46 +000048 self._fullcircle = fullcircle
49 self._invradian = pi / (fullcircle * 0.5)
50
51 def radians(self):
Georg Brandle3a25832006-05-17 14:56:04 +000052 """ Set the angle measurement units to radians.
53
54 Example:
55 >>> turtle.radians()
56 """
Guido van Rossumb241b671998-12-04 16:42:46 +000057 self.degrees(2.0*pi)
58
59 def reset(self):
Georg Brandle3a25832006-05-17 14:56:04 +000060 """ Clear the screen, re-center the pen, and set variables to
61 the default values.
62
63 Example:
64 >>> turtle.position()
65 [0.0, -22.0]
66 >>> turtle.heading()
67 100.0
68 >>> turtle.reset()
69 >>> turtle.position()
70 [0.0, 0.0]
71 >>> turtle.heading()
72 0.0
73 """
Guido van Rossumb241b671998-12-04 16:42:46 +000074 canvas = self._canvas
Martin v. Löwis73b9b662002-09-22 13:00:26 +000075 self._canvas.update()
Guido van Rossumb241b671998-12-04 16:42:46 +000076 width = canvas.winfo_width()
77 height = canvas.winfo_height()
78 if width <= 1:
79 width = canvas['width']
80 if height <= 1:
81 height = canvas['height']
82 self._origin = float(width)/2.0, float(height)/2.0
83 self._position = self._origin
84 self._angle = 0.0
85 self._drawing = 1
86 self._width = 1
87 self._color = "black"
88 self._filling = 0
89 self._path = []
Guido van Rossumb241b671998-12-04 16:42:46 +000090 self.clear()
91 canvas._root().tkraise()
92
93 def clear(self):
Georg Brandle3a25832006-05-17 14:56:04 +000094 """ Clear the screen. The turtle does not move.
95
96 Example:
97 >>> turtle.clear()
98 """
Guido van Rossumb241b671998-12-04 16:42:46 +000099 self.fill(0)
100 canvas = self._canvas
101 items = self._items
102 self._items = []
103 for item in items:
104 canvas.delete(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000105 self._delete_turtle()
106 self._draw_turtle()
107
Guido van Rossumb241b671998-12-04 16:42:46 +0000108 def tracer(self, flag):
Georg Brandle3a25832006-05-17 14:56:04 +0000109 """ Set tracing on if flag is True, and off if it is False.
110 Tracing means line are drawn more slowly, with an
111 animation of an arrow along the line.
112
113 Example:
114 >>> turtle.tracer(False) # turns off Tracer
115 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000116 self._tracing = flag
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000117 if not self._tracing:
118 self._delete_turtle()
119 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000120
121 def forward(self, distance):
Georg Brandle3a25832006-05-17 14:56:04 +0000122 """ Go forward distance steps.
123
124 Example:
125 >>> turtle.position()
126 [0.0, 0.0]
127 >>> turtle.forward(25)
128 >>> turtle.position()
129 [25.0, 0.0]
130 >>> turtle.forward(-75)
131 >>> turtle.position()
132 [-50.0, 0.0]
133 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000134 x0, y0 = start = self._position
135 x1 = x0 + distance * cos(self._angle*self._invradian)
136 y1 = y0 - distance * sin(self._angle*self._invradian)
137 self._goto(x1, y1)
138
139 def backward(self, distance):
Georg Brandle3a25832006-05-17 14:56:04 +0000140 """ Go backwards distance steps.
141
142 The turtle's heading does not change.
143
144 Example:
145 >>> turtle.position()
146 [0.0, 0.0]
147 >>> turtle.backward(30)
148 >>> turtle.position()
149 [-30.0, 0.0]
150 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000151 self.forward(-distance)
152
153 def left(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000154 """ Turn left angle units (units are by default degrees,
155 but can be set via the degrees() and radians() functions.)
156
157 When viewed from above, the turning happens in-place around
158 its front tip.
159
160 Example:
161 >>> turtle.heading()
162 22
163 >>> turtle.left(45)
164 >>> turtle.heading()
165 67.0
166 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000167 self._angle = (self._angle + angle) % self._fullcircle
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000168 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000169
170 def right(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000171 """ Turn right angle units (units are by default degrees,
172 but can be set via the degrees() and radians() functions.)
173
174 When viewed from above, the turning happens in-place around
175 its front tip.
176
177 Example:
178 >>> turtle.heading()
179 22
180 >>> turtle.right(45)
181 >>> turtle.heading()
182 337.0
183 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000184 self.left(-angle)
185
186 def up(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000187 """ Pull the pen up -- no drawing when moving.
188
189 Example:
190 >>> turtle.up()
191 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000192 self._drawing = 0
193
194 def down(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000195 """ Put the pen down -- draw when moving.
196
197 Example:
198 >>> turtle.down()
199 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000200 self._drawing = 1
201
202 def width(self, width):
Georg Brandle3a25832006-05-17 14:56:04 +0000203 """ Set the line to thickness to width.
204
205 Example:
206 >>> turtle.width(10)
207 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000208 self._width = float(width)
209
210 def color(self, *args):
Georg Brandle3a25832006-05-17 14:56:04 +0000211 """ Set the pen color.
212
213 Three input formats are allowed:
214
215 color(s)
216 s is a Tk specification string, such as "red" or "yellow"
217
218 color((r, g, b))
219 *a tuple* of r, g, and b, which represent, an RGB color,
220 and each of r, g, and b are in the range [0..1]
221
222 color(r, g, b)
223 r, g, and b represent an RGB color, and each of r, g, and b
224 are in the range [0..1]
225
226 Example:
227
228 >>> turtle.color('brown')
229 >>> tup = (0.2, 0.8, 0.55)
230 >>> turtle.color(tup)
231 >>> turtle.color(0, .5, 0)
232 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000233 if not args:
234 raise Error, "no color arguments"
235 if len(args) == 1:
236 color = args[0]
237 if type(color) == type(""):
238 # Test the color first
239 try:
240 id = self._canvas.create_line(0, 0, 0, 0, fill=color)
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000241 except Tkinter.TclError:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000242 raise Error, "bad color string: %r" % (color,)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000243 self._set_color(color)
Guido van Rossumb241b671998-12-04 16:42:46 +0000244 return
245 try:
246 r, g, b = color
247 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000248 raise Error, "bad color sequence: %r" % (color,)
Guido van Rossumb241b671998-12-04 16:42:46 +0000249 else:
250 try:
251 r, g, b = args
252 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000253 raise Error, "bad color arguments: %r" % (args,)
Guido van Rossumb241b671998-12-04 16:42:46 +0000254 assert 0 <= r <= 1
255 assert 0 <= g <= 1
256 assert 0 <= b <= 1
257 x = 255.0
258 y = 0.5
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000259 self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y)))
260
261 def _set_color(self,color):
262 self._color = color
263 self._draw_turtle()
264
Georg Brandle3a25832006-05-17 14:56:04 +0000265 def write(self, text, move=False):
266 """ Write text at the current pen position.
267
268 If move is true, the pen is moved to the bottom-right corner
269 of the text. By default, move is False.
270
271 Example:
272 >>> turtle.write('The race is on!')
273 >>> turtle.write('Home = (0, 0)', True)
274 """
275 x, y = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000276 x = x-1 # correction -- calibrated for Windows
Fred Draked038ca82000-10-23 18:31:14 +0000277 item = self._canvas.create_text(x, y,
Georg Brandle3a25832006-05-17 14:56:04 +0000278 text=str(text), anchor="sw",
Guido van Rossumb241b671998-12-04 16:42:46 +0000279 fill=self._color)
280 self._items.append(item)
281 if move:
282 x0, y0, x1, y1 = self._canvas.bbox(item)
283 self._goto(x1, y1)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000284 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000285
286 def fill(self, flag):
Georg Brandle3a25832006-05-17 14:56:04 +0000287 """ Call fill(1) before drawing the shape you
288 want to fill, and fill(0) when done.
289
290 Example:
291 >>> turtle.fill(1)
292 >>> turtle.forward(100)
293 >>> turtle.left(90)
294 >>> turtle.forward(100)
295 >>> turtle.left(90)
296 >>> turtle.forward(100)
297 >>> turtle.left(90)
298 >>> turtle.forward(100)
299 >>> turtle.fill(0)
300 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000301 if self._filling:
302 path = tuple(self._path)
303 smooth = self._filling < 0
304 if len(path) > 2:
305 item = self._canvas._create('polygon', path,
306 {'fill': self._color,
307 'smooth': smooth})
308 self._items.append(item)
Georg Brandl4235e6f2008-09-26 07:17:03 +0000309 self._canvas.update()
Guido van Rossumb241b671998-12-04 16:42:46 +0000310 self._path = []
Guido van Rossumb241b671998-12-04 16:42:46 +0000311 self._filling = flag
312 if flag:
313 self._path.append(self._position)
314
Georg Brandle3a25832006-05-17 14:56:04 +0000315 def begin_fill(self):
316 """ Called just before drawing a shape to be filled.
Martin v. Löwis06c68b82006-07-10 22:11:28 +0000317 Must eventually be followed by a corresponding end_fill() call.
318 Otherwise it will be ignored.
Georg Brandle3a25832006-05-17 14:56:04 +0000319
320 Example:
321 >>> turtle.begin_fill()
322 >>> turtle.forward(100)
323 >>> turtle.left(90)
324 >>> turtle.forward(100)
325 >>> turtle.left(90)
326 >>> turtle.forward(100)
327 >>> turtle.left(90)
328 >>> turtle.forward(100)
329 >>> turtle.end_fill()
330 """
Martin v. Löwis06c68b82006-07-10 22:11:28 +0000331 self._path = [self._position]
332 self._filling = 1
Georg Brandle3a25832006-05-17 14:56:04 +0000333
334 def end_fill(self):
335 """ Called after drawing a shape to be filled.
336
337 Example:
338 >>> turtle.begin_fill()
339 >>> turtle.forward(100)
340 >>> turtle.left(90)
341 >>> turtle.forward(100)
342 >>> turtle.left(90)
343 >>> turtle.forward(100)
344 >>> turtle.left(90)
345 >>> turtle.forward(100)
346 >>> turtle.end_fill()
347 """
348 self.fill(0)
349
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000350 def circle(self, radius, extent = None):
Georg Brandle3a25832006-05-17 14:56:04 +0000351 """ Draw a circle with given radius.
352 The center is radius units left of the turtle; extent
353 determines which part of the circle is drawn. If not given,
354 the entire circle is drawn.
355
356 If extent is not a full circle, one endpoint of the arc is the
357 current pen position. The arc is drawn in a counter clockwise
358 direction if radius is positive, otherwise in a clockwise
359 direction. In the process, the direction of the turtle is
360 changed by the amount of the extent.
361
362 >>> turtle.circle(50)
363 >>> turtle.circle(120, 180) # half a circle
364 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000365 if extent is None:
Tim Peters63597f12006-07-08 19:55:05 +0000366 extent = self._fullcircle
367 frac = abs(extent)/self._fullcircle
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000368 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
369 w = 1.0 * extent / steps
370 w2 = 0.5 * w
Martin v. Löwis2b88f632006-07-03 10:19:49 +0000371 l = 2.0 * radius * sin(w2*self._invradian)
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000372 if radius < 0:
373 l, w, w2 = -l, -w, -w2
374 self.left(w2)
375 for i in range(steps):
376 self.forward(l)
377 self.left(w)
378 self.right(w2)
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000379
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000380 def heading(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000381 """ Return the turtle's current heading.
382
383 Example:
384 >>> turtle.heading()
385 67.0
386 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000387 return self._angle
388
389 def setheading(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000390 """ Set the turtle facing the given angle.
391
392 Here are some common directions in degrees:
393
394 0 - east
395 90 - north
396 180 - west
397 270 - south
398
399 Example:
400 >>> turtle.setheading(90)
401 >>> turtle.heading()
402 90
403 >>> turtle.setheading(128)
404 >>> turtle.heading()
405 128
406 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000407 self._angle = angle
408 self._draw_turtle()
409
410 def window_width(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000411 """ Returns the width of the turtle window.
412
413 Example:
414 >>> turtle.window_width()
415 640
416 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000417 width = self._canvas.winfo_width()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000418 if width <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000419 width = self._canvas['width']
420 return width
421
422 def window_height(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000423 """ Return the height of the turtle window.
424
425 Example:
426 >>> turtle.window_height()
427 768
428 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000429 height = self._canvas.winfo_height()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000430 if height <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000431 height = self._canvas['height']
432 return height
433
434 def position(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000435 """ Return the current (x, y) location of the turtle.
436
437 Example:
438 >>> turtle.position()
439 [0.0, 240.0]
440 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000441 x0, y0 = self._origin
442 x1, y1 = self._position
443 return [x1-x0, -y1+y0]
444
445 def setx(self, xpos):
Georg Brandle3a25832006-05-17 14:56:04 +0000446 """ Set the turtle's x coordinate to be xpos.
447
448 Example:
449 >>> turtle.position()
450 [10.0, 240.0]
451 >>> turtle.setx(10)
452 >>> turtle.position()
453 [10.0, 240.0]
454 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000455 x0, y0 = self._origin
456 x1, y1 = self._position
457 self._goto(x0+xpos, y1)
458
459 def sety(self, ypos):
Georg Brandle3a25832006-05-17 14:56:04 +0000460 """ Set the turtle's y coordinate to be ypos.
461
462 Example:
463 >>> turtle.position()
464 [0.0, 0.0]
465 >>> turtle.sety(-22)
466 >>> turtle.position()
467 [0.0, -22.0]
468 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000469 x0, y0 = self._origin
470 x1, y1 = self._position
471 self._goto(x1, y0-ypos)
Guido van Rossumb241b671998-12-04 16:42:46 +0000472
Georg Brandle3a25832006-05-17 14:56:04 +0000473 def towards(self, *args):
474 """Returs the angle, which corresponds to the line
475 from turtle-position to point (x,y).
476
477 Argument can be two coordinates or one pair of coordinates
478 or a RawPen/Pen instance.
479
480 Example:
481 >>> turtle.position()
482 [10.0, 10.0]
483 >>> turtle.towards(0,0)
484 225.0
485 """
486 if len(args) == 2:
487 x, y = args
488 else:
489 arg = args[0]
490 if isinstance(arg, RawPen):
491 x, y = arg.position()
492 else:
493 x, y = arg
494 x0, y0 = self.position()
495 dx = x - x0
496 dy = y - y0
497 return (atan2(dy,dx) / self._invradian) % self._fullcircle
498
Guido van Rossumb241b671998-12-04 16:42:46 +0000499 def goto(self, *args):
Georg Brandle3a25832006-05-17 14:56:04 +0000500 """ Go to the given point.
501
502 If the pen is down, then a line will be drawn. The turtle's
503 orientation does not change.
504
505 Two input formats are accepted:
506
507 goto(x, y)
508 go to point (x, y)
509
510 goto((x, y))
511 go to point (x, y)
512
513 Example:
514 >>> turtle.position()
515 [0.0, 0.0]
516 >>> turtle.goto(50, -45)
517 >>> turtle.position()
518 [50.0, -45.0]
519 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000520 if len(args) == 1:
521 try:
522 x, y = args[0]
523 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000524 raise Error, "bad point argument: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000525 else:
526 try:
527 x, y = args
528 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000529 raise Error, "bad coordinates: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000530 x0, y0 = self._origin
531 self._goto(x0+x, y0-y)
532
533 def _goto(self, x1, y1):
Georg Brandle3a25832006-05-17 14:56:04 +0000534 x0, y0 = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000535 self._position = map(float, (x1, y1))
536 if self._filling:
537 self._path.append(self._position)
538 if self._drawing:
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000539 if self._tracing:
Guido van Rossumb241b671998-12-04 16:42:46 +0000540 dx = float(x1 - x0)
541 dy = float(y1 - y0)
542 distance = hypot(dx, dy)
543 nhops = int(distance)
544 item = self._canvas.create_line(x0, y0, x0, y0,
545 width=self._width,
Guido van Rossumb241b671998-12-04 16:42:46 +0000546 capstyle="round",
547 fill=self._color)
548 try:
549 for i in range(1, 1+nhops):
550 x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
551 self._canvas.coords(item, x0, y0, x, y)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000552 self._draw_turtle((x,y))
Guido van Rossumb241b671998-12-04 16:42:46 +0000553 self._canvas.update()
Georg Brandle3a25832006-05-17 14:56:04 +0000554 self._canvas.after(self._delay)
Guido van Rossuma659efe2001-01-01 19:11:07 +0000555 # in case nhops==0
556 self._canvas.coords(item, x0, y0, x1, y1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000557 self._canvas.itemconfigure(item, arrow="none")
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000558 except Tkinter.TclError:
Guido van Rossumb241b671998-12-04 16:42:46 +0000559 # Probably the window was closed!
560 return
561 else:
562 item = self._canvas.create_line(x0, y0, x1, y1,
563 width=self._width,
564 capstyle="round",
565 fill=self._color)
566 self._items.append(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000567 self._draw_turtle()
568
Georg Brandle3a25832006-05-17 14:56:04 +0000569 def speed(self, speed):
570 """ Set the turtle's speed.
571
572 speed must one of these five strings:
573
574 'fastest' is a 0 ms delay
575 'fast' is a 5 ms delay
576 'normal' is a 10 ms delay
577 'slow' is a 15 ms delay
578 'slowest' is a 20 ms delay
579
580 Example:
581 >>> turtle.speed('slow')
582 """
583 try:
584 speed = speed.strip().lower()
585 self._delay = speeds.index(speed) * 5
586 except:
587 raise ValueError("%r is not a valid speed. speed must be "
588 "one of %s" % (speed, speeds))
589
590
591 def delay(self, delay):
592 """ Set the drawing delay in milliseconds.
593
594 This is intended to allow finer control of the drawing speed
595 than the speed() method
596
597 Example:
598 >>> turtle.delay(15)
599 """
600 if int(delay) < 0:
601 raise ValueError("delay must be greater than or equal to 0")
602 self._delay = int(delay)
603
604 def _draw_turtle(self, position=[]):
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000605 if not self._tracing:
Martin v. Löwis3798da02006-06-17 18:44:27 +0000606 self._canvas.update()
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000607 return
608 if position == []:
609 position = self._position
610 x,y = position
611 distance = 8
612 dx = distance * cos(self._angle*self._invradian)
613 dy = distance * sin(self._angle*self._invradian)
614 self._delete_turtle()
Martin v. Löwis4157ffb2002-03-28 15:45:57 +0000615 self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000616 width=self._width,
617 arrow="last",
618 capstyle="round",
619 fill=self._color)
620 self._canvas.update()
621
622 def _delete_turtle(self):
623 if self._arrow != 0:
624 self._canvas.delete(self._arrow)
Georg Brandle3a25832006-05-17 14:56:04 +0000625 self._arrow = 0
Guido van Rossumb241b671998-12-04 16:42:46 +0000626
627
628_root = None
629_canvas = None
630_pen = None
Georg Brandle3a25832006-05-17 14:56:04 +0000631_width = 0.50 # 50% of window width
632_height = 0.75 # 75% of window height
633_startx = None
634_starty = None
635_title = "Turtle Graphics" # default title
Guido van Rossumb241b671998-12-04 16:42:46 +0000636
637class Pen(RawPen):
638
639 def __init__(self):
640 global _root, _canvas
641 if _root is None:
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000642 _root = Tkinter.Tk()
Guido van Rossumb241b671998-12-04 16:42:46 +0000643 _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
Georg Brandle3a25832006-05-17 14:56:04 +0000644 _root.title(_title)
645
Guido van Rossumb241b671998-12-04 16:42:46 +0000646 if _canvas is None:
647 # XXX Should have scroll bars
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000648 _canvas = Tkinter.Canvas(_root, background="white")
Guido van Rossumb241b671998-12-04 16:42:46 +0000649 _canvas.pack(expand=1, fill="both")
Georg Brandle3a25832006-05-17 14:56:04 +0000650
Martin v. Löwisbd39c032006-07-03 09:44:00 +0000651 setup(width=_width, height= _height, startx=_startx, starty=_starty)
Georg Brandle3a25832006-05-17 14:56:04 +0000652
Guido van Rossumb241b671998-12-04 16:42:46 +0000653 RawPen.__init__(self, _canvas)
654
655 def _destroy(self):
656 global _root, _canvas, _pen
657 root = self._canvas._root()
658 if root is _root:
659 _pen = None
660 _root = None
661 _canvas = None
662 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +0000663
Guido van Rossumb241b671998-12-04 16:42:46 +0000664def _getpen():
665 global _pen
Georg Brandle3a25832006-05-17 14:56:04 +0000666 if not _pen:
667 _pen = Pen()
668 return _pen
669
670class Turtle(Pen):
671 pass
672
673"""For documentation of the following functions see
674 the RawPen methods with the same names
675"""
Guido van Rossumb241b671998-12-04 16:42:46 +0000676
677def degrees(): _getpen().degrees()
678def radians(): _getpen().radians()
679def reset(): _getpen().reset()
680def clear(): _getpen().clear()
681def tracer(flag): _getpen().tracer(flag)
682def forward(distance): _getpen().forward(distance)
683def backward(distance): _getpen().backward(distance)
684def left(angle): _getpen().left(angle)
685def right(angle): _getpen().right(angle)
686def up(): _getpen().up()
687def down(): _getpen().down()
688def width(width): _getpen().width(width)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000689def color(*args): _getpen().color(*args)
Guido van Rossumb241b671998-12-04 16:42:46 +0000690def write(arg, move=0): _getpen().write(arg, move)
691def fill(flag): _getpen().fill(flag)
Georg Brandle3a25832006-05-17 14:56:04 +0000692def begin_fill(): _getpen().begin_fill()
Georg Brandl1be63af2006-06-28 20:23:25 +0000693def end_fill(): _getpen().end_fill()
Guido van Rossumb241b671998-12-04 16:42:46 +0000694def circle(radius, extent=None): _getpen().circle(radius, extent)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000695def goto(*args): _getpen().goto(*args)
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000696def heading(): return _getpen().heading()
697def setheading(angle): _getpen().setheading(angle)
698def position(): return _getpen().position()
699def window_width(): return _getpen().window_width()
700def window_height(): return _getpen().window_height()
701def setx(xpos): _getpen().setx(xpos)
702def sety(ypos): _getpen().sety(ypos)
Georg Brandle3a25832006-05-17 14:56:04 +0000703def towards(*args): return _getpen().towards(*args)
704
705def done(): _root.mainloop()
706def delay(delay): return _getpen().delay(delay)
707def speed(speed): return _getpen().speed(speed)
708
709for methodname in dir(RawPen):
710 """ copies RawPen docstrings to module functions of same name """
711 if not methodname.startswith("_"):
712 eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
713
714
715def setup(**geometry):
716 """ Sets the size and position of the main window.
717
Andrew M. Kuchlinge2222a02006-07-29 14:43:55 +0000718 Keywords are width, height, startx and starty:
Georg Brandle3a25832006-05-17 14:56:04 +0000719
720 width: either a size in pixels or a fraction of the screen.
721 Default is 50% of screen.
722 height: either the height in pixels or a fraction of the screen.
723 Default is 75% of screen.
724
725 Setting either width or height to None before drawing will force
726 use of default geometry as in older versions of turtle.py
Tim Petersfd4c4192006-05-18 02:06:40 +0000727
Georg Brandle3a25832006-05-17 14:56:04 +0000728 startx: starting position in pixels from the left edge of the screen.
729 Default is to center window. Setting startx to None is the default
730 and centers window horizontally on screen.
Tim Petersfd4c4192006-05-18 02:06:40 +0000731
Georg Brandle3a25832006-05-17 14:56:04 +0000732 starty: starting position in pixels from the top edge of the screen.
733 Default is to center window. Setting starty to None is the default
734 and centers window vertically on screen.
735
736 Examples:
737 >>> setup (width=200, height=200, startx=0, starty=0)
738
739 sets window to 200x200 pixels, in upper left of screen
740
741 >>> setup(width=.75, height=0.5, startx=None, starty=None)
742
743 sets window to 75% of screen by 50% of screen and centers
744
745 >>> setup(width=None)
746
747 forces use of default geometry as in older versions of turtle.py
748 """
Tim Petersfd4c4192006-05-18 02:06:40 +0000749
Georg Brandle3a25832006-05-17 14:56:04 +0000750 global _width, _height, _startx, _starty
751
752 width = geometry.get('width',_width)
753 if width >= 0 or width == None:
754 _width = width
755 else:
756 raise ValueError, "width can not be less than 0"
757
758 height = geometry.get('height',_height)
759 if height >= 0 or height == None:
760 _height = height
761 else:
762 raise ValueError, "height can not be less than 0"
Tim Petersfd4c4192006-05-18 02:06:40 +0000763
Georg Brandle3a25832006-05-17 14:56:04 +0000764 startx = geometry.get('startx', _startx)
765 if startx >= 0 or startx == None:
Georg Brandl00d71432008-09-26 07:14:57 +0000766 _startx = startx
Georg Brandle3a25832006-05-17 14:56:04 +0000767 else:
768 raise ValueError, "startx can not be less than 0"
769
770 starty = geometry.get('starty', _starty)
771 if starty >= 0 or starty == None:
772 _starty = starty
773 else:
774 raise ValueError, "startx can not be less than 0"
775
776
777 if _root and _width and _height:
778 if 0 < _width <= 1:
779 _width = _root.winfo_screenwidth() * +width
780 if 0 < _height <= 1:
781 _height = _root.winfo_screenheight() * _height
782
783 # center window on screen
784 if _startx is None:
785 _startx = (_root.winfo_screenwidth() - _width) / 2
Tim Petersfd4c4192006-05-18 02:06:40 +0000786
Georg Brandle3a25832006-05-17 14:56:04 +0000787 if _starty is None:
788 _starty = (_root.winfo_screenheight() - _height) / 2
789
790 _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
791
792def title(title):
Andrew M. Kuchlinge2222a02006-07-29 14:43:55 +0000793 """Set the window title.
Georg Brandle3a25832006-05-17 14:56:04 +0000794
795 By default this is set to 'Turtle Graphics'
796
797 Example:
798 >>> title("My Window")
799 """
Tim Petersfd4c4192006-05-18 02:06:40 +0000800
Georg Brandle3a25832006-05-17 14:56:04 +0000801 global _title
802 _title = title
Guido van Rossumb241b671998-12-04 16:42:46 +0000803
804def demo():
805 reset()
806 tracer(1)
807 up()
808 backward(100)
809 down()
810 # draw 3 squares; the last filled
811 width(3)
812 for i in range(3):
813 if i == 2:
814 fill(1)
815 for j in range(4):
816 forward(20)
817 left(90)
818 if i == 2:
819 color("maroon")
820 fill(0)
821 up()
822 forward(30)
823 down()
824 width(1)
825 color("black")
826 # move out of the way
827 tracer(0)
828 up()
829 right(90)
830 forward(100)
831 right(90)
832 forward(100)
833 right(180)
834 down()
835 # some text
836 write("startstart", 1)
837 write("start", 1)
838 color("red")
839 # staircase
840 for i in range(5):
841 forward(20)
842 left(90)
843 forward(20)
844 right(90)
845 # filled staircase
846 fill(1)
847 for i in range(5):
848 forward(20)
849 left(90)
850 forward(20)
851 right(90)
852 fill(0)
Georg Brandle3a25832006-05-17 14:56:04 +0000853 tracer(1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000854 # more text
855 write("end")
Georg Brandle3a25832006-05-17 14:56:04 +0000856
857def demo2():
858 # exercises some new and improved features
859 speed('fast')
860 width(3)
861
862 # draw a segmented half-circle
863 setheading(towards(0,0))
864 x,y = position()
865 r = (x**2+y**2)**.5/2.0
866 right(90)
867 pendown = True
868 for i in range(18):
869 if pendown:
870 up()
871 pendown = False
872 else:
873 down()
874 pendown = True
875 circle(r,10)
876 sleep(2)
Tim Petersfd4c4192006-05-18 02:06:40 +0000877
878 reset()
Georg Brandle3a25832006-05-17 14:56:04 +0000879 left(90)
Tim Petersfd4c4192006-05-18 02:06:40 +0000880
Georg Brandle3a25832006-05-17 14:56:04 +0000881 # draw a series of triangles
882 l = 10
883 color("green")
884 width(3)
885 left(180)
886 sp = 5
887 for i in range(-2,16):
888 if i > 0:
889 color(1.0-0.05*i,0,0.05*i)
890 fill(1)
891 color("green")
892 for j in range(3):
893 forward(l)
894 left(120)
895 l += 10
896 left(15)
897 if sp > 0:
898 sp = sp-1
899 speed(speeds[sp])
900 color(0.25,0,0.75)
901 fill(0)
Georg Brandle3a25832006-05-17 14:56:04 +0000902
Martin v. Löwis06c68b82006-07-10 22:11:28 +0000903 # draw and fill a concave shape
904 left(120)
Georg Brandle3a25832006-05-17 14:56:04 +0000905 up()
Martin v. Löwis06c68b82006-07-10 22:11:28 +0000906 forward(70)
907 right(30)
908 down()
Georg Brandle3a25832006-05-17 14:56:04 +0000909 color("red")
Martin v. Löwis06c68b82006-07-10 22:11:28 +0000910 speed("fastest")
911 fill(1)
912 for i in range(4):
913 circle(50,90)
914 right(90)
915 forward(30)
916 right(90)
917 color("yellow")
918 fill(0)
919 left(90)
920 up()
921 forward(30)
Tim Petersfd4c4192006-05-18 02:06:40 +0000922 down();
Georg Brandle3a25832006-05-17 14:56:04 +0000923
Martin v. Löwis06c68b82006-07-10 22:11:28 +0000924 color("red")
Tim Peters12c00f72006-07-11 02:17:48 +0000925
Georg Brandle3a25832006-05-17 14:56:04 +0000926 # create a second turtle and make the original pursue and catch it
927 turtle=Turtle()
928 turtle.reset()
929 turtle.left(90)
930 turtle.speed('normal')
931 turtle.up()
932 turtle.goto(280,40)
933 turtle.left(24)
934 turtle.down()
935 turtle.speed('fast')
936 turtle.color("blue")
937 turtle.width(2)
938 speed('fastest')
939
940 # turn default turtle towards new turtle object
941 setheading(towards(turtle))
942 while ( abs(position()[0]-turtle.position()[0])>4 or
943 abs(position()[1]-turtle.position()[1])>4):
944 turtle.forward(3.5)
945 turtle.left(0.6)
946 # turn default turtle towards new turtle object
947 setheading(towards(turtle))
948 forward(4)
949 write("CAUGHT! ", move=True)
950
Tim Petersfd4c4192006-05-18 02:06:40 +0000951
Guido van Rossumb241b671998-12-04 16:42:46 +0000952
953if __name__ == '__main__':
954 demo()
Georg Brandle3a25832006-05-17 14:56:04 +0000955 sleep(3)
956 demo2()
957 done()