blob: 90d1aa327d0ff544f1a374537f95eff76c52a456 [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
18import Tkinter
Guido van Rossumfd2ede22002-09-23 16:55:05 +000019
Georg Brandle3a25832006-05-17 14:56:04 +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
Georg Brandle3a25832006-05-17 14:56:04 +000032 self._delay = 10 # default delay for drawing
Martin v. Löwis2b88f632006-07-03 10:19:49 +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):
Georg Brandle3a25832006-05-17 14:56:04 +000038 """ Set angle measurement units to degrees.
39
40 Example:
41 >>> turtle.degrees()
42 """
Martin v. Löwis2b88f632006-07-03 10:19:49 +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):
Georg Brandle3a25832006-05-17 14:56:04 +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):
Georg Brandle3a25832006-05-17 14:56:04 +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 = []
89 self._tofill = []
90 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)
Guido van Rossumb241b671998-12-04 16:42:46 +0000309 if self._tofill:
310 for item in self._tofill:
311 self._canvas.itemconfigure(item, fill=self._color)
312 self._items.append(item)
313 self._path = []
314 self._tofill = []
315 self._filling = flag
316 if flag:
317 self._path.append(self._position)
Raymond Hettingeraf81c2e2003-06-09 08:50:57 +0000318 self.forward(0)
Guido van Rossumb241b671998-12-04 16:42:46 +0000319
Georg Brandle3a25832006-05-17 14:56:04 +0000320 def begin_fill(self):
321 """ Called just before drawing a shape to be filled.
322
323 Example:
324 >>> turtle.begin_fill()
325 >>> turtle.forward(100)
326 >>> turtle.left(90)
327 >>> turtle.forward(100)
328 >>> turtle.left(90)
329 >>> turtle.forward(100)
330 >>> turtle.left(90)
331 >>> turtle.forward(100)
332 >>> turtle.end_fill()
333 """
334 self.fill(1)
335
336 def end_fill(self):
337 """ Called after drawing a shape to be filled.
338
339 Example:
340 >>> turtle.begin_fill()
341 >>> turtle.forward(100)
342 >>> turtle.left(90)
343 >>> turtle.forward(100)
344 >>> turtle.left(90)
345 >>> turtle.forward(100)
346 >>> turtle.left(90)
347 >>> turtle.forward(100)
348 >>> turtle.end_fill()
349 """
350 self.fill(0)
351
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000352 def circle(self, radius, extent = None):
Georg Brandle3a25832006-05-17 14:56:04 +0000353 """ Draw a circle with given radius.
354 The center is radius units left of the turtle; extent
355 determines which part of the circle is drawn. If not given,
356 the entire circle is drawn.
357
358 If extent is not a full circle, one endpoint of the arc is the
359 current pen position. The arc is drawn in a counter clockwise
360 direction if radius is positive, otherwise in a clockwise
361 direction. In the process, the direction of the turtle is
362 changed by the amount of the extent.
363
364 >>> turtle.circle(50)
365 >>> turtle.circle(120, 180) # half a circle
366 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000367 if extent is None:
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000368 extent = self._fullcircle
369 frac = abs(extent)/self._fullcircle
370 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
371 w = 1.0 * extent / steps
372 w2 = 0.5 * w
Martin v. Löwis2b88f632006-07-03 10:19:49 +0000373 l = 2.0 * radius * sin(w2*self._invradian)
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000374 if radius < 0:
375 l, w, w2 = -l, -w, -w2
376 self.left(w2)
377 for i in range(steps):
378 self.forward(l)
379 self.left(w)
380 self.right(w2)
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000381
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000382 def heading(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000383 """ Return the turtle's current heading.
384
385 Example:
386 >>> turtle.heading()
387 67.0
388 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000389 return self._angle
390
391 def setheading(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000392 """ Set the turtle facing the given angle.
393
394 Here are some common directions in degrees:
395
396 0 - east
397 90 - north
398 180 - west
399 270 - south
400
401 Example:
402 >>> turtle.setheading(90)
403 >>> turtle.heading()
404 90
405 >>> turtle.setheading(128)
406 >>> turtle.heading()
407 128
408 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000409 self._angle = angle
410 self._draw_turtle()
411
412 def window_width(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000413 """ Returns the width of the turtle window.
414
415 Example:
416 >>> turtle.window_width()
417 640
418 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000419 width = self._canvas.winfo_width()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000420 if width <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000421 width = self._canvas['width']
422 return width
423
424 def window_height(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000425 """ Return the height of the turtle window.
426
427 Example:
428 >>> turtle.window_height()
429 768
430 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000431 height = self._canvas.winfo_height()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000432 if height <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000433 height = self._canvas['height']
434 return height
435
436 def position(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000437 """ Return the current (x, y) location of the turtle.
438
439 Example:
440 >>> turtle.position()
441 [0.0, 240.0]
442 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000443 x0, y0 = self._origin
444 x1, y1 = self._position
445 return [x1-x0, -y1+y0]
446
447 def setx(self, xpos):
Georg Brandle3a25832006-05-17 14:56:04 +0000448 """ Set the turtle's x coordinate to be xpos.
449
450 Example:
451 >>> turtle.position()
452 [10.0, 240.0]
453 >>> turtle.setx(10)
454 >>> turtle.position()
455 [10.0, 240.0]
456 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000457 x0, y0 = self._origin
458 x1, y1 = self._position
459 self._goto(x0+xpos, y1)
460
461 def sety(self, ypos):
Georg Brandle3a25832006-05-17 14:56:04 +0000462 """ Set the turtle's y coordinate to be ypos.
463
464 Example:
465 >>> turtle.position()
466 [0.0, 0.0]
467 >>> turtle.sety(-22)
468 >>> turtle.position()
469 [0.0, -22.0]
470 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000471 x0, y0 = self._origin
472 x1, y1 = self._position
473 self._goto(x1, y0-ypos)
Guido van Rossumb241b671998-12-04 16:42:46 +0000474
Georg Brandle3a25832006-05-17 14:56:04 +0000475 def towards(self, *args):
476 """Returs the angle, which corresponds to the line
477 from turtle-position to point (x,y).
478
479 Argument can be two coordinates or one pair of coordinates
480 or a RawPen/Pen instance.
481
482 Example:
483 >>> turtle.position()
484 [10.0, 10.0]
485 >>> turtle.towards(0,0)
486 225.0
487 """
488 if len(args) == 2:
489 x, y = args
490 else:
491 arg = args[0]
492 if isinstance(arg, RawPen):
493 x, y = arg.position()
494 else:
495 x, y = arg
496 x0, y0 = self.position()
497 dx = x - x0
498 dy = y - y0
499 return (atan2(dy,dx) / self._invradian) % self._fullcircle
500
Guido van Rossumb241b671998-12-04 16:42:46 +0000501 def goto(self, *args):
Georg Brandle3a25832006-05-17 14:56:04 +0000502 """ Go to the given point.
503
504 If the pen is down, then a line will be drawn. The turtle's
505 orientation does not change.
506
507 Two input formats are accepted:
508
509 goto(x, y)
510 go to point (x, y)
511
512 goto((x, y))
513 go to point (x, y)
514
515 Example:
516 >>> turtle.position()
517 [0.0, 0.0]
518 >>> turtle.goto(50, -45)
519 >>> turtle.position()
520 [50.0, -45.0]
521 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000522 if len(args) == 1:
523 try:
524 x, y = args[0]
525 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000526 raise Error, "bad point argument: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000527 else:
528 try:
529 x, y = args
530 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000531 raise Error, "bad coordinates: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000532 x0, y0 = self._origin
533 self._goto(x0+x, y0-y)
534
535 def _goto(self, x1, y1):
Georg Brandle3a25832006-05-17 14:56:04 +0000536 x0, y0 = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000537 self._position = map(float, (x1, y1))
538 if self._filling:
539 self._path.append(self._position)
540 if self._drawing:
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000541 if self._tracing:
Guido van Rossumb241b671998-12-04 16:42:46 +0000542 dx = float(x1 - x0)
543 dy = float(y1 - y0)
544 distance = hypot(dx, dy)
545 nhops = int(distance)
546 item = self._canvas.create_line(x0, y0, x0, y0,
547 width=self._width,
Guido van Rossumb241b671998-12-04 16:42:46 +0000548 capstyle="round",
549 fill=self._color)
550 try:
551 for i in range(1, 1+nhops):
552 x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
553 self._canvas.coords(item, x0, y0, x, y)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000554 self._draw_turtle((x,y))
Guido van Rossumb241b671998-12-04 16:42:46 +0000555 self._canvas.update()
Georg Brandle3a25832006-05-17 14:56:04 +0000556 self._canvas.after(self._delay)
Guido van Rossuma659efe2001-01-01 19:11:07 +0000557 # in case nhops==0
558 self._canvas.coords(item, x0, y0, x1, y1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000559 self._canvas.itemconfigure(item, arrow="none")
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000560 except Tkinter.TclError:
Guido van Rossumb241b671998-12-04 16:42:46 +0000561 # Probably the window was closed!
562 return
563 else:
564 item = self._canvas.create_line(x0, y0, x1, y1,
565 width=self._width,
566 capstyle="round",
567 fill=self._color)
568 self._items.append(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000569 self._draw_turtle()
570
Georg Brandle3a25832006-05-17 14:56:04 +0000571 def speed(self, speed):
572 """ Set the turtle's speed.
573
574 speed must one of these five strings:
575
576 'fastest' is a 0 ms delay
577 'fast' is a 5 ms delay
578 'normal' is a 10 ms delay
579 'slow' is a 15 ms delay
580 'slowest' is a 20 ms delay
581
582 Example:
583 >>> turtle.speed('slow')
584 """
585 try:
586 speed = speed.strip().lower()
587 self._delay = speeds.index(speed) * 5
588 except:
589 raise ValueError("%r is not a valid speed. speed must be "
590 "one of %s" % (speed, speeds))
591
592
593 def delay(self, delay):
594 """ Set the drawing delay in milliseconds.
595
596 This is intended to allow finer control of the drawing speed
597 than the speed() method
598
599 Example:
600 >>> turtle.delay(15)
601 """
602 if int(delay) < 0:
603 raise ValueError("delay must be greater than or equal to 0")
604 self._delay = int(delay)
605
606 def _draw_turtle(self, position=[]):
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000607 if not self._tracing:
Martin v. Löwis3798da02006-06-17 18:44:27 +0000608 self._canvas.update()
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000609 return
610 if position == []:
611 position = self._position
612 x,y = position
613 distance = 8
614 dx = distance * cos(self._angle*self._invradian)
615 dy = distance * sin(self._angle*self._invradian)
616 self._delete_turtle()
Martin v. Löwis4157ffb2002-03-28 15:45:57 +0000617 self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000618 width=self._width,
619 arrow="last",
620 capstyle="round",
621 fill=self._color)
622 self._canvas.update()
623
624 def _delete_turtle(self):
625 if self._arrow != 0:
626 self._canvas.delete(self._arrow)
Georg Brandle3a25832006-05-17 14:56:04 +0000627 self._arrow = 0
Guido van Rossumb241b671998-12-04 16:42:46 +0000628
629
630_root = None
631_canvas = None
632_pen = None
Georg Brandle3a25832006-05-17 14:56:04 +0000633_width = 0.50 # 50% of window width
634_height = 0.75 # 75% of window height
635_startx = None
636_starty = None
637_title = "Turtle Graphics" # default title
Guido van Rossumb241b671998-12-04 16:42:46 +0000638
639class Pen(RawPen):
640
641 def __init__(self):
642 global _root, _canvas
643 if _root is None:
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000644 _root = Tkinter.Tk()
Guido van Rossumb241b671998-12-04 16:42:46 +0000645 _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
Georg Brandle3a25832006-05-17 14:56:04 +0000646 _root.title(_title)
647
Guido van Rossumb241b671998-12-04 16:42:46 +0000648 if _canvas is None:
649 # XXX Should have scroll bars
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000650 _canvas = Tkinter.Canvas(_root, background="white")
Guido van Rossumb241b671998-12-04 16:42:46 +0000651 _canvas.pack(expand=1, fill="both")
Georg Brandle3a25832006-05-17 14:56:04 +0000652
Martin v. Löwisbd39c032006-07-03 09:44:00 +0000653 setup(width=_width, height= _height, startx=_startx, starty=_starty)
Georg Brandle3a25832006-05-17 14:56:04 +0000654
Guido van Rossumb241b671998-12-04 16:42:46 +0000655 RawPen.__init__(self, _canvas)
656
657 def _destroy(self):
658 global _root, _canvas, _pen
659 root = self._canvas._root()
660 if root is _root:
661 _pen = None
662 _root = None
663 _canvas = None
664 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +0000665
Guido van Rossumb241b671998-12-04 16:42:46 +0000666def _getpen():
667 global _pen
Georg Brandle3a25832006-05-17 14:56:04 +0000668 if not _pen:
669 _pen = Pen()
670 return _pen
671
672class Turtle(Pen):
673 pass
674
675"""For documentation of the following functions see
676 the RawPen methods with the same names
677"""
Guido van Rossumb241b671998-12-04 16:42:46 +0000678
679def degrees(): _getpen().degrees()
680def radians(): _getpen().radians()
681def reset(): _getpen().reset()
682def clear(): _getpen().clear()
683def tracer(flag): _getpen().tracer(flag)
684def forward(distance): _getpen().forward(distance)
685def backward(distance): _getpen().backward(distance)
686def left(angle): _getpen().left(angle)
687def right(angle): _getpen().right(angle)
688def up(): _getpen().up()
689def down(): _getpen().down()
690def width(width): _getpen().width(width)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000691def color(*args): _getpen().color(*args)
Guido van Rossumb241b671998-12-04 16:42:46 +0000692def write(arg, move=0): _getpen().write(arg, move)
693def fill(flag): _getpen().fill(flag)
Georg Brandle3a25832006-05-17 14:56:04 +0000694def begin_fill(): _getpen().begin_fill()
Georg Brandl1be63af2006-06-28 20:23:25 +0000695def end_fill(): _getpen().end_fill()
Guido van Rossumb241b671998-12-04 16:42:46 +0000696def circle(radius, extent=None): _getpen().circle(radius, extent)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000697def goto(*args): _getpen().goto(*args)
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000698def heading(): return _getpen().heading()
699def setheading(angle): _getpen().setheading(angle)
700def position(): return _getpen().position()
701def window_width(): return _getpen().window_width()
702def window_height(): return _getpen().window_height()
703def setx(xpos): _getpen().setx(xpos)
704def sety(ypos): _getpen().sety(ypos)
Georg Brandle3a25832006-05-17 14:56:04 +0000705def towards(*args): return _getpen().towards(*args)
706
707def done(): _root.mainloop()
708def delay(delay): return _getpen().delay(delay)
709def speed(speed): return _getpen().speed(speed)
710
711for methodname in dir(RawPen):
712 """ copies RawPen docstrings to module functions of same name """
713 if not methodname.startswith("_"):
714 eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
715
716
717def setup(**geometry):
718 """ Sets the size and position of the main window.
719
720 Keywords are width, height, startx and starty
721
722 width: either a size in pixels or a fraction of the screen.
723 Default is 50% of screen.
724 height: either the height in pixels or a fraction of the screen.
725 Default is 75% of screen.
726
727 Setting either width or height to None before drawing will force
728 use of default geometry as in older versions of turtle.py
Tim Petersfd4c4192006-05-18 02:06:40 +0000729
Georg Brandle3a25832006-05-17 14:56:04 +0000730 startx: starting position in pixels from the left edge of the screen.
731 Default is to center window. Setting startx to None is the default
732 and centers window horizontally on screen.
Tim Petersfd4c4192006-05-18 02:06:40 +0000733
Georg Brandle3a25832006-05-17 14:56:04 +0000734 starty: starting position in pixels from the top edge of the screen.
735 Default is to center window. Setting starty to None is the default
736 and centers window vertically on screen.
737
738 Examples:
739 >>> setup (width=200, height=200, startx=0, starty=0)
740
741 sets window to 200x200 pixels, in upper left of screen
742
743 >>> setup(width=.75, height=0.5, startx=None, starty=None)
744
745 sets window to 75% of screen by 50% of screen and centers
746
747 >>> setup(width=None)
748
749 forces use of default geometry as in older versions of turtle.py
750 """
Tim Petersfd4c4192006-05-18 02:06:40 +0000751
Georg Brandle3a25832006-05-17 14:56:04 +0000752 global _width, _height, _startx, _starty
753
754 width = geometry.get('width',_width)
755 if width >= 0 or width == None:
756 _width = width
757 else:
758 raise ValueError, "width can not be less than 0"
759
760 height = geometry.get('height',_height)
761 if height >= 0 or height == None:
762 _height = height
763 else:
764 raise ValueError, "height can not be less than 0"
Tim Petersfd4c4192006-05-18 02:06:40 +0000765
Georg Brandle3a25832006-05-17 14:56:04 +0000766 startx = geometry.get('startx', _startx)
767 if startx >= 0 or startx == None:
768 _startx = _startx
769 else:
770 raise ValueError, "startx can not be less than 0"
771
772 starty = geometry.get('starty', _starty)
773 if starty >= 0 or starty == None:
774 _starty = starty
775 else:
776 raise ValueError, "startx can not be less than 0"
777
778
779 if _root and _width and _height:
780 if 0 < _width <= 1:
781 _width = _root.winfo_screenwidth() * +width
782 if 0 < _height <= 1:
783 _height = _root.winfo_screenheight() * _height
784
785 # center window on screen
786 if _startx is None:
787 _startx = (_root.winfo_screenwidth() - _width) / 2
Tim Petersfd4c4192006-05-18 02:06:40 +0000788
Georg Brandle3a25832006-05-17 14:56:04 +0000789 if _starty is None:
790 _starty = (_root.winfo_screenheight() - _height) / 2
791
792 _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
793
794def title(title):
795 """ set the window title.
796
797 By default this is set to 'Turtle Graphics'
798
799 Example:
800 >>> title("My Window")
801 """
Tim Petersfd4c4192006-05-18 02:06:40 +0000802
Georg Brandle3a25832006-05-17 14:56:04 +0000803 global _title
804 _title = title
Guido van Rossumb241b671998-12-04 16:42:46 +0000805
806def demo():
807 reset()
808 tracer(1)
809 up()
810 backward(100)
811 down()
812 # draw 3 squares; the last filled
813 width(3)
814 for i in range(3):
815 if i == 2:
816 fill(1)
817 for j in range(4):
818 forward(20)
819 left(90)
820 if i == 2:
821 color("maroon")
822 fill(0)
823 up()
824 forward(30)
825 down()
826 width(1)
827 color("black")
828 # move out of the way
829 tracer(0)
830 up()
831 right(90)
832 forward(100)
833 right(90)
834 forward(100)
835 right(180)
836 down()
837 # some text
838 write("startstart", 1)
839 write("start", 1)
840 color("red")
841 # staircase
842 for i in range(5):
843 forward(20)
844 left(90)
845 forward(20)
846 right(90)
847 # filled staircase
848 fill(1)
849 for i in range(5):
850 forward(20)
851 left(90)
852 forward(20)
853 right(90)
854 fill(0)
Georg Brandle3a25832006-05-17 14:56:04 +0000855 tracer(1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000856 # more text
857 write("end")
Georg Brandle3a25832006-05-17 14:56:04 +0000858
859def demo2():
860 # exercises some new and improved features
861 speed('fast')
862 width(3)
863
864 # draw a segmented half-circle
865 setheading(towards(0,0))
866 x,y = position()
867 r = (x**2+y**2)**.5/2.0
868 right(90)
869 pendown = True
870 for i in range(18):
871 if pendown:
872 up()
873 pendown = False
874 else:
875 down()
876 pendown = True
877 circle(r,10)
878 sleep(2)
Tim Petersfd4c4192006-05-18 02:06:40 +0000879
880 reset()
Georg Brandle3a25832006-05-17 14:56:04 +0000881 left(90)
Tim Petersfd4c4192006-05-18 02:06:40 +0000882
Georg Brandle3a25832006-05-17 14:56:04 +0000883 # draw a series of triangles
884 l = 10
885 color("green")
886 width(3)
887 left(180)
888 sp = 5
889 for i in range(-2,16):
890 if i > 0:
891 color(1.0-0.05*i,0,0.05*i)
892 fill(1)
893 color("green")
894 for j in range(3):
895 forward(l)
896 left(120)
897 l += 10
898 left(15)
899 if sp > 0:
900 sp = sp-1
901 speed(speeds[sp])
902 color(0.25,0,0.75)
903 fill(0)
904 color("green")
905
906 left(130)
907 up()
908 forward(90)
909 color("red")
910 speed('fastest')
Tim Petersfd4c4192006-05-18 02:06:40 +0000911 down();
Georg Brandle3a25832006-05-17 14:56:04 +0000912
913 # create a second turtle and make the original pursue and catch it
914 turtle=Turtle()
915 turtle.reset()
916 turtle.left(90)
917 turtle.speed('normal')
918 turtle.up()
919 turtle.goto(280,40)
920 turtle.left(24)
921 turtle.down()
922 turtle.speed('fast')
923 turtle.color("blue")
924 turtle.width(2)
925 speed('fastest')
926
927 # turn default turtle towards new turtle object
928 setheading(towards(turtle))
929 while ( abs(position()[0]-turtle.position()[0])>4 or
930 abs(position()[1]-turtle.position()[1])>4):
931 turtle.forward(3.5)
932 turtle.left(0.6)
933 # turn default turtle towards new turtle object
934 setheading(towards(turtle))
935 forward(4)
936 write("CAUGHT! ", move=True)
937
Tim Petersfd4c4192006-05-18 02:06:40 +0000938
Guido van Rossumb241b671998-12-04 16:42:46 +0000939
940if __name__ == '__main__':
Georg Brandle3a25832006-05-17 14:56:04 +0000941 from time import sleep
Guido van Rossumb241b671998-12-04 16:42:46 +0000942 demo()
Georg Brandle3a25832006-05-17 14:56:04 +0000943 sleep(3)
944 demo2()
945 done()