blob: ddc69c995e1ffdc902f4dbf256f4c12e95cad64f [file] [log] [blame]
Martin v. Löwis87184592008-06-04 06:29:55 +00001#
2# turtle.py: a Tkinter based turtle graphics module for Python
3# Version 1.0b1 - 31. 5. 2008
4#
5# Copyright (C) 2006 - 2008 Gregor Lingl
6# email: glingl@aon.at
7#
8# This software is provided 'as-is', without any express or implied
9# warranty. In no event will the authors be held liable for any damages
10# arising from the use of this software.
11#
12# Permission is granted to anyone to use this software for any purpose,
13# including commercial applications, and to alter it and redistribute it
14# freely, subject to the following restrictions:
15#
16# 1. The origin of this software must not be misrepresented; you must not
17# claim that you wrote the original software. If you use this software
18# in a product, an acknowledgment in the product documentation would be
19# appreciated but is not required.
20# 2. Altered source versions must be plainly marked as such, and must not be
21# misrepresented as being the original software.
22# 3. This notice may not be removed or altered from any source distribution.
23
Georg Brandl33cece02008-05-20 06:58:21 +000024
25"""
26Turtle graphics is a popular way for introducing programming to
27kids. It was part of the original Logo programming language developed
Martin v. Löwis87184592008-06-04 06:29:55 +000028by Wally Feurzig and Seymour Papert in 1966.
Georg Brandl33cece02008-05-20 06:58:21 +000029
30Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it
31the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
32the direction it is facing, drawing a line as it moves. Give it the
33command turtle.left(25), and it rotates in-place 25 degrees clockwise.
34
35By combining together these and similar commands, intricate shapes and
36pictures can easily be drawn.
Martin v. Löwis87184592008-06-04 06:29:55 +000037
38----- turtle.py
39
40This module is an extended reimplementation of turtle.py from the
41Python standard distribution up to Python 2.5. (See: http:\\www.python.org)
42
43It tries to keep the merits of turtle.py and to be (nearly) 100%
44compatible with it. This means in the first place to enable the
45learning programmer to use all the commands, classes and methods
46interactively when using the module from within IDLE run with
47the -n switch.
48
49Roughly it has the following features added:
50
51- Better animation of the turtle movements, especially of turning the
52 turtle. So the turtles can more easily be used as a visual feedback
53 instrument by the (beginning) programmer.
54
55- Different turtle shapes, gif-images as turtle shapes, user defined
56 and user controllable turtle shapes, among them compound
57 (multicolored) shapes. Turtle shapes can be stgretched and tilted, which
58 makes turtles zu very versatile geometrical objects.
59
60- Fine control over turtle movement and screen updates via delay(),
61 and enhanced tracer() and speed() methods.
62
63- Aliases for the most commonly used commands, like fd for forward etc.,
64 following the early Logo traditions. This reduces the boring work of
65 typing long sequences of commands, which often occur in a natural way
66 when kids try to program fancy pictures on their first encounter with
67 turtle graphcis.
68
69- Turtles now have an undo()-method with configurable undo-buffer.
70
71- Some simple commands/methods for creating event driven programs
72 (mouse-, key-, timer-events). Especially useful for programming games.
73
74- A scrollable Canvas class. The default scrollable Canvas can be
75 extended interactively as needed while playing around with the turtle(s).
76
77- A TurtleScreen class with methods controlling background color or
78 background image, window and canvas size and other properties of the
79 TurtleScreen.
80
81- There is a method, setworldcoordinates(), to install a user defined
82 coordinate-system for the TurtleScreen.
83
84- The implementation uses a 2-vector class named Vec2D, derived from tuple.
85 This class is public, so it can be imported by the application programmer,
86 which makes certain types of computations very natural and compact.
87
88- Appearance of the TurtleScreen and the Turtles at startup/import can be
89 configured by means of a turtle.cfg configuration file.
90 The default configuration mimics the appearance of the old turtle module.
91
92- If configured appropriately the module reads in docstrings from a docstring
93 dictionary in some different language, supplied separately and replaces
94 the english ones by those read in. There is a utility function
95 write_docstringdict() to write a dictionary with the original (english)
96 docstrings to disc, so it can serve as a template for translations.
97
98Behind the scenes there are some features included with possible
99extensionsin in mind. These will be commented and documented elsewhere.
100
Georg Brandl33cece02008-05-20 06:58:21 +0000101"""
102
Martin v. Löwis87184592008-06-04 06:29:55 +0000103_ver = "turtle 1.0b1 - for Python 2.6 - 30. 5. 2008, 18:08"
Georg Brandl33cece02008-05-20 06:58:21 +0000104
Martin v. Löwis87184592008-06-04 06:29:55 +0000105#print _ver
Georg Brandl33cece02008-05-20 06:58:21 +0000106
Martin v. Löwis87184592008-06-04 06:29:55 +0000107import Tkinter as TK
108import types
109import math
110import time
111import os
Georg Brandl33cece02008-05-20 06:58:21 +0000112
Martin v. Löwis87184592008-06-04 06:29:55 +0000113from os.path import isfile, split, join
114from copy import deepcopy
Georg Brandl33cece02008-05-20 06:58:21 +0000115
Martin v. Löwis87184592008-06-04 06:29:55 +0000116from math import * ## for compatibility with old turtle module
Georg Brandl33cece02008-05-20 06:58:21 +0000117
Martin v. Löwis87184592008-06-04 06:29:55 +0000118_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
119 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
120_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
121 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
122 'getshapes', 'listen', 'mode', 'onkey', 'onscreenclick', 'ontimer',
123 'register_shape', 'resetscreen', 'screensize', 'setup',
124 'setworldcoordinates', 'title', 'tracer', 'turtles', 'update',
125 'window_height', 'window_width']
126_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
127 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
128 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
129 'fill', 'fillcolor', 'forward', 'get_poly', 'getpen', 'getscreen',
130 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
131 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
132 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
133 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
134 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
135 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'showturtle',
136 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', 'tracer',
137 'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
138 'window_height', 'window_width', 'write', 'xcor', 'ycor']
139_tg_utilities = ['write_docstringdict', 'done', 'mainloop']
140_math_functions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
141 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
142 'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
Georg Brandl33cece02008-05-20 06:58:21 +0000143
Martin v. Löwis87184592008-06-04 06:29:55 +0000144__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
145 _tg_utilities + _math_functions)
Georg Brandl33cece02008-05-20 06:58:21 +0000146
Martin v. Löwis87184592008-06-04 06:29:55 +0000147_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
148 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
149 'turtlesize', 'up', 'width']
Georg Brandl33cece02008-05-20 06:58:21 +0000150
Martin v. Löwis87184592008-06-04 06:29:55 +0000151_CFG = {"width" : 0.5, # Screen
152 "height" : 0.75,
153 "canvwidth" : 400,
154 "canvheight": 300,
155 "leftright": None,
156 "topbottom": None,
157 "mode": "standard", # TurtleScreen
158 "colormode": 1.0,
159 "delay": 10,
160 "undobuffersize": 1000, # RawTurtle
161 "shape": "classic",
162 "pencolor" : "black",
163 "fillcolor" : "black",
164 "resizemode" : "noresize",
165 "visible" : True,
166 "language": "english", # docstrings
167 "exampleturtle": "turtle",
168 "examplescreen": "screen",
169 "title": "Python Turtle Graphics",
170 "using_IDLE": False
171 }
Georg Brandl33cece02008-05-20 06:58:21 +0000172
Martin v. Löwis87184592008-06-04 06:29:55 +0000173##print "cwd:", os.getcwd()
174##print "__file__:", __file__
175##
176##def show(dictionary):
177## print "=========================="
178## for key in sorted(dictionary.keys()):
179## print key, ":", dictionary[key]
180## print "=========================="
181## print
Georg Brandl33cece02008-05-20 06:58:21 +0000182
Martin v. Löwis87184592008-06-04 06:29:55 +0000183def config_dict(filename):
184 """Convert content of config-file into dictionary."""
185 f = open(filename, "r")
186 cfglines = f.readlines()
187 f.close()
188 cfgdict = {}
189 for line in cfglines:
190 line = line.strip()
191 if not line or line.startswith("#"):
192 continue
193 try:
194 key, value = line.split("=")
195 except:
196 print "Bad line in config-file %s:\n%s" % (filename,line)
197 continue
198 key = key.strip()
199 value = value.strip()
200 if value in ["True", "False", "None", "''", '""']:
201 value = eval(value)
Georg Brandl33cece02008-05-20 06:58:21 +0000202 else:
203 try:
Martin v. Löwis87184592008-06-04 06:29:55 +0000204 if "." in value:
205 value = float(value)
206 else:
207 value = int(value)
Georg Brandl33cece02008-05-20 06:58:21 +0000208 except:
Martin v. Löwis87184592008-06-04 06:29:55 +0000209 pass # value need not be converted
210 cfgdict[key] = value
211 return cfgdict
Georg Brandl33cece02008-05-20 06:58:21 +0000212
Martin v. Löwis87184592008-06-04 06:29:55 +0000213def readconfig(cfgdict):
214 """Read config-files, change configuration-dict accordingly.
Georg Brandl33cece02008-05-20 06:58:21 +0000215
Martin v. Löwis87184592008-06-04 06:29:55 +0000216 If there is a turtle.cfg file in the current working directory,
217 read it from there. If this contains an importconfig-value,
218 say 'myway', construct filename turtle_mayway.cfg else use
219 turtle.cfg and read it from the import-directory, where
220 turtle.py is located.
221 Update configuration dictionary first according to config-file,
222 in the import directory, then according to config-file in the
223 current working directory.
224 If no config-file is found, the default configuration is used.
225 """
226 default_cfg = "turtle.cfg"
227 cfgdict1 = {}
228 cfgdict2 = {}
229 if isfile(default_cfg):
230 cfgdict1 = config_dict(default_cfg)
231 #print "1. Loading config-file %s from: %s" % (default_cfg, os.getcwd())
232 if "importconfig" in cfgdict1:
233 default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
234 try:
235 head, tail = split(__file__)
236 cfg_file2 = join(head, default_cfg)
237 except:
238 cfg_file2 = ""
239 if isfile(cfg_file2):
240 #print "2. Loading config-file %s:" % cfg_file2
241 cfgdict2 = config_dict(cfg_file2)
242## show(_CFG)
243## show(cfgdict2)
244 _CFG.update(cfgdict2)
245## show(_CFG)
246## show(cfgdict1)
247 _CFG.update(cfgdict1)
248## show(_CFG)
Georg Brandl33cece02008-05-20 06:58:21 +0000249
Martin v. Löwis87184592008-06-04 06:29:55 +0000250try:
251 readconfig(_CFG)
252except:
253 print "No configfile read, reason unknown"
254
255
256class Vec2D(tuple):
257 """A 2 dimensional vector class, used as a helper class
258 for implementing turtle graphics.
259 May be useful for turtle graphics programs also.
260 Derived from tuple, so a vector is a tuple!
261
262 Provides (for a, b vectors, k number):
263 a+b vector addition
264 a-b vector subtraction
265 a*b inner product
266 k*a and a*k multiplication with scalar
267 |a| absolute value of a
268 a.rotate(angle) rotation
269 """
270 def __new__(cls, x, y):
271 return tuple.__new__(cls, (x, y))
272 def __add__(self, other):
273 return Vec2D(self[0]+other[0], self[1]+other[1])
274 def __mul__(self, other):
275 if isinstance(other, Vec2D):
276 return self[0]*other[0]+self[1]*other[1]
277 return Vec2D(self[0]*other, self[1]*other)
278 def __rmul__(self, other):
279 if isinstance(other, int) or isinstance(other, float):
280 return Vec2D(self[0]*other, self[1]*other)
281 def __sub__(self, other):
282 return Vec2D(self[0]-other[0], self[1]-other[1])
283 def __neg__(self):
284 return Vec2D(-self[0], -self[1])
285 def __abs__(self):
286 return (self[0]**2 + self[1]**2)**0.5
287 def rotate(self, angle):
288 """rotate self counterclockwise by angle
289 """
290 perp = Vec2D(-self[1], self[0])
291 angle = angle * math.pi / 180.0
292 c, s = math.cos(angle), math.sin(angle)
293 return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
294 def __getnewargs__(self):
295 return (self[0], self[1])
296 def __repr__(self):
297 return "(%.2f,%.2f)" % self
298
299
300##############################################################################
301### From here up to line : Tkinter - Interface for turtle.py ###
302### May be replaced by an interface to some different graphcis-toolkit ###
303##############################################################################
304
305## helper functions for Scrolled Canvas, to forward Canvas-methods
306## to ScrolledCanvas class
307
308def __methodDict(cls, _dict):
309 """helper function for Scrolled Canvas"""
310 baseList = list(cls.__bases__)
311 baseList.reverse()
312 for _super in baseList:
313 __methodDict(_super, _dict)
314 for key, value in cls.__dict__.items():
315 if type(value) == types.FunctionType:
316 _dict[key] = value
317
318def __methods(cls):
319 """helper function for Scrolled Canvas"""
320 _dict = {}
321 __methodDict(cls, _dict)
322 return _dict.keys()
323
324__stringBody = (
325 'def %(method)s(self, *args, **kw): return ' +
326 'self.%(attribute)s.%(method)s(*args, **kw)')
327
328def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
329 """Helper functions for Scrolled Canvas, used to forward
330 ScrolledCanvas-methods to Tkinter.Canvas class.
331 """
332 _dict = {}
333 __methodDict(toClass, _dict)
334 for ex in _dict.keys():
335 if ex[:1] == '_' or ex[-1:] == '_':
336 del _dict[ex]
337 for ex in exclude:
338 if _dict.has_key(ex):
339 del _dict[ex]
340 for ex in __methods(fromClass):
341 if _dict.has_key(ex):
342 del _dict[ex]
343
344 for method, func in _dict.items():
345 d = {'method': method, 'func': func}
346 if type(toPart) == types.StringType:
347 execString = \
348 __stringBody % {'method' : method, 'attribute' : toPart}
349 exec execString in d
350 fromClass.__dict__[method] = d[method]
351
352
353class ScrolledCanvas(TK.Frame):
354 """Modeled after the scrolled canvas class from Grayons's Tkinter book.
355
356 Used as the default canvas, which pops up automatically when
357 using turtle graphics functions or the Turtle class.
358 """
359 def __init__(self, master, width=500, height=350,
360 canvwidth=600, canvheight=500):
361 TK.Frame.__init__(self, master, width=width, height=height)
362 self._root = self.winfo_toplevel()
363 self.width, self.height = width, height
364 self.canvwidth, self.canvheight = canvwidth, canvheight
365 self.bg = "white"
366 self._canvas = TK.Canvas(master, width=width, height=height,
367 bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
368 self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
369 orient=TK.HORIZONTAL)
370 self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
371 self._canvas.configure(xscrollcommand=self.hscroll.set,
372 yscrollcommand=self.vscroll.set)
373 self.rowconfigure(0, weight=1, minsize=0)
374 self.columnconfigure(0, weight=1, minsize=0)
375 self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
376 column=0, rowspan=1, columnspan=1, sticky='news')
377 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
378 column=1, rowspan=1, columnspan=1, sticky='news')
379 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
380 column=0, rowspan=1, columnspan=1, sticky='news')
381 self.reset()
382 self._root.bind('<Configure>', self.onResize)
383
384 def reset(self, canvwidth=None, canvheight=None, bg = None):
385 """Ajust canvas and scrollbars according to given canvas size."""
386 if canvwidth:
387 self.canvwidth = canvwidth
388 if canvheight:
389 self.canvheight = canvheight
390 if bg:
391 self.bg = bg
392 self._canvas.config(bg=bg,
393 scrollregion=(-self.canvwidth//2, -self.canvheight//2,
394 self.canvwidth//2, self.canvheight//2))
395 self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
396 self.canvwidth)
397 self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
398 self.canvheight)
399 self.adjustScrolls()
400
401
402 def adjustScrolls(self):
403 """ Adjust scrollbars according to window- and canvas-size.
404 """
405 cwidth = self._canvas.winfo_width()
406 cheight = self._canvas.winfo_height()
407 self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
408 self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
409 if cwidth < self.canvwidth or cheight < self.canvheight:
410 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
411 column=0, rowspan=1, columnspan=1, sticky='news')
412 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
413 column=1, rowspan=1, columnspan=1, sticky='news')
414 else:
415 self.hscroll.grid_forget()
416 self.vscroll.grid_forget()
417
418 def onResize(self, event):
419 """self-explanatory"""
420 self.adjustScrolls()
421
422 def bbox(self, *args):
423 """ 'forward' method, which canvas itself has inherited...
424 """
425 return self._canvas.bbox(*args)
426
427 def cget(self, *args, **kwargs):
428 """ 'forward' method, which canvas itself has inherited...
429 """
430 return self._canvas.cget(*args, **kwargs)
431
432 def config(self, *args, **kwargs):
433 """ 'forward' method, which canvas itself has inherited...
434 """
435 self._canvas.config(*args, **kwargs)
436
437 def bind(self, *args, **kwargs):
438 """ 'forward' method, which canvas itself has inherited...
439 """
440 self._canvas.bind(*args, **kwargs)
441
442 def unbind(self, *args, **kwargs):
443 """ 'forward' method, which canvas itself has inherited...
444 """
445 self._canvas.unbind(*args, **kwargs)
446
447 def focus_force(self):
448 """ 'forward' method, which canvas itself has inherited...
449 """
450 self._canvas.focus_force()
451
452__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
453
454
455class _Root(TK.Tk):
456 """Root class for Screen based on Tkinter."""
457 def __init__(self):
458 TK.Tk.__init__(self)
459
460 def setupcanvas(self, width, height, cwidth, cheight):
461 self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
462 self._canvas.pack(expand=1, fill="both")
463
464 def _getcanvas(self):
465 return self._canvas
466
467 def set_geometry(self, width, height, startx, starty):
468 self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
469
470 def ondestroy(self, destroy):
471 self.wm_protocol("WM_DELETE_WINDOW", destroy)
472
473 def win_width(self):
474 return self.winfo_screenwidth()
475
476 def win_height(self):
477 return self.winfo_screenheight()
478
479Canvas = TK.Canvas
480
481
482class TurtleScreenBase(object):
483 """Provide the basic graphics functionality.
484 Interface between Tkinter and turtle.py.
485
486 To port turtle.py to some different graphics toolkit
487 a corresponding TurtleScreenBase class has to be implemented.
488 """
489
490 @staticmethod
491 def _blankimage():
492 """return a blank image object
493 """
494 img = TK.PhotoImage(width=1, height=1)
495 img.blank()
496 return img
497
498 @staticmethod
499 def _image(filename):
500 """return an image object containing the
501 imagedata from a gif-file named filename.
502 """
503 return TK.PhotoImage(file=filename)
504
505 def __init__(self, cv):
506 self.cv = cv
507 if isinstance(cv, ScrolledCanvas):
508 w = self.cv.canvwidth
509 h = self.cv.canvheight
510 else: # expected: ordinary TK.Canvas
511 w = int(self.cv.cget("width"))
512 h = int(self.cv.cget("height"))
513 self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
514 self.canvwidth = w
515 self.canvheight = h
516 self.xscale = self.yscale = 1.0
517
518 def _createpoly(self):
519 """Create an invisible polygon item on canvas self.cv)
520 """
521 return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
522
523 def _drawpoly(self, polyitem, coordlist, fill=None,
524 outline=None, width=None, top=False):
525 """Configure polygonitem polyitem according to provided
526 arguments:
527 coordlist is sequence of coordinates
528 fill is filling color
529 outline is outline color
530 top is a boolean value, which specifies if polyitem
531 will be put on top of the canvas' displaylist so it
532 will not be covered by other items.
533 """
534 cl = []
535 for x, y in coordlist:
536 cl.append(x * self.xscale)
537 cl.append(-y * self.yscale)
538 self.cv.coords(polyitem, *cl)
539 if fill is not None:
540 self.cv.itemconfigure(polyitem, fill=fill)
541 if outline is not None:
542 self.cv.itemconfigure(polyitem, outline=outline)
543 if width is not None:
544 self.cv.itemconfigure(polyitem, width=width)
545 if top:
546 self.cv.tag_raise(polyitem)
547
548 def _createline(self):
549 """Create an invisible line item on canvas self.cv)
550 """
551 return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
552 capstyle = TK.ROUND)
553
554 def _drawline(self, lineitem, coordlist=None,
555 fill=None, width=None, top=False):
556 """Configure lineitem according to provided arguments:
557 coordlist is sequence of coordinates
558 fill is drawing color
559 width is width of drawn line.
560 top is a boolean value, which specifies if polyitem
561 will be put on top of the canvas' displaylist so it
562 will not be covered by other items.
563 """
564 if coordlist is not None:
565 cl = []
566 for x, y in coordlist:
567 cl.append(x * self.xscale)
568 cl.append(-y * self.yscale)
569 self.cv.coords(lineitem, *cl)
570 if fill is not None:
571 self.cv.itemconfigure(lineitem, fill=fill)
572 if width is not None:
573 self.cv.itemconfigure(lineitem, width=width)
574 if top:
575 self.cv.tag_raise(lineitem)
576
577 def _delete(self, item):
578 """Delete graphics item from canvas.
579 If item is"all" delete all graphics items.
580 """
581 self.cv.delete(item)
582
583 def _update(self):
584 """Redraw graphics items on canvas
585 """
586 self.cv.update()
587
588 def _delay(self, delay):
589 """Delay subsequent canvas actions for delay ms."""
590 self.cv.after(delay)
591
592 def _iscolorstring(self, color):
593 """Check if the string color is a legal Tkinter color string.
594 """
595 try:
596 rgb = self.cv.winfo_rgb(color)
597 ok = True
598 except TK.TclError:
599 ok = False
600 return ok
601
602 def _bgcolor(self, color=None):
603 """Set canvas' backgroundcolor if color is not None,
604 else return backgroundcolor."""
605 if color is not None:
606 self.cv.config(bg = color)
607 self._update()
608 else:
609 return self.cv.cget("bg")
610
611 def _write(self, pos, txt, align, font, pencolor):
612 """Write txt at pos in canvas with specified font
613 and color.
614 Return text item and x-coord of right bottom corner
615 of text's bounding box."""
616 x, y = pos
617 x = x * self.xscale
618 y = y * self.yscale
619 anchor = {"left":"sw", "center":"s", "right":"se" }
620 item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
621 fill = pencolor, font = font)
622 x0, y0, x1, y1 = self.cv.bbox(item)
623 self.cv.update()
624 return item, x1-1
625
626## def _dot(self, pos, size, color):
627## """may be implemented for some other graphics toolkit"""
628
629 def _onclick(self, item, fun, num=1, add=None):
630 """Bind fun to mouse-click event on turtle.
631 fun must be a function with two arguments, the coordinates
632 of the clicked point on the canvas.
633 num, the number of the mouse-button defaults to 1
634 """
635 if fun is None:
636 self.cv.tag_unbind(item, "<Button-%s>" % num)
637 else:
638 def eventfun(event):
639 x, y = (self.cv.canvasx(event.x)/self.xscale,
640 -self.cv.canvasy(event.y)/self.yscale)
641 fun(x, y)
642 self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
643
644 def _onrelease(self, item, fun, num=1, add=None):
645 """Bind fun to mouse-button-release event on turtle.
646 fun must be a function with two arguments, the coordinates
647 of the point on the canvas where mouse button is released.
648 num, the number of the mouse-button defaults to 1
649
650 If a turtle is clicked, first _onclick-event will be performed,
651 then _onscreensclick-event.
652 """
653 if fun is None:
654 self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
655 else:
656 def eventfun(event):
657 x, y = (self.cv.canvasx(event.x)/self.xscale,
658 -self.cv.canvasy(event.y)/self.yscale)
659 fun(x, y)
660 self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
661 eventfun, add)
662
663 def _ondrag(self, item, fun, num=1, add=None):
664 """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
665 fun must be a function with two arguments, the coordinates of the
666 actual mouse position on the canvas.
667 num, the number of the mouse-button defaults to 1
668
669 Every sequence of mouse-move-events on a turtle is preceded by a
670 mouse-click event on that turtle.
671 """
672 if fun is None:
673 self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
674 else:
675 def eventfun(event):
676 try:
677 x, y = (self.cv.canvasx(event.x)/self.xscale,
678 -self.cv.canvasy(event.y)/self.yscale)
679 fun(x, y)
680 except:
681 pass
682 self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
683
684 def _onscreenclick(self, fun, num=1, add=None):
685 """Bind fun to mouse-click event on canvas.
686 fun must be a function with two arguments, the coordinates
687 of the clicked point on the canvas.
688 num, the number of the mouse-button defaults to 1
689
690 If a turtle is clicked, first _onclick-event will be performed,
691 then _onscreensclick-event.
692 """
693 if fun is None:
694 self.cv.unbind("<Button-%s>" % num)
695 else:
696 def eventfun(event):
697 x, y = (self.cv.canvasx(event.x)/self.xscale,
698 -self.cv.canvasy(event.y)/self.yscale)
699 fun(x, y)
700 self.cv.bind("<Button-%s>" % num, eventfun, add)
701
702 def _onkey(self, fun, key):
703 """Bind fun to key-release event of key.
704 Canvas must have focus. See method listen
705 """
706 if fun is None:
707 self.cv.unbind("<KeyRelease-%s>" % key, None)
708 else:
709 def eventfun(event):
710 fun()
711 self.cv.bind("<KeyRelease-%s>" % key, eventfun)
712
713 def _listen(self):
714 """Set focus on canvas (in order to collect key-events)
715 """
716 self.cv.focus_force()
717
718 def _ontimer(self, fun, t):
719 """Install a timer, which calls fun after t milliseconds.
720 """
721 if t == 0:
722 self.cv.after_idle(fun)
723 else:
724 self.cv.after(t, fun)
725
726 def _createimage(self, image):
727 """Create and return image item on canvas.
728 """
729 return self.cv.create_image(0, 0, image=image)
730
731 def _drawimage(self, item, (x, y), image):
732 """Configure image item as to draw image object
733 at position (x,y) on canvas)
734 """
735 self.cv.coords(item, (x, -y))
736 self.cv.itemconfig(item, image=image)
737
738 def _setbgpic(self, item, image):
739 """Configure image item as to draw image object
740 at center of canvas. Set item to the first item
741 in the displaylist, so it will be drawn below
742 any other item ."""
743 self.cv.itemconfig(item, image=image)
744 self.cv.tag_lower(item)
745
746 def _type(self, item):
747 """Return 'line' or 'polygon' or 'image' depending on
748 type of item.
749 """
750 return self.cv.type(item)
751
752 def _pointlist(self, item):
753 """returns list of coordinate-pairs of points of item
754 Example (for insiders):
755 >>> from turtle import *
756 >>> getscreen()._pointlist(getturtle().turtle._item)
757 [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
758 (9.9999999999999982, 0.0)]
759 >>> """
760 cl = self.cv.coords(item)
761 pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
762 return pl
763
764 def _setscrollregion(self, srx1, sry1, srx2, sry2):
765 self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
766
767 def _rescale(self, xscalefactor, yscalefactor):
768 items = self.cv.find_all()
769 for item in items:
770 coordinates = self.cv.coords(item)
771 newcoordlist = []
772 while coordinates:
773 x, y = coordinates[:2]
774 newcoordlist.append(x * xscalefactor)
775 newcoordlist.append(y * yscalefactor)
776 coordinates = coordinates[2:]
777 self.cv.coords(item, *newcoordlist)
778
779 def _resize(self, canvwidth=None, canvheight=None, bg=None):
780 """Resize the canvas, the turtles are drawing on. Does
781 not alter the drawing window.
782 """
783 # needs amendment
784 if not isinstance(self.cv, ScrolledCanvas):
785 return self.canvwidth, self.canvheight
786 if canvwidth is None and canvheight is None and bg is None:
787 return self.cv.canvwidth, self.cv.canvheight
788 if canvwidth is not None:
789 self.canvwidth = canvwidth
790 if canvheight is not None:
791 self.canvheight = canvheight
792 self.cv.reset(canvwidth, canvheight, bg)
793
794 def _window_size(self):
795 """ Return the width and height of the turtle window.
796 """
797 width = self.cv.winfo_width()
798 if width <= 1: # the window isn't managed by a geometry manager
799 width = self.cv['width']
800 height = self.cv.winfo_height()
801 if height <= 1: # the window isn't managed by a geometry manager
802 height = self.cv['height']
803 return width, height
804
805
806##############################################################################
807### End of Tkinter - interface ###
808##############################################################################
809
810
811class Terminator (Exception):
812 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
813
814 Thus stops execution of turtle graphics script. Main purpose: use in
815 in the Demo-Viewer turtle.Demo.py.
816 """
817 pass
818
819
820class TurtleGraphicsError(Exception):
821 """Some TurtleGraphics Error
822 """
823
824
825class Shape(object):
826 """Data structure modeling shapes.
827
828 attribute _type is one of "polygon", "image", "compound"
829 attribute _data is - depending on _type a poygon-tuple,
830 an image or a list constructed using the addcomponent method.
831 """
832 def __init__(self, type_, data=None):
833 self._type = type_
834 if type_ == "polygon":
835 if isinstance(data, list):
836 data = tuple(data)
837 elif type_ == "image":
838 if isinstance(data, str):
839 if data.lower().endswith(".gif") and isfile(data):
840 data = TurtleScreen._image(data)
841 # else data assumed to be Photoimage
842 elif type_ == "compound":
843 data = []
844 else:
845 raise TurtleGraphicsError("There is no shape type %s" % type_)
846 self._data = data
847
848 def addcomponent(self, poly, fill, outline=None):
849 """Add component to a shape of type compound.
850
851 Arguments: poly is a polygon, i. e. a tuple of number pairs.
852 fill is the fillcolor of the component,
853 outline is the outline color of the component.
854
855 call (for a Shapeobject namend s):
856 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Georg Brandl33cece02008-05-20 06:58:21 +0000857
858 Example:
Martin v. Löwis87184592008-06-04 06:29:55 +0000859 >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
860 >>> s = Shape("compound")
861 >>> s.addcomponent(poly, "red", "blue")
862 ### .. add more components and then use register_shape()
Georg Brandl33cece02008-05-20 06:58:21 +0000863 """
Martin v. Löwis87184592008-06-04 06:29:55 +0000864 if self._type != "compound":
865 raise TurtleGraphicsError("Cannot add component to %s Shape"
866 % self._type)
867 if outline is None:
868 outline = fill
869 self._data.append([poly, fill, outline])
Georg Brandl33cece02008-05-20 06:58:21 +0000870
Georg Brandl33cece02008-05-20 06:58:21 +0000871
Martin v. Löwis87184592008-06-04 06:29:55 +0000872class Tbuffer(object):
873 """Ring buffer used as undobuffer for RawTurtle objects."""
874 def __init__(self, bufsize=10):
875 self.bufsize = bufsize
876 self.buffer = [[None]] * bufsize
877 self.ptr = -1
878 self.cumulate = False
879 def reset(self, bufsize=None):
880 if bufsize is None:
881 for i in range(self.bufsize):
882 self.buffer[i] = [None]
883 else:
884 self.bufsize = bufsize
885 self.buffer = [[None]] * bufsize
886 self.ptr = -1
887 def push(self, item):
888 if self.bufsize > 0:
889 if not self.cumulate:
890 self.ptr = (self.ptr + 1) % self.bufsize
891 self.buffer[self.ptr] = item
892 else:
893 self.buffer[self.ptr].append(item)
894 def pop(self):
895 if self.bufsize > 0:
896 item = self.buffer[self.ptr]
897 if item is None:
898 return None
899 else:
900 self.buffer[self.ptr] = [None]
901 self.ptr = (self.ptr - 1) % self.bufsize
902 return (item)
903 def nr_of_items(self):
904 return self.bufsize - self.buffer.count([None])
905 def __repr__(self):
906 return str(self.buffer) + " " + str(self.ptr)
907
908
909
910class TurtleScreen(TurtleScreenBase):
911 """Provides screen oriented methods like setbg etc.
912
913 Only relies upon the methods of TurtleScreenBase and NOT
914 upon components of the underlying graphics toolkit -
915 which is Tkinter in this case.
916 """
917# _STANDARD_DELAY = 5
918 _RUNNING = True
919
920 def __init__(self, cv, mode=_CFG["mode"],
921 colormode=_CFG["colormode"], delay=_CFG["delay"]):
922 self._shapes = {
923 "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
924 "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
925 (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
926 (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
927 (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
928 (2,14))),
929 "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
930 (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
931 (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
932 (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
933 (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
934 (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
935 "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
936 (-10,-10))),
937 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
938 (-10,-5.77))),
939 "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
940 "blank" : Shape("image", self._blankimage())
941 }
942
943 self._bgpics = {"nopic" : ""}
944
945 TurtleScreenBase.__init__(self, cv)
946 self._mode = mode
947 self._delayvalue = delay
948 self._colormode = _CFG["colormode"]
949 self._keys = []
950 self.clear()
951
952 def clear(self):
953 """Delete all drawings and all turtles from the TurtleScreen.
954
955 Reset empty TurtleScreen to it's initial state: white background,
956 no backgroundimage, no eventbindings and tracing on.
957
958 No argument.
959
960 Example (for a TurtleScreen instance named screen):
961 screen.clear()
962
963 Note: this method is not available as function.
Georg Brandl33cece02008-05-20 06:58:21 +0000964 """
Martin v. Löwis87184592008-06-04 06:29:55 +0000965 self._delayvalue = _CFG["delay"]
966 self._colormode = _CFG["colormode"]
967 self._delete("all")
968 self._bgpic = self._createimage("")
969 self._bgpicname = "nopic"
970 self._tracing = 1
971 self._updatecounter = 0
972 self._turtles = []
973 self.bgcolor("white")
974 for btn in 1, 2, 3:
975 self.onclick(None, btn)
976 for key in self._keys[:]:
977 self.onkey(None, key)
978 Turtle._pen = None
Georg Brandl33cece02008-05-20 06:58:21 +0000979
Martin v. Löwis87184592008-06-04 06:29:55 +0000980 def mode(self, mode=None):
981 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Georg Brandl33cece02008-05-20 06:58:21 +0000982
Martin v. Löwis87184592008-06-04 06:29:55 +0000983 Optional argument:
984 mode -- on of the strings 'standard', 'logo' or 'world'
985
986 Mode 'standard' is compatible with turtle.py.
987 Mode 'logo' is compatible with most Logo-Turtle-Graphics.
988 Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
989 this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
990 If mode is not given, return the current mode.
991
992 Mode Initial turtle heading positive angles
993 ------------|-------------------------|-------------------
994 'standard' to the right (east) counterclockwise
995 'logo' upward (north) clockwise
996
997 Examples:
998 >>> mode('logo') # resets turtle heading to north
999 >>> mode()
1000 'logo'
Georg Brandl33cece02008-05-20 06:58:21 +00001001 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001002 if mode == None:
1003 return self._mode
1004 mode = mode.lower()
1005 if mode not in ["standard", "logo", "world"]:
1006 raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1007 self._mode = mode
1008 if mode in ["standard", "logo"]:
1009 self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1010 self.canvwidth//2, self.canvheight//2)
1011 self.xscale = self.yscale = 1.0
1012 self.reset()
Georg Brandl33cece02008-05-20 06:58:21 +00001013
Martin v. Löwis87184592008-06-04 06:29:55 +00001014 def setworldcoordinates(self, llx, lly, urx, ury):
1015 """Set up a user defined coordinate-system.
Georg Brandl33cece02008-05-20 06:58:21 +00001016
Martin v. Löwis87184592008-06-04 06:29:55 +00001017 Arguments:
1018 llx -- a number, x-coordinate of lower left corner of canvas
1019 lly -- a number, y-coordinate of lower left corner of canvas
1020 urx -- a number, x-coordinate of upper right corner of canvas
1021 ury -- a number, y-coordinate of upper right corner of canvas
1022
1023 Set up user coodinat-system and switch to mode 'world' if necessary.
1024 This performs a screen.reset. If mode 'world' is already active,
1025 all drawings are redrawn according to the new coordinates.
1026
1027 But ATTENTION: in user-defined coordinatesystems angles may appear
1028 distorted. (see Screen.mode())
1029
1030 Example (for a TurtleScreen instance named screen):
1031 >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1032 >>> for _ in range(36):
1033 left(10)
1034 forward(0.5)
Georg Brandl33cece02008-05-20 06:58:21 +00001035 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001036 if self.mode() != "world":
1037 self.mode("world")
1038 xspan = float(urx - llx)
1039 yspan = float(ury - lly)
1040 wx, wy = self._window_size()
1041 self.screensize(wx-20, wy-20)
1042 oldxscale, oldyscale = self.xscale, self.yscale
1043 self.xscale = self.canvwidth / xspan
1044 self.yscale = self.canvheight / yspan
1045 srx1 = llx * self.xscale
1046 sry1 = -ury * self.yscale
1047 srx2 = self.canvwidth + srx1
1048 sry2 = self.canvheight + sry1
1049 self._setscrollregion(srx1, sry1, srx2, sry2)
1050 self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1051 self.update()
Georg Brandl33cece02008-05-20 06:58:21 +00001052
Martin v. Löwis87184592008-06-04 06:29:55 +00001053 def register_shape(self, name, shape=None):
1054 """Adds a turtle shape to TurtleScreen's shapelist.
Georg Brandl33cece02008-05-20 06:58:21 +00001055
Martin v. Löwis87184592008-06-04 06:29:55 +00001056 Arguments:
1057 (1) name is the name of a gif-file and shape is None.
1058 Installs the corresponding image shape.
1059 !! Image-shapes DO NOT rotate when turning the turtle,
1060 !! so they do not display the heading of the turtle!
1061 (2) name is an arbitrary string and shape is a tuple
1062 of pairs of coordinates. Installs the corresponding
1063 polygon shape
1064 (3) name is an arbitrary string and shape is a
1065 (compound) Shape object. Installs the corresponding
1066 compound shape.
1067 To use a shape, you have to issue the command shape(shapename).
Georg Brandl33cece02008-05-20 06:58:21 +00001068
Martin v. Löwis87184592008-06-04 06:29:55 +00001069 call: register_shape("turtle.gif")
1070 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1071
1072 Example (for a TurtleScreen instance named screen):
1073 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1074
Georg Brandl33cece02008-05-20 06:58:21 +00001075 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001076 if shape is None:
1077 # image
1078 if name.lower().endswith(".gif"):
1079 shape = Shape("image", self._image(name))
1080 else:
1081 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1082 + "Use help(register_shape)" )
1083 elif isinstance(shape, tuple):
1084 shape = Shape("polygon", shape)
1085 ## else shape assumed to be Shape-instance
1086 self._shapes[name] = shape
1087 # print "shape added:" , self._shapes
Georg Brandl33cece02008-05-20 06:58:21 +00001088
Martin v. Löwis87184592008-06-04 06:29:55 +00001089 def _colorstr(self, color):
1090 """Return color string corresponding to args.
Georg Brandl33cece02008-05-20 06:58:21 +00001091
Martin v. Löwis87184592008-06-04 06:29:55 +00001092 Argument may be a string or a tuple of three
1093 numbers corresponding to actual colormode,
1094 i.e. in the range 0<=n<=colormode.
1095
1096 If the argument doesn't represent a color,
1097 an error is raised.
Georg Brandl33cece02008-05-20 06:58:21 +00001098 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001099 if len(color) == 1:
1100 color = color[0]
1101 if isinstance(color, str):
1102 if self._iscolorstring(color) or color == "":
1103 return color
1104 else:
1105 raise TurtleGraphicsError("bad color string: %s" % str(color))
1106 try:
1107 r, g, b = color
1108 except:
1109 raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1110 if self._colormode == 1.0:
1111 r, g, b = [round(255.0*x) for x in (r, g, b)]
1112 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1113 raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1114 return "#%02x%02x%02x" % (r, g, b)
Georg Brandl33cece02008-05-20 06:58:21 +00001115
Martin v. Löwis87184592008-06-04 06:29:55 +00001116 def _color(self, cstr):
1117 if not cstr.startswith("#"):
1118 return cstr
1119 if len(cstr) == 7:
1120 cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1121 elif len(cstr) == 4:
1122 cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1123 else:
1124 raise TurtleGraphicsError("bad colorstring: %s" % cstr)
1125 return tuple([c * self._colormode/255 for c in cl])
Georg Brandl33cece02008-05-20 06:58:21 +00001126
Martin v. Löwis87184592008-06-04 06:29:55 +00001127 def colormode(self, cmode=None):
1128 """Return the colormode or set it to 1.0 or 255.
Georg Brandl33cece02008-05-20 06:58:21 +00001129
Martin v. Löwis87184592008-06-04 06:29:55 +00001130 Optional argument:
1131 cmode -- one of the values 1.0 or 255
Georg Brandl33cece02008-05-20 06:58:21 +00001132
Martin v. Löwis87184592008-06-04 06:29:55 +00001133 r, g, b values of colortriples have to be in range 0..cmode.
1134
1135 Example (for a TurtleScreen instance named screen):
1136 >>> screen.colormode()
1137 1.0
1138 >>> screen.colormode(255)
1139 >>> turtle.pencolor(240,160,80)
Georg Brandl33cece02008-05-20 06:58:21 +00001140 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001141 if cmode is None:
1142 return self._colormode
1143 if cmode == 1.0:
1144 self._colormode = float(cmode)
1145 elif cmode == 255:
1146 self._colormode = int(cmode)
1147
1148 def reset(self):
1149 """Reset all Turtles on the Screen to their initial state.
1150
1151 No argument.
1152
1153 Example (for a TurtleScreen instance named screen):
1154 >>> screen.reset()
1155 """
1156 for turtle in self._turtles:
1157 turtle._setmode(self._mode)
1158 turtle.reset()
1159
1160 def turtles(self):
1161 """Return the list of turtles on the screen.
1162
1163 Example (for a TurtleScreen instance named screen):
1164 >>> screen.turtles()
1165 [<turtle.Turtle object at 0x00E11FB0>]
1166 """
1167 return self._turtles
1168
1169 def bgcolor(self, *args):
1170 """Set or return backgroundcolor of the TurtleScreen.
1171
1172 Arguments (if given): a color string or three numbers
1173 in the range 0..colormode or a 3-tuple of such numbers.
1174
1175 Example (for a TurtleScreen instance named screen):
1176 >>> screen.bgcolor("orange")
1177 >>> screen.bgcolor()
1178 'orange'
1179 >>> screen.bgcolor(0.5,0,0.5)
1180 >>> screen.bgcolor()
1181 '#800080'
1182 """
1183 if args:
1184 color = self._colorstr(args)
1185 else:
1186 color = None
1187 color = self._bgcolor(color)
1188 if color is not None:
1189 color = self._color(color)
1190 return color
1191
1192 def tracer(self, n=None, delay=None):
1193 """Turns turtle animation on/off and set delay for update drawings.
1194
1195 Optional arguments:
1196 n -- nonnegative integer
1197 delay -- nonnegative integer
1198
1199 If n is given, only each n-th regular screen update is really performed.
1200 (Can be used to accelerate the drawing of complex graphics.)
1201 Second arguments sets delay value (see RawTurtle.delay())
1202
1203 Example (for a TurtleScreen instance named screen):
1204 >>> screen.tracer(8, 25)
1205 >>> dist = 2
1206 >>> for i in range(200):
1207 fd(dist)
1208 rt(90)
1209 dist += 2
1210 """
1211 if n is None:
1212 return self._tracing
1213 self._tracing = int(n)
1214 self._updatecounter = 0
1215 if delay is not None:
1216 self._delayvalue = int(delay)
1217 if self._tracing:
1218 self.update()
1219
1220 def delay(self, delay=None):
1221 """ Return or set the drawing delay in milliseconds.
1222
1223 Optional argument:
1224 delay -- positive integer
1225
1226 Example (for a TurtleScreen instance named screen):
1227 >>> screen.delay(15)
1228 >>> screen.delay()
1229 15
1230 """
1231 if delay is None:
1232 return self._delayvalue
1233 self._delayvalue = int(delay)
1234
1235 def _incrementudc(self):
1236 "Increment upadate counter."""
1237 if not TurtleScreen._RUNNING:
1238 TurtleScreen._RUNNNING = True
1239 raise Terminator
1240 if self._tracing > 0:
1241 self._updatecounter += 1
1242 self._updatecounter %= self._tracing
1243
1244 def update(self):
1245 """Perform a TurtleScreen update.
1246 """
1247 for t in self.turtles():
1248 t._update_data()
1249 t._drawturtle()
1250 self._update()
Georg Brandl33cece02008-05-20 06:58:21 +00001251
1252 def window_width(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00001253 """ Return the width of the turtle window.
Georg Brandl33cece02008-05-20 06:58:21 +00001254
Martin v. Löwis87184592008-06-04 06:29:55 +00001255 Example (for a TurtleScreen instance named screen):
1256 >>> screen.window_width()
Georg Brandl33cece02008-05-20 06:58:21 +00001257 640
1258 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001259 return self._window_size()[0]
Georg Brandl33cece02008-05-20 06:58:21 +00001260
1261 def window_height(self):
1262 """ Return the height of the turtle window.
1263
Martin v. Löwis87184592008-06-04 06:29:55 +00001264 Example (for a TurtleScreen instance named screen):
1265 >>> screen.window_height()
1266 480
Georg Brandl33cece02008-05-20 06:58:21 +00001267 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001268 return self._window_size()[1]
Georg Brandl33cece02008-05-20 06:58:21 +00001269
Martin v. Löwis87184592008-06-04 06:29:55 +00001270 def getcanvas(self):
1271 """Return the Canvas of this TurtleScreen.
Georg Brandl33cece02008-05-20 06:58:21 +00001272
Martin v. Löwis87184592008-06-04 06:29:55 +00001273 No argument.
1274
1275 Example (for a Screen instance named screen):
1276 >>> cv = screen.getcanvas()
1277 >>> cv
1278 <turtle.ScrolledCanvas instance at 0x010742D8>
Georg Brandl33cece02008-05-20 06:58:21 +00001279 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001280 return self.cv
Georg Brandl33cece02008-05-20 06:58:21 +00001281
Martin v. Löwis87184592008-06-04 06:29:55 +00001282 def getshapes(self):
1283 """Return a list of names of all currently available turtle shapes.
Georg Brandl33cece02008-05-20 06:58:21 +00001284
Martin v. Löwis87184592008-06-04 06:29:55 +00001285 No argument.
1286
1287 Example (for a TurtleScreen instance named screen):
1288 >>> screen.getshapes()
1289 ['arrow', 'blank', 'circle', ... , 'turtle']
1290 """
1291 return sorted(self._shapes.keys())
1292
1293 def onclick(self, fun, btn=1, add=None):
1294 """Bind fun to mouse-click event on canvas.
1295
1296 Arguments:
1297 fun -- a function with two arguments, the coordinates of the
1298 clicked point on the canvas.
1299 num -- the number of the mouse-button, defaults to 1
1300
1301 Example (for a TurtleScreen instance named screen
1302 and a Turtle instance named turtle):
1303
1304 >>> screen.onclick(turtle.goto)
1305
1306 ### Subsequently clicking into the TurtleScreen will
1307 ### make the turtle move to the clicked point.
1308 >>> screen.onclick(None)
1309
1310 ### event-binding will be removed
1311 """
1312 self._onscreenclick(fun, btn, add)
1313
1314 def onkey(self, fun, key):
1315 """Bind fun to key-release event of key.
1316
1317 Arguments:
1318 fun -- a function with no arguments
1319 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1320
1321 In order ro be able to register key-events, TurtleScreen
1322 must have focus. (See method listen.)
1323
1324 Example (for a TurtleScreen instance named screen
1325 and a Turtle instance named turtle):
1326
1327 >>> def f():
1328 fd(50)
1329 lt(60)
1330
1331
1332 >>> screen.onkey(f, "Up")
1333 >>> screen.listen()
1334
1335 ### Subsequently the turtle can be moved by
1336 ### repeatedly pressing the up-arrow key,
1337 ### consequently drawing a hexagon
1338 """
1339 if fun == None:
1340 self._keys.remove(key)
1341 elif key not in self._keys:
1342 self._keys.append(key)
1343 self._onkey(fun, key)
1344
1345 def listen(self, xdummy=None, ydummy=None):
1346 """Set focus on TurtleScreen (in order to collect key-events)
1347
1348 No arguments.
1349 Dummy arguments are provided in order
1350 to be able to pass listen to the onclick method.
1351
1352 Example (for a TurtleScreen instance named screen):
1353 >>> screen.listen()
1354 """
1355 self._listen()
1356
1357 def ontimer(self, fun, t=0):
1358 """Install a timer, which calls fun after t milliseconds.
1359
1360 Arguments:
1361 fun -- a function with no arguments.
1362 t -- a number >= 0
1363
1364 Example (for a TurtleScreen instance named screen):
1365
1366 >>> running = True
1367 >>> def f():
1368 if running:
1369 fd(50)
1370 lt(60)
1371 screen.ontimer(f, 250)
1372
1373 >>> f() ### makes the turtle marching around
1374 >>> running = False
1375 """
1376 self._ontimer(fun, t)
1377
1378 def bgpic(self, picname=None):
1379 """Set background image or return name of current backgroundimage.
1380
1381 Optional argument:
1382 picname -- a string, name of a gif-file or "nopic".
1383
1384 If picname is a filename, set the corresponing image as background.
1385 If picname is "nopic", delete backgroundimage, if present.
1386 If picname is None, return the filename of the current backgroundimage.
1387
1388 Example (for a TurtleScreen instance named screen):
1389 >>> screen.bgpic()
1390 'nopic'
1391 >>> screen.bgpic("landscape.gif")
1392 >>> screen.bgpic()
1393 'landscape.gif'
1394 """
1395 if picname is None:
1396 return self._bgpicname
1397 if picname not in self._bgpics:
1398 self._bgpics[picname] = self._image(picname)
1399 self._setbgpic(self._bgpic, self._bgpics[picname])
1400 self._bgpicname = picname
1401
1402 def screensize(self, canvwidth=None, canvheight=None, bg=None):
1403 """Resize the canvas, the turtles are drawing on.
1404
1405 Optional arguments:
1406 canvwidth -- positive integer, new width of canvas in pixels
1407 canvheight -- positive integer, new height of canvas in pixels
1408 bg -- colorstring or color-tupel, new backgroundcolor
1409 If no arguments are given, return current (canvaswidth, canvasheight)
1410
1411 Do not alter the drawing window. To observe hidden parts of
1412 the canvas use the scrollbars. (Can make visible those parts
1413 of a drawing, which were outside the canvas before!)
1414
1415 Example (for a Turtle instance named turtle):
1416 >>> turtle.screensize(2000,1500)
1417 ### e. g. to search for an erroneously escaped turtle ;-)
1418 """
1419 return self._resize(canvwidth, canvheight, bg)
1420
1421 onscreenclick = onclick
1422 resetscreen = reset
1423 clearscreen = clear
1424 addshape = register_shape
1425
1426class TNavigator(object):
1427 """Navigation part of the RawTurtle.
1428 Implements methods for turtle movement.
1429 """
1430 START_ORIENTATION = {
1431 "standard": Vec2D(1.0, 0.0),
1432 "world" : Vec2D(1.0, 0.0),
1433 "logo" : Vec2D(0.0, 1.0) }
1434 DEFAULT_MODE = "standard"
1435 DEFAULT_ANGLEOFFSET = 0
1436 DEFAULT_ANGLEORIENT = 1
1437
1438 def __init__(self, mode=DEFAULT_MODE):
1439 self._angleOffset = self.DEFAULT_ANGLEOFFSET
1440 self._angleOrient = self.DEFAULT_ANGLEORIENT
1441 self._mode = mode
1442 self.undobuffer = None
1443 self.degrees()
1444 self._mode = None
1445 self._setmode(mode)
1446 TNavigator.reset(self)
1447
1448 def reset(self):
1449 """reset turtle to its initial values
1450
1451 Will be overwritten by parent class
1452 """
1453 self._position = Vec2D(0.0, 0.0)
1454 self._orient = TNavigator.START_ORIENTATION[self._mode]
1455
1456 def _setmode(self, mode=None):
1457 """Set turtle-mode to 'standard', 'world' or 'logo'.
1458 """
1459 if mode == None:
1460 return self._mode
1461 if mode not in ["standard", "logo", "world"]:
1462 return
1463 self._mode = mode
1464 if mode in ["standard", "world"]:
1465 self._angleOffset = 0
1466 self._angleOrient = 1
1467 else: # mode == "logo":
1468 self._angleOffset = self._fullcircle/4.
1469 self._angleOrient = -1
1470
1471 def _setDegreesPerAU(self, fullcircle):
1472 """Helper function for degrees() and radians()"""
1473 self._fullcircle = fullcircle
1474 self._degreesPerAU = 360/fullcircle
1475 if self._mode == "standard":
1476 self._angleOffset = 0
1477 else:
1478 self._angleOffset = fullcircle/4.
1479
1480 def degrees(self, fullcircle=360.0):
1481 """ Set angle measurement units to degrees.
1482
1483 Optional argument:
1484 fullcircle - a number
1485
1486 Set angle measurement units, i. e. set number
1487 of 'degrees' for a full circle. Dafault value is
1488 360 degrees.
1489
1490 Example (for a Turtle instance named turtle):
1491 >>> turtle.left(90)
1492 >>> turtle.heading()
1493 90
1494 >>> turtle.degrees(400.0) # angle measurement in gon
1495 >>> turtle.heading()
1496 100
1497
1498 """
1499 self._setDegreesPerAU(fullcircle)
1500
1501 def radians(self):
1502 """ Set the angle measurement units to radians.
1503
1504 No arguments.
1505
1506 Example (for a Turtle instance named turtle):
1507 >>> turtle.heading()
1508 90
1509 >>> turtle.radians()
1510 >>> turtle.heading()
1511 1.5707963267948966
1512 """
1513 self._setDegreesPerAU(2*math.pi)
1514
1515 def _go(self, distance):
1516 """move turtle forward by specified distance"""
1517 ende = self._position + self._orient * distance
1518 self._goto(ende)
1519
1520 def _rotate(self, angle):
1521 """Turn turtle counterclockwise by specified angle if angle > 0."""
1522 angle *= self._degreesPerAU
1523 self._orient = self._orient.rotate(angle)
1524
1525 def _goto(self, end):
1526 """move turtle to position end."""
1527 self._position = end
1528
1529 def forward(self, distance):
1530 """Move the turtle forward by the specified distance.
1531
1532 Aliases: forward | fd
1533
1534 Argument:
1535 distance -- a number (integer or float)
1536
1537 Move the turtle forward by the specified distance, in the direction
1538 the turtle is headed.
1539
1540 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001541 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001542 (0.00, 0.00)
1543 >>> turtle.forward(25)
1544 >>> turtle.position()
1545 (25.00,0.00)
1546 >>> turtle.forward(-75)
1547 >>> turtle.position()
1548 (-50.00,0.00)
1549 """
1550 self._go(distance)
1551
1552 def back(self, distance):
1553 """Move the turtle backward by distance.
1554
1555 Aliases: back | backward | bk
1556
1557 Argument:
1558 distance -- a number
1559
1560 Move the turtle backward by distance ,opposite to the direction the
1561 turtle is headed. Do not change the turtle's heading.
1562
1563 Example (for a Turtle instance named turtle):
1564 >>> turtle.position()
1565 (0.00, 0.00)
1566 >>> turtle.backward(30)
1567 >>> turtle.position()
1568 (-30.00, 0.00)
1569 """
1570 self._go(-distance)
1571
1572 def right(self, angle):
1573 """Turn turtle right by angle units.
1574
1575 Aliases: right | rt
1576
1577 Argument:
1578 angle -- a number (integer or float)
1579
1580 Turn turtle right by angle units. (Units are by default degrees,
1581 but can be set via the degrees() and radians() functions.)
1582 Angle orientation depends on mode. (See this.)
1583
1584 Example (for a Turtle instance named turtle):
1585 >>> turtle.heading()
1586 22.0
1587 >>> turtle.right(45)
1588 >>> turtle.heading()
1589 337.0
1590 """
1591 self._rotate(-angle)
1592
1593 def left(self, angle):
1594 """Turn turtle left by angle units.
1595
1596 Aliases: left | lt
1597
1598 Argument:
1599 angle -- a number (integer or float)
1600
1601 Turn turtle left by angle units. (Units are by default degrees,
1602 but can be set via the degrees() and radians() functions.)
1603 Angle orientation depends on mode. (See this.)
1604
1605 Example (for a Turtle instance named turtle):
1606 >>> turtle.heading()
1607 22.0
1608 >>> turtle.left(45)
1609 >>> turtle.heading()
1610 67.0
1611 """
1612 self._rotate(angle)
1613
1614 def pos(self):
1615 """Return the turtle's current location (x,y), as a Vec2D-vector.
1616
1617 Aliases: pos | position
1618
1619 No arguments.
1620
1621 Example (for a Turtle instance named turtle):
1622 >>> turtle.pos()
1623 (0.00, 240.00)
1624 """
1625 return self._position
1626
1627 def xcor(self):
1628 """ Return the turtle's x coordinate.
1629
1630 No arguments.
1631
1632 Example (for a Turtle instance named turtle):
1633 >>> reset()
1634 >>> turtle.left(60)
1635 >>> turtle.forward(100)
1636 >>> print turtle.xcor()
1637 50.0
1638 """
1639 return self._position[0]
1640
1641 def ycor(self):
1642 """ Return the turtle's y coordinate
1643 ---
1644 No arguments.
1645
1646 Example (for a Turtle instance named turtle):
1647 >>> reset()
1648 >>> turtle.left(60)
1649 >>> turtle.forward(100)
1650 >>> print turtle.ycor()
1651 86.6025403784
1652 """
1653 return self._position[1]
1654
1655
1656 def goto(self, x, y=None):
1657 """Move turtle to an absolute position.
1658
1659 Aliases: setpos | setposition | goto:
1660
1661 Arguments:
1662 x -- a number or a pair/vector of numbers
1663 y -- a number None
1664
1665 call: goto(x, y) # two coordinates
1666 --or: goto((x, y)) # a pair (tuple) of coordinates
1667 --or: goto(vec) # e.g. as returned by pos()
1668
1669 Move turtle to an absolute position. If the pen is down,
1670 a line will be drawn. The turtle's orientation does not change.
1671
1672 Example (for a Turtle instance named turtle):
1673 >>> tp = turtle.pos()
1674 >>> tp
1675 (0.00, 0.00)
1676 >>> turtle.setpos(60,30)
1677 >>> turtle.pos()
1678 (60.00,30.00)
1679 >>> turtle.setpos((20,80))
1680 >>> turtle.pos()
1681 (20.00,80.00)
1682 >>> turtle.setpos(tp)
1683 >>> turtle.pos()
1684 (0.00,0.00)
1685 """
1686 if y is None:
1687 self._goto(Vec2D(*x))
1688 else:
1689 self._goto(Vec2D(x, y))
1690
1691 def home(self):
1692 """Move turtle to the origin - coordinates (0,0).
1693
1694 No arguments.
1695
1696 Move turtle to the origin - coordinates (0,0) and set it's
1697 heading to it's start-orientation (which depends on mode).
1698
1699 Example (for a Turtle instance named turtle):
1700 >>> turtle.home()
1701 """
1702 self.goto(0, 0)
1703 self.setheading(0)
1704
1705 def setx(self, x):
1706 """Set the turtle's first coordinate to x
1707
1708 Argument:
1709 x -- a number (integer or float)
1710
1711 Set the turtle's first coordinate to x, leave second coordinate
1712 unchanged.
1713
1714 Example (for a Turtle instance named turtle):
1715 >>> turtle.position()
1716 (0.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001717 >>> turtle.setx(10)
1718 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001719 (10.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001720 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001721 self._goto(Vec2D(x, self._position[1]))
Georg Brandl33cece02008-05-20 06:58:21 +00001722
Martin v. Löwis87184592008-06-04 06:29:55 +00001723 def sety(self, y):
1724 """Set the turtle's second coordinate to y
Georg Brandl33cece02008-05-20 06:58:21 +00001725
Martin v. Löwis87184592008-06-04 06:29:55 +00001726 Argument:
1727 y -- a number (integer or float)
1728
1729 Set the turtle's first coordinate to x, second coordinate remains
1730 unchanged.
1731
1732 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001733 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001734 (0.00, 40.00)
1735 >>> turtle.sety(-10)
Georg Brandl33cece02008-05-20 06:58:21 +00001736 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001737 (0.00, -10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001738 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001739 self._goto(Vec2D(self._position[0], y))
Georg Brandl33cece02008-05-20 06:58:21 +00001740
Martin v. Löwis87184592008-06-04 06:29:55 +00001741 def distance(self, x, y=None):
1742 """Return the distance from the turtle to (x,y) in turtle step units.
Georg Brandl33cece02008-05-20 06:58:21 +00001743
Martin v. Löwis87184592008-06-04 06:29:55 +00001744 Arguments:
1745 x -- a number or a pair/vector of numbers or a turtle instance
1746 y -- a number None None
Georg Brandl33cece02008-05-20 06:58:21 +00001747
Martin v. Löwis87184592008-06-04 06:29:55 +00001748 call: distance(x, y) # two coordinates
1749 --or: distance((x, y)) # a pair (tuple) of coordinates
1750 --or: distance(vec) # e.g. as returned by pos()
1751 --or: distance(mypen) # where mypen is another turtle
1752
1753 Example (for a Turtle instance named turtle):
1754 >>> turtle.pos()
1755 (0.00, 0.00)
1756 >>> turtle.distance(30,40)
1757 50.0
1758 >>> pen = Turtle()
1759 >>> pen.forward(77)
1760 >>> turtle.distance(pen)
1761 77.0
1762 """
1763 if y is not None:
1764 pos = Vec2D(x, y)
1765 if isinstance(x, Vec2D):
1766 pos = x
1767 elif isinstance(x, tuple):
1768 pos = Vec2D(*x)
1769 elif isinstance(x, TNavigator):
1770 pos = x._position
1771 return abs(pos - self._position)
1772
1773 def towards(self, x, y=None):
1774 """Return the angle of the line from the turtle's position to (x, y).
1775
1776 Arguments:
1777 x -- a number or a pair/vector of numbers or a turtle instance
1778 y -- a number None None
1779
1780 call: distance(x, y) # two coordinates
1781 --or: distance((x, y)) # a pair (tuple) of coordinates
1782 --or: distance(vec) # e.g. as returned by pos()
1783 --or: distance(mypen) # where mypen is another turtle
1784
1785 Return the angle, between the line from turtle-position to position
1786 specified by x, y and the turtle's start orientation. (Depends on
1787 modes - "standard" or "logo")
1788
1789 Example (for a Turtle instance named turtle):
1790 >>> turtle.pos()
1791 (10.00, 10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001792 >>> turtle.towards(0,0)
1793 225.0
1794 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001795 if y is not None:
1796 pos = Vec2D(x, y)
1797 if isinstance(x, Vec2D):
1798 pos = x
1799 elif isinstance(x, tuple):
1800 pos = Vec2D(*x)
1801 elif isinstance(x, TNavigator):
1802 pos = x._position
1803 x, y = pos - self._position
1804 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1805 result /= self._degreesPerAU
1806 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1807
1808 def heading(self):
1809 """ Return the turtle's current heading.
1810
1811 No arguments.
1812
1813 Example (for a Turtle instance named turtle):
1814 >>> turtle.left(67)
1815 >>> turtle.heading()
1816 67.0
1817 """
1818 x, y = self._orient
1819 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1820 result /= self._degreesPerAU
1821 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1822
1823 def setheading(self, to_angle):
1824 """Set the orientation of the turtle to to_angle.
1825
1826 Aliases: setheading | seth
1827
1828 Argument:
1829 to_angle -- a number (integer or float)
1830
1831 Set the orientation of the turtle to to_angle.
1832 Here are some common directions in degrees:
1833
1834 standard - mode: logo-mode:
1835 -------------------|--------------------
1836 0 - east 0 - north
1837 90 - north 90 - east
1838 180 - west 180 - south
1839 270 - south 270 - west
1840
1841 Example (for a Turtle instance named turtle):
1842 >>> turtle.setheading(90)
1843 >>> turtle.heading()
1844 90
1845 """
1846 angle = (to_angle - self.heading())*self._angleOrient
1847 full = self._fullcircle
1848 angle = (angle+full/2.)%full - full/2.
1849 self._rotate(angle)
1850
1851 def circle(self, radius, extent = None, steps = None):
1852 """ Draw a circle with given radius.
1853
1854 Arguments:
1855 radius -- a number
1856 extent (optional) -- a number
1857 steps (optional) -- an integer
1858
1859 Draw a circle with given radius. The center is radius units left
1860 of the turtle; extent - an angle - determines which part of the
1861 circle is drawn. If extent is not given, draw the entire circle.
1862 If extent is not a full circle, one endpoint of the arc is the
1863 current pen position. Draw the arc in counterclockwise direction
1864 if radius is positive, otherwise in clockwise direction. Finally
1865 the direction of the turtle is changed by the amount of extent.
1866
1867 As the circle is approximated by an inscribed regular polygon,
1868 steps determines the number of steps to use. If not given,
1869 it will be calculated automatically. Maybe used to draw regular
1870 polygons.
1871
1872 call: circle(radius) # full circle
1873 --or: circle(radius, extent) # arc
1874 --or: circle(radius, extent, steps)
1875 --or: circle(radius, steps=6) # 6-sided polygon
1876
1877 Example (for a Turtle instance named turtle):
1878 >>> turtle.circle(50)
1879 >>> turtle.circle(120, 180) # semicircle
1880 """
1881 if self.undobuffer:
1882 self.undobuffer.push(["seq"])
1883 self.undobuffer.cumulate = True
1884 speed = self.speed()
1885 if extent is None:
1886 extent = self._fullcircle
1887 if steps is None:
1888 frac = abs(extent)/self._fullcircle
1889 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1890 w = 1.0 * extent / steps
1891 w2 = 0.5 * w
1892 l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1893 if radius < 0:
1894 l, w, w2 = -l, -w, -w2
1895 tr = self.tracer()
1896 dl = self._delay()
1897 if speed == 0:
1898 self.tracer(0, 0)
Georg Brandl33cece02008-05-20 06:58:21 +00001899 else:
Martin v. Löwis87184592008-06-04 06:29:55 +00001900 self.speed(0)
1901 self._rotate(w2)
1902 for i in range(steps):
1903 self.speed(speed)
1904 self._go(l)
1905 self.speed(0)
1906 self._rotate(w)
1907 self._rotate(-w2)
1908 if speed == 0:
1909 self.tracer(tr, dl)
1910 self.speed(speed)
1911 if self.undobuffer:
1912 self.undobuffer.cumulate = False
Georg Brandl33cece02008-05-20 06:58:21 +00001913
Martin v. Löwis87184592008-06-04 06:29:55 +00001914## three dummy methods to be implemented by child class:
Georg Brandl33cece02008-05-20 06:58:21 +00001915
Martin v. Löwis87184592008-06-04 06:29:55 +00001916 def speed(self, s=0):
1917 """dummy method - to be overwritten by child class"""
1918 def tracer(self, a=None, b=None):
1919 """dummy method - to be overwritten by child class"""
1920 def _delay(self, n=None):
1921 """dummy method - to be overwritten by child class"""
Georg Brandl33cece02008-05-20 06:58:21 +00001922
Martin v. Löwis87184592008-06-04 06:29:55 +00001923 fd = forward
1924 bk = back
1925 backward = back
1926 rt = right
1927 lt = left
1928 position = pos
1929 setpos = goto
1930 setposition = goto
1931 seth = setheading
Georg Brandl33cece02008-05-20 06:58:21 +00001932
Georg Brandl33cece02008-05-20 06:58:21 +00001933
Martin v. Löwis87184592008-06-04 06:29:55 +00001934class TPen(object):
1935 """Drawing part of the RawTurtle.
1936 Implements drawing properties.
1937 """
1938 def __init__(self, resizemode=_CFG["resizemode"]):
1939 self._resizemode = resizemode # or "user" or "noresize"
1940 self.undobuffer = None
1941 TPen._reset(self)
Georg Brandl33cece02008-05-20 06:58:21 +00001942
Martin v. Löwis87184592008-06-04 06:29:55 +00001943 def _reset(self, pencolor=_CFG["pencolor"],
1944 fillcolor=_CFG["fillcolor"]):
1945 self._pensize = 1
1946 self._shown = True
1947 self._pencolor = pencolor
1948 self._fillcolor = fillcolor
1949 self._drawing = True
1950 self._speed = 3
1951 self._stretchfactor = (1, 1)
1952 self._tilt = 0
1953 self._outlinewidth = 1
1954 ### self.screen = None # to override by child class
1955
1956 def resizemode(self, rmode=None):
1957 """Set resizemode to one of the values: "auto", "user", "noresize".
1958
1959 (Optional) Argument:
1960 rmode -- one of the strings "auto", "user", "noresize"
1961
1962 Different resizemodes have the following effects:
1963 - "auto" adapts the appearance of the turtle
1964 corresponding to the value of pensize.
1965 - "user" adapts the appearance of the turtle according to the
1966 values of stretchfactor and outlinewidth (outline),
1967 which are set by shapesize()
1968 - "noresize" no adaption of the turtle's appearance takes place.
1969 If no argument is given, return current resizemode.
1970 resizemode("user") is called by a call of shapesize with arguments.
1971
1972
1973 Examples (for a Turtle instance named turtle):
1974 >>> turtle.resizemode("noresize")
1975 >>> turtle.resizemode()
1976 'noresize'
Georg Brandl33cece02008-05-20 06:58:21 +00001977 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001978 if rmode is None:
1979 return self._resizemode
1980 rmode = rmode.lower()
1981 if rmode in ["auto", "user", "noresize"]:
1982 self.pen(resizemode=rmode)
Georg Brandl33cece02008-05-20 06:58:21 +00001983
Martin v. Löwis87184592008-06-04 06:29:55 +00001984 def pensize(self, width=None):
1985 """Set or return the line thickness.
Georg Brandl33cece02008-05-20 06:58:21 +00001986
Martin v. Löwis87184592008-06-04 06:29:55 +00001987 Aliases: pensize | width
Georg Brandl33cece02008-05-20 06:58:21 +00001988
Martin v. Löwis87184592008-06-04 06:29:55 +00001989 Argument:
1990 width -- positive number
Georg Brandl33cece02008-05-20 06:58:21 +00001991
Martin v. Löwis87184592008-06-04 06:29:55 +00001992 Set the line thickness to width or return it. If resizemode is set
1993 to "auto" and turtleshape is a polygon, that polygon is drawn with
1994 the same line thickness. If no argument is given, current pensize
1995 is returned.
Georg Brandl33cece02008-05-20 06:58:21 +00001996
Martin v. Löwis87184592008-06-04 06:29:55 +00001997 Example (for a Turtle instance named turtle):
1998 >>> turtle.pensize()
1999 1
2000 turtle.pensize(10) # from here on lines of width 10 are drawn
Georg Brandl33cece02008-05-20 06:58:21 +00002001 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002002 if width is None:
2003 return self._pensize
2004 self.pen(pensize=width)
Georg Brandl33cece02008-05-20 06:58:21 +00002005
2006
Martin v. Löwis87184592008-06-04 06:29:55 +00002007 def penup(self):
2008 """Pull the pen up -- no drawing when moving.
Georg Brandl33cece02008-05-20 06:58:21 +00002009
Martin v. Löwis87184592008-06-04 06:29:55 +00002010 Aliases: penup | pu | up
Georg Brandl33cece02008-05-20 06:58:21 +00002011
Martin v. Löwis87184592008-06-04 06:29:55 +00002012 No argument
2013
2014 Example (for a Turtle instance named turtle):
2015 >>> turtle.penup()
Georg Brandl33cece02008-05-20 06:58:21 +00002016 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002017 if not self._drawing:
Georg Brandl33cece02008-05-20 06:58:21 +00002018 return
Martin v. Löwis87184592008-06-04 06:29:55 +00002019 self.pen(pendown=False)
Georg Brandl33cece02008-05-20 06:58:21 +00002020
Martin v. Löwis87184592008-06-04 06:29:55 +00002021 def pendown(self):
2022 """Pull the pen down -- drawing when moving.
2023
2024 Aliases: pendown | pd | down
2025
2026 No argument.
2027
2028 Example (for a Turtle instance named turtle):
2029 >>> turtle.pendown()
2030 """
2031 if self._drawing:
2032 return
2033 self.pen(pendown=True)
2034
2035 def isdown(self):
2036 """Return True if pen is down, False if it's up.
2037
2038 No argument.
2039
2040 Example (for a Turtle instance named turtle):
2041 >>> turtle.penup()
2042 >>> turtle.isdown()
2043 False
2044 >>> turtle.pendown()
2045 >>> turtle.isdown()
2046 True
2047 """
2048 return self._drawing
2049
2050 def speed(self, speed=None):
2051 """ Return or set the turtle's speed.
2052
2053 Optional argument:
2054 speed -- an integer in the range 0..10 or a speedstring (see below)
2055
2056 Set the turtle's speed to an integer value in the range 0 .. 10.
2057 If no argument is given: return current speed.
2058
2059 If input is a number greater than 10 or smaller than 0.5,
2060 speed is set to 0.
2061 Speedstrings are mapped to speedvalues in the following way:
2062 'fastest' : 0
2063 'fast' : 10
2064 'normal' : 6
2065 'slow' : 3
2066 'slowest' : 1
2067 speeds from 1 to 10 enforce increasingly faster animation of
2068 line drawing and turtle turning.
2069
2070 Attention:
2071 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2072 and likewise left/right make the turtle turn instantly.
2073
2074 Example (for a Turtle instance named turtle):
2075 >>> turtle.speed(3)
2076 """
2077 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2078 if speed is None:
2079 return self._speed
2080 if speed in speeds:
2081 speed = speeds[speed]
2082 elif 0.5 < speed < 10.5:
2083 speed = int(round(speed))
2084 else:
2085 speed = 0
2086 self.pen(speed=speed)
2087
2088 def color(self, *args):
2089 """Return or set the pencolor and fillcolor.
2090
2091 Arguments:
2092 Several input formats are allowed.
2093 They use 0, 1, 2, or 3 arguments as follows:
2094
2095 color()
2096 Return the current pencolor and the current fillcolor
2097 as a pair of color specification strings as are returned
2098 by pencolor and fillcolor.
2099 color(colorstring), color((r,g,b)), color(r,g,b)
2100 inputs as in pencolor, set both, fillcolor and pencolor,
2101 to the given value.
2102 color(colorstring1, colorstring2),
2103 color((r1,g1,b1), (r2,g2,b2))
2104 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2105 and analogously, if the other input format is used.
2106
2107 If turtleshape is a polygon, outline and interior of that polygon
2108 is drawn with the newly set colors.
2109 For mor info see: pencolor, fillcolor
2110
2111 Example (for a Turtle instance named turtle):
2112 >>> turtle.color('red', 'green')
2113 >>> turtle.color()
2114 ('red', 'green')
2115 >>> colormode(255)
2116 >>> color((40, 80, 120), (160, 200, 240))
2117 >>> color()
2118 ('#285078', '#a0c8f0')
2119 """
2120 if args:
2121 l = len(args)
2122 if l == 1:
2123 pcolor = fcolor = args[0]
2124 elif l == 2:
2125 pcolor, fcolor = args
2126 elif l == 3:
2127 pcolor = fcolor = args
2128 pcolor = self._colorstr(pcolor)
2129 fcolor = self._colorstr(fcolor)
2130 self.pen(pencolor=pcolor, fillcolor=fcolor)
2131 else:
2132 return self._color(self._pencolor), self._color(self._fillcolor)
2133
2134 def pencolor(self, *args):
2135 """ Return or set the pencolor.
2136
2137 Arguments:
2138 Four input formats are allowed:
2139 - pencolor()
2140 Return the current pencolor as color specification string,
2141 possibly in hex-number format (see example).
2142 May be used as input to another color/pencolor/fillcolor call.
2143 - pencolor(colorstring)
2144 s is a Tk color specification string, such as "red" or "yellow"
2145 - pencolor((r, g, b))
2146 *a tuple* of r, g, and b, which represent, an RGB color,
2147 and each of r, g, and b are in the range 0..colormode,
2148 where colormode is either 1.0 or 255
2149 - pencolor(r, g, b)
2150 r, g, and b represent an RGB color, and each of r, g, and b
2151 are in the range 0..colormode
2152
2153 If turtleshape is a polygon, the outline of that polygon is drawn
2154 with the newly set pencolor.
2155
2156 Example (for a Turtle instance named turtle):
2157 >>> turtle.pencolor('brown')
2158 >>> tup = (0.2, 0.8, 0.55)
2159 >>> turtle.pencolor(tup)
2160 >>> turtle.pencolor()
2161 '#33cc8c'
2162 """
2163 if args:
2164 color = self._colorstr(args)
2165 if color == self._pencolor:
2166 return
2167 self.pen(pencolor=color)
2168 else:
2169 return self._color(self._pencolor)
2170
2171 def fillcolor(self, *args):
2172 """ Return or set the fillcolor.
2173
2174 Arguments:
2175 Four input formats are allowed:
2176 - fillcolor()
2177 Return the current fillcolor as color specification string,
2178 possibly in hex-number format (see example).
2179 May be used as input to another color/pencolor/fillcolor call.
2180 - fillcolor(colorstring)
2181 s is a Tk color specification string, such as "red" or "yellow"
2182 - fillcolor((r, g, b))
2183 *a tuple* of r, g, and b, which represent, an RGB color,
2184 and each of r, g, and b are in the range 0..colormode,
2185 where colormode is either 1.0 or 255
2186 - fillcolor(r, g, b)
2187 r, g, and b represent an RGB color, and each of r, g, and b
2188 are in the range 0..colormode
2189
2190 If turtleshape is a polygon, the interior of that polygon is drawn
2191 with the newly set fillcolor.
2192
2193 Example (for a Turtle instance named turtle):
2194 >>> turtle.fillcolor('violet')
2195 >>> col = turtle.pencolor()
2196 >>> turtle.fillcolor(col)
2197 >>> turtle.fillcolor(0, .5, 0)
2198 """
2199 if args:
2200 color = self._colorstr(args)
2201 if color == self._fillcolor:
2202 return
2203 self.pen(fillcolor=color)
2204 else:
2205 return self._color(self._fillcolor)
2206
2207 def showturtle(self):
2208 """Makes the turtle visible.
2209
2210 Aliases: showturtle | st
2211
2212 No argument.
2213
2214 Example (for a Turtle instance named turtle):
2215 >>> turtle.hideturtle()
2216 >>> turtle.showturtle()
2217 """
2218 self.pen(shown=True)
2219
2220 def hideturtle(self):
2221 """Makes the turtle invisible.
2222
2223 Aliases: hideturtle | ht
2224
2225 No argument.
2226
2227 It's a good idea to do this while you're in the
2228 middle of a complicated drawing, because hiding
2229 the turtle speeds up the drawing observably.
2230
2231 Example (for a Turtle instance named turtle):
2232 >>> turtle.hideturtle()
2233 """
2234 self.pen(shown=False)
2235
2236 def isvisible(self):
2237 """Return True if the Turtle is shown, False if it's hidden.
2238
2239 No argument.
2240
2241 Example (for a Turtle instance named turtle):
2242 >>> turtle.hideturtle()
2243 >>> print turtle.isvisible():
2244 False
2245 """
2246 return self._shown
2247
2248 def pen(self, pen=None, **pendict):
2249 """Return or set the pen's attributes.
2250
2251 Arguments:
2252 pen -- a dictionary with some or all of the below listed keys.
2253 **pendict -- one or more keyword-arguments with the below
2254 listed keys as keywords.
2255
2256 Return or set the pen's attributes in a 'pen-dictionary'
2257 with the following key/value pairs:
2258 "shown" : True/False
2259 "pendown" : True/False
2260 "pencolor" : color-string or color-tuple
2261 "fillcolor" : color-string or color-tuple
2262 "pensize" : positive number
2263 "speed" : number in range 0..10
2264 "resizemode" : "auto" or "user" or "noresize"
2265 "stretchfactor": (positive number, positive number)
2266 "outline" : positive number
2267 "tilt" : number
2268
2269 This dicionary can be used as argument for a subsequent
2270 pen()-call to restore the former pen-state. Moreover one
2271 or more of these attributes can be provided as keyword-arguments.
2272 This can be used to set several pen attributes in one statement.
Georg Brandl33cece02008-05-20 06:58:21 +00002273
2274
Martin v. Löwis87184592008-06-04 06:29:55 +00002275 Examples (for a Turtle instance named turtle):
2276 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2277 >>> turtle.pen()
2278 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2279 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
2280 'stretchfactor': (1,1), 'speed': 3}
2281 >>> penstate=turtle.pen()
2282 >>> turtle.color("yellow","")
2283 >>> turtle.penup()
2284 >>> turtle.pen()
2285 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2286 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
2287 'stretchfactor': (1,1), 'speed': 3}
2288 >>> p.pen(penstate, fillcolor="green")
2289 >>> p.pen()
2290 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2291 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
2292 'stretchfactor': (1,1), 'speed': 3}
2293 """
2294 _pd = {"shown" : self._shown,
2295 "pendown" : self._drawing,
2296 "pencolor" : self._pencolor,
2297 "fillcolor" : self._fillcolor,
2298 "pensize" : self._pensize,
2299 "speed" : self._speed,
2300 "resizemode" : self._resizemode,
2301 "stretchfactor" : self._stretchfactor,
2302 "outline" : self._outlinewidth,
2303 "tilt" : self._tilt
2304 }
Georg Brandl33cece02008-05-20 06:58:21 +00002305
Martin v. Löwis87184592008-06-04 06:29:55 +00002306 if not (pen or pendict):
2307 return _pd
2308
2309 if isinstance(pen, dict):
2310 p = pen
2311 else:
2312 p = {}
2313 p.update(pendict)
2314
2315 _p_buf = {}
2316 for key in p:
2317 _p_buf[key] = _pd[key]
2318
2319 if self.undobuffer:
2320 self.undobuffer.push(("pen", _p_buf))
2321
2322 newLine = False
2323 if "pendown" in p:
2324 if self._drawing != p["pendown"]:
2325 newLine = True
2326 if "pencolor" in p:
2327 if isinstance(p["pencolor"], tuple):
2328 p["pencolor"] = self._colorstr((p["pencolor"],))
2329 if self._pencolor != p["pencolor"]:
2330 newLine = True
2331 if "pensize" in p:
2332 if self._pensize != p["pensize"]:
2333 newLine = True
2334 if newLine:
2335 self._newLine()
2336 if "pendown" in p:
2337 self._drawing = p["pendown"]
2338 if "pencolor" in p:
2339 self._pencolor = p["pencolor"]
2340 if "pensize" in p:
2341 self._pensize = p["pensize"]
2342 if "fillcolor" in p:
2343 if isinstance(p["fillcolor"], tuple):
2344 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2345 self._fillcolor = p["fillcolor"]
2346 if "speed" in p:
2347 self._speed = p["speed"]
2348 if "resizemode" in p:
2349 self._resizemode = p["resizemode"]
2350 if "stretchfactor" in p:
2351 sf = p["stretchfactor"]
2352 if isinstance(sf, (int, float)):
2353 sf = (sf, sf)
2354 self._stretchfactor = sf
2355 if "outline" in p:
2356 self._outlinewidth = p["outline"]
2357 if "shown" in p:
2358 self._shown = p["shown"]
2359 if "tilt" in p:
2360 self._tilt = p["tilt"]
2361 self._update()
2362
2363## three dummy methods to be implemented by child class:
2364
2365 def _newLine(self, usePos = True):
2366 """dummy method - to be overwritten by child class"""
2367 def _update(self, count=True, forced=False):
2368 """dummy method - to be overwritten by child class"""
2369 def _color(self, args):
2370 """dummy method - to be overwritten by child class"""
2371 def _colorstr(self, args):
2372 """dummy method - to be overwritten by child class"""
2373
2374 width = pensize
2375 up = penup
2376 pu = penup
2377 pd = pendown
2378 down = pendown
2379 st = showturtle
2380 ht = hideturtle
2381
2382
2383class _TurtleImage(object):
2384 """Helper class: Datatype to store Turtle attributes
2385 """
2386
2387 def __init__(self, screen, shapeIndex):
2388 self.screen = screen
2389 self._type = None
2390 self._setshape(shapeIndex)
2391
2392 def _setshape(self, shapeIndex):
2393 screen = self.screen # RawTurtle.screens[self.screenIndex]
2394 self.shapeIndex = shapeIndex
2395 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2396 return
2397 if self._type == "image" == screen._shapes[shapeIndex]._type:
2398 return
2399 if self._type in ["image", "polygon"]:
2400 screen._delete(self._item)
2401 elif self._type == "compound":
2402 for item in self._item:
2403 screen._delete(item)
2404 self._type = screen._shapes[shapeIndex]._type
2405 if self._type == "polygon":
2406 self._item = screen._createpoly()
2407 elif self._type == "image":
2408 self._item = screen._createimage(screen._shapes["blank"]._data)
2409 elif self._type == "compound":
2410 self._item = [screen._createpoly() for item in
2411 screen._shapes[shapeIndex]._data]
2412
2413
2414class RawTurtle(TPen, TNavigator):
2415 """Animation part of the RawTurtle.
2416 Puts RawTurtle upon a TurtleScreen and provides tools for
2417 it's animation.
2418 """
2419 screens = []
2420
2421 def __init__(self, canvas=None,
2422 shape=_CFG["shape"],
2423 undobuffersize=_CFG["undobuffersize"],
2424 visible=_CFG["visible"]):
2425 if isinstance(canvas, Screen):
2426 self.screen = canvas
2427 elif isinstance(canvas, TurtleScreen):
2428 if canvas not in RawTurtle.screens:
2429 RawTurtle.screens.append(canvas)
2430 self.screen = canvas
2431 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2432 for screen in RawTurtle.screens:
2433 if screen.cv == canvas:
2434 self.screen = screen
2435 break
2436 else:
2437 self.screen = TurtleScreen(canvas)
2438 RawTurtle.screens.append(self.screen)
2439 else:
2440 raise TurtleGraphicsError("bad cavas argument %s" % canvas)
2441
2442 screen = self.screen
2443 TNavigator.__init__(self, screen.mode())
2444 TPen.__init__(self)
2445 screen._turtles.append(self)
2446 self.drawingLineItem = screen._createline()
2447 self.turtle = _TurtleImage(screen, shape)
2448 self._poly = None
2449 self._creatingPoly = False
2450 self._fillitem = self._fillpath = None
2451 self._shown = visible
2452 self._hidden_from_screen = False
2453 self.currentLineItem = screen._createline()
2454 self.currentLine = [self._position]
2455 self.items = [self.currentLineItem]
2456 self.stampItems = []
2457 self._undobuffersize = undobuffersize
2458 self.undobuffer = Tbuffer(undobuffersize)
2459 self._update()
2460
2461 def reset(self):
2462 """Delete the turtle's drawings and restore it's default values.
2463
2464 No argument.
2465,
2466 Delete the turtle's drawings from the screen, re-center the turtle
2467 and set variables to the default values.
2468
2469 Example (for a Turtle instance named turtle):
2470 >>> turtle.position()
2471 (0.00,-22.00)
2472 >>> turtle.heading()
2473 100.0
2474 >>> turtle.reset()
2475 >>> turtle.position()
2476 (0.00,0.00)
2477 >>> turtle.heading()
2478 0.0
2479 """
2480 TNavigator.reset(self)
2481 TPen._reset(self)
2482 self._clear()
2483 self._drawturtle()
2484 self._update()
2485
2486 def setundobuffer(self, size):
2487 """Set or disable undobuffer.
2488
2489 Argument:
2490 size -- an integer or None
2491
2492 If size is an integer an empty undobuffer of given size is installed.
2493 Size gives the maximum number of turtle-actions that can be undone
2494 by the undo() function.
2495 If size is None, no undobuffer is present.
2496
2497 Example (for a Turtle instance named turtle):
2498 >>> turtle.setundobuffer(42)
2499 """
2500 if size is None:
2501 self.undobuffer = None
2502 else:
2503 self.undobuffer = Tbuffer(size)
2504
2505 def undobufferentries(self):
2506 """Return count of entries in the undobuffer.
2507
2508 No argument.
2509
2510 Example (for a Turtle instance named turtle):
2511 >>> while undobufferentries():
2512 undo()
2513 """
2514 if self.undobuffer is None:
2515 return 0
2516 return self.undobuffer.nr_of_items()
2517
2518 def _clear(self):
2519 """Delete all of pen's drawings"""
2520 self._fillitem = self._fillpath = None
2521 for item in self.items:
2522 self.screen._delete(item)
2523 self.currentLineItem = self.screen._createline()
2524 self.currentLine = []
2525 if self._drawing:
2526 self.currentLine.append(self._position)
2527 self.items = [self.currentLineItem]
2528 self.clearstamps()
2529 self.setundobuffer(self._undobuffersize)
2530
2531
2532 def clear(self):
2533 """Delete the turtle's drawings from the screen. Do not move turtle.
2534
2535 No arguments.
2536
2537 Delete the turtle's drawings from the screen. Do not move turtle.
2538 State and position of the turtle as well as drawings of other
2539 turtles are not affected.
2540
2541 Examples (for a Turtle instance named turtle):
2542 >>> turtle.clear()
2543 """
2544 self._clear()
2545 self._update()
2546
2547 def _update_data(self):
2548 self.screen._incrementudc()
2549 if self.screen._updatecounter != 0:
2550 return
2551 if len(self.currentLine)>1:
2552 self.screen._drawline(self.currentLineItem, self.currentLine,
2553 self._pencolor, self._pensize)
2554
2555 def _update(self):
2556 """Perform a Turtle-data update.
2557 """
2558 screen = self.screen
2559 if screen._tracing == 0:
2560 return
2561 elif screen._tracing == 1:
2562 self._update_data()
2563 self._drawturtle()
2564 screen._update() # TurtleScreenBase
2565 screen._delay(screen._delayvalue) # TurtleScreenBase
2566 else:
2567 self._update_data()
2568 if screen._updatecounter == 0:
2569 for t in screen.turtles():
2570 t._drawturtle()
2571 screen._update()
2572
2573 def tracer(self, flag=None, delay=None):
2574 """Turns turtle animation on/off and set delay for update drawings.
2575
2576 Optional arguments:
2577 n -- nonnegative integer
2578 delay -- nonnegative integer
2579
2580 If n is given, only each n-th regular screen update is really performed.
2581 (Can be used to accelerate the drawing of complex graphics.)
2582 Second arguments sets delay value (see RawTurtle.delay())
2583
2584 Example (for a Turtle instance named turtle):
2585 >>> turtle.tracer(8, 25)
2586 >>> dist = 2
2587 >>> for i in range(200):
2588 turtle.fd(dist)
2589 turtle.rt(90)
2590 dist += 2
2591 """
2592 return self.screen.tracer(flag, delay)
2593
2594 def _color(self, args):
2595 return self.screen._color(args)
2596
2597 def _colorstr(self, args):
2598 return self.screen._colorstr(args)
2599
2600 def _cc(self, args):
2601 """Convert colortriples to hexstrings.
2602 """
2603 if isinstance(args, str):
2604 return args
2605 try:
2606 r, g, b = args
2607 except:
2608 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2609 if self.screen._colormode == 1.0:
2610 r, g, b = [round(255.0*x) for x in (r, g, b)]
2611 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2612 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2613 return "#%02x%02x%02x" % (r, g, b)
2614
2615 def clone(self):
2616 """Create and return a clone of the turtle.
2617
2618 No argument.
2619
2620 Create and return a clone of the turtle with same position, heading
2621 and turtle properties.
2622
2623 Example (for a Turtle instance named mick):
2624 mick = Turtle()
2625 joe = mick.clone()
2626 """
2627 screen = self.screen
2628 self._newLine(self._drawing)
2629
2630 turtle = self.turtle
2631 self.screen = None
2632 self.turtle = None # too make self deepcopy-able
2633
2634 q = deepcopy(self)
2635
2636 self.screen = screen
2637 self.turtle = turtle
2638
2639 q.screen = screen
2640 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2641
2642 screen._turtles.append(q)
2643 ttype = screen._shapes[self.turtle.shapeIndex]._type
2644 if ttype == "polygon":
2645 q.turtle._item = screen._createpoly()
2646 elif ttype == "image":
2647 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2648 elif ttype == "compound":
2649 q.turtle._item = [screen._createpoly() for item in
2650 screen._shapes[self.turtle.shapeIndex]._data]
2651 q.currentLineItem = screen._createline()
2652 q._update()
2653 return q
2654
2655 def shape(self, name=None):
2656 """Set turtle shape to shape with given name / return current shapename.
2657
2658 Optional argument:
2659 name -- a string, which is a valid shapename
2660
2661 Set turtle shape to shape with given name or, if name is not given,
2662 return name of current shape.
2663 Shape with name must exist in the TurtleScreen's shape dictionary.
2664 Initially there are the following polygon shapes:
2665 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2666 To learn about how to deal with shapes see Screen-method register_shape.
2667
2668 Example (for a Turtle instance named turtle):
2669 >>> turtle.shape()
2670 'arrow'
2671 >>> turtle.shape("turtle")
2672 >>> turtle.shape()
2673 'turtle'
2674 """
2675 if name is None:
2676 return self.turtle.shapeIndex
2677 if not name in self.screen.getshapes():
2678 raise TurtleGraphicsError("There is no shape named %s" % name)
2679 self.turtle._setshape(name)
2680 self._update()
2681
2682 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2683 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2684
2685 Optinonal arguments:
2686 stretch_wid : positive number
2687 stretch_len : positive number
2688 outline : positive number
2689
2690 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2691 Set resizemode to "user".
2692 If and only if resizemode is set to "user", the turtle will be displayed
2693 stretched according to its stretchfactors:
2694 stretch_wid is stretchfactor perpendicular to orientation
2695 stretch_len is stretchfactor in direction of turtles orientation.
2696 outline determines the width of the shapes's outline.
2697
2698 Examples (for a Turtle instance named turtle):
2699 >>> turtle.resizemode("user")
2700 >>> turtle.shapesize(5, 5, 12)
2701 >>> turtle.shapesize(outline=8)
2702 """
2703 if stretch_wid is None and stretch_len is None and outline == None:
2704 stretch_wid, stretch_len = self._stretchfactor
2705 return stretch_wid, stretch_len, self._outlinewidth
2706 if stretch_wid is not None:
2707 if stretch_len is None:
2708 stretchfactor = stretch_wid, stretch_wid
2709 else:
2710 stretchfactor = stretch_wid, stretch_len
2711 elif stretch_len is not None:
2712 stretchfactor = self._stretchfactor[0], stretch_len
2713 else:
2714 stretchfactor = self._stretchfactor
2715 if outline is None:
2716 outline = self._outlinewidth
2717 self.pen(resizemode="user",
2718 stretchfactor=stretchfactor, outline=outline)
2719
2720 def settiltangle(self, angle):
2721 """Rotate the turtleshape to point in the specified direction
2722
2723 Optional argument:
2724 angle -- number
2725
2726 Rotate the turtleshape to point in the direction specified by angle,
2727 regardless of its current tilt-angle. DO NOT change the turtle's
2728 heading (direction of movement).
2729
2730
2731 Examples (for a Turtle instance named turtle):
2732 >>> turtle.shape("circle")
2733 >>> turtle.shapesize(5,2)
2734 >>> turtle.settiltangle(45)
2735 >>> stamp()
2736 >>> turtle.fd(50)
2737 >>> turtle.settiltangle(-45)
2738 >>> stamp()
2739 >>> turtle.fd(50)
2740 """
2741 tilt = -angle * self._degreesPerAU * self._angleOrient
2742 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2743 self.pen(resizemode="user", tilt=tilt)
2744
2745 def tiltangle(self):
2746 """Return the current tilt-angle.
2747
2748 No argument.
2749
2750 Return the current tilt-angle, i. e. the angle between the
2751 orientation of the turtleshape and the heading of the turtle
2752 (it's direction of movement).
2753
2754 Examples (for a Turtle instance named turtle):
2755 >>> turtle.shape("circle")
2756 >>> turtle.shapesize(5,2)
2757 >>> turtle.tilt(45)
2758 >>> turtle.tiltangle()
2759 >>>
2760 """
2761 tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2762 return (tilt / self._degreesPerAU) % self._fullcircle
2763
2764 def tilt(self, angle):
2765 """Rotate the turtleshape by angle.
2766
2767 Argument:
2768 angle - a number
2769
2770 Rotate the turtleshape by angle from its current tilt-angle,
2771 but do NOT change the turtle's heading (direction of movement).
2772
2773 Examples (for a Turtle instance named turtle):
2774 >>> turtle.shape("circle")
2775 >>> turtle.shapesize(5,2)
2776 >>> turtle.tilt(30)
2777 >>> turtle.fd(50)
2778 >>> turtle.tilt(30)
2779 >>> turtle.fd(50)
2780 """
2781 self.settiltangle(angle + self.tiltangle())
2782
2783 def _polytrafo(self, poly):
2784 """Computes transformed polygon shapes from a shape
2785 according to current position and heading.
2786 """
2787 screen = self.screen
2788 p0, p1 = self._position
2789 e0, e1 = self._orient
2790 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2791 e0, e1 = (1.0 / abs(e)) * e
2792 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2793 for (x, y) in poly]
2794
2795 def _drawturtle(self):
2796 """Manages the correct rendering of the turtle with respect to
2797 it's shape, resizemode, strech and tilt etc."""
2798 screen = self.screen
2799 shape = screen._shapes[self.turtle.shapeIndex]
2800 ttype = shape._type
2801 titem = self.turtle._item
2802 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
2803 self._hidden_from_screen = False
2804 tshape = shape._data
2805 if ttype == "polygon":
2806 if self._resizemode == "noresize":
2807 w = 1
2808 shape = tshape
2809 else:
2810 if self._resizemode == "auto":
2811 lx = ly = max(1, self._pensize/5.0)
2812 w = self._pensize
2813 tiltangle = 0
2814 elif self._resizemode == "user":
2815 lx, ly = self._stretchfactor
2816 w = self._outlinewidth
2817 tiltangle = self._tilt
2818 shape = [(lx*x, ly*y) for (x, y) in tshape]
2819 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2820 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2821 shape = self._polytrafo(shape)
2822 fc, oc = self._fillcolor, self._pencolor
2823 screen._drawpoly(titem, shape, fill=fc, outline=oc,
2824 width=w, top=True)
2825 elif ttype == "image":
2826 screen._drawimage(titem, self._position, tshape)
2827 elif ttype == "compound":
2828 lx, ly = self._stretchfactor
2829 w = self._outlinewidth
2830 for item, (poly, fc, oc) in zip(titem, tshape):
2831 poly = [(lx*x, ly*y) for (x, y) in poly]
2832 poly = self._polytrafo(poly)
2833 screen._drawpoly(item, poly, fill=self._cc(fc),
2834 outline=self._cc(oc), width=w, top=True)
2835 else:
2836 if self._hidden_from_screen:
2837 return
2838 if ttype == "polygon":
2839 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
2840 elif ttype == "image":
2841 screen._drawimage(titem, self._position,
2842 screen._shapes["blank"]._data)
2843 elif ttype == "compound":
2844 for item in titem:
2845 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
2846 self._hidden_from_screen = True
2847
2848############################## stamp stuff ###############################
2849
2850 def stamp(self):
2851 """Stamp a copy of the turtleshape onto the canvas and return it's id.
2852
2853 No argument.
2854
2855 Stamp a copy of the turtle shape onto the canvas at the current
2856 turtle position. Return a stamp_id for that stamp, which can be
2857 used to delete it by calling clearstamp(stamp_id).
2858
2859 Example (for a Turtle instance named turtle):
2860 >>> turtle.color("blue")
2861 >>> turtle.stamp()
2862 13
2863 >>> turtle.fd(50)
2864 """
2865 screen = self.screen
2866 shape = screen._shapes[self.turtle.shapeIndex]
2867 ttype = shape._type
2868 tshape = shape._data
2869 if ttype == "polygon":
2870 stitem = screen._createpoly()
2871 if self._resizemode == "noresize":
2872 w = 1
2873 shape = tshape
2874 else:
2875 if self._resizemode == "auto":
2876 lx = ly = max(1, self._pensize/5.0)
2877 w = self._pensize
2878 tiltangle = 0
2879 elif self._resizemode == "user":
2880 lx, ly = self._stretchfactor
2881 w = self._outlinewidth
2882 tiltangle = self._tilt
2883 shape = [(lx*x, ly*y) for (x, y) in tshape]
2884 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2885 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2886 shape = self._polytrafo(shape)
2887 fc, oc = self._fillcolor, self._pencolor
2888 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
2889 width=w, top=True)
2890 elif ttype == "image":
2891 stitem = screen._createimage("")
2892 screen._drawimage(stitem, self._position, tshape)
2893 elif ttype == "compound":
2894 stitem = []
2895 for element in tshape:
2896 item = screen._createpoly()
2897 stitem.append(item)
2898 stitem = tuple(stitem)
2899 lx, ly = self._stretchfactor
2900 w = self._outlinewidth
2901 for item, (poly, fc, oc) in zip(stitem, tshape):
2902 poly = [(lx*x, ly*y) for (x, y) in poly]
2903 poly = self._polytrafo(poly)
2904 screen._drawpoly(item, poly, fill=self._cc(fc),
2905 outline=self._cc(oc), width=w, top=True)
2906 self.stampItems.append(stitem)
2907 self.undobuffer.push(("stamp", stitem))
2908 return stitem
2909
2910 def _clearstamp(self, stampid):
2911 """does the work for clearstamp() and clearstamps()
2912 """
2913 if stampid in self.stampItems:
2914 if isinstance(stampid, tuple):
2915 for subitem in stampid:
2916 self.screen._delete(subitem)
2917 else:
2918 self.screen._delete(stampid)
2919 self.stampItems.remove(stampid)
2920 # Delete stampitem from undobuffer if necessary
2921 # if clearstamp is called directly.
2922 item = ("stamp", stampid)
2923 buf = self.undobuffer
2924 if item not in buf.buffer:
2925 return
2926 index = buf.buffer.index(item)
2927 buf.buffer.remove(item)
2928 if index <= buf.ptr:
2929 buf.ptr = (buf.ptr - 1) % buf.bufsize
2930 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
2931
2932 def clearstamp(self, stampid):
2933 """Delete stamp with given stampid
2934
2935 Argument:
2936 stampid - an integer, must be return value of previous stamp() call.
2937
2938 Example (for a Turtle instance named turtle):
2939 >>> turtle.color("blue")
2940 >>> astamp = turtle.stamp()
2941 >>> turtle.fd(50)
2942 >>> turtle.clearstamp(astamp)
2943 """
2944 self._clearstamp(stampid)
2945 self._update()
2946
2947 def clearstamps(self, n=None):
2948 """Delete all or first/last n of turtle's stamps.
2949
2950 Optional argument:
2951 n -- an integer
2952
2953 If n is None, delete all of pen's stamps,
2954 else if n > 0 delete first n stamps
2955 else if n < 0 delete last n stamps.
2956
2957 Example (for a Turtle instance named turtle):
2958 >>> for i in range(8):
2959 turtle.stamp(); turtle.fd(30)
2960 ...
2961 >>> turtle.clearstamps(2)
2962 >>> turtle.clearstamps(-2)
2963 >>> turtle.clearstamps()
2964 """
2965 if n is None:
2966 toDelete = self.stampItems[:]
2967 elif n >= 0:
2968 toDelete = self.stampItems[:n]
2969 else:
2970 toDelete = self.stampItems[n:]
2971 for item in toDelete:
2972 self._clearstamp(item)
2973 self._update()
2974
2975 def _goto(self, end):
2976 """Move the pen to the point end, thereby drawing a line
2977 if pen is down. All other methodes for turtle movement depend
2978 on this one.
2979 """
2980 ## Version mit undo-stuff
2981 go_modes = ( self._drawing,
2982 self._pencolor,
2983 self._pensize,
2984 isinstance(self._fillpath, list))
2985 screen = self.screen
2986 undo_entry = ("go", self._position, end, go_modes,
2987 (self.currentLineItem,
2988 self.currentLine[:],
2989 screen._pointlist(self.currentLineItem),
2990 self.items[:])
2991 )
2992 if self.undobuffer:
2993 self.undobuffer.push(undo_entry)
2994 start = self._position
2995 if self._speed and screen._tracing == 1:
2996 diff = (end-start)
2997 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
2998 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
2999 delta = diff * (1.0/nhops)
3000 for n in range(1, nhops):
3001 if n == 1:
3002 top = True
3003 else:
3004 top = False
3005 self._position = start + delta * n
3006 if self._drawing:
3007 screen._drawline(self.drawingLineItem,
3008 (start, self._position),
3009 self._pencolor, self._pensize, top)
3010 self._update()
3011 if self._drawing:
3012 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3013 fill="", width=self._pensize)
3014 # Turtle now at end,
3015 if self._drawing: # now update currentLine
3016 self.currentLine.append(end)
3017 if isinstance(self._fillpath, list):
3018 self._fillpath.append(end)
3019 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3020 self._position = end
3021 if self._creatingPoly:
3022 self._poly.append(end)
3023 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3024 # of life, the universe and everything
3025 self._newLine()
3026 self._update() #count=True)
3027
3028 def _undogoto(self, entry):
3029 """Reverse a _goto. Used for undo()
3030 """
3031 old, new, go_modes, coodata = entry
3032 drawing, pc, ps, filling = go_modes
3033 cLI, cL, pl, items = coodata
3034 screen = self.screen
3035 if abs(self._position - new) > 0.5:
3036 print "undogoto: HALLO-DA-STIMMT-WAS-NICHT!"
3037 # restore former situation
3038 self.currentLineItem = cLI
3039 self.currentLine = cL
3040
3041 if pl == [(0, 0), (0, 0)]:
3042 usepc = ""
3043 else:
3044 usepc = pc
3045 screen._drawline(cLI, pl, fill=usepc, width=ps)
3046
3047 todelete = [i for i in self.items if (i not in items) and
3048 (screen._type(i) == "line")]
3049 for i in todelete:
3050 screen._delete(i)
3051 self.items.remove(i)
3052
3053 start = old
3054 if self._speed and screen._tracing == 1:
3055 diff = old - new
3056 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3057 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3058 delta = diff * (1.0/nhops)
3059 for n in range(1, nhops):
3060 if n == 1:
3061 top = True
3062 else:
3063 top = False
3064 self._position = new + delta * n
3065 if drawing:
3066 screen._drawline(self.drawingLineItem,
3067 (start, self._position),
3068 pc, ps, top)
3069 self._update()
3070 if drawing:
3071 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3072 fill="", width=ps)
3073 # Turtle now at position old,
3074 self._position = old
3075 ## if undo is done during crating a polygon, the last vertex
3076 ## will be deleted. if the polygon is entirel deleted,
3077 ## creatigPoly will be set to False.
3078 ## Polygons created before the last one will not be affected by undo()
3079 if self._creatingPoly:
3080 if len(self._poly) > 0:
3081 self._poly.pop()
3082 if self._poly == []:
3083 self._creatingPoly = False
3084 self._poly = None
3085 if filling:
3086 if self._fillpath == []:
3087 self._fillpath = None
3088 print "Unwahrscheinlich in _undogoto!"
3089 elif self._fillpath is not None:
3090 self._fillpath.pop()
3091 self._update() #count=True)
3092
3093 def _rotate(self, angle):
3094 """Turns pen clockwise by angle.
3095 """
3096 if self.undobuffer:
3097 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3098 angle *= self._degreesPerAU
3099 neworient = self._orient.rotate(angle)
3100 tracing = self.screen._tracing
3101 if tracing == 1 and self._speed > 0:
3102 anglevel = 3.0 * self._speed
3103 steps = 1 + int(abs(angle)/anglevel)
3104 delta = 1.0*angle/steps
3105 for _ in range(steps):
3106 self._orient = self._orient.rotate(delta)
3107 self._update()
3108 self._orient = neworient
3109 self._update()
3110
3111 def _newLine(self, usePos=True):
3112 """Closes current line item and starts a new one.
3113 Remark: if current line became too long, animation
3114 performance (via _drawline) slowed down considerably.
3115 """
3116 if len(self.currentLine) > 1:
3117 self.screen._drawline(self.currentLineItem, self.currentLine,
3118 self._pencolor, self._pensize)
3119 self.currentLineItem = self.screen._createline()
3120 self.items.append(self.currentLineItem)
3121 else:
3122 self.screen._drawline(self.currentLineItem, top=True)
3123 self.currentLine = []
3124 if usePos:
3125 self.currentLine = [self._position]
3126
3127 def fill(self, flag=None):
3128 """Call fill(True) before drawing a shape to fill, fill(False) when done.
3129
3130 Optional argument:
3131 flag -- True/False (or 1/0 respectively)
3132
3133 Call fill(True) before drawing the shape you want to fill,
3134 and fill(False) when done.
3135 When used without argument: return fillstate (True if filling,
3136 False else)
3137
3138 Example (for a Turtle instance named turtle):
3139 >>> turtle.fill(True)
3140 >>> turtle.forward(100)
3141 >>> turtle.left(90)
3142 >>> turtle.forward(100)
3143 >>> turtle.left(90)
3144 >>> turtle.forward(100)
3145 >>> turtle.left(90)
3146 >>> turtle.forward(100)
3147 >>> turtle.fill(False)
3148 """
3149 filling = isinstance(self._fillpath, list)
3150 if flag is None:
3151 return filling
3152 screen = self.screen
3153 entry1 = entry2 = ()
3154 if filling:
3155 if len(self._fillpath) > 2:
3156 self.screen._drawpoly(self._fillitem, self._fillpath,
3157 fill=self._fillcolor)
3158 entry1 = ("dofill", self._fillitem)
3159 if flag:
3160 self._fillitem = self.screen._createpoly()
3161 self.items.append(self._fillitem)
3162 self._fillpath = [self._position]
3163 entry2 = ("beginfill", self._fillitem) # , self._fillpath)
3164 self._newLine()
3165 else:
3166 self._fillitem = self._fillpath = None
3167 if self.undobuffer:
3168 if entry1 == ():
3169 if entry2 != ():
3170 self.undobuffer.push(entry2)
3171 else:
3172 if entry2 == ():
3173 self.undobuffer.push(entry1)
3174 else:
3175 self.undobuffer.push(["seq", entry1, entry2])
3176 self._update()
3177
3178 def begin_fill(self):
3179 """Called just before drawing a shape to be filled.
3180
3181 No argument.
3182
3183 Example (for a Turtle instance named turtle):
3184 >>> turtle.begin_fill()
3185 >>> turtle.forward(100)
3186 >>> turtle.left(90)
3187 >>> turtle.forward(100)
3188 >>> turtle.left(90)
3189 >>> turtle.forward(100)
3190 >>> turtle.left(90)
3191 >>> turtle.forward(100)
3192 >>> turtle.end_fill()
3193 """
3194 self.fill(True)
3195
3196 def end_fill(self):
3197 """Fill the shape drawn after the call begin_fill().
3198
3199 No argument.
3200
3201 Example (for a Turtle instance named turtle):
3202 >>> turtle.begin_fill()
3203 >>> turtle.forward(100)
3204 >>> turtle.left(90)
3205 >>> turtle.forward(100)
3206 >>> turtle.left(90)
3207 >>> turtle.forward(100)
3208 >>> turtle.left(90)
3209 >>> turtle.forward(100)
3210 >>> turtle.end_fill()
3211 """
3212 self.fill(False)
3213
3214 def dot(self, size=None, *color):
3215 """Draw a dot with diameter size, using color.
3216
3217 Optional argumentS:
3218 size -- an integer >= 1 (if given)
3219 color -- a colorstring or a numeric color tuple
3220
3221 Draw a circular dot with diameter size, using color.
3222 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3223
3224 Example (for a Turtle instance named turtle):
3225 >>> turtle.dot()
3226 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3227 """
3228 #print "dot-1:", size, color
3229 if not color:
3230 if isinstance(size, (str, tuple)):
3231 color = self._colorstr(size)
3232 size = self._pensize + max(self._pensize, 4)
3233 else:
3234 color = self._pencolor
3235 if not size:
3236 size = self._pensize + max(self._pensize, 4)
3237 else:
3238 if size is None:
3239 size = self._pensize + max(self._pensize, 4)
3240 color = self._colorstr(color)
3241 #print "dot-2:", size, color
3242 if hasattr(self.screen, "_dot"):
3243 item = self.screen._dot(self._position, size, color)
3244 #print "dot:", size, color, "item:", item
3245 self.items.append(item)
3246 if self.undobuffer:
3247 self.undobuffer.push(("dot", item))
3248 else:
3249 pen = self.pen()
3250 if self.undobuffer:
3251 self.undobuffer.push(["seq"])
3252 self.undobuffer.cumulate = True
3253 try:
3254 if self.resizemode() == 'auto':
3255 self.ht()
3256 self.pendown()
3257 self.pensize(size)
3258 self.pencolor(color)
3259 self.forward(0)
3260 finally:
3261 self.pen(pen)
3262 if self.undobuffer:
3263 self.undobuffer.cumulate = False
3264
3265 def _write(self, txt, align, font):
3266 """Performs the writing for write()
3267 """
3268 item, end = self.screen._write(self._position, txt, align, font,
3269 self._pencolor)
3270 self.items.append(item)
3271 if self.undobuffer:
3272 self.undobuffer.push(("wri", item))
3273 return end
3274
3275 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3276 """Write text at the current turtle position.
3277
3278 Arguments:
3279 arg -- info, which is to be written to the TurtleScreen
3280 move (optional) -- True/False
3281 align (optional) -- one of the strings "left", "center" or right"
3282 font (optional) -- a triple (fontname, fontsize, fonttype)
3283
3284 Write text - the string representation of arg - at the current
3285 turtle position according to align ("left", "center" or right")
3286 and with the given font.
3287 If move is True, the pen is moved to the bottom-right corner
3288 of the text. By default, move is False.
3289
3290 Example (for a Turtle instance named turtle):
3291 >>> turtle.write('Home = ', True, align="center")
3292 >>> turtle.write((0,0), True)
3293 """
3294 if self.undobuffer:
3295 self.undobuffer.push(["seq"])
3296 self.undobuffer.cumulate = True
3297 end = self._write(str(arg), align.lower(), font)
3298 if move:
3299 x, y = self.pos()
3300 self.setpos(end, y)
3301 if self.undobuffer:
3302 self.undobuffer.cumulate = False
3303
3304 def begin_poly(self):
3305 """Start recording the vertices of a polygon.
3306
3307 No argument.
3308
3309 Start recording the vertices of a polygon. Current turtle position
3310 is first point of polygon.
3311
3312 Example (for a Turtle instance named turtle):
3313 >>> turtle.begin_poly()
3314 """
3315 self._poly = [self._position]
3316 self._creatingPoly = True
3317
3318 def end_poly(self):
3319 """Stop recording the vertices of a polygon.
3320
3321 No argument.
3322
3323 Stop recording the vertices of a polygon. Current turtle position is
3324 last point of polygon. This will be connected with the first point.
3325
3326 Example (for a Turtle instance named turtle):
3327 >>> turtle.end_poly()
3328 """
3329 self._creatingPoly = False
3330
3331 def get_poly(self):
3332 """Return the lastly recorded polygon.
3333
3334 No argument.
3335
3336 Example (for a Turtle instance named turtle):
3337 >>> p = turtle.get_poly()
3338 >>> turtle.register_shape("myFavouriteShape", p)
3339 """
3340 ## check if there is any poly? -- 1st solution:
3341 if self._poly is not None:
3342 return tuple(self._poly)
3343
3344 def getscreen(self):
3345 """Return the TurtleScreen object, the turtle is drawing on.
3346
3347 No argument.
3348
3349 Return the TurtleScreen object, the turtle is drawing on.
3350 So TurtleScreen-methods can be called for that object.
3351
3352 Example (for a Turtle instance named turtle):
3353 >>> ts = turtle.getscreen()
3354 >>> ts
3355 <turtle.TurtleScreen object at 0x0106B770>
3356 >>> ts.bgcolor("pink")
3357 """
3358 return self.screen
3359
3360 def getturtle(self):
3361 """Return the Turtleobject itself.
3362
3363 No argument.
3364
3365 Only reasonable use: as a function to return the 'anonymous turtle':
3366
3367 Example:
3368 >>> pet = getturtle()
3369 >>> pet.fd(50)
3370 >>> pet
3371 <turtle.Turtle object at 0x0187D810>
3372 >>> turtles()
3373 [<turtle.Turtle object at 0x0187D810>]
3374 """
3375 return self
3376
3377 getpen = getturtle
3378
3379
3380 ################################################################
3381 ### screen oriented methods recurring to methods of TurtleScreen
3382 ################################################################
3383
3384 def window_width(self):
3385 """ Returns the width of the turtle window.
3386
3387 No argument.
3388
3389 Example (for a TurtleScreen instance named screen):
3390 >>> screen.window_width()
3391 640
3392 """
3393 return self.screen._window_size()[0]
3394
3395 def window_height(self):
3396 """ Return the height of the turtle window.
3397
3398 No argument.
3399
3400 Example (for a TurtleScreen instance named screen):
3401 >>> screen.window_height()
3402 480
3403 """
3404 return self.screen._window_size()[1]
3405
3406 def _delay(self, delay=None):
3407 """Set delay value which determines speed of turtle animation.
3408 """
3409 return self.screen.delay(delay)
3410
3411 ##### event binding methods #####
3412
3413 def onclick(self, fun, btn=1, add=None):
3414 """Bind fun to mouse-click event on this turtle on canvas.
3415
3416 Arguments:
3417 fun -- a function with two arguments, to which will be assigned
3418 the coordinates of the clicked point on the canvas.
3419 num -- number of the mouse-button defaults to 1 (left mouse button).
3420 add -- True or False. If True, new binding will be added, otherwise
3421 it will replace a former binding.
3422
3423 Example for the anonymous turtle, i. e. the procedural way:
3424
3425 >>> def turn(x, y):
3426 left(360)
3427
3428 >>> onclick(turn) # Now clicking into the turtle will turn it.
3429 >>> onclick(None) # event-binding will be removed
3430 """
3431 self.screen._onclick(self.turtle._item, fun, btn, add)
3432 self._update()
3433
3434 def onrelease(self, fun, btn=1, add=None):
3435 """Bind fun to mouse-button-release event on this turtle on canvas.
3436
3437 Arguments:
3438 fun -- a function with two arguments, to which will be assigned
3439 the coordinates of the clicked point on the canvas.
3440 num -- number of the mouse-button defaults to 1 (left mouse button).
3441
3442 Example (for a MyTurtle instance named joe):
3443 >>> class MyTurtle(Turtle):
3444 def glow(self,x,y):
3445 self.fillcolor("red")
3446 def unglow(self,x,y):
3447 self.fillcolor("")
3448
3449 >>> joe = MyTurtle()
3450 >>> joe.onclick(joe.glow)
3451 >>> joe.onrelease(joe.unglow)
3452 ### clicking on joe turns fillcolor red,
3453 ### unclicking turns it to transparent.
3454 """
3455 self.screen._onrelease(self.turtle._item, fun, btn, add)
3456 self._update()
3457
3458 def ondrag(self, fun, btn=1, add=None):
3459 """Bind fun to mouse-move event on this turtle on canvas.
3460
3461 Arguments:
3462 fun -- a function with two arguments, to which will be assigned
3463 the coordinates of the clicked point on the canvas.
3464 num -- number of the mouse-button defaults to 1 (left mouse button).
3465
3466 Every sequence of mouse-move-events on a turtle is preceded by a
3467 mouse-click event on that turtle.
3468
3469 Example (for a Turtle instance named turtle):
3470 >>> turtle.ondrag(turtle.goto)
3471
3472 ### Subsequently clicking and dragging a Turtle will
3473 ### move it across the screen thereby producing handdrawings
3474 ### (if pen is down).
3475 """
3476 self.screen._ondrag(self.turtle._item, fun, btn, add)
3477
3478
3479 def _undo(self, action, data):
3480 """Does the main part of the work for undo()
3481 """
3482 if self.undobuffer is None:
3483 return
3484 if action == "rot":
3485 angle, degPAU = data
3486 self._rotate(-angle*degPAU/self._degreesPerAU)
3487 dummy = self.undobuffer.pop()
3488 elif action == "stamp":
3489 stitem = data[0]
3490 self.clearstamp(stitem)
3491 elif action == "go":
3492 self._undogoto(data)
3493 elif action in ["wri", "dot"]:
3494 item = data[0]
3495 self.screen._delete(item)
3496 self.items.remove(item)
3497 elif action == "dofill":
3498 item = data[0]
3499 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3500 fill="", outline="")
3501 elif action == "beginfill":
3502 item = data[0]
3503 self._fillitem = self._fillpath = None
3504 self.screen._delete(item)
3505 self.items.remove(item)
3506 elif action == "pen":
3507 TPen.pen(self, data[0])
3508 self.undobuffer.pop()
3509
3510 def undo(self):
3511 """undo (repeatedly) the last turtle action.
3512
3513 No argument.
3514
3515 undo (repeatedly) the last turtle action.
3516 Number of available undo actions is determined by the size of
3517 the undobuffer.
3518
3519 Example (for a Turtle instance named turtle):
3520 >>> for i in range(4):
3521 turtle.fd(50); turtle.lt(80)
3522
3523 >>> for i in range(8):
3524 turtle.undo()
3525 """
3526 if self.undobuffer is None:
3527 return
3528 item = self.undobuffer.pop()
3529 action = item[0]
3530 data = item[1:]
3531 if action == "seq":
3532 while data:
3533 item = data.pop()
3534 self._undo(item[0], item[1:])
3535 else:
3536 self._undo(action, data)
3537
3538 turtlesize = shapesize
3539
3540RawPen = RawTurtle
3541
3542### Screen - Klasse ########################
3543
3544class Screen(TurtleScreen):
3545
3546 _root = None
3547 _canvas = None
3548 _title = _CFG["title"]
3549
3550 # Borg-Idiom
3551
3552 _shared_state = {}
3553
3554 def __new__(cls, *args, **kwargs):
3555 obj = object.__new__(cls, *args, **kwargs)
3556 obj.__dict__ = cls._shared_state
3557 return obj
Georg Brandl33cece02008-05-20 06:58:21 +00003558
3559 def __init__(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00003560 if Screen._root is None:
3561 Screen._root = self._root = _Root()
3562 self._root.title(Screen._title)
3563 self._root.ondestroy(self._destroy)
3564 if Screen._canvas is None:
3565 width = _CFG["width"]
3566 height = _CFG["height"]
3567 canvwidth = _CFG["canvwidth"]
3568 canvheight = _CFG["canvheight"]
3569 leftright = _CFG["leftright"]
3570 topbottom = _CFG["topbottom"]
3571 self._root.setupcanvas(width, height, canvwidth, canvheight)
3572 Screen._canvas = self._root._getcanvas()
3573 self.setup(width, height, leftright, topbottom)
3574 TurtleScreen.__init__(self, Screen._canvas)
3575 Turtle._screen = self
Georg Brandl33cece02008-05-20 06:58:21 +00003576
Martin v. Löwis87184592008-06-04 06:29:55 +00003577 def setup(self, width=_CFG["width"], height=_CFG["height"],
3578 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3579 """ Set the size and position of the main window.
Georg Brandl33cece02008-05-20 06:58:21 +00003580
Martin v. Löwis87184592008-06-04 06:29:55 +00003581 Arguments:
3582 width: as integer a size in pixels, as float a fraction of the screen.
3583 Default is 50% of screen.
3584 height: as integer the height in pixels, as float a fraction of the
3585 screen. Default is 75% of screen.
3586 startx: if positive, starting position in pixels from the left
3587 edge of the screen, if negative from the right edge
3588 Default, startx=None is to center window horizontally.
3589 starty: if positive, starting position in pixels from the top
3590 edge of the screen, if negative from the bottom edge
3591 Default, starty=None is to center window vertically.
Georg Brandl33cece02008-05-20 06:58:21 +00003592
Martin v. Löwis87184592008-06-04 06:29:55 +00003593 Examples (for a Screen instance named screen):
3594 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3595
3596 sets window to 200x200 pixels, in upper left of screen
3597
3598 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3599
3600 sets window to 75% of screen by 50% of screen and centers
3601 """
3602 if not hasattr(self._root, "set_geometry"):
3603 return
3604 sw = self._root.win_width()
3605 sh = self._root.win_height()
3606 if isinstance(width, float) and 0 <= width <= 1:
3607 width = sw*width
3608 if startx is None:
3609 startx = (sw - width) / 2
3610 if isinstance(height, float) and 0 <= height <= 1:
3611 height = sh*height
3612 if starty is None:
3613 starty = (sh - height) / 2
3614 self._root.set_geometry(width, height, startx, starty)
3615
3616 def title(self, titlestring):
3617 """Set title of turtle-window
3618
3619 Argument:
3620 titlestring -- a string, to appear in the titlebar of the
3621 turtle graphics window.
3622
3623 This is a method of Screen-class. Not available for TurtleScreen-
3624 objects.
3625
3626 Example (for a Screen instance named screen):
3627 >>> screen.title("Welcome to the turtle-zoo!")
3628 """
3629 if Screen._root is not None:
3630 Screen._root.title(titlestring)
3631 Screen._title = titlestring
Georg Brandl33cece02008-05-20 06:58:21 +00003632
3633 def _destroy(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00003634 root = self._root
3635 if root is Screen._root:
3636 Turtle._pen = None
3637 Turtle._screen = None
3638 Screen._root = None
3639 Screen._canvas = None
3640 TurtleScreen._RUNNING = True
Georg Brandl33cece02008-05-20 06:58:21 +00003641 root.destroy()
3642
Martin v. Löwis87184592008-06-04 06:29:55 +00003643 def bye(self):
3644 """Shut the turtlegraphics window.
3645
3646 Example (for a TurtleScreen instance named screen):
3647 >>> screen.bye()
3648 """
3649 self._destroy()
3650
3651 def exitonclick(self):
3652 """Go into mainloop until the mouse is clicked.
3653
3654 No arguments.
3655
3656 Bind bye() method to mouseclick on TurtleScreen.
3657 If "using_IDLE" - value in configuration dictionary is False
3658 (default value), enter mainloop.
3659 If IDLE with -n switch (no subprocess) is used, this value should be
3660 set to True in turtle.cfg. In this case IDLE's mainloop
3661 is active also for the client script.
3662
3663 This is a method of the Screen-class and not available for
3664 TurtleScreen instances.
3665
3666 Example (for a Screen instance named screen):
3667 >>> screen.exitonclick()
3668
3669 """
3670 def exitGracefully(x, y):
3671 """Screen.bye() with two dummy-parameters"""
3672 self.bye()
3673 self.onclick(exitGracefully)
3674 if _CFG["using_IDLE"]:
3675 return
3676 try:
3677 mainloop()
3678 except AttributeError:
3679 exit(0)
3680
3681
3682class Turtle(RawTurtle):
3683 """RawTurtle auto-crating (scrolled) canvas.
3684
3685 When a Turtle object is created or a function derived from some
3686 Turtle method is called a TurtleScreen object is automatically created.
3687 """
3688 _pen = None
3689 _screen = None
3690
3691 def __init__(self,
3692 shape=_CFG["shape"],
3693 undobuffersize=_CFG["undobuffersize"],
3694 visible=_CFG["visible"]):
3695 if Turtle._screen is None:
3696 Turtle._screen = Screen()
3697 RawTurtle.__init__(self, Turtle._screen,
3698 shape=shape,
3699 undobuffersize=undobuffersize,
3700 visible=visible)
3701
3702Pen = Turtle
3703
Georg Brandl33cece02008-05-20 06:58:21 +00003704def _getpen():
Martin v. Löwis87184592008-06-04 06:29:55 +00003705 """Create the 'anonymous' turtle if not already present."""
3706 if Turtle._pen is None:
3707 Turtle._pen = Turtle()
3708 return Turtle._pen
Georg Brandl33cece02008-05-20 06:58:21 +00003709
Martin v. Löwis87184592008-06-04 06:29:55 +00003710def _getscreen():
3711 """Create a TurtleScreen if not already present."""
3712 if Turtle._screen is None:
3713 Turtle._screen = Screen()
3714 return Turtle._screen
Georg Brandl33cece02008-05-20 06:58:21 +00003715
Martin v. Löwis87184592008-06-04 06:29:55 +00003716def write_docstringdict(filename="turtle_docstringdict"):
3717 """Create and write docstring-dictionary to file.
Georg Brandl33cece02008-05-20 06:58:21 +00003718
Martin v. Löwis87184592008-06-04 06:29:55 +00003719 Optional argument:
3720 filename -- a string, used as filename
3721 default value is turtle_docstringdict
Georg Brandl33cece02008-05-20 06:58:21 +00003722
Martin v. Löwis87184592008-06-04 06:29:55 +00003723 Has to be called explicitely, (not used by the turtle-graphics classes)
3724 The docstring dictionary will be written to the Python script <filname>.py
3725 It is intended to serve as a template for translation of the docstrings
3726 into different languages.
Georg Brandl33cece02008-05-20 06:58:21 +00003727 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003728 docsdict = {}
Georg Brandl33cece02008-05-20 06:58:21 +00003729
Martin v. Löwis87184592008-06-04 06:29:55 +00003730 for methodname in _tg_screen_functions:
3731 key = "Screen."+methodname
3732 docsdict[key] = eval(key).__doc__
3733 for methodname in _tg_turtle_functions:
3734 key = "Turtle."+methodname
3735 docsdict[key] = eval(key).__doc__
Georg Brandl33cece02008-05-20 06:58:21 +00003736
Martin v. Löwis87184592008-06-04 06:29:55 +00003737 f = open("%s.py" % filename,"w")
3738 keys = sorted([x for x in docsdict.keys()
3739 if x.split('.')[1] not in _alias_list])
3740 f.write('docsdict = {\n\n')
3741 for key in keys[:-1]:
3742 f.write('%s :\n' % repr(key))
3743 f.write(' """%s\n""",\n\n' % docsdict[key])
3744 key = keys[-1]
3745 f.write('%s :\n' % repr(key))
3746 f.write(' """%s\n"""\n\n' % docsdict[key])
3747 f.write("}\n")
3748 f.close()
Georg Brandl33cece02008-05-20 06:58:21 +00003749
Martin v. Löwis87184592008-06-04 06:29:55 +00003750def read_docstrings(lang):
3751 """Read in docstrings from lang-specific docstring dictionary.
Georg Brandl33cece02008-05-20 06:58:21 +00003752
Martin v. Löwis87184592008-06-04 06:29:55 +00003753 Transfer docstrings, translated to lang, from a dictionary-file
3754 to the methods of classes Screen and Turtle and - in revised form -
3755 to the corresponding functions.
Georg Brandl33cece02008-05-20 06:58:21 +00003756 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003757 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3758 module = __import__(modname)
3759 docsdict = module.docsdict
3760 for key in docsdict:
3761 #print key
3762 try:
3763 eval(key).im_func.__doc__ = docsdict[key]
3764 except:
3765 print "Bad docstring-entry: %s" % key
Georg Brandl33cece02008-05-20 06:58:21 +00003766
Martin v. Löwis87184592008-06-04 06:29:55 +00003767_LANGUAGE = _CFG["language"]
Georg Brandl33cece02008-05-20 06:58:21 +00003768
Martin v. Löwis87184592008-06-04 06:29:55 +00003769try:
3770 if _LANGUAGE != "english":
3771 read_docstrings(_LANGUAGE)
3772except ImportError:
3773 print "Cannot find docsdict for", _LANGUAGE
3774except:
3775 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3776 _LANGUAGE)
3777
3778
3779def getmethparlist(ob):
3780 "Get strings describing the arguments for the given object"
3781 argText1 = argText2 = ""
3782 # bit of a hack for methods - turn it into a function
3783 # but we drop the "self" param.
3784 if type(ob)==types.MethodType:
3785 fob = ob.im_func
3786 argOffset = 1
3787 else:
3788 fob = ob
3789 argOffset = 0
3790 # Try and build one for Python defined functions
3791 if type(fob) in [types.FunctionType, types.LambdaType]:
3792 try:
3793 counter = fob.func_code.co_argcount
3794 items2 = list(fob.func_code.co_varnames[argOffset:counter])
3795 realArgs = fob.func_code.co_varnames[argOffset:counter]
3796 defaults = fob.func_defaults or []
3797 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
3798 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3799 items1 = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
3800 if fob.func_code.co_flags & 0x4:
3801 items1.append("*"+fob.func_code.co_varnames[counter])
3802 items2.append("*"+fob.func_code.co_varnames[counter])
3803 counter += 1
3804 if fob.func_code.co_flags & 0x8:
3805 items1.append("**"+fob.func_code.co_varnames[counter])
3806 items2.append("**"+fob.func_code.co_varnames[counter])
3807 argText1 = ", ".join(items1)
3808 argText1 = "(%s)" % argText1
3809 argText2 = ", ".join(items2)
3810 argText2 = "(%s)" % argText2
3811 except:
3812 pass
3813 return argText1, argText2
3814
3815def _turtle_docrevise(docstr):
3816 """To reduce docstrings from RawTurtle class for functions
3817 """
3818 import re
3819 if docstr is None:
3820 return None
3821 turtlename = _CFG["exampleturtle"]
3822 newdocstr = docstr.replace("%s." % turtlename,"")
3823 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3824 newdocstr = parexp.sub(":", newdocstr)
3825 return newdocstr
3826
3827def _screen_docrevise(docstr):
3828 """To reduce docstrings from TurtleScreen class for functions
3829 """
3830 import re
3831 if docstr is None:
3832 return None
3833 screenname = _CFG["examplescreen"]
3834 newdocstr = docstr.replace("%s." % screenname,"")
3835 parexp = re.compile(r' \(.+ %s\):' % screenname)
3836 newdocstr = parexp.sub(":", newdocstr)
3837 return newdocstr
3838
3839## The following mechanism makes all methods of RawTurtle and Turtle available
3840## as functions. So we can enhance, change, add, delete methods to these
3841## classes and do not need to change anything here.
3842
3843
3844for methodname in _tg_screen_functions:
3845 pl1, pl2 = getmethparlist(eval('Screen.' + methodname))
3846 if pl1 == "":
3847 print ">>>>>>", pl1, pl2
3848 continue
3849 defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
3850 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3851 exec defstr
3852 eval(methodname).__doc__ = _screen_docrevise(eval('Screen.'+methodname).__doc__)
3853
3854for methodname in _tg_turtle_functions:
3855 pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
3856 if pl1 == "":
3857 print ">>>>>>", pl1, pl2
3858 continue
3859 defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
3860 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3861 exec defstr
3862 eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
3863
3864
3865done = mainloop = TK.mainloop
3866del pl1, pl2, defstr
3867
3868if __name__ == "__main__":
3869 def switchpen():
3870 if isdown():
3871 pu()
3872 else:
3873 pd()
3874
3875 def demo1():
3876 """Demo of old turtle.py - module"""
3877 reset()
3878 tracer(True)
3879 up()
3880 backward(100)
3881 down()
3882 # draw 3 squares; the last filled
3883 width(3)
3884 for i in range(3):
3885 if i == 2:
3886 fill(1)
3887 for _ in range(4):
3888 forward(20)
3889 left(90)
3890 if i == 2:
3891 color("maroon")
3892 fill(0)
3893 up()
3894 forward(30)
3895 down()
3896 width(1)
3897 color("black")
3898 # move out of the way
3899 tracer(False)
3900 up()
3901 right(90)
3902 forward(100)
3903 right(90)
3904 forward(100)
3905 right(180)
3906 down()
3907 # some text
3908 write("startstart", 1)
3909 write("start", 1)
3910 color("red")
3911 # staircase
3912 for i in range(5):
Georg Brandl33cece02008-05-20 06:58:21 +00003913 forward(20)
3914 left(90)
Martin v. Löwis87184592008-06-04 06:29:55 +00003915 forward(20)
3916 right(90)
3917 # filled staircase
3918 tracer(True)
3919 fill(1)
3920 for i in range(5):
3921 forward(20)
3922 left(90)
3923 forward(20)
3924 right(90)
3925 fill(0)
3926 # more text
Georg Brandl33cece02008-05-20 06:58:21 +00003927
Martin v. Löwis87184592008-06-04 06:29:55 +00003928 def demo2():
3929 """Demo of some new features."""
3930 speed(1)
3931 st()
3932 pensize(3)
3933 setheading(towards(0, 0))
3934 radius = distance(0, 0)/2.0
3935 rt(90)
3936 for _ in range(18):
3937 switchpen()
3938 circle(radius, 10)
3939 write("wait a moment...")
3940 while undobufferentries():
3941 undo()
3942 reset()
3943 lt(90)
3944 colormode(255)
3945 laenge = 10
3946 pencolor("green")
3947 pensize(3)
3948 lt(180)
3949 for i in range(-2, 16):
3950 if i > 0:
3951 begin_fill()
3952 fillcolor(255-15*i, 0, 15*i)
3953 for _ in range(3):
3954 fd(laenge)
3955 lt(120)
3956 laenge += 10
3957 lt(15)
3958 speed((speed()+1)%12)
3959 end_fill()
Georg Brandl33cece02008-05-20 06:58:21 +00003960
Martin v. Löwis87184592008-06-04 06:29:55 +00003961 lt(120)
3962 pu()
3963 fd(70)
3964 rt(30)
3965 pd()
3966 color("red","yellow")
3967 speed(0)
3968 fill(1)
3969 for _ in range(4):
3970 circle(50, 90)
3971 rt(90)
3972 fd(30)
3973 rt(90)
3974 fill(0)
3975 lt(90)
3976 pu()
3977 fd(30)
3978 pd()
3979 shape("turtle")
Georg Brandl33cece02008-05-20 06:58:21 +00003980
Martin v. Löwis87184592008-06-04 06:29:55 +00003981 tri = getturtle()
3982 tri.resizemode("auto")
3983 turtle = Turtle()
3984 turtle.resizemode("auto")
3985 turtle.shape("turtle")
3986 turtle.reset()
3987 turtle.left(90)
3988 turtle.speed(0)
3989 turtle.up()
3990 turtle.goto(280, 40)
3991 turtle.lt(30)
3992 turtle.down()
3993 turtle.speed(6)
3994 turtle.color("blue","orange")
3995 turtle.pensize(2)
3996 tri.speed(6)
Georg Brandl33cece02008-05-20 06:58:21 +00003997 setheading(towards(turtle))
Martin v. Löwis87184592008-06-04 06:29:55 +00003998 count = 1
3999 while tri.distance(turtle) > 4:
4000 turtle.fd(3.5)
4001 turtle.lt(0.6)
4002 tri.setheading(tri.towards(turtle))
4003 tri.fd(4)
4004 if count % 20 == 0:
4005 turtle.stamp()
4006 tri.stamp()
4007 switchpen()
4008 count += 1
4009 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4010 tri.pencolor("black")
4011 tri.pencolor("red")
Georg Brandl33cece02008-05-20 06:58:21 +00004012
Martin v. Löwis87184592008-06-04 06:29:55 +00004013 def baba(xdummy, ydummy):
4014 clearscreen()
4015 bye()
Georg Brandl33cece02008-05-20 06:58:21 +00004016
Martin v. Löwis87184592008-06-04 06:29:55 +00004017 time.sleep(2)
Georg Brandl33cece02008-05-20 06:58:21 +00004018
Martin v. Löwis87184592008-06-04 06:29:55 +00004019 while undobufferentries():
4020 tri.undo()
4021 turtle.undo()
4022 tri.fd(50)
4023 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4024 tri.onclick(baba, 1)
4025
4026 demo1()
Georg Brandl33cece02008-05-20 06:58:21 +00004027 demo2()
Martin v. Löwis87184592008-06-04 06:29:55 +00004028 exitonclick()