blob: fcde9af4ed4e6eb92c571b9707b269997f7be069 [file] [log] [blame]
Guido van Rossumb241b671998-12-04 16:42:46 +00001# LogoMation-like turtle graphics
2
Thomas Wouters477c8d52006-05-27 19:21:47 +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
Thomas Wouters89f507f2006-12-13 04:49:30 +000018from time import sleep
Guido van Rossumb241b671998-12-04 16:42:46 +000019import Tkinter
Guido van Rossumfd2ede22002-09-23 16:55:05 +000020
Thomas Wouters477c8d52006-05-27 19:21:47 +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
Thomas Wouters477c8d52006-05-27 19:21:47 +000033 self._delay = 10 # default delay for drawing
Thomas Wouters0e3f5912006-08-11 14:57:12 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +000039 """ Set angle measurement units to degrees.
40
41 Example:
42 >>> turtle.degrees()
43 """
Thomas Wouters0e3f5912006-08-11 14:57:12 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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
Thomas Wouters477c8d52006-05-27 19:21:47 +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,
Thomas Wouters477c8d52006-05-27 19:21:47 +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):
Thomas Wouters477c8d52006-05-27 19:21:47 +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)
Guido van Rossumb241b671998-12-04 16:42:46 +0000309 self._path = []
Guido van Rossumb241b671998-12-04 16:42:46 +0000310 self._filling = flag
311 if flag:
312 self._path.append(self._position)
313
Thomas Wouters477c8d52006-05-27 19:21:47 +0000314 def begin_fill(self):
315 """ Called just before drawing a shape to be filled.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000316 Must eventually be followed by a corresponding end_fill() call.
317 Otherwise it will be ignored.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000318
319 Example:
320 >>> turtle.begin_fill()
321 >>> turtle.forward(100)
322 >>> turtle.left(90)
323 >>> turtle.forward(100)
324 >>> turtle.left(90)
325 >>> turtle.forward(100)
326 >>> turtle.left(90)
327 >>> turtle.forward(100)
328 >>> turtle.end_fill()
329 """
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000330 self._path = [self._position]
331 self._filling = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +0000332
333 def end_fill(self):
334 """ Called after drawing a shape to be filled.
335
336 Example:
337 >>> turtle.begin_fill()
338 >>> turtle.forward(100)
339 >>> turtle.left(90)
340 >>> turtle.forward(100)
341 >>> turtle.left(90)
342 >>> turtle.forward(100)
343 >>> turtle.left(90)
344 >>> turtle.forward(100)
345 >>> turtle.end_fill()
346 """
347 self.fill(0)
348
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000349 def circle(self, radius, extent = None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000350 """ Draw a circle with given radius.
351 The center is radius units left of the turtle; extent
352 determines which part of the circle is drawn. If not given,
353 the entire circle is drawn.
354
355 If extent is not a full circle, one endpoint of the arc is the
356 current pen position. The arc is drawn in a counter clockwise
357 direction if radius is positive, otherwise in a clockwise
358 direction. In the process, the direction of the turtle is
359 changed by the amount of the extent.
360
361 >>> turtle.circle(50)
362 >>> turtle.circle(120, 180) # half a circle
363 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000364 if extent is None:
365 extent = self._fullcircle
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000366 frac = abs(extent)/self._fullcircle
367 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
368 w = 1.0 * extent / steps
369 w2 = 0.5 * w
370 l = 2.0 * radius * sin(w2*self._invradian)
371 if radius < 0:
372 l, w, w2 = -l, -w, -w2
373 self.left(w2)
374 for i in range(steps):
375 self.forward(l)
376 self.left(w)
377 self.right(w2)
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000378
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000379 def heading(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000380 """ Return the turtle's current heading.
381
382 Example:
383 >>> turtle.heading()
384 67.0
385 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000386 return self._angle
387
388 def setheading(self, angle):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000389 """ Set the turtle facing the given angle.
390
391 Here are some common directions in degrees:
392
393 0 - east
394 90 - north
395 180 - west
396 270 - south
397
398 Example:
399 >>> turtle.setheading(90)
400 >>> turtle.heading()
401 90
402 >>> turtle.setheading(128)
403 >>> turtle.heading()
404 128
405 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000406 self._angle = angle
407 self._draw_turtle()
408
409 def window_width(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000410 """ Returns the width of the turtle window.
411
412 Example:
413 >>> turtle.window_width()
414 640
415 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000416 width = self._canvas.winfo_width()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000417 if width <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000418 width = self._canvas['width']
419 return width
420
421 def window_height(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000422 """ Return the height of the turtle window.
423
424 Example:
425 >>> turtle.window_height()
426 768
427 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000428 height = self._canvas.winfo_height()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000429 if height <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000430 height = self._canvas['height']
431 return height
432
433 def position(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000434 """ Return the current (x, y) location of the turtle.
435
436 Example:
437 >>> turtle.position()
438 [0.0, 240.0]
439 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000440 x0, y0 = self._origin
441 x1, y1 = self._position
442 return [x1-x0, -y1+y0]
443
444 def setx(self, xpos):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000445 """ Set the turtle's x coordinate to be xpos.
446
447 Example:
448 >>> turtle.position()
449 [10.0, 240.0]
450 >>> turtle.setx(10)
451 >>> turtle.position()
452 [10.0, 240.0]
453 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000454 x0, y0 = self._origin
455 x1, y1 = self._position
456 self._goto(x0+xpos, y1)
457
458 def sety(self, ypos):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000459 """ Set the turtle's y coordinate to be ypos.
460
461 Example:
462 >>> turtle.position()
463 [0.0, 0.0]
464 >>> turtle.sety(-22)
465 >>> turtle.position()
466 [0.0, -22.0]
467 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000468 x0, y0 = self._origin
469 x1, y1 = self._position
470 self._goto(x1, y0-ypos)
Guido van Rossumb241b671998-12-04 16:42:46 +0000471
Thomas Wouters477c8d52006-05-27 19:21:47 +0000472 def towards(self, *args):
473 """Returs the angle, which corresponds to the line
474 from turtle-position to point (x,y).
475
476 Argument can be two coordinates or one pair of coordinates
477 or a RawPen/Pen instance.
478
479 Example:
480 >>> turtle.position()
481 [10.0, 10.0]
482 >>> turtle.towards(0,0)
483 225.0
484 """
485 if len(args) == 2:
486 x, y = args
487 else:
488 arg = args[0]
489 if isinstance(arg, RawPen):
490 x, y = arg.position()
491 else:
492 x, y = arg
493 x0, y0 = self.position()
494 dx = x - x0
495 dy = y - y0
496 return (atan2(dy,dx) / self._invradian) % self._fullcircle
497
Guido van Rossumb241b671998-12-04 16:42:46 +0000498 def goto(self, *args):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000499 """ Go to the given point.
500
501 If the pen is down, then a line will be drawn. The turtle's
502 orientation does not change.
503
504 Two input formats are accepted:
505
506 goto(x, y)
507 go to point (x, y)
508
509 goto((x, y))
510 go to point (x, y)
511
512 Example:
513 >>> turtle.position()
514 [0.0, 0.0]
515 >>> turtle.goto(50, -45)
516 >>> turtle.position()
517 [50.0, -45.0]
518 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000519 if len(args) == 1:
520 try:
521 x, y = args[0]
522 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000523 raise Error, "bad point argument: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000524 else:
525 try:
526 x, y = args
527 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000528 raise Error, "bad coordinates: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000529 x0, y0 = self._origin
530 self._goto(x0+x, y0-y)
531
532 def _goto(self, x1, y1):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000533 x0, y0 = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000534 self._position = map(float, (x1, y1))
535 if self._filling:
536 self._path.append(self._position)
537 if self._drawing:
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000538 if self._tracing:
Guido van Rossumb241b671998-12-04 16:42:46 +0000539 dx = float(x1 - x0)
540 dy = float(y1 - y0)
541 distance = hypot(dx, dy)
542 nhops = int(distance)
543 item = self._canvas.create_line(x0, y0, x0, y0,
544 width=self._width,
Guido van Rossumb241b671998-12-04 16:42:46 +0000545 capstyle="round",
546 fill=self._color)
547 try:
548 for i in range(1, 1+nhops):
549 x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
550 self._canvas.coords(item, x0, y0, x, y)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000551 self._draw_turtle((x,y))
Guido van Rossumb241b671998-12-04 16:42:46 +0000552 self._canvas.update()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000553 self._canvas.after(self._delay)
Guido van Rossuma659efe2001-01-01 19:11:07 +0000554 # in case nhops==0
555 self._canvas.coords(item, x0, y0, x1, y1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000556 self._canvas.itemconfigure(item, arrow="none")
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000557 except Tkinter.TclError:
Guido van Rossumb241b671998-12-04 16:42:46 +0000558 # Probably the window was closed!
559 return
560 else:
561 item = self._canvas.create_line(x0, y0, x1, y1,
562 width=self._width,
563 capstyle="round",
564 fill=self._color)
565 self._items.append(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000566 self._draw_turtle()
567
Thomas Wouters477c8d52006-05-27 19:21:47 +0000568 def speed(self, speed):
569 """ Set the turtle's speed.
570
571 speed must one of these five strings:
572
573 'fastest' is a 0 ms delay
574 'fast' is a 5 ms delay
575 'normal' is a 10 ms delay
576 'slow' is a 15 ms delay
577 'slowest' is a 20 ms delay
578
579 Example:
580 >>> turtle.speed('slow')
581 """
582 try:
583 speed = speed.strip().lower()
584 self._delay = speeds.index(speed) * 5
585 except:
586 raise ValueError("%r is not a valid speed. speed must be "
587 "one of %s" % (speed, speeds))
588
589
590 def delay(self, delay):
591 """ Set the drawing delay in milliseconds.
592
593 This is intended to allow finer control of the drawing speed
594 than the speed() method
595
596 Example:
597 >>> turtle.delay(15)
598 """
599 if int(delay) < 0:
600 raise ValueError("delay must be greater than or equal to 0")
601 self._delay = int(delay)
602
603 def _draw_turtle(self, position=[]):
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000604 if not self._tracing:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000605 self._canvas.update()
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000606 return
607 if position == []:
608 position = self._position
609 x,y = position
610 distance = 8
611 dx = distance * cos(self._angle*self._invradian)
612 dy = distance * sin(self._angle*self._invradian)
613 self._delete_turtle()
Martin v. Löwis4157ffb2002-03-28 15:45:57 +0000614 self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000615 width=self._width,
616 arrow="last",
617 capstyle="round",
618 fill=self._color)
619 self._canvas.update()
620
621 def _delete_turtle(self):
622 if self._arrow != 0:
623 self._canvas.delete(self._arrow)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000624 self._arrow = 0
Guido van Rossumb241b671998-12-04 16:42:46 +0000625
626
627_root = None
628_canvas = None
629_pen = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000630_width = 0.50 # 50% of window width
631_height = 0.75 # 75% of window height
632_startx = None
633_starty = None
634_title = "Turtle Graphics" # default title
Guido van Rossumb241b671998-12-04 16:42:46 +0000635
636class Pen(RawPen):
637
638 def __init__(self):
639 global _root, _canvas
640 if _root is None:
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000641 _root = Tkinter.Tk()
Guido van Rossumb241b671998-12-04 16:42:46 +0000642 _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000643 _root.title(_title)
644
Guido van Rossumb241b671998-12-04 16:42:46 +0000645 if _canvas is None:
646 # XXX Should have scroll bars
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000647 _canvas = Tkinter.Canvas(_root, background="white")
Guido van Rossumb241b671998-12-04 16:42:46 +0000648 _canvas.pack(expand=1, fill="both")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000649
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000650 setup(width=_width, height= _height, startx=_startx, starty=_starty)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000651
Guido van Rossumb241b671998-12-04 16:42:46 +0000652 RawPen.__init__(self, _canvas)
653
654 def _destroy(self):
655 global _root, _canvas, _pen
656 root = self._canvas._root()
657 if root is _root:
658 _pen = None
659 _root = None
660 _canvas = None
661 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +0000662
Guido van Rossumb241b671998-12-04 16:42:46 +0000663def _getpen():
664 global _pen
Thomas Wouters477c8d52006-05-27 19:21:47 +0000665 if not _pen:
666 _pen = Pen()
667 return _pen
668
669class Turtle(Pen):
670 pass
671
672"""For documentation of the following functions see
673 the RawPen methods with the same names
674"""
Guido van Rossumb241b671998-12-04 16:42:46 +0000675
676def degrees(): _getpen().degrees()
677def radians(): _getpen().radians()
678def reset(): _getpen().reset()
679def clear(): _getpen().clear()
680def tracer(flag): _getpen().tracer(flag)
681def forward(distance): _getpen().forward(distance)
682def backward(distance): _getpen().backward(distance)
683def left(angle): _getpen().left(angle)
684def right(angle): _getpen().right(angle)
685def up(): _getpen().up()
686def down(): _getpen().down()
687def width(width): _getpen().width(width)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000688def color(*args): _getpen().color(*args)
Guido van Rossumb241b671998-12-04 16:42:46 +0000689def write(arg, move=0): _getpen().write(arg, move)
690def fill(flag): _getpen().fill(flag)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000691def begin_fill(): _getpen().begin_fill()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000692def end_fill(): _getpen().end_fill()
Guido van Rossumb241b671998-12-04 16:42:46 +0000693def circle(radius, extent=None): _getpen().circle(radius, extent)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000694def goto(*args): _getpen().goto(*args)
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000695def heading(): return _getpen().heading()
696def setheading(angle): _getpen().setheading(angle)
697def position(): return _getpen().position()
698def window_width(): return _getpen().window_width()
699def window_height(): return _getpen().window_height()
700def setx(xpos): _getpen().setx(xpos)
701def sety(ypos): _getpen().sety(ypos)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000702def towards(*args): return _getpen().towards(*args)
703
704def done(): _root.mainloop()
705def delay(delay): return _getpen().delay(delay)
706def speed(speed): return _getpen().speed(speed)
707
708for methodname in dir(RawPen):
709 """ copies RawPen docstrings to module functions of same name """
710 if not methodname.startswith("_"):
711 eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
712
713
714def setup(**geometry):
715 """ Sets the size and position of the main window.
716
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000717 Keywords are width, height, startx and starty:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000718
719 width: either a size in pixels or a fraction of the screen.
720 Default is 50% of screen.
721 height: either the height in pixels or a fraction of the screen.
722 Default is 75% of screen.
723
724 Setting either width or height to None before drawing will force
725 use of default geometry as in older versions of turtle.py
726
727 startx: starting position in pixels from the left edge of the screen.
728 Default is to center window. Setting startx to None is the default
729 and centers window horizontally on screen.
730
731 starty: starting position in pixels from the top edge of the screen.
732 Default is to center window. Setting starty to None is the default
733 and centers window vertically on screen.
734
735 Examples:
736 >>> setup (width=200, height=200, startx=0, starty=0)
737
738 sets window to 200x200 pixels, in upper left of screen
739
740 >>> setup(width=.75, height=0.5, startx=None, starty=None)
741
742 sets window to 75% of screen by 50% of screen and centers
743
744 >>> setup(width=None)
745
746 forces use of default geometry as in older versions of turtle.py
747 """
748
749 global _width, _height, _startx, _starty
750
751 width = geometry.get('width',_width)
752 if width >= 0 or width == None:
753 _width = width
754 else:
755 raise ValueError, "width can not be less than 0"
756
757 height = geometry.get('height',_height)
758 if height >= 0 or height == None:
759 _height = height
760 else:
761 raise ValueError, "height can not be less than 0"
762
763 startx = geometry.get('startx', _startx)
764 if startx >= 0 or startx == None:
765 _startx = _startx
766 else:
767 raise ValueError, "startx can not be less than 0"
768
769 starty = geometry.get('starty', _starty)
770 if starty >= 0 or starty == None:
771 _starty = starty
772 else:
773 raise ValueError, "startx can not be less than 0"
774
775
776 if _root and _width and _height:
777 if 0 < _width <= 1:
778 _width = _root.winfo_screenwidth() * +width
779 if 0 < _height <= 1:
780 _height = _root.winfo_screenheight() * _height
781
782 # center window on screen
783 if _startx is None:
784 _startx = (_root.winfo_screenwidth() - _width) / 2
785
786 if _starty is None:
787 _starty = (_root.winfo_screenheight() - _height) / 2
788
789 _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
790
791def title(title):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000792 """Set the window title.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000793
794 By default this is set to 'Turtle Graphics'
795
796 Example:
797 >>> title("My Window")
798 """
799
800 global _title
801 _title = title
Guido van Rossumb241b671998-12-04 16:42:46 +0000802
803def demo():
804 reset()
805 tracer(1)
806 up()
807 backward(100)
808 down()
809 # draw 3 squares; the last filled
810 width(3)
811 for i in range(3):
812 if i == 2:
813 fill(1)
814 for j in range(4):
815 forward(20)
816 left(90)
817 if i == 2:
818 color("maroon")
819 fill(0)
820 up()
821 forward(30)
822 down()
823 width(1)
824 color("black")
825 # move out of the way
826 tracer(0)
827 up()
828 right(90)
829 forward(100)
830 right(90)
831 forward(100)
832 right(180)
833 down()
834 # some text
835 write("startstart", 1)
836 write("start", 1)
837 color("red")
838 # staircase
839 for i in range(5):
840 forward(20)
841 left(90)
842 forward(20)
843 right(90)
844 # filled staircase
845 fill(1)
846 for i in range(5):
847 forward(20)
848 left(90)
849 forward(20)
850 right(90)
851 fill(0)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000852 tracer(1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000853 # more text
854 write("end")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000855
856def demo2():
857 # exercises some new and improved features
858 speed('fast')
859 width(3)
860
861 # draw a segmented half-circle
862 setheading(towards(0,0))
863 x,y = position()
864 r = (x**2+y**2)**.5/2.0
865 right(90)
866 pendown = True
867 for i in range(18):
868 if pendown:
869 up()
870 pendown = False
871 else:
872 down()
873 pendown = True
874 circle(r,10)
875 sleep(2)
876
877 reset()
878 left(90)
879
880 # draw a series of triangles
881 l = 10
882 color("green")
883 width(3)
884 left(180)
885 sp = 5
886 for i in range(-2,16):
887 if i > 0:
888 color(1.0-0.05*i,0,0.05*i)
889 fill(1)
890 color("green")
891 for j in range(3):
892 forward(l)
893 left(120)
894 l += 10
895 left(15)
896 if sp > 0:
897 sp = sp-1
898 speed(speeds[sp])
899 color(0.25,0,0.75)
900 fill(0)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000901
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000902 # draw and fill a concave shape
903 left(120)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000904 up()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000905 forward(70)
906 right(30)
907 down()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000908 color("red")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000909 speed("fastest")
910 fill(1)
911 for i in range(4):
912 circle(50,90)
913 right(90)
914 forward(30)
915 right(90)
916 color("yellow")
917 fill(0)
918 left(90)
919 up()
920 forward(30)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000921 down();
922
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000923 color("red")
924
Thomas Wouters477c8d52006-05-27 19:21:47 +0000925 # create a second turtle and make the original pursue and catch it
926 turtle=Turtle()
927 turtle.reset()
928 turtle.left(90)
929 turtle.speed('normal')
930 turtle.up()
931 turtle.goto(280,40)
932 turtle.left(24)
933 turtle.down()
934 turtle.speed('fast')
935 turtle.color("blue")
936 turtle.width(2)
937 speed('fastest')
938
939 # turn default turtle towards new turtle object
940 setheading(towards(turtle))
941 while ( abs(position()[0]-turtle.position()[0])>4 or
942 abs(position()[1]-turtle.position()[1])>4):
943 turtle.forward(3.5)
944 turtle.left(0.6)
945 # turn default turtle towards new turtle object
946 setheading(towards(turtle))
947 forward(4)
948 write("CAUGHT! ", move=True)
949
950
Guido van Rossumb241b671998-12-04 16:42:46 +0000951
952if __name__ == '__main__':
953 demo()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000954 sleep(3)
955 demo2()
956 done()