blob: 01a55b1522df23b89de8c31d5c738fb2caaf98e2 [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
18import Tkinter
Guido van Rossumfd2ede22002-09-23 16:55:05 +000019
Thomas Wouters477c8d52006-05-27 19:21:47 +000020speeds = ['fastest', 'fast', 'normal', 'slow', 'slowest']
21
Martin v. Löwis4b6ea792000-10-01 17:52:01 +000022class Error(Exception):
23 pass
Guido van Rossumb241b671998-12-04 16:42:46 +000024
25class RawPen:
26
27 def __init__(self, canvas):
28 self._canvas = canvas
29 self._items = []
30 self._tracing = 1
Guido van Rossum3c7a25a2001-08-09 16:42:07 +000031 self._arrow = 0
Thomas Wouters477c8d52006-05-27 19:21:47 +000032 self._delay = 10 # default delay for drawing
Thomas Wouters0e3f5912006-08-11 14:57:12 +000033 self._angle = 0.0
Guido van Rossumb241b671998-12-04 16:42:46 +000034 self.degrees()
35 self.reset()
36
37 def degrees(self, fullcircle=360.0):
Thomas Wouters477c8d52006-05-27 19:21:47 +000038 """ Set angle measurement units to degrees.
39
40 Example:
41 >>> turtle.degrees()
42 """
Thomas Wouters0e3f5912006-08-11 14:57:12 +000043 # Don't try to change _angle if it is 0, because
44 # _fullcircle might not be set, yet
45 if self._angle:
46 self._angle = (self._angle / self._fullcircle) * fullcircle
Guido van Rossumb241b671998-12-04 16:42:46 +000047 self._fullcircle = fullcircle
48 self._invradian = pi / (fullcircle * 0.5)
49
50 def radians(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +000051 """ Set the angle measurement units to radians.
52
53 Example:
54 >>> turtle.radians()
55 """
Guido van Rossumb241b671998-12-04 16:42:46 +000056 self.degrees(2.0*pi)
57
58 def reset(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +000059 """ Clear the screen, re-center the pen, and set variables to
60 the default values.
61
62 Example:
63 >>> turtle.position()
64 [0.0, -22.0]
65 >>> turtle.heading()
66 100.0
67 >>> turtle.reset()
68 >>> turtle.position()
69 [0.0, 0.0]
70 >>> turtle.heading()
71 0.0
72 """
Guido van Rossumb241b671998-12-04 16:42:46 +000073 canvas = self._canvas
Martin v. Löwis73b9b662002-09-22 13:00:26 +000074 self._canvas.update()
Guido van Rossumb241b671998-12-04 16:42:46 +000075 width = canvas.winfo_width()
76 height = canvas.winfo_height()
77 if width <= 1:
78 width = canvas['width']
79 if height <= 1:
80 height = canvas['height']
81 self._origin = float(width)/2.0, float(height)/2.0
82 self._position = self._origin
83 self._angle = 0.0
84 self._drawing = 1
85 self._width = 1
86 self._color = "black"
87 self._filling = 0
88 self._path = []
Guido van Rossumb241b671998-12-04 16:42:46 +000089 self.clear()
90 canvas._root().tkraise()
91
92 def clear(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +000093 """ Clear the screen. The turtle does not move.
94
95 Example:
96 >>> turtle.clear()
97 """
Guido van Rossumb241b671998-12-04 16:42:46 +000098 self.fill(0)
99 canvas = self._canvas
100 items = self._items
101 self._items = []
102 for item in items:
103 canvas.delete(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000104 self._delete_turtle()
105 self._draw_turtle()
106
Guido van Rossumb241b671998-12-04 16:42:46 +0000107 def tracer(self, flag):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000108 """ Set tracing on if flag is True, and off if it is False.
109 Tracing means line are drawn more slowly, with an
110 animation of an arrow along the line.
111
112 Example:
113 >>> turtle.tracer(False) # turns off Tracer
114 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000115 self._tracing = flag
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000116 if not self._tracing:
117 self._delete_turtle()
118 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000119
120 def forward(self, distance):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000121 """ Go forward distance steps.
122
123 Example:
124 >>> turtle.position()
125 [0.0, 0.0]
126 >>> turtle.forward(25)
127 >>> turtle.position()
128 [25.0, 0.0]
129 >>> turtle.forward(-75)
130 >>> turtle.position()
131 [-50.0, 0.0]
132 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000133 x0, y0 = start = self._position
134 x1 = x0 + distance * cos(self._angle*self._invradian)
135 y1 = y0 - distance * sin(self._angle*self._invradian)
136 self._goto(x1, y1)
137
138 def backward(self, distance):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000139 """ Go backwards distance steps.
140
141 The turtle's heading does not change.
142
143 Example:
144 >>> turtle.position()
145 [0.0, 0.0]
146 >>> turtle.backward(30)
147 >>> turtle.position()
148 [-30.0, 0.0]
149 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000150 self.forward(-distance)
151
152 def left(self, angle):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000153 """ Turn left angle units (units are by default degrees,
154 but can be set via the degrees() and radians() functions.)
155
156 When viewed from above, the turning happens in-place around
157 its front tip.
158
159 Example:
160 >>> turtle.heading()
161 22
162 >>> turtle.left(45)
163 >>> turtle.heading()
164 67.0
165 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000166 self._angle = (self._angle + angle) % self._fullcircle
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000167 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000168
169 def right(self, angle):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000170 """ Turn right angle units (units are by default degrees,
171 but can be set via the degrees() and radians() functions.)
172
173 When viewed from above, the turning happens in-place around
174 its front tip.
175
176 Example:
177 >>> turtle.heading()
178 22
179 >>> turtle.right(45)
180 >>> turtle.heading()
181 337.0
182 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000183 self.left(-angle)
184
185 def up(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000186 """ Pull the pen up -- no drawing when moving.
187
188 Example:
189 >>> turtle.up()
190 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000191 self._drawing = 0
192
193 def down(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000194 """ Put the pen down -- draw when moving.
195
196 Example:
197 >>> turtle.down()
198 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000199 self._drawing = 1
200
201 def width(self, width):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000202 """ Set the line to thickness to width.
203
204 Example:
205 >>> turtle.width(10)
206 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000207 self._width = float(width)
208
209 def color(self, *args):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000210 """ Set the pen color.
211
212 Three input formats are allowed:
213
214 color(s)
215 s is a Tk specification string, such as "red" or "yellow"
216
217 color((r, g, b))
218 *a tuple* of r, g, and b, which represent, an RGB color,
219 and each of r, g, and b are in the range [0..1]
220
221 color(r, g, b)
222 r, g, and b represent an RGB color, and each of r, g, and b
223 are in the range [0..1]
224
225 Example:
226
227 >>> turtle.color('brown')
228 >>> tup = (0.2, 0.8, 0.55)
229 >>> turtle.color(tup)
230 >>> turtle.color(0, .5, 0)
231 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000232 if not args:
233 raise Error, "no color arguments"
234 if len(args) == 1:
235 color = args[0]
236 if type(color) == type(""):
237 # Test the color first
238 try:
239 id = self._canvas.create_line(0, 0, 0, 0, fill=color)
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000240 except Tkinter.TclError:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000241 raise Error, "bad color string: %r" % (color,)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000242 self._set_color(color)
Guido van Rossumb241b671998-12-04 16:42:46 +0000243 return
244 try:
245 r, g, b = color
246 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000247 raise Error, "bad color sequence: %r" % (color,)
Guido van Rossumb241b671998-12-04 16:42:46 +0000248 else:
249 try:
250 r, g, b = args
251 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000252 raise Error, "bad color arguments: %r" % (args,)
Guido van Rossumb241b671998-12-04 16:42:46 +0000253 assert 0 <= r <= 1
254 assert 0 <= g <= 1
255 assert 0 <= b <= 1
256 x = 255.0
257 y = 0.5
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000258 self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y)))
259
260 def _set_color(self,color):
261 self._color = color
262 self._draw_turtle()
263
Thomas Wouters477c8d52006-05-27 19:21:47 +0000264 def write(self, text, move=False):
265 """ Write text at the current pen position.
266
267 If move is true, the pen is moved to the bottom-right corner
268 of the text. By default, move is False.
269
270 Example:
271 >>> turtle.write('The race is on!')
272 >>> turtle.write('Home = (0, 0)', True)
273 """
274 x, y = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000275 x = x-1 # correction -- calibrated for Windows
Fred Draked038ca82000-10-23 18:31:14 +0000276 item = self._canvas.create_text(x, y,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000277 text=str(text), anchor="sw",
Guido van Rossumb241b671998-12-04 16:42:46 +0000278 fill=self._color)
279 self._items.append(item)
280 if move:
281 x0, y0, x1, y1 = self._canvas.bbox(item)
282 self._goto(x1, y1)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000283 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000284
285 def fill(self, flag):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000286 """ Call fill(1) before drawing the shape you
287 want to fill, and fill(0) when done.
288
289 Example:
290 >>> turtle.fill(1)
291 >>> turtle.forward(100)
292 >>> turtle.left(90)
293 >>> turtle.forward(100)
294 >>> turtle.left(90)
295 >>> turtle.forward(100)
296 >>> turtle.left(90)
297 >>> turtle.forward(100)
298 >>> turtle.fill(0)
299 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000300 if self._filling:
301 path = tuple(self._path)
302 smooth = self._filling < 0
303 if len(path) > 2:
304 item = self._canvas._create('polygon', path,
305 {'fill': self._color,
306 'smooth': smooth})
307 self._items.append(item)
Guido van Rossumb241b671998-12-04 16:42:46 +0000308 self._path = []
Guido van Rossumb241b671998-12-04 16:42:46 +0000309 self._filling = flag
310 if flag:
311 self._path.append(self._position)
312
Thomas Wouters477c8d52006-05-27 19:21:47 +0000313 def begin_fill(self):
314 """ Called just before drawing a shape to be filled.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000315 Must eventually be followed by a corresponding end_fill() call.
316 Otherwise it will be ignored.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000317
318 Example:
319 >>> turtle.begin_fill()
320 >>> turtle.forward(100)
321 >>> turtle.left(90)
322 >>> turtle.forward(100)
323 >>> turtle.left(90)
324 >>> turtle.forward(100)
325 >>> turtle.left(90)
326 >>> turtle.forward(100)
327 >>> turtle.end_fill()
328 """
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000329 self._path = [self._position]
330 self._filling = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +0000331
332 def end_fill(self):
333 """ Called after drawing a shape to be filled.
334
335 Example:
336 >>> turtle.begin_fill()
337 >>> turtle.forward(100)
338 >>> turtle.left(90)
339 >>> turtle.forward(100)
340 >>> turtle.left(90)
341 >>> turtle.forward(100)
342 >>> turtle.left(90)
343 >>> turtle.forward(100)
344 >>> turtle.end_fill()
345 """
346 self.fill(0)
347
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000348 def circle(self, radius, extent = None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000349 """ Draw a circle with given radius.
350 The center is radius units left of the turtle; extent
351 determines which part of the circle is drawn. If not given,
352 the entire circle is drawn.
353
354 If extent is not a full circle, one endpoint of the arc is the
355 current pen position. The arc is drawn in a counter clockwise
356 direction if radius is positive, otherwise in a clockwise
357 direction. In the process, the direction of the turtle is
358 changed by the amount of the extent.
359
360 >>> turtle.circle(50)
361 >>> turtle.circle(120, 180) # half a circle
362 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000363 if extent is None:
364 extent = self._fullcircle
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000365 frac = abs(extent)/self._fullcircle
366 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
367 w = 1.0 * extent / steps
368 w2 = 0.5 * w
369 l = 2.0 * radius * sin(w2*self._invradian)
370 if radius < 0:
371 l, w, w2 = -l, -w, -w2
372 self.left(w2)
373 for i in range(steps):
374 self.forward(l)
375 self.left(w)
376 self.right(w2)
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000377
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000378 def heading(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000379 """ Return the turtle's current heading.
380
381 Example:
382 >>> turtle.heading()
383 67.0
384 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000385 return self._angle
386
387 def setheading(self, angle):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000388 """ Set the turtle facing the given angle.
389
390 Here are some common directions in degrees:
391
392 0 - east
393 90 - north
394 180 - west
395 270 - south
396
397 Example:
398 >>> turtle.setheading(90)
399 >>> turtle.heading()
400 90
401 >>> turtle.setheading(128)
402 >>> turtle.heading()
403 128
404 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000405 self._angle = angle
406 self._draw_turtle()
407
408 def window_width(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000409 """ Returns the width of the turtle window.
410
411 Example:
412 >>> turtle.window_width()
413 640
414 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000415 width = self._canvas.winfo_width()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000416 if width <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000417 width = self._canvas['width']
418 return width
419
420 def window_height(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000421 """ Return the height of the turtle window.
422
423 Example:
424 >>> turtle.window_height()
425 768
426 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000427 height = self._canvas.winfo_height()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000428 if height <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000429 height = self._canvas['height']
430 return height
431
432 def position(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000433 """ Return the current (x, y) location of the turtle.
434
435 Example:
436 >>> turtle.position()
437 [0.0, 240.0]
438 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000439 x0, y0 = self._origin
440 x1, y1 = self._position
441 return [x1-x0, -y1+y0]
442
443 def setx(self, xpos):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000444 """ Set the turtle's x coordinate to be xpos.
445
446 Example:
447 >>> turtle.position()
448 [10.0, 240.0]
449 >>> turtle.setx(10)
450 >>> turtle.position()
451 [10.0, 240.0]
452 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000453 x0, y0 = self._origin
454 x1, y1 = self._position
455 self._goto(x0+xpos, y1)
456
457 def sety(self, ypos):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000458 """ Set the turtle's y coordinate to be ypos.
459
460 Example:
461 >>> turtle.position()
462 [0.0, 0.0]
463 >>> turtle.sety(-22)
464 >>> turtle.position()
465 [0.0, -22.0]
466 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000467 x0, y0 = self._origin
468 x1, y1 = self._position
469 self._goto(x1, y0-ypos)
Guido van Rossumb241b671998-12-04 16:42:46 +0000470
Thomas Wouters477c8d52006-05-27 19:21:47 +0000471 def towards(self, *args):
472 """Returs the angle, which corresponds to the line
473 from turtle-position to point (x,y).
474
475 Argument can be two coordinates or one pair of coordinates
476 or a RawPen/Pen instance.
477
478 Example:
479 >>> turtle.position()
480 [10.0, 10.0]
481 >>> turtle.towards(0,0)
482 225.0
483 """
484 if len(args) == 2:
485 x, y = args
486 else:
487 arg = args[0]
488 if isinstance(arg, RawPen):
489 x, y = arg.position()
490 else:
491 x, y = arg
492 x0, y0 = self.position()
493 dx = x - x0
494 dy = y - y0
495 return (atan2(dy,dx) / self._invradian) % self._fullcircle
496
Guido van Rossumb241b671998-12-04 16:42:46 +0000497 def goto(self, *args):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000498 """ Go to the given point.
499
500 If the pen is down, then a line will be drawn. The turtle's
501 orientation does not change.
502
503 Two input formats are accepted:
504
505 goto(x, y)
506 go to point (x, y)
507
508 goto((x, y))
509 go to point (x, y)
510
511 Example:
512 >>> turtle.position()
513 [0.0, 0.0]
514 >>> turtle.goto(50, -45)
515 >>> turtle.position()
516 [50.0, -45.0]
517 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000518 if len(args) == 1:
519 try:
520 x, y = args[0]
521 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000522 raise Error, "bad point argument: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000523 else:
524 try:
525 x, y = args
526 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000527 raise Error, "bad coordinates: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000528 x0, y0 = self._origin
529 self._goto(x0+x, y0-y)
530
531 def _goto(self, x1, y1):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000532 x0, y0 = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000533 self._position = map(float, (x1, y1))
534 if self._filling:
535 self._path.append(self._position)
536 if self._drawing:
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000537 if self._tracing:
Guido van Rossumb241b671998-12-04 16:42:46 +0000538 dx = float(x1 - x0)
539 dy = float(y1 - y0)
540 distance = hypot(dx, dy)
541 nhops = int(distance)
542 item = self._canvas.create_line(x0, y0, x0, y0,
543 width=self._width,
Guido van Rossumb241b671998-12-04 16:42:46 +0000544 capstyle="round",
545 fill=self._color)
546 try:
547 for i in range(1, 1+nhops):
548 x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
549 self._canvas.coords(item, x0, y0, x, y)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000550 self._draw_turtle((x,y))
Guido van Rossumb241b671998-12-04 16:42:46 +0000551 self._canvas.update()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552 self._canvas.after(self._delay)
Guido van Rossuma659efe2001-01-01 19:11:07 +0000553 # in case nhops==0
554 self._canvas.coords(item, x0, y0, x1, y1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000555 self._canvas.itemconfigure(item, arrow="none")
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000556 except Tkinter.TclError:
Guido van Rossumb241b671998-12-04 16:42:46 +0000557 # Probably the window was closed!
558 return
559 else:
560 item = self._canvas.create_line(x0, y0, x1, y1,
561 width=self._width,
562 capstyle="round",
563 fill=self._color)
564 self._items.append(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000565 self._draw_turtle()
566
Thomas Wouters477c8d52006-05-27 19:21:47 +0000567 def speed(self, speed):
568 """ Set the turtle's speed.
569
570 speed must one of these five strings:
571
572 'fastest' is a 0 ms delay
573 'fast' is a 5 ms delay
574 'normal' is a 10 ms delay
575 'slow' is a 15 ms delay
576 'slowest' is a 20 ms delay
577
578 Example:
579 >>> turtle.speed('slow')
580 """
581 try:
582 speed = speed.strip().lower()
583 self._delay = speeds.index(speed) * 5
584 except:
585 raise ValueError("%r is not a valid speed. speed must be "
586 "one of %s" % (speed, speeds))
587
588
589 def delay(self, delay):
590 """ Set the drawing delay in milliseconds.
591
592 This is intended to allow finer control of the drawing speed
593 than the speed() method
594
595 Example:
596 >>> turtle.delay(15)
597 """
598 if int(delay) < 0:
599 raise ValueError("delay must be greater than or equal to 0")
600 self._delay = int(delay)
601
602 def _draw_turtle(self, position=[]):
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000603 if not self._tracing:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000604 self._canvas.update()
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000605 return
606 if position == []:
607 position = self._position
608 x,y = position
609 distance = 8
610 dx = distance * cos(self._angle*self._invradian)
611 dy = distance * sin(self._angle*self._invradian)
612 self._delete_turtle()
Martin v. Löwis4157ffb2002-03-28 15:45:57 +0000613 self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000614 width=self._width,
615 arrow="last",
616 capstyle="round",
617 fill=self._color)
618 self._canvas.update()
619
620 def _delete_turtle(self):
621 if self._arrow != 0:
622 self._canvas.delete(self._arrow)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000623 self._arrow = 0
Guido van Rossumb241b671998-12-04 16:42:46 +0000624
625
626_root = None
627_canvas = None
628_pen = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000629_width = 0.50 # 50% of window width
630_height = 0.75 # 75% of window height
631_startx = None
632_starty = None
633_title = "Turtle Graphics" # default title
Guido van Rossumb241b671998-12-04 16:42:46 +0000634
635class Pen(RawPen):
636
637 def __init__(self):
638 global _root, _canvas
639 if _root is None:
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000640 _root = Tkinter.Tk()
Guido van Rossumb241b671998-12-04 16:42:46 +0000641 _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000642 _root.title(_title)
643
Guido van Rossumb241b671998-12-04 16:42:46 +0000644 if _canvas is None:
645 # XXX Should have scroll bars
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000646 _canvas = Tkinter.Canvas(_root, background="white")
Guido van Rossumb241b671998-12-04 16:42:46 +0000647 _canvas.pack(expand=1, fill="both")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000648
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000649 setup(width=_width, height= _height, startx=_startx, starty=_starty)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000650
Guido van Rossumb241b671998-12-04 16:42:46 +0000651 RawPen.__init__(self, _canvas)
652
653 def _destroy(self):
654 global _root, _canvas, _pen
655 root = self._canvas._root()
656 if root is _root:
657 _pen = None
658 _root = None
659 _canvas = None
660 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +0000661
Guido van Rossumb241b671998-12-04 16:42:46 +0000662def _getpen():
663 global _pen
Thomas Wouters477c8d52006-05-27 19:21:47 +0000664 if not _pen:
665 _pen = Pen()
666 return _pen
667
668class Turtle(Pen):
669 pass
670
671"""For documentation of the following functions see
672 the RawPen methods with the same names
673"""
Guido van Rossumb241b671998-12-04 16:42:46 +0000674
675def degrees(): _getpen().degrees()
676def radians(): _getpen().radians()
677def reset(): _getpen().reset()
678def clear(): _getpen().clear()
679def tracer(flag): _getpen().tracer(flag)
680def forward(distance): _getpen().forward(distance)
681def backward(distance): _getpen().backward(distance)
682def left(angle): _getpen().left(angle)
683def right(angle): _getpen().right(angle)
684def up(): _getpen().up()
685def down(): _getpen().down()
686def width(width): _getpen().width(width)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000687def color(*args): _getpen().color(*args)
Guido van Rossumb241b671998-12-04 16:42:46 +0000688def write(arg, move=0): _getpen().write(arg, move)
689def fill(flag): _getpen().fill(flag)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000690def begin_fill(): _getpen().begin_fill()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000691def end_fill(): _getpen().end_fill()
Guido van Rossumb241b671998-12-04 16:42:46 +0000692def circle(radius, extent=None): _getpen().circle(radius, extent)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000693def goto(*args): _getpen().goto(*args)
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000694def heading(): return _getpen().heading()
695def setheading(angle): _getpen().setheading(angle)
696def position(): return _getpen().position()
697def window_width(): return _getpen().window_width()
698def window_height(): return _getpen().window_height()
699def setx(xpos): _getpen().setx(xpos)
700def sety(ypos): _getpen().sety(ypos)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000701def towards(*args): return _getpen().towards(*args)
702
703def done(): _root.mainloop()
704def delay(delay): return _getpen().delay(delay)
705def speed(speed): return _getpen().speed(speed)
706
707for methodname in dir(RawPen):
708 """ copies RawPen docstrings to module functions of same name """
709 if not methodname.startswith("_"):
710 eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
711
712
713def setup(**geometry):
714 """ Sets the size and position of the main window.
715
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000716 Keywords are width, height, startx and starty:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000717
718 width: either a size in pixels or a fraction of the screen.
719 Default is 50% of screen.
720 height: either the height in pixels or a fraction of the screen.
721 Default is 75% of screen.
722
723 Setting either width or height to None before drawing will force
724 use of default geometry as in older versions of turtle.py
725
726 startx: starting position in pixels from the left edge of the screen.
727 Default is to center window. Setting startx to None is the default
728 and centers window horizontally on screen.
729
730 starty: starting position in pixels from the top edge of the screen.
731 Default is to center window. Setting starty to None is the default
732 and centers window vertically on screen.
733
734 Examples:
735 >>> setup (width=200, height=200, startx=0, starty=0)
736
737 sets window to 200x200 pixels, in upper left of screen
738
739 >>> setup(width=.75, height=0.5, startx=None, starty=None)
740
741 sets window to 75% of screen by 50% of screen and centers
742
743 >>> setup(width=None)
744
745 forces use of default geometry as in older versions of turtle.py
746 """
747
748 global _width, _height, _startx, _starty
749
750 width = geometry.get('width',_width)
751 if width >= 0 or width == None:
752 _width = width
753 else:
754 raise ValueError, "width can not be less than 0"
755
756 height = geometry.get('height',_height)
757 if height >= 0 or height == None:
758 _height = height
759 else:
760 raise ValueError, "height can not be less than 0"
761
762 startx = geometry.get('startx', _startx)
763 if startx >= 0 or startx == None:
764 _startx = _startx
765 else:
766 raise ValueError, "startx can not be less than 0"
767
768 starty = geometry.get('starty', _starty)
769 if starty >= 0 or starty == None:
770 _starty = starty
771 else:
772 raise ValueError, "startx can not be less than 0"
773
774
775 if _root and _width and _height:
776 if 0 < _width <= 1:
777 _width = _root.winfo_screenwidth() * +width
778 if 0 < _height <= 1:
779 _height = _root.winfo_screenheight() * _height
780
781 # center window on screen
782 if _startx is None:
783 _startx = (_root.winfo_screenwidth() - _width) / 2
784
785 if _starty is None:
786 _starty = (_root.winfo_screenheight() - _height) / 2
787
788 _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
789
790def title(title):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000791 """Set the window title.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000792
793 By default this is set to 'Turtle Graphics'
794
795 Example:
796 >>> title("My Window")
797 """
798
799 global _title
800 _title = title
Guido van Rossumb241b671998-12-04 16:42:46 +0000801
802def demo():
803 reset()
804 tracer(1)
805 up()
806 backward(100)
807 down()
808 # draw 3 squares; the last filled
809 width(3)
810 for i in range(3):
811 if i == 2:
812 fill(1)
813 for j in range(4):
814 forward(20)
815 left(90)
816 if i == 2:
817 color("maroon")
818 fill(0)
819 up()
820 forward(30)
821 down()
822 width(1)
823 color("black")
824 # move out of the way
825 tracer(0)
826 up()
827 right(90)
828 forward(100)
829 right(90)
830 forward(100)
831 right(180)
832 down()
833 # some text
834 write("startstart", 1)
835 write("start", 1)
836 color("red")
837 # staircase
838 for i in range(5):
839 forward(20)
840 left(90)
841 forward(20)
842 right(90)
843 # filled staircase
844 fill(1)
845 for i in range(5):
846 forward(20)
847 left(90)
848 forward(20)
849 right(90)
850 fill(0)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000851 tracer(1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000852 # more text
853 write("end")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000854
855def demo2():
856 # exercises some new and improved features
857 speed('fast')
858 width(3)
859
860 # draw a segmented half-circle
861 setheading(towards(0,0))
862 x,y = position()
863 r = (x**2+y**2)**.5/2.0
864 right(90)
865 pendown = True
866 for i in range(18):
867 if pendown:
868 up()
869 pendown = False
870 else:
871 down()
872 pendown = True
873 circle(r,10)
874 sleep(2)
875
876 reset()
877 left(90)
878
879 # draw a series of triangles
880 l = 10
881 color("green")
882 width(3)
883 left(180)
884 sp = 5
885 for i in range(-2,16):
886 if i > 0:
887 color(1.0-0.05*i,0,0.05*i)
888 fill(1)
889 color("green")
890 for j in range(3):
891 forward(l)
892 left(120)
893 l += 10
894 left(15)
895 if sp > 0:
896 sp = sp-1
897 speed(speeds[sp])
898 color(0.25,0,0.75)
899 fill(0)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000900
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000901 # draw and fill a concave shape
902 left(120)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000903 up()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000904 forward(70)
905 right(30)
906 down()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000907 color("red")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000908 speed("fastest")
909 fill(1)
910 for i in range(4):
911 circle(50,90)
912 right(90)
913 forward(30)
914 right(90)
915 color("yellow")
916 fill(0)
917 left(90)
918 up()
919 forward(30)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000920 down();
921
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000922 color("red")
923
Thomas Wouters477c8d52006-05-27 19:21:47 +0000924 # create a second turtle and make the original pursue and catch it
925 turtle=Turtle()
926 turtle.reset()
927 turtle.left(90)
928 turtle.speed('normal')
929 turtle.up()
930 turtle.goto(280,40)
931 turtle.left(24)
932 turtle.down()
933 turtle.speed('fast')
934 turtle.color("blue")
935 turtle.width(2)
936 speed('fastest')
937
938 # turn default turtle towards new turtle object
939 setheading(towards(turtle))
940 while ( abs(position()[0]-turtle.position()[0])>4 or
941 abs(position()[1]-turtle.position()[1])>4):
942 turtle.forward(3.5)
943 turtle.left(0.6)
944 # turn default turtle towards new turtle object
945 setheading(towards(turtle))
946 forward(4)
947 write("CAUGHT! ", move=True)
948
949
Guido van Rossumb241b671998-12-04 16:42:46 +0000950
951if __name__ == '__main__':
Thomas Wouters477c8d52006-05-27 19:21:47 +0000952 from time import sleep
Guido van Rossumb241b671998-12-04 16:42:46 +0000953 demo()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000954 sleep(3)
955 demo2()
956 done()