blob: cc5ffa104952e8424c8eb34e64fbcba18c70ed8c [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
Guido van Rossumb241b671998-12-04 16:42:46 +000033 self.degrees()
34 self.reset()
35
36 def degrees(self, fullcircle=360.0):
Georg Brandle3a25832006-05-17 14:56:04 +000037 """ Set angle measurement units to degrees.
38
39 Example:
40 >>> turtle.degrees()
41 """
Guido van Rossumb241b671998-12-04 16:42:46 +000042 self._fullcircle = fullcircle
43 self._invradian = pi / (fullcircle * 0.5)
44
45 def radians(self):
Georg Brandle3a25832006-05-17 14:56:04 +000046 """ Set the angle measurement units to radians.
47
48 Example:
49 >>> turtle.radians()
50 """
Guido van Rossumb241b671998-12-04 16:42:46 +000051 self.degrees(2.0*pi)
52
53 def reset(self):
Georg Brandle3a25832006-05-17 14:56:04 +000054 """ Clear the screen, re-center the pen, and set variables to
55 the default values.
56
57 Example:
58 >>> turtle.position()
59 [0.0, -22.0]
60 >>> turtle.heading()
61 100.0
62 >>> turtle.reset()
63 >>> turtle.position()
64 [0.0, 0.0]
65 >>> turtle.heading()
66 0.0
67 """
Guido van Rossumb241b671998-12-04 16:42:46 +000068 canvas = self._canvas
Martin v. Löwis73b9b662002-09-22 13:00:26 +000069 self._canvas.update()
Guido van Rossumb241b671998-12-04 16:42:46 +000070 width = canvas.winfo_width()
71 height = canvas.winfo_height()
72 if width <= 1:
73 width = canvas['width']
74 if height <= 1:
75 height = canvas['height']
76 self._origin = float(width)/2.0, float(height)/2.0
77 self._position = self._origin
78 self._angle = 0.0
79 self._drawing = 1
80 self._width = 1
81 self._color = "black"
82 self._filling = 0
83 self._path = []
84 self._tofill = []
85 self.clear()
86 canvas._root().tkraise()
87
88 def clear(self):
Georg Brandle3a25832006-05-17 14:56:04 +000089 """ Clear the screen. The turtle does not move.
90
91 Example:
92 >>> turtle.clear()
93 """
Guido van Rossumb241b671998-12-04 16:42:46 +000094 self.fill(0)
95 canvas = self._canvas
96 items = self._items
97 self._items = []
98 for item in items:
99 canvas.delete(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000100 self._delete_turtle()
101 self._draw_turtle()
102
Guido van Rossumb241b671998-12-04 16:42:46 +0000103 def tracer(self, flag):
Georg Brandle3a25832006-05-17 14:56:04 +0000104 """ Set tracing on if flag is True, and off if it is False.
105 Tracing means line are drawn more slowly, with an
106 animation of an arrow along the line.
107
108 Example:
109 >>> turtle.tracer(False) # turns off Tracer
110 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000111 self._tracing = flag
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000112 if not self._tracing:
113 self._delete_turtle()
114 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000115
116 def forward(self, distance):
Georg Brandle3a25832006-05-17 14:56:04 +0000117 """ Go forward distance steps.
118
119 Example:
120 >>> turtle.position()
121 [0.0, 0.0]
122 >>> turtle.forward(25)
123 >>> turtle.position()
124 [25.0, 0.0]
125 >>> turtle.forward(-75)
126 >>> turtle.position()
127 [-50.0, 0.0]
128 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000129 x0, y0 = start = self._position
130 x1 = x0 + distance * cos(self._angle*self._invradian)
131 y1 = y0 - distance * sin(self._angle*self._invradian)
132 self._goto(x1, y1)
133
134 def backward(self, distance):
Georg Brandle3a25832006-05-17 14:56:04 +0000135 """ Go backwards distance steps.
136
137 The turtle's heading does not change.
138
139 Example:
140 >>> turtle.position()
141 [0.0, 0.0]
142 >>> turtle.backward(30)
143 >>> turtle.position()
144 [-30.0, 0.0]
145 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000146 self.forward(-distance)
147
148 def left(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000149 """ Turn left angle units (units are by default degrees,
150 but can be set via the degrees() and radians() functions.)
151
152 When viewed from above, the turning happens in-place around
153 its front tip.
154
155 Example:
156 >>> turtle.heading()
157 22
158 >>> turtle.left(45)
159 >>> turtle.heading()
160 67.0
161 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000162 self._angle = (self._angle + angle) % self._fullcircle
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000163 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000164
165 def right(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000166 """ Turn right angle units (units are by default degrees,
167 but can be set via the degrees() and radians() functions.)
168
169 When viewed from above, the turning happens in-place around
170 its front tip.
171
172 Example:
173 >>> turtle.heading()
174 22
175 >>> turtle.right(45)
176 >>> turtle.heading()
177 337.0
178 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000179 self.left(-angle)
180
181 def up(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000182 """ Pull the pen up -- no drawing when moving.
183
184 Example:
185 >>> turtle.up()
186 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000187 self._drawing = 0
188
189 def down(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000190 """ Put the pen down -- draw when moving.
191
192 Example:
193 >>> turtle.down()
194 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000195 self._drawing = 1
196
197 def width(self, width):
Georg Brandle3a25832006-05-17 14:56:04 +0000198 """ Set the line to thickness to width.
199
200 Example:
201 >>> turtle.width(10)
202 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000203 self._width = float(width)
204
205 def color(self, *args):
Georg Brandle3a25832006-05-17 14:56:04 +0000206 """ Set the pen color.
207
208 Three input formats are allowed:
209
210 color(s)
211 s is a Tk specification string, such as "red" or "yellow"
212
213 color((r, g, b))
214 *a tuple* of r, g, and b, which represent, an RGB color,
215 and each of r, g, and b are in the range [0..1]
216
217 color(r, g, b)
218 r, g, and b represent an RGB color, and each of r, g, and b
219 are in the range [0..1]
220
221 Example:
222
223 >>> turtle.color('brown')
224 >>> tup = (0.2, 0.8, 0.55)
225 >>> turtle.color(tup)
226 >>> turtle.color(0, .5, 0)
227 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000228 if not args:
229 raise Error, "no color arguments"
230 if len(args) == 1:
231 color = args[0]
232 if type(color) == type(""):
233 # Test the color first
234 try:
235 id = self._canvas.create_line(0, 0, 0, 0, fill=color)
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000236 except Tkinter.TclError:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000237 raise Error, "bad color string: %r" % (color,)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000238 self._set_color(color)
Guido van Rossumb241b671998-12-04 16:42:46 +0000239 return
240 try:
241 r, g, b = color
242 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000243 raise Error, "bad color sequence: %r" % (color,)
Guido van Rossumb241b671998-12-04 16:42:46 +0000244 else:
245 try:
246 r, g, b = args
247 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000248 raise Error, "bad color arguments: %r" % (args,)
Guido van Rossumb241b671998-12-04 16:42:46 +0000249 assert 0 <= r <= 1
250 assert 0 <= g <= 1
251 assert 0 <= b <= 1
252 x = 255.0
253 y = 0.5
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000254 self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y)))
255
256 def _set_color(self,color):
257 self._color = color
258 self._draw_turtle()
259
Georg Brandle3a25832006-05-17 14:56:04 +0000260 def write(self, text, move=False):
261 """ Write text at the current pen position.
262
263 If move is true, the pen is moved to the bottom-right corner
264 of the text. By default, move is False.
265
266 Example:
267 >>> turtle.write('The race is on!')
268 >>> turtle.write('Home = (0, 0)', True)
269 """
270 x, y = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000271 x = x-1 # correction -- calibrated for Windows
Fred Draked038ca82000-10-23 18:31:14 +0000272 item = self._canvas.create_text(x, y,
Georg Brandle3a25832006-05-17 14:56:04 +0000273 text=str(text), anchor="sw",
Guido van Rossumb241b671998-12-04 16:42:46 +0000274 fill=self._color)
275 self._items.append(item)
276 if move:
277 x0, y0, x1, y1 = self._canvas.bbox(item)
278 self._goto(x1, y1)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000279 self._draw_turtle()
Guido van Rossumb241b671998-12-04 16:42:46 +0000280
281 def fill(self, flag):
Georg Brandle3a25832006-05-17 14:56:04 +0000282 """ Call fill(1) before drawing the shape you
283 want to fill, and fill(0) when done.
284
285 Example:
286 >>> turtle.fill(1)
287 >>> turtle.forward(100)
288 >>> turtle.left(90)
289 >>> turtle.forward(100)
290 >>> turtle.left(90)
291 >>> turtle.forward(100)
292 >>> turtle.left(90)
293 >>> turtle.forward(100)
294 >>> turtle.fill(0)
295 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000296 if self._filling:
297 path = tuple(self._path)
298 smooth = self._filling < 0
299 if len(path) > 2:
300 item = self._canvas._create('polygon', path,
301 {'fill': self._color,
302 'smooth': smooth})
303 self._items.append(item)
Guido van Rossumb241b671998-12-04 16:42:46 +0000304 if self._tofill:
305 for item in self._tofill:
306 self._canvas.itemconfigure(item, fill=self._color)
307 self._items.append(item)
308 self._path = []
309 self._tofill = []
310 self._filling = flag
311 if flag:
312 self._path.append(self._position)
Raymond Hettingeraf81c2e2003-06-09 08:50:57 +0000313 self.forward(0)
Guido van Rossumb241b671998-12-04 16:42:46 +0000314
Georg Brandle3a25832006-05-17 14:56:04 +0000315 def begin_fill(self):
316 """ Called just before drawing a shape to be filled.
317
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 """
329 self.fill(1)
330
331 def end_fill(self):
332 """ Called after drawing a shape to be filled.
333
334 Example:
335 >>> turtle.begin_fill()
336 >>> turtle.forward(100)
337 >>> turtle.left(90)
338 >>> turtle.forward(100)
339 >>> turtle.left(90)
340 >>> turtle.forward(100)
341 >>> turtle.left(90)
342 >>> turtle.forward(100)
343 >>> turtle.end_fill()
344 """
345 self.fill(0)
346
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000347 def circle(self, radius, extent = None):
Georg Brandle3a25832006-05-17 14:56:04 +0000348 """ Draw a circle with given radius.
349 The center is radius units left of the turtle; extent
350 determines which part of the circle is drawn. If not given,
351 the entire circle is drawn.
352
353 If extent is not a full circle, one endpoint of the arc is the
354 current pen position. The arc is drawn in a counter clockwise
355 direction if radius is positive, otherwise in a clockwise
356 direction. In the process, the direction of the turtle is
357 changed by the amount of the extent.
358
359 >>> turtle.circle(50)
360 >>> turtle.circle(120, 180) # half a circle
361 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000362 if extent is None:
Martin v. Löwis4c4300d2006-07-03 10:05:30 +0000363 extent = self._fullcircle
364 frac = abs(extent)/self._fullcircle
365 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
366 w = 1.0 * extent / steps
367 w2 = 0.5 * w
368 l = 2.0 * radius * sin(w2*self._invradian)
369 if radius < 0:
370 l, w, w2 = -l, -w, -w2
371 self.left(w2)
372 for i in range(steps):
373 self.forward(l)
374 self.left(w)
375 self.right(w2)
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000376
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000377 def heading(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000378 """ Return the turtle's current heading.
379
380 Example:
381 >>> turtle.heading()
382 67.0
383 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000384 return self._angle
385
386 def setheading(self, angle):
Georg Brandle3a25832006-05-17 14:56:04 +0000387 """ Set the turtle facing the given angle.
388
389 Here are some common directions in degrees:
390
391 0 - east
392 90 - north
393 180 - west
394 270 - south
395
396 Example:
397 >>> turtle.setheading(90)
398 >>> turtle.heading()
399 90
400 >>> turtle.setheading(128)
401 >>> turtle.heading()
402 128
403 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000404 self._angle = angle
405 self._draw_turtle()
406
407 def window_width(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000408 """ Returns the width of the turtle window.
409
410 Example:
411 >>> turtle.window_width()
412 640
413 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000414 width = self._canvas.winfo_width()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000415 if width <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000416 width = self._canvas['width']
417 return width
418
419 def window_height(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000420 """ Return the height of the turtle window.
421
422 Example:
423 >>> turtle.window_height()
424 768
425 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000426 height = self._canvas.winfo_height()
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000427 if height <= 1: # the window isn't managed by a geometry manager
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000428 height = self._canvas['height']
429 return height
430
431 def position(self):
Georg Brandle3a25832006-05-17 14:56:04 +0000432 """ Return the current (x, y) location of the turtle.
433
434 Example:
435 >>> turtle.position()
436 [0.0, 240.0]
437 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000438 x0, y0 = self._origin
439 x1, y1 = self._position
440 return [x1-x0, -y1+y0]
441
442 def setx(self, xpos):
Georg Brandle3a25832006-05-17 14:56:04 +0000443 """ Set the turtle's x coordinate to be xpos.
444
445 Example:
446 >>> turtle.position()
447 [10.0, 240.0]
448 >>> turtle.setx(10)
449 >>> turtle.position()
450 [10.0, 240.0]
451 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000452 x0, y0 = self._origin
453 x1, y1 = self._position
454 self._goto(x0+xpos, y1)
455
456 def sety(self, ypos):
Georg Brandle3a25832006-05-17 14:56:04 +0000457 """ Set the turtle's y coordinate to be ypos.
458
459 Example:
460 >>> turtle.position()
461 [0.0, 0.0]
462 >>> turtle.sety(-22)
463 >>> turtle.position()
464 [0.0, -22.0]
465 """
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000466 x0, y0 = self._origin
467 x1, y1 = self._position
468 self._goto(x1, y0-ypos)
Guido van Rossumb241b671998-12-04 16:42:46 +0000469
Georg Brandle3a25832006-05-17 14:56:04 +0000470 def towards(self, *args):
471 """Returs the angle, which corresponds to the line
472 from turtle-position to point (x,y).
473
474 Argument can be two coordinates or one pair of coordinates
475 or a RawPen/Pen instance.
476
477 Example:
478 >>> turtle.position()
479 [10.0, 10.0]
480 >>> turtle.towards(0,0)
481 225.0
482 """
483 if len(args) == 2:
484 x, y = args
485 else:
486 arg = args[0]
487 if isinstance(arg, RawPen):
488 x, y = arg.position()
489 else:
490 x, y = arg
491 x0, y0 = self.position()
492 dx = x - x0
493 dy = y - y0
494 return (atan2(dy,dx) / self._invradian) % self._fullcircle
495
Guido van Rossumb241b671998-12-04 16:42:46 +0000496 def goto(self, *args):
Georg Brandle3a25832006-05-17 14:56:04 +0000497 """ Go to the given point.
498
499 If the pen is down, then a line will be drawn. The turtle's
500 orientation does not change.
501
502 Two input formats are accepted:
503
504 goto(x, y)
505 go to point (x, y)
506
507 goto((x, y))
508 go to point (x, y)
509
510 Example:
511 >>> turtle.position()
512 [0.0, 0.0]
513 >>> turtle.goto(50, -45)
514 >>> turtle.position()
515 [50.0, -45.0]
516 """
Guido van Rossumb241b671998-12-04 16:42:46 +0000517 if len(args) == 1:
518 try:
519 x, y = args[0]
520 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000521 raise Error, "bad point argument: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000522 else:
523 try:
524 x, y = args
525 except:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000526 raise Error, "bad coordinates: %r" % (args[0],)
Guido van Rossumb241b671998-12-04 16:42:46 +0000527 x0, y0 = self._origin
528 self._goto(x0+x, y0-y)
529
530 def _goto(self, x1, y1):
Georg Brandle3a25832006-05-17 14:56:04 +0000531 x0, y0 = self._position
Guido van Rossumb241b671998-12-04 16:42:46 +0000532 self._position = map(float, (x1, y1))
533 if self._filling:
534 self._path.append(self._position)
535 if self._drawing:
Guido van Rossumbffa52f2002-09-29 00:25:51 +0000536 if self._tracing:
Guido van Rossumb241b671998-12-04 16:42:46 +0000537 dx = float(x1 - x0)
538 dy = float(y1 - y0)
539 distance = hypot(dx, dy)
540 nhops = int(distance)
541 item = self._canvas.create_line(x0, y0, x0, y0,
542 width=self._width,
Guido van Rossumb241b671998-12-04 16:42:46 +0000543 capstyle="round",
544 fill=self._color)
545 try:
546 for i in range(1, 1+nhops):
547 x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
548 self._canvas.coords(item, x0, y0, x, y)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000549 self._draw_turtle((x,y))
Guido van Rossumb241b671998-12-04 16:42:46 +0000550 self._canvas.update()
Georg Brandle3a25832006-05-17 14:56:04 +0000551 self._canvas.after(self._delay)
Guido van Rossuma659efe2001-01-01 19:11:07 +0000552 # in case nhops==0
553 self._canvas.coords(item, x0, y0, x1, y1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000554 self._canvas.itemconfigure(item, arrow="none")
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000555 except Tkinter.TclError:
Guido van Rossumb241b671998-12-04 16:42:46 +0000556 # Probably the window was closed!
557 return
558 else:
559 item = self._canvas.create_line(x0, y0, x1, y1,
560 width=self._width,
561 capstyle="round",
562 fill=self._color)
563 self._items.append(item)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000564 self._draw_turtle()
565
Georg Brandle3a25832006-05-17 14:56:04 +0000566 def speed(self, speed):
567 """ Set the turtle's speed.
568
569 speed must one of these five strings:
570
571 'fastest' is a 0 ms delay
572 'fast' is a 5 ms delay
573 'normal' is a 10 ms delay
574 'slow' is a 15 ms delay
575 'slowest' is a 20 ms delay
576
577 Example:
578 >>> turtle.speed('slow')
579 """
580 try:
581 speed = speed.strip().lower()
582 self._delay = speeds.index(speed) * 5
583 except:
584 raise ValueError("%r is not a valid speed. speed must be "
585 "one of %s" % (speed, speeds))
586
587
588 def delay(self, delay):
589 """ Set the drawing delay in milliseconds.
590
591 This is intended to allow finer control of the drawing speed
592 than the speed() method
593
594 Example:
595 >>> turtle.delay(15)
596 """
597 if int(delay) < 0:
598 raise ValueError("delay must be greater than or equal to 0")
599 self._delay = int(delay)
600
601 def _draw_turtle(self, position=[]):
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000602 if not self._tracing:
Martin v. Löwis3798da02006-06-17 18:44:27 +0000603 self._canvas.update()
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000604 return
605 if position == []:
606 position = self._position
607 x,y = position
608 distance = 8
609 dx = distance * cos(self._angle*self._invradian)
610 dy = distance * sin(self._angle*self._invradian)
611 self._delete_turtle()
Martin v. Löwis4157ffb2002-03-28 15:45:57 +0000612 self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000613 width=self._width,
614 arrow="last",
615 capstyle="round",
616 fill=self._color)
617 self._canvas.update()
618
619 def _delete_turtle(self):
620 if self._arrow != 0:
621 self._canvas.delete(self._arrow)
Georg Brandle3a25832006-05-17 14:56:04 +0000622 self._arrow = 0
Guido van Rossumb241b671998-12-04 16:42:46 +0000623
624
625_root = None
626_canvas = None
627_pen = None
Georg Brandle3a25832006-05-17 14:56:04 +0000628_width = 0.50 # 50% of window width
629_height = 0.75 # 75% of window height
630_startx = None
631_starty = None
632_title = "Turtle Graphics" # default title
Guido van Rossumb241b671998-12-04 16:42:46 +0000633
634class Pen(RawPen):
635
636 def __init__(self):
637 global _root, _canvas
638 if _root is None:
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000639 _root = Tkinter.Tk()
Guido van Rossumb241b671998-12-04 16:42:46 +0000640 _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
Georg Brandle3a25832006-05-17 14:56:04 +0000641 _root.title(_title)
642
Guido van Rossumb241b671998-12-04 16:42:46 +0000643 if _canvas is None:
644 # XXX Should have scroll bars
Martin v. Löwis4b6ea792000-10-01 17:52:01 +0000645 _canvas = Tkinter.Canvas(_root, background="white")
Guido van Rossumb241b671998-12-04 16:42:46 +0000646 _canvas.pack(expand=1, fill="both")
Georg Brandle3a25832006-05-17 14:56:04 +0000647
Martin v. Löwisbd39c032006-07-03 09:44:00 +0000648 setup(width=_width, height= _height, startx=_startx, starty=_starty)
Georg Brandle3a25832006-05-17 14:56:04 +0000649
Guido van Rossumb241b671998-12-04 16:42:46 +0000650 RawPen.__init__(self, _canvas)
651
652 def _destroy(self):
653 global _root, _canvas, _pen
654 root = self._canvas._root()
655 if root is _root:
656 _pen = None
657 _root = None
658 _canvas = None
659 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +0000660
Guido van Rossumb241b671998-12-04 16:42:46 +0000661def _getpen():
662 global _pen
Georg Brandle3a25832006-05-17 14:56:04 +0000663 if not _pen:
664 _pen = Pen()
665 return _pen
666
667class Turtle(Pen):
668 pass
669
670"""For documentation of the following functions see
671 the RawPen methods with the same names
672"""
Guido van Rossumb241b671998-12-04 16:42:46 +0000673
674def degrees(): _getpen().degrees()
675def radians(): _getpen().radians()
676def reset(): _getpen().reset()
677def clear(): _getpen().clear()
678def tracer(flag): _getpen().tracer(flag)
679def forward(distance): _getpen().forward(distance)
680def backward(distance): _getpen().backward(distance)
681def left(angle): _getpen().left(angle)
682def right(angle): _getpen().right(angle)
683def up(): _getpen().up()
684def down(): _getpen().down()
685def width(width): _getpen().width(width)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000686def color(*args): _getpen().color(*args)
Guido van Rossumb241b671998-12-04 16:42:46 +0000687def write(arg, move=0): _getpen().write(arg, move)
688def fill(flag): _getpen().fill(flag)
Georg Brandle3a25832006-05-17 14:56:04 +0000689def begin_fill(): _getpen().begin_fill()
Georg Brandl1be63af2006-06-28 20:23:25 +0000690def end_fill(): _getpen().end_fill()
Guido van Rossumb241b671998-12-04 16:42:46 +0000691def circle(radius, extent=None): _getpen().circle(radius, extent)
Raymond Hettingerff41c482003-04-06 09:01:11 +0000692def goto(*args): _getpen().goto(*args)
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000693def heading(): return _getpen().heading()
694def setheading(angle): _getpen().setheading(angle)
695def position(): return _getpen().position()
696def window_width(): return _getpen().window_width()
697def window_height(): return _getpen().window_height()
698def setx(xpos): _getpen().setx(xpos)
699def sety(ypos): _getpen().sety(ypos)
Georg Brandle3a25832006-05-17 14:56:04 +0000700def towards(*args): return _getpen().towards(*args)
701
702def done(): _root.mainloop()
703def delay(delay): return _getpen().delay(delay)
704def speed(speed): return _getpen().speed(speed)
705
706for methodname in dir(RawPen):
707 """ copies RawPen docstrings to module functions of same name """
708 if not methodname.startswith("_"):
709 eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
710
711
712def setup(**geometry):
713 """ Sets the size and position of the main window.
714
715 Keywords are width, height, startx and starty
716
717 width: either a size in pixels or a fraction of the screen.
718 Default is 50% of screen.
719 height: either the height in pixels or a fraction of the screen.
720 Default is 75% of screen.
721
722 Setting either width or height to None before drawing will force
723 use of default geometry as in older versions of turtle.py
Tim Petersfd4c4192006-05-18 02:06:40 +0000724
Georg Brandle3a25832006-05-17 14:56:04 +0000725 startx: starting position in pixels from the left edge of the screen.
726 Default is to center window. Setting startx to None is the default
727 and centers window horizontally on screen.
Tim Petersfd4c4192006-05-18 02:06:40 +0000728
Georg Brandle3a25832006-05-17 14:56:04 +0000729 starty: starting position in pixels from the top edge of the screen.
730 Default is to center window. Setting starty to None is the default
731 and centers window vertically on screen.
732
733 Examples:
734 >>> setup (width=200, height=200, startx=0, starty=0)
735
736 sets window to 200x200 pixels, in upper left of screen
737
738 >>> setup(width=.75, height=0.5, startx=None, starty=None)
739
740 sets window to 75% of screen by 50% of screen and centers
741
742 >>> setup(width=None)
743
744 forces use of default geometry as in older versions of turtle.py
745 """
Tim Petersfd4c4192006-05-18 02:06:40 +0000746
Georg Brandle3a25832006-05-17 14:56:04 +0000747 global _width, _height, _startx, _starty
748
749 width = geometry.get('width',_width)
750 if width >= 0 or width == None:
751 _width = width
752 else:
753 raise ValueError, "width can not be less than 0"
754
755 height = geometry.get('height',_height)
756 if height >= 0 or height == None:
757 _height = height
758 else:
759 raise ValueError, "height can not be less than 0"
Tim Petersfd4c4192006-05-18 02:06:40 +0000760
Georg Brandle3a25832006-05-17 14:56:04 +0000761 startx = geometry.get('startx', _startx)
762 if startx >= 0 or startx == None:
763 _startx = _startx
764 else:
765 raise ValueError, "startx can not be less than 0"
766
767 starty = geometry.get('starty', _starty)
768 if starty >= 0 or starty == None:
769 _starty = starty
770 else:
771 raise ValueError, "startx can not be less than 0"
772
773
774 if _root and _width and _height:
775 if 0 < _width <= 1:
776 _width = _root.winfo_screenwidth() * +width
777 if 0 < _height <= 1:
778 _height = _root.winfo_screenheight() * _height
779
780 # center window on screen
781 if _startx is None:
782 _startx = (_root.winfo_screenwidth() - _width) / 2
Tim Petersfd4c4192006-05-18 02:06:40 +0000783
Georg Brandle3a25832006-05-17 14:56:04 +0000784 if _starty is None:
785 _starty = (_root.winfo_screenheight() - _height) / 2
786
787 _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
788
789def title(title):
790 """ set the window title.
791
792 By default this is set to 'Turtle Graphics'
793
794 Example:
795 >>> title("My Window")
796 """
Tim Petersfd4c4192006-05-18 02:06:40 +0000797
Georg Brandle3a25832006-05-17 14:56:04 +0000798 global _title
799 _title = title
Guido van Rossumb241b671998-12-04 16:42:46 +0000800
801def demo():
802 reset()
803 tracer(1)
804 up()
805 backward(100)
806 down()
807 # draw 3 squares; the last filled
808 width(3)
809 for i in range(3):
810 if i == 2:
811 fill(1)
812 for j in range(4):
813 forward(20)
814 left(90)
815 if i == 2:
816 color("maroon")
817 fill(0)
818 up()
819 forward(30)
820 down()
821 width(1)
822 color("black")
823 # move out of the way
824 tracer(0)
825 up()
826 right(90)
827 forward(100)
828 right(90)
829 forward(100)
830 right(180)
831 down()
832 # some text
833 write("startstart", 1)
834 write("start", 1)
835 color("red")
836 # staircase
837 for i in range(5):
838 forward(20)
839 left(90)
840 forward(20)
841 right(90)
842 # filled staircase
843 fill(1)
844 for i in range(5):
845 forward(20)
846 left(90)
847 forward(20)
848 right(90)
849 fill(0)
Georg Brandle3a25832006-05-17 14:56:04 +0000850 tracer(1)
Guido van Rossumb241b671998-12-04 16:42:46 +0000851 # more text
852 write("end")
Georg Brandle3a25832006-05-17 14:56:04 +0000853
854def demo2():
855 # exercises some new and improved features
856 speed('fast')
857 width(3)
858
859 # draw a segmented half-circle
860 setheading(towards(0,0))
861 x,y = position()
862 r = (x**2+y**2)**.5/2.0
863 right(90)
864 pendown = True
865 for i in range(18):
866 if pendown:
867 up()
868 pendown = False
869 else:
870 down()
871 pendown = True
872 circle(r,10)
873 sleep(2)
Tim Petersfd4c4192006-05-18 02:06:40 +0000874
875 reset()
Georg Brandle3a25832006-05-17 14:56:04 +0000876 left(90)
Tim Petersfd4c4192006-05-18 02:06:40 +0000877
Georg Brandle3a25832006-05-17 14:56:04 +0000878 # draw a series of triangles
879 l = 10
880 color("green")
881 width(3)
882 left(180)
883 sp = 5
884 for i in range(-2,16):
885 if i > 0:
886 color(1.0-0.05*i,0,0.05*i)
887 fill(1)
888 color("green")
889 for j in range(3):
890 forward(l)
891 left(120)
892 l += 10
893 left(15)
894 if sp > 0:
895 sp = sp-1
896 speed(speeds[sp])
897 color(0.25,0,0.75)
898 fill(0)
899 color("green")
900
901 left(130)
902 up()
903 forward(90)
904 color("red")
905 speed('fastest')
Tim Petersfd4c4192006-05-18 02:06:40 +0000906 down();
Georg Brandle3a25832006-05-17 14:56:04 +0000907
908 # create a second turtle and make the original pursue and catch it
909 turtle=Turtle()
910 turtle.reset()
911 turtle.left(90)
912 turtle.speed('normal')
913 turtle.up()
914 turtle.goto(280,40)
915 turtle.left(24)
916 turtle.down()
917 turtle.speed('fast')
918 turtle.color("blue")
919 turtle.width(2)
920 speed('fastest')
921
922 # turn default turtle towards new turtle object
923 setheading(towards(turtle))
924 while ( abs(position()[0]-turtle.position()[0])>4 or
925 abs(position()[1]-turtle.position()[1])>4):
926 turtle.forward(3.5)
927 turtle.left(0.6)
928 # turn default turtle towards new turtle object
929 setheading(towards(turtle))
930 forward(4)
931 write("CAUGHT! ", move=True)
932
Tim Petersfd4c4192006-05-18 02:06:40 +0000933
Guido van Rossumb241b671998-12-04 16:42:46 +0000934
935if __name__ == '__main__':
Georg Brandle3a25832006-05-17 14:56:04 +0000936 from time import sleep
Guido van Rossumb241b671998-12-04 16:42:46 +0000937 demo()
Georg Brandle3a25832006-05-17 14:56:04 +0000938 sleep(3)
939 demo2()
940 done()