Back to index

python3.2  3.2.2
turtle.py
Go to the documentation of this file.
00001 #
00002 # turtle.py: a Tkinter based turtle graphics module for Python
00003 # Version 1.1b - 4. 5. 2009
00004 #
00005 # Copyright (C) 2006 - 2010  Gregor Lingl
00006 # email: glingl@aon.at
00007 #
00008 # This software is provided 'as-is', without any express or implied
00009 # warranty.  In no event will the authors be held liable for any damages
00010 # arising from the use of this software.
00011 #
00012 # Permission is granted to anyone to use this software for any purpose,
00013 # including commercial applications, and to alter it and redistribute it
00014 # freely, subject to the following restrictions:
00015 #
00016 # 1. The origin of this software must not be misrepresented; you must not
00017 #    claim that you wrote the original software. If you use this software
00018 #    in a product, an acknowledgment in the product documentation would be
00019 #    appreciated but is not required.
00020 # 2. Altered source versions must be plainly marked as such, and must not be
00021 #    misrepresented as being the original software.
00022 # 3. This notice may not be removed or altered from any source distribution.
00023 
00024 
00025 """
00026 Turtle graphics is a popular way for introducing programming to
00027 kids. It was part of the original Logo programming language developed
00028 by Wally Feurzig and Seymour Papert in 1966.
00029 
00030 Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it
00031 the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
00032 the direction it is facing, drawing a line as it moves. Give it the
00033 command turtle.right(25), and it rotates in-place 25 degrees clockwise.
00034 
00035 By combining together these and similar commands, intricate shapes and
00036 pictures can easily be drawn.
00037 
00038 ----- turtle.py
00039 
00040 This module is an extended reimplementation of turtle.py from the
00041 Python standard distribution up to Python 2.5. (See: http://www.python.org)
00042 
00043 It tries to keep the merits of turtle.py and to be (nearly) 100%
00044 compatible with it. This means in the first place to enable the
00045 learning programmer to use all the commands, classes and methods
00046 interactively when using the module from within IDLE run with
00047 the -n switch.
00048 
00049 Roughly it has the following features added:
00050 
00051 - Better animation of the turtle movements, especially of turning the
00052   turtle. So the turtles can more easily be used as a visual feedback
00053   instrument by the (beginning) programmer.
00054 
00055 - Different turtle shapes, gif-images as turtle shapes, user defined
00056   and user controllable turtle shapes, among them compound
00057   (multicolored) shapes. Turtle shapes can be stretched and tilted, which
00058   makes turtles very versatile geometrical objects.
00059 
00060 - Fine control over turtle movement and screen updates via delay(),
00061   and enhanced tracer() and speed() methods.
00062 
00063 - Aliases for the most commonly used commands, like fd for forward etc.,
00064   following the early Logo traditions. This reduces the boring work of
00065   typing long sequences of commands, which often occur in a natural way
00066   when kids try to program fancy pictures on their first encounter with
00067   turtle graphics.
00068 
00069 - Turtles now have an undo()-method with configurable undo-buffer.
00070 
00071 - Some simple commands/methods for creating event driven programs
00072   (mouse-, key-, timer-events). Especially useful for programming games.
00073 
00074 - A scrollable Canvas class. The default scrollable Canvas can be
00075   extended interactively as needed while playing around with the turtle(s).
00076 
00077 - A TurtleScreen class with methods controlling background color or
00078   background image, window and canvas size and other properties of the
00079   TurtleScreen.
00080 
00081 - There is a method, setworldcoordinates(), to install a user defined
00082   coordinate-system for the TurtleScreen.
00083 
00084 - The implementation uses a 2-vector class named Vec2D, derived from tuple.
00085   This class is public, so it can be imported by the application programmer,
00086   which makes certain types of computations very natural and compact.
00087 
00088 - Appearance of the TurtleScreen and the Turtles at startup/import can be
00089   configured by means of a turtle.cfg configuration file.
00090   The default configuration mimics the appearance of the old turtle module.
00091 
00092 - If configured appropriately the module reads in docstrings from a docstring
00093   dictionary in some different language, supplied separately  and replaces
00094   the English ones by those read in. There is a utility function
00095   write_docstringdict() to write a dictionary with the original (English)
00096   docstrings to disc, so it can serve as a template for translations.
00097 
00098 Behind the scenes there are some features included with possible
00099 extensions in in mind. These will be commented and documented elsewhere.
00100 
00101 """
00102 
00103 _ver = "turtle 1.1b- - for Python 3.1   -  4. 5. 2009"
00104 
00105 # print(_ver)
00106 
00107 import tkinter as TK
00108 import types
00109 import math
00110 import time
00111 import os
00112 import inspect
00113 
00114 from os.path import isfile, split, join
00115 from copy import deepcopy
00116 from tkinter import simpledialog
00117 
00118 _tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
00119                'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
00120 _tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
00121         'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
00122         'getshapes', 'listen', 'mainloop', 'mode', 'numinput',
00123         'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer',
00124         'register_shape', 'resetscreen', 'screensize', 'setup',
00125         'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update',
00126         'window_height', 'window_width']
00127 _tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
00128         'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
00129         'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
00130         'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly',
00131         'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
00132         'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
00133         'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
00134         'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
00135         'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
00136         'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle',
00137         'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards',
00138         'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
00139         'write', 'xcor', 'ycor']
00140 _tg_utilities = ['write_docstringdict', 'done']
00141 
00142 __all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
00143            _tg_utilities) # + _math_functions)
00144 
00145 _alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
00146                'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
00147                'turtlesize', 'up', 'width']
00148 
00149 _CFG = {"width" : 0.5,               # Screen
00150         "height" : 0.75,
00151         "canvwidth" : 400,
00152         "canvheight": 300,
00153         "leftright": None,
00154         "topbottom": None,
00155         "mode": "standard",          # TurtleScreen
00156         "colormode": 1.0,
00157         "delay": 10,
00158         "undobuffersize": 1000,      # RawTurtle
00159         "shape": "classic",
00160         "pencolor" : "black",
00161         "fillcolor" : "black",
00162         "resizemode" : "noresize",
00163         "visible" : True,
00164         "language": "english",        # docstrings
00165         "exampleturtle": "turtle",
00166         "examplescreen": "screen",
00167         "title": "Python Turtle Graphics",
00168         "using_IDLE": False
00169        }
00170 
00171 def config_dict(filename):
00172     """Convert content of config-file into dictionary."""
00173     with open(filename, "r") as f:
00174         cfglines = f.readlines()
00175     cfgdict = {}
00176     for line in cfglines:
00177         line = line.strip()
00178         if not line or line.startswith("#"):
00179             continue
00180         try:
00181             key, value = line.split("=")
00182         except:
00183             print("Bad line in config-file %s:\n%s" % (filename,line))
00184             continue
00185         key = key.strip()
00186         value = value.strip()
00187         if value in ["True", "False", "None", "''", '""']:
00188             value = eval(value)
00189         else:
00190             try:
00191                 if "." in value:
00192                     value = float(value)
00193                 else:
00194                     value = int(value)
00195             except:
00196                 pass # value need not be converted
00197         cfgdict[key] = value
00198     return cfgdict
00199 
00200 def readconfig(cfgdict):
00201     """Read config-files, change configuration-dict accordingly.
00202 
00203     If there is a turtle.cfg file in the current working directory,
00204     read it from there. If this contains an importconfig-value,
00205     say 'myway', construct filename turtle_mayway.cfg else use
00206     turtle.cfg and read it from the import-directory, where
00207     turtle.py is located.
00208     Update configuration dictionary first according to config-file,
00209     in the import directory, then according to config-file in the
00210     current working directory.
00211     If no config-file is found, the default configuration is used.
00212     """
00213     default_cfg = "turtle.cfg"
00214     cfgdict1 = {}
00215     cfgdict2 = {}
00216     if isfile(default_cfg):
00217         cfgdict1 = config_dict(default_cfg)
00218     if "importconfig" in cfgdict1:
00219         default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
00220     try:
00221         head, tail = split(__file__)
00222         cfg_file2 = join(head, default_cfg)
00223     except:
00224         cfg_file2 = ""
00225     if isfile(cfg_file2):
00226         cfgdict2 = config_dict(cfg_file2)
00227     _CFG.update(cfgdict2)
00228     _CFG.update(cfgdict1)
00229 
00230 try:
00231     readconfig(_CFG)
00232 except:
00233     print ("No configfile read, reason unknown")
00234 
00235 
00236 class Vec2D(tuple):
00237     """A 2 dimensional vector class, used as a helper class
00238     for implementing turtle graphics.
00239     May be useful for turtle graphics programs also.
00240     Derived from tuple, so a vector is a tuple!
00241 
00242     Provides (for a, b vectors, k number):
00243        a+b vector addition
00244        a-b vector subtraction
00245        a*b inner product
00246        k*a and a*k multiplication with scalar
00247        |a| absolute value of a
00248        a.rotate(angle) rotation
00249     """
00250     def __new__(cls, x, y):
00251         return tuple.__new__(cls, (x, y))
00252     def __add__(self, other):
00253         return Vec2D(self[0]+other[0], self[1]+other[1])
00254     def __mul__(self, other):
00255         if isinstance(other, Vec2D):
00256             return self[0]*other[0]+self[1]*other[1]
00257         return Vec2D(self[0]*other, self[1]*other)
00258     def __rmul__(self, other):
00259         if isinstance(other, int) or isinstance(other, float):
00260             return Vec2D(self[0]*other, self[1]*other)
00261     def __sub__(self, other):
00262         return Vec2D(self[0]-other[0], self[1]-other[1])
00263     def __neg__(self):
00264         return Vec2D(-self[0], -self[1])
00265     def __abs__(self):
00266         return (self[0]**2 + self[1]**2)**0.5
00267     def rotate(self, angle):
00268         """rotate self counterclockwise by angle
00269         """
00270         perp = Vec2D(-self[1], self[0])
00271         angle = angle * math.pi / 180.0
00272         c, s = math.cos(angle), math.sin(angle)
00273         return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
00274     def __getnewargs__(self):
00275         return (self[0], self[1])
00276     def __repr__(self):
00277         return "(%.2f,%.2f)" % self
00278 
00279 
00280 ##############################################################################
00281 ### From here up to line    : Tkinter - Interface for turtle.py            ###
00282 ### May be replaced by an interface to some different graphics toolkit     ###
00283 ##############################################################################
00284 
00285 ## helper functions for Scrolled Canvas, to forward Canvas-methods
00286 ## to ScrolledCanvas class
00287 
00288 def __methodDict(cls, _dict):
00289     """helper function for Scrolled Canvas"""
00290     baseList = list(cls.__bases__)
00291     baseList.reverse()
00292     for _super in baseList:
00293         __methodDict(_super, _dict)
00294     for key, value in cls.__dict__.items():
00295         if type(value) == types.FunctionType:
00296             _dict[key] = value
00297 
00298 def __methods(cls):
00299     """helper function for Scrolled Canvas"""
00300     _dict = {}
00301     __methodDict(cls, _dict)
00302     return _dict.keys()
00303 
00304 __stringBody = (
00305     'def %(method)s(self, *args, **kw): return ' +
00306     'self.%(attribute)s.%(method)s(*args, **kw)')
00307 
00308 def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
00309     ### MANY CHANGES ###
00310     _dict_1 = {}
00311     __methodDict(toClass, _dict_1)
00312     _dict = {}
00313     mfc = __methods(fromClass)
00314     for ex in _dict_1.keys():
00315         if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
00316             pass
00317         else:
00318             _dict[ex] = _dict_1[ex]
00319 
00320     for method, func in _dict.items():
00321         d = {'method': method, 'func': func}
00322         if isinstance(toPart, str):
00323             execString = \
00324                 __stringBody % {'method' : method, 'attribute' : toPart}
00325         exec(execString, d)
00326         setattr(fromClass, method, d[method])   ### NEWU!
00327 
00328 
00329 class ScrolledCanvas(TK.Frame):
00330     """Modeled after the scrolled canvas class from Grayons's Tkinter book.
00331 
00332     Used as the default canvas, which pops up automatically when
00333     using turtle graphics functions or the Turtle class.
00334     """
00335     def __init__(self, master, width=500, height=350,
00336                                           canvwidth=600, canvheight=500):
00337         TK.Frame.__init__(self, master, width=width, height=height)
00338         self._rootwindow = self.winfo_toplevel()
00339         self.width, self.height = width, height
00340         self.canvwidth, self.canvheight = canvwidth, canvheight
00341         self.bg = "white"
00342         self._canvas = TK.Canvas(master, width=width, height=height,
00343                                  bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
00344         self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
00345                                     orient=TK.HORIZONTAL)
00346         self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
00347         self._canvas.configure(xscrollcommand=self.hscroll.set,
00348                                yscrollcommand=self.vscroll.set)
00349         self.rowconfigure(0, weight=1, minsize=0)
00350         self.columnconfigure(0, weight=1, minsize=0)
00351         self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
00352                 column=0, rowspan=1, columnspan=1, sticky='news')
00353         self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
00354                 column=1, rowspan=1, columnspan=1, sticky='news')
00355         self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
00356                 column=0, rowspan=1, columnspan=1, sticky='news')
00357         self.reset()
00358         self._rootwindow.bind('<Configure>', self.onResize)
00359 
00360     def reset(self, canvwidth=None, canvheight=None, bg = None):
00361         """Adjust canvas and scrollbars according to given canvas size."""
00362         if canvwidth:
00363             self.canvwidth = canvwidth
00364         if canvheight:
00365             self.canvheight = canvheight
00366         if bg:
00367             self.bg = bg
00368         self._canvas.config(bg=bg,
00369                         scrollregion=(-self.canvwidth//2, -self.canvheight//2,
00370                                        self.canvwidth//2, self.canvheight//2))
00371         self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
00372                                                                self.canvwidth)
00373         self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
00374                                                               self.canvheight)
00375         self.adjustScrolls()
00376 
00377 
00378     def adjustScrolls(self):
00379         """ Adjust scrollbars according to window- and canvas-size.
00380         """
00381         cwidth = self._canvas.winfo_width()
00382         cheight = self._canvas.winfo_height()
00383         self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
00384         self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
00385         if cwidth < self.canvwidth or cheight < self.canvheight:
00386             self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
00387                               column=0, rowspan=1, columnspan=1, sticky='news')
00388             self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
00389                               column=1, rowspan=1, columnspan=1, sticky='news')
00390         else:
00391             self.hscroll.grid_forget()
00392             self.vscroll.grid_forget()
00393 
00394     def onResize(self, event):
00395         """self-explanatory"""
00396         self.adjustScrolls()
00397 
00398     def bbox(self, *args):
00399         """ 'forward' method, which canvas itself has inherited...
00400         """
00401         return self._canvas.bbox(*args)
00402 
00403     def cget(self, *args, **kwargs):
00404         """ 'forward' method, which canvas itself has inherited...
00405         """
00406         return self._canvas.cget(*args, **kwargs)
00407 
00408     def config(self, *args, **kwargs):
00409         """ 'forward' method, which canvas itself has inherited...
00410         """
00411         self._canvas.config(*args, **kwargs)
00412 
00413     def bind(self, *args, **kwargs):
00414         """ 'forward' method, which canvas itself has inherited...
00415         """
00416         self._canvas.bind(*args, **kwargs)
00417 
00418     def unbind(self, *args, **kwargs):
00419         """ 'forward' method, which canvas itself has inherited...
00420         """
00421         self._canvas.unbind(*args, **kwargs)
00422 
00423     def focus_force(self):
00424         """ 'forward' method, which canvas itself has inherited...
00425         """
00426         self._canvas.focus_force()
00427 
00428 __forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
00429 
00430 
00431 class _Root(TK.Tk):
00432     """Root class for Screen based on Tkinter."""
00433     def __init__(self):
00434         TK.Tk.__init__(self)
00435 
00436     def setupcanvas(self, width, height, cwidth, cheight):
00437         self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
00438         self._canvas.pack(expand=1, fill="both")
00439 
00440     def _getcanvas(self):
00441         return self._canvas
00442 
00443     def set_geometry(self, width, height, startx, starty):
00444         self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
00445 
00446     def ondestroy(self, destroy):
00447         self.wm_protocol("WM_DELETE_WINDOW", destroy)
00448 
00449     def win_width(self):
00450         return self.winfo_screenwidth()
00451 
00452     def win_height(self):
00453         return self.winfo_screenheight()
00454 
00455 Canvas = TK.Canvas
00456 
00457 
00458 class TurtleScreenBase(object):
00459     """Provide the basic graphics functionality.
00460        Interface between Tkinter and turtle.py.
00461 
00462        To port turtle.py to some different graphics toolkit
00463        a corresponding TurtleScreenBase class has to be implemented.
00464     """
00465 
00466     @staticmethod
00467     def _blankimage():
00468         """return a blank image object
00469         """
00470         img = TK.PhotoImage(width=1, height=1)
00471         img.blank()
00472         return img
00473 
00474     @staticmethod
00475     def _image(filename):
00476         """return an image object containing the
00477         imagedata from a gif-file named filename.
00478         """
00479         return TK.PhotoImage(file=filename)
00480 
00481     def __init__(self, cv):
00482         self.cv = cv
00483         if isinstance(cv, ScrolledCanvas):
00484             w = self.cv.canvwidth
00485             h = self.cv.canvheight
00486         else:  # expected: ordinary TK.Canvas
00487             w = int(self.cv.cget("width"))
00488             h = int(self.cv.cget("height"))
00489             self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
00490         self.canvwidth = w
00491         self.canvheight = h
00492         self.xscale = self.yscale = 1.0
00493 
00494     def _createpoly(self):
00495         """Create an invisible polygon item on canvas self.cv)
00496         """
00497         return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
00498 
00499     def _drawpoly(self, polyitem, coordlist, fill=None,
00500                   outline=None, width=None, top=False):
00501         """Configure polygonitem polyitem according to provided
00502         arguments:
00503         coordlist is sequence of coordinates
00504         fill is filling color
00505         outline is outline color
00506         top is a boolean value, which specifies if polyitem
00507         will be put on top of the canvas' displaylist so it
00508         will not be covered by other items.
00509         """
00510         cl = []
00511         for x, y in coordlist:
00512             cl.append(x * self.xscale)
00513             cl.append(-y * self.yscale)
00514         self.cv.coords(polyitem, *cl)
00515         if fill is not None:
00516             self.cv.itemconfigure(polyitem, fill=fill)
00517         if outline is not None:
00518             self.cv.itemconfigure(polyitem, outline=outline)
00519         if width is not None:
00520             self.cv.itemconfigure(polyitem, width=width)
00521         if top:
00522             self.cv.tag_raise(polyitem)
00523 
00524     def _createline(self):
00525         """Create an invisible line item on canvas self.cv)
00526         """
00527         return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
00528                                    capstyle = TK.ROUND)
00529 
00530     def _drawline(self, lineitem, coordlist=None,
00531                   fill=None, width=None, top=False):
00532         """Configure lineitem according to provided arguments:
00533         coordlist is sequence of coordinates
00534         fill is drawing color
00535         width is width of drawn line.
00536         top is a boolean value, which specifies if polyitem
00537         will be put on top of the canvas' displaylist so it
00538         will not be covered by other items.
00539         """
00540         if coordlist is not None:
00541             cl = []
00542             for x, y in coordlist:
00543                 cl.append(x * self.xscale)
00544                 cl.append(-y * self.yscale)
00545             self.cv.coords(lineitem, *cl)
00546         if fill is not None:
00547             self.cv.itemconfigure(lineitem, fill=fill)
00548         if width is not None:
00549             self.cv.itemconfigure(lineitem, width=width)
00550         if top:
00551             self.cv.tag_raise(lineitem)
00552 
00553     def _delete(self, item):
00554         """Delete graphics item from canvas.
00555         If item is"all" delete all graphics items.
00556         """
00557         self.cv.delete(item)
00558 
00559     def _update(self):
00560         """Redraw graphics items on canvas
00561         """
00562         self.cv.update()
00563 
00564     def _delay(self, delay):
00565         """Delay subsequent canvas actions for delay ms."""
00566         self.cv.after(delay)
00567 
00568     def _iscolorstring(self, color):
00569         """Check if the string color is a legal Tkinter color string.
00570         """
00571         try:
00572             rgb = self.cv.winfo_rgb(color)
00573             ok = True
00574         except TK.TclError:
00575             ok = False
00576         return ok
00577 
00578     def _bgcolor(self, color=None):
00579         """Set canvas' backgroundcolor if color is not None,
00580         else return backgroundcolor."""
00581         if color is not None:
00582             self.cv.config(bg = color)
00583             self._update()
00584         else:
00585             return self.cv.cget("bg")
00586 
00587     def _write(self, pos, txt, align, font, pencolor):
00588         """Write txt at pos in canvas with specified font
00589         and color.
00590         Return text item and x-coord of right bottom corner
00591         of text's bounding box."""
00592         x, y = pos
00593         x = x * self.xscale
00594         y = y * self.yscale
00595         anchor = {"left":"sw", "center":"s", "right":"se" }
00596         item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
00597                                         fill = pencolor, font = font)
00598         x0, y0, x1, y1 = self.cv.bbox(item)
00599         self.cv.update()
00600         return item, x1-1
00601 
00602 ##    def _dot(self, pos, size, color):
00603 ##        """may be implemented for some other graphics toolkit"""
00604 
00605     def _onclick(self, item, fun, num=1, add=None):
00606         """Bind fun to mouse-click event on turtle.
00607         fun must be a function with two arguments, the coordinates
00608         of the clicked point on the canvas.
00609         num, the number of the mouse-button defaults to 1
00610         """
00611         if fun is None:
00612             self.cv.tag_unbind(item, "<Button-%s>" % num)
00613         else:
00614             def eventfun(event):
00615                 x, y = (self.cv.canvasx(event.x)/self.xscale,
00616                         -self.cv.canvasy(event.y)/self.yscale)
00617                 fun(x, y)
00618             self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
00619 
00620     def _onrelease(self, item, fun, num=1, add=None):
00621         """Bind fun to mouse-button-release event on turtle.
00622         fun must be a function with two arguments, the coordinates
00623         of the point on the canvas where mouse button is released.
00624         num, the number of the mouse-button defaults to 1
00625 
00626         If a turtle is clicked, first _onclick-event will be performed,
00627         then _onscreensclick-event.
00628         """
00629         if fun is None:
00630             self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
00631         else:
00632             def eventfun(event):
00633                 x, y = (self.cv.canvasx(event.x)/self.xscale,
00634                         -self.cv.canvasy(event.y)/self.yscale)
00635                 fun(x, y)
00636             self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
00637                              eventfun, add)
00638 
00639     def _ondrag(self, item, fun, num=1, add=None):
00640         """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
00641         fun must be a function with two arguments, the coordinates of the
00642         actual mouse position on the canvas.
00643         num, the number of the mouse-button defaults to 1
00644 
00645         Every sequence of mouse-move-events on a turtle is preceded by a
00646         mouse-click event on that turtle.
00647         """
00648         if fun is None:
00649             self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
00650         else:
00651             def eventfun(event):
00652                 try:
00653                     x, y = (self.cv.canvasx(event.x)/self.xscale,
00654                            -self.cv.canvasy(event.y)/self.yscale)
00655                     fun(x, y)
00656                 except:
00657                     pass
00658             self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
00659 
00660     def _onscreenclick(self, fun, num=1, add=None):
00661         """Bind fun to mouse-click event on canvas.
00662         fun must be a function with two arguments, the coordinates
00663         of the clicked point on the canvas.
00664         num, the number of the mouse-button defaults to 1
00665 
00666         If a turtle is clicked, first _onclick-event will be performed,
00667         then _onscreensclick-event.
00668         """
00669         if fun is None:
00670             self.cv.unbind("<Button-%s>" % num)
00671         else:
00672             def eventfun(event):
00673                 x, y = (self.cv.canvasx(event.x)/self.xscale,
00674                         -self.cv.canvasy(event.y)/self.yscale)
00675                 fun(x, y)
00676             self.cv.bind("<Button-%s>" % num, eventfun, add)
00677 
00678     def _onkeyrelease(self, fun, key):
00679         """Bind fun to key-release event of key.
00680         Canvas must have focus. See method listen
00681         """
00682         if fun is None:
00683             self.cv.unbind("<KeyRelease-%s>" % key, None)
00684         else:
00685             def eventfun(event):
00686                 fun()
00687             self.cv.bind("<KeyRelease-%s>" % key, eventfun)
00688 
00689     def _onkeypress(self, fun, key=None):
00690         """If key is given, bind fun to key-press event of key.
00691         Otherwise bind fun to any key-press.
00692         Canvas must have focus. See method listen.
00693         """
00694         if fun is None:
00695             if key is None:
00696                 self.cv.unbind("<KeyPress>", None)
00697             else:
00698                 self.cv.unbind("<KeyPress-%s>" % key, None)
00699         else:
00700             def eventfun(event):
00701                 fun()
00702             if key is None:
00703                 self.cv.bind("<KeyPress>", eventfun)
00704             else:
00705                 self.cv.bind("<KeyPress-%s>" % key, eventfun)
00706 
00707     def _listen(self):
00708         """Set focus on canvas (in order to collect key-events)
00709         """
00710         self.cv.focus_force()
00711 
00712     def _ontimer(self, fun, t):
00713         """Install a timer, which calls fun after t milliseconds.
00714         """
00715         if t == 0:
00716             self.cv.after_idle(fun)
00717         else:
00718             self.cv.after(t, fun)
00719 
00720     def _createimage(self, image):
00721         """Create and return image item on canvas.
00722         """
00723         return self.cv.create_image(0, 0, image=image)
00724 
00725     def _drawimage(self, item, pos, image):
00726         """Configure image item as to draw image object
00727         at position (x,y) on canvas)
00728         """
00729         x, y = pos
00730         self.cv.coords(item, (x * self.xscale, -y * self.yscale))
00731         self.cv.itemconfig(item, image=image)
00732 
00733     def _setbgpic(self, item, image):
00734         """Configure image item as to draw image object
00735         at center of canvas. Set item to the first item
00736         in the displaylist, so it will be drawn below
00737         any other item ."""
00738         self.cv.itemconfig(item, image=image)
00739         self.cv.tag_lower(item)
00740 
00741     def _type(self, item):
00742         """Return 'line' or 'polygon' or 'image' depending on
00743         type of item.
00744         """
00745         return self.cv.type(item)
00746 
00747     def _pointlist(self, item):
00748         """returns list of coordinate-pairs of points of item
00749         Example (for insiders):
00750         >>> from turtle import *
00751         >>> getscreen()._pointlist(getturtle().turtle._item)
00752         [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
00753         (9.9999999999999982, 0.0)]
00754         >>> """
00755         cl = self.cv.coords(item)
00756         pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
00757         return  pl
00758 
00759     def _setscrollregion(self, srx1, sry1, srx2, sry2):
00760         self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
00761 
00762     def _rescale(self, xscalefactor, yscalefactor):
00763         items = self.cv.find_all()
00764         for item in items:
00765             coordinates = list(self.cv.coords(item))
00766             newcoordlist = []
00767             while coordinates:
00768                 x, y = coordinates[:2]
00769                 newcoordlist.append(x * xscalefactor)
00770                 newcoordlist.append(y * yscalefactor)
00771                 coordinates = coordinates[2:]
00772             self.cv.coords(item, *newcoordlist)
00773 
00774     def _resize(self, canvwidth=None, canvheight=None, bg=None):
00775         """Resize the canvas the turtles are drawing on. Does
00776         not alter the drawing window.
00777         """
00778         # needs amendment
00779         if not isinstance(self.cv, ScrolledCanvas):
00780             return self.canvwidth, self.canvheight
00781         if canvwidth is canvheight is bg is None:
00782             return self.cv.canvwidth, self.cv.canvheight
00783         if canvwidth is not None:
00784             self.canvwidth = canvwidth
00785         if canvheight is not None:
00786             self.canvheight = canvheight
00787         self.cv.reset(canvwidth, canvheight, bg)
00788 
00789     def _window_size(self):
00790         """ Return the width and height of the turtle window.
00791         """
00792         width = self.cv.winfo_width()
00793         if width <= 1:  # the window isn't managed by a geometry manager
00794             width = self.cv['width']
00795         height = self.cv.winfo_height()
00796         if height <= 1: # the window isn't managed by a geometry manager
00797             height = self.cv['height']
00798         return width, height
00799 
00800     def mainloop(self):
00801         """Starts event loop - calling Tkinter's mainloop function.
00802 
00803         No argument.
00804 
00805         Must be last statement in a turtle graphics program.
00806         Must NOT be used if a script is run from within IDLE in -n mode
00807         (No subprocess) - for interactive use of turtle graphics.
00808 
00809         Example (for a TurtleScreen instance named screen):
00810         >>> screen.mainloop()
00811 
00812         """
00813         TK.mainloop()
00814 
00815     def textinput(self, title, prompt):
00816         """Pop up a dialog window for input of a string.
00817 
00818         Arguments: title is the title of the dialog window,
00819         prompt is a text mostly describing what information to input.
00820 
00821         Return the string input
00822         If the dialog is canceled, return None.
00823 
00824         Example (for a TurtleScreen instance named screen):
00825         >>> screen.textinput("NIM", "Name of first player:")
00826 
00827         """
00828         return simpledialog.askstring(title, prompt)
00829 
00830     def numinput(self, title, prompt, default=None, minval=None, maxval=None):
00831         """Pop up a dialog window for input of a number.
00832 
00833         Arguments: title is the title of the dialog window,
00834         prompt is a text mostly describing what numerical information to input.
00835         default: default value
00836         minval: minimum value for imput
00837         maxval: maximum value for input
00838 
00839         The number input must be in the range minval .. maxval if these are
00840         given. If not, a hint is issued and the dialog remains open for
00841         correction. Return the number input.
00842         If the dialog is canceled,  return None.
00843 
00844         Example (for a TurtleScreen instance named screen):
00845         >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000)
00846 
00847         """
00848         return simpledialog.askfloat(title, prompt, initialvalue=default,
00849                                      minvalue=minval, maxvalue=maxval)
00850 
00851 
00852 ##############################################################################
00853 ###                  End of Tkinter - interface                            ###
00854 ##############################################################################
00855 
00856 
00857 class Terminator (Exception):
00858     """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
00859 
00860     Thus stops execution of turtle graphics script. Main purpose: use in
00861     in the Demo-Viewer turtle.Demo.py.
00862     """
00863     pass
00864 
00865 
00866 class TurtleGraphicsError(Exception):
00867     """Some TurtleGraphics Error
00868     """
00869 
00870 
00871 class Shape(object):
00872     """Data structure modeling shapes.
00873 
00874     attribute _type is one of "polygon", "image", "compound"
00875     attribute _data is - depending on _type a poygon-tuple,
00876     an image or a list constructed using the addcomponent method.
00877     """
00878     def __init__(self, type_, data=None):
00879         self._type = type_
00880         if type_ == "polygon":
00881             if isinstance(data, list):
00882                 data = tuple(data)
00883         elif type_ == "image":
00884             if isinstance(data, str):
00885                 if data.lower().endswith(".gif") and isfile(data):
00886                     data = TurtleScreen._image(data)
00887                 # else data assumed to be Photoimage
00888         elif type_ == "compound":
00889             data = []
00890         else:
00891             raise TurtleGraphicsError("There is no shape type %s" % type_)
00892         self._data = data
00893 
00894     def addcomponent(self, poly, fill, outline=None):
00895         """Add component to a shape of type compound.
00896 
00897         Arguments: poly is a polygon, i. e. a tuple of number pairs.
00898         fill is the fillcolor of the component,
00899         outline is the outline color of the component.
00900 
00901         call (for a Shapeobject namend s):
00902         --   s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
00903 
00904         Example:
00905         >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
00906         >>> s = Shape("compound")
00907         >>> s.addcomponent(poly, "red", "blue")
00908         ### .. add more components and then use register_shape()
00909         """
00910         if self._type != "compound":
00911             raise TurtleGraphicsError("Cannot add component to %s Shape"
00912                                                                 % self._type)
00913         if outline is None:
00914             outline = fill
00915         self._data.append([poly, fill, outline])
00916 
00917 
00918 class Tbuffer(object):
00919     """Ring buffer used as undobuffer for RawTurtle objects."""
00920     def __init__(self, bufsize=10):
00921         self.bufsize = bufsize
00922         self.buffer = [[None]] * bufsize
00923         self.ptr = -1
00924         self.cumulate = False
00925     def reset(self, bufsize=None):
00926         if bufsize is None:
00927             for i in range(self.bufsize):
00928                 self.buffer[i] = [None]
00929         else:
00930             self.bufsize = bufsize
00931             self.buffer = [[None]] * bufsize
00932         self.ptr = -1
00933     def push(self, item):
00934         if self.bufsize > 0:
00935             if not self.cumulate:
00936                 self.ptr = (self.ptr + 1) % self.bufsize
00937                 self.buffer[self.ptr] = item
00938             else:
00939                 self.buffer[self.ptr].append(item)
00940     def pop(self):
00941         if self.bufsize > 0:
00942             item = self.buffer[self.ptr]
00943             if item is None:
00944                 return None
00945             else:
00946                 self.buffer[self.ptr] = [None]
00947                 self.ptr = (self.ptr - 1) % self.bufsize
00948                 return (item)
00949     def nr_of_items(self):
00950         return self.bufsize - self.buffer.count([None])
00951     def __repr__(self):
00952         return str(self.buffer) + " " + str(self.ptr)
00953 
00954 
00955 
00956 class TurtleScreen(TurtleScreenBase):
00957     """Provides screen oriented methods like setbg etc.
00958 
00959     Only relies upon the methods of TurtleScreenBase and NOT
00960     upon components of the underlying graphics toolkit -
00961     which is Tkinter in this case.
00962     """
00963     _RUNNING = True
00964 
00965     def __init__(self, cv, mode=_CFG["mode"],
00966                  colormode=_CFG["colormode"], delay=_CFG["delay"]):
00967         self._shapes = {
00968                    "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
00969                   "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
00970                               (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
00971                               (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
00972                               (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
00973                               (2,14))),
00974                   "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
00975                               (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
00976                               (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
00977                               (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
00978                               (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
00979                               (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
00980                   "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
00981                               (-10,-10))),
00982                 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
00983                               (-10,-5.77))),
00984                   "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
00985                    "blank" : Shape("image", self._blankimage())
00986                   }
00987 
00988         self._bgpics = {"nopic" : ""}
00989 
00990         TurtleScreenBase.__init__(self, cv)
00991         self._mode = mode
00992         self._delayvalue = delay
00993         self._colormode = _CFG["colormode"]
00994         self._keys = []
00995         self.clear()
00996 
00997     def clear(self):
00998         """Delete all drawings and all turtles from the TurtleScreen.
00999 
01000         No argument.
01001 
01002         Reset empty TurtleScreen to its initial state: white background,
01003         no backgroundimage, no eventbindings and tracing on.
01004 
01005         Example (for a TurtleScreen instance named screen):
01006         screen.clear()
01007 
01008         Note: this method is not available as function.
01009         """
01010         self._delayvalue = _CFG["delay"]
01011         self._colormode = _CFG["colormode"]
01012         self._delete("all")
01013         self._bgpic = self._createimage("")
01014         self._bgpicname = "nopic"
01015         self._tracing = 1
01016         self._updatecounter = 0
01017         self._turtles = []
01018         self.bgcolor("white")
01019         for btn in 1, 2, 3:
01020             self.onclick(None, btn)
01021         self.onkeypress(None)
01022         for key in self._keys[:]:
01023             self.onkey(None, key)
01024             self.onkeypress(None, key)
01025         Turtle._pen = None
01026 
01027     def mode(self, mode=None):
01028         """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
01029 
01030         Optional argument:
01031         mode -- on of the strings 'standard', 'logo' or 'world'
01032 
01033         Mode 'standard' is compatible with turtle.py.
01034         Mode 'logo' is compatible with most Logo-Turtle-Graphics.
01035         Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
01036         this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
01037         If mode is not given, return the current mode.
01038 
01039              Mode      Initial turtle heading     positive angles
01040          ------------|-------------------------|-------------------
01041           'standard'    to the right (east)       counterclockwise
01042             'logo'        upward    (north)         clockwise
01043 
01044         Examples:
01045         >>> mode('logo')   # resets turtle heading to north
01046         >>> mode()
01047         'logo'
01048         """
01049         if mode is None:
01050             return self._mode
01051         mode = mode.lower()
01052         if mode not in ["standard", "logo", "world"]:
01053             raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
01054         self._mode = mode
01055         if mode in ["standard", "logo"]:
01056             self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
01057                                        self.canvwidth//2, self.canvheight//2)
01058             self.xscale = self.yscale = 1.0
01059         self.reset()
01060 
01061     def setworldcoordinates(self, llx, lly, urx, ury):
01062         """Set up a user defined coordinate-system.
01063 
01064         Arguments:
01065         llx -- a number, x-coordinate of lower left corner of canvas
01066         lly -- a number, y-coordinate of lower left corner of canvas
01067         urx -- a number, x-coordinate of upper right corner of canvas
01068         ury -- a number, y-coordinate of upper right corner of canvas
01069 
01070         Set up user coodinat-system and switch to mode 'world' if necessary.
01071         This performs a screen.reset. If mode 'world' is already active,
01072         all drawings are redrawn according to the new coordinates.
01073 
01074         But ATTENTION: in user-defined coordinatesystems angles may appear
01075         distorted. (see Screen.mode())
01076 
01077         Example (for a TurtleScreen instance named screen):
01078         >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
01079         >>> for _ in range(36):
01080                 left(10)
01081                 forward(0.5)
01082         """
01083         if self.mode() != "world":
01084             self.mode("world")
01085         xspan = float(urx - llx)
01086         yspan = float(ury - lly)
01087         wx, wy = self._window_size()
01088         self.screensize(wx-20, wy-20)
01089         oldxscale, oldyscale = self.xscale, self.yscale
01090         self.xscale = self.canvwidth / xspan
01091         self.yscale = self.canvheight / yspan
01092         srx1 = llx * self.xscale
01093         sry1 = -ury * self.yscale
01094         srx2 = self.canvwidth + srx1
01095         sry2 = self.canvheight + sry1
01096         self._setscrollregion(srx1, sry1, srx2, sry2)
01097         self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
01098         self.update()
01099 
01100     def register_shape(self, name, shape=None):
01101         """Adds a turtle shape to TurtleScreen's shapelist.
01102 
01103         Arguments:
01104         (1) name is the name of a gif-file and shape is None.
01105             Installs the corresponding image shape.
01106             !! Image-shapes DO NOT rotate when turning the turtle,
01107             !! so they do not display the heading of the turtle!
01108         (2) name is an arbitrary string and shape is a tuple
01109             of pairs of coordinates. Installs the corresponding
01110             polygon shape
01111         (3) name is an arbitrary string and shape is a
01112             (compound) Shape object. Installs the corresponding
01113             compound shape.
01114         To use a shape, you have to issue the command shape(shapename).
01115 
01116         call: register_shape("turtle.gif")
01117         --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
01118 
01119         Example (for a TurtleScreen instance named screen):
01120         >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
01121 
01122         """
01123         if shape is None:
01124             # image
01125             if name.lower().endswith(".gif"):
01126                 shape = Shape("image", self._image(name))
01127             else:
01128                 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
01129                                           + "Use  help(register_shape)" )
01130         elif isinstance(shape, tuple):
01131             shape = Shape("polygon", shape)
01132         ## else shape assumed to be Shape-instance
01133         self._shapes[name] = shape
01134 
01135     def _colorstr(self, color):
01136         """Return color string corresponding to args.
01137 
01138         Argument may be a string or a tuple of three
01139         numbers corresponding to actual colormode,
01140         i.e. in the range 0<=n<=colormode.
01141 
01142         If the argument doesn't represent a color,
01143         an error is raised.
01144         """
01145         if len(color) == 1:
01146             color = color[0]
01147         if isinstance(color, str):
01148             if self._iscolorstring(color) or color == "":
01149                 return color
01150             else:
01151                 raise TurtleGraphicsError("bad color string: %s" % str(color))
01152         try:
01153             r, g, b = color
01154         except:
01155             raise TurtleGraphicsError("bad color arguments: %s" % str(color))
01156         if self._colormode == 1.0:
01157             r, g, b = [round(255.0*x) for x in (r, g, b)]
01158         if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
01159             raise TurtleGraphicsError("bad color sequence: %s" % str(color))
01160         return "#%02x%02x%02x" % (r, g, b)
01161 
01162     def _color(self, cstr):
01163         if not cstr.startswith("#"):
01164             return cstr
01165         if len(cstr) == 7:
01166             cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
01167         elif len(cstr) == 4:
01168             cl = [16*int(cstr[h], 16) for h in cstr[1:]]
01169         else:
01170             raise TurtleGraphicsError("bad colorstring: %s" % cstr)
01171         return tuple([c * self._colormode/255 for c in cl])
01172 
01173     def colormode(self, cmode=None):
01174         """Return the colormode or set it to 1.0 or 255.
01175 
01176         Optional argument:
01177         cmode -- one of the values 1.0 or 255
01178 
01179         r, g, b values of colortriples have to be in range 0..cmode.
01180 
01181         Example (for a TurtleScreen instance named screen):
01182         >>> screen.colormode()
01183         1.0
01184         >>> screen.colormode(255)
01185         >>> turtle.pencolor(240,160,80)
01186         """
01187         if cmode is None:
01188             return self._colormode
01189         if cmode == 1.0:
01190             self._colormode = float(cmode)
01191         elif cmode == 255:
01192             self._colormode = int(cmode)
01193 
01194     def reset(self):
01195         """Reset all Turtles on the Screen to their initial state.
01196 
01197         No argument.
01198 
01199         Example (for a TurtleScreen instance named screen):
01200         >>> screen.reset()
01201         """
01202         for turtle in self._turtles:
01203             turtle._setmode(self._mode)
01204             turtle.reset()
01205 
01206     def turtles(self):
01207         """Return the list of turtles on the screen.
01208 
01209         Example (for a TurtleScreen instance named screen):
01210         >>> screen.turtles()
01211         [<turtle.Turtle object at 0x00E11FB0>]
01212         """
01213         return self._turtles
01214 
01215     def bgcolor(self, *args):
01216         """Set or return backgroundcolor of the TurtleScreen.
01217 
01218         Arguments (if given): a color string or three numbers
01219         in the range 0..colormode or a 3-tuple of such numbers.
01220 
01221         Example (for a TurtleScreen instance named screen):
01222         >>> screen.bgcolor("orange")
01223         >>> screen.bgcolor()
01224         'orange'
01225         >>> screen.bgcolor(0.5,0,0.5)
01226         >>> screen.bgcolor()
01227         '#800080'
01228         """
01229         if args:
01230             color = self._colorstr(args)
01231         else:
01232             color = None
01233         color = self._bgcolor(color)
01234         if color is not None:
01235             color = self._color(color)
01236         return color
01237 
01238     def tracer(self, n=None, delay=None):
01239         """Turns turtle animation on/off and set delay for update drawings.
01240 
01241         Optional arguments:
01242         n -- nonnegative  integer
01243         delay -- nonnegative  integer
01244 
01245         If n is given, only each n-th regular screen update is really performed.
01246         (Can be used to accelerate the drawing of complex graphics.)
01247         Second arguments sets delay value (see RawTurtle.delay())
01248 
01249         Example (for a TurtleScreen instance named screen):
01250         >>> screen.tracer(8, 25)
01251         >>> dist = 2
01252         >>> for i in range(200):
01253                 fd(dist)
01254                 rt(90)
01255                 dist += 2
01256         """
01257         if n is None:
01258             return self._tracing
01259         self._tracing = int(n)
01260         self._updatecounter = 0
01261         if delay is not None:
01262             self._delayvalue = int(delay)
01263         if self._tracing:
01264             self.update()
01265 
01266     def delay(self, delay=None):
01267         """ Return or set the drawing delay in milliseconds.
01268 
01269         Optional argument:
01270         delay -- positive integer
01271 
01272         Example (for a TurtleScreen instance named screen):
01273         >>> screen.delay(15)
01274         >>> screen.delay()
01275         15
01276         """
01277         if delay is None:
01278             return self._delayvalue
01279         self._delayvalue = int(delay)
01280 
01281     def _incrementudc(self):
01282         "Increment upadate counter."""
01283         if not TurtleScreen._RUNNING:
01284             TurtleScreen._RUNNNING = True
01285             raise Terminator
01286         if self._tracing > 0:
01287             self._updatecounter += 1
01288             self._updatecounter %= self._tracing
01289 
01290     def update(self):
01291         """Perform a TurtleScreen update.
01292         """
01293         tracing = self._tracing
01294         self._tracing = True
01295         for t in self.turtles():
01296             t._update_data()
01297             t._drawturtle()
01298         self._tracing = tracing
01299         self._update()
01300 
01301     def window_width(self):
01302         """ Return the width of the turtle window.
01303 
01304         Example (for a TurtleScreen instance named screen):
01305         >>> screen.window_width()
01306         640
01307         """
01308         return self._window_size()[0]
01309 
01310     def window_height(self):
01311         """ Return the height of the turtle window.
01312 
01313         Example (for a TurtleScreen instance named screen):
01314         >>> screen.window_height()
01315         480
01316         """
01317         return self._window_size()[1]
01318 
01319     def getcanvas(self):
01320         """Return the Canvas of this TurtleScreen.
01321 
01322         No argument.
01323 
01324         Example (for a Screen instance named screen):
01325         >>> cv = screen.getcanvas()
01326         >>> cv
01327         <turtle.ScrolledCanvas instance at 0x010742D8>
01328         """
01329         return self.cv
01330 
01331     def getshapes(self):
01332         """Return a list of names of all currently available turtle shapes.
01333 
01334         No argument.
01335 
01336         Example (for a TurtleScreen instance named screen):
01337         >>> screen.getshapes()
01338         ['arrow', 'blank', 'circle', ... , 'turtle']
01339         """
01340         return sorted(self._shapes.keys())
01341 
01342     def onclick(self, fun, btn=1, add=None):
01343         """Bind fun to mouse-click event on canvas.
01344 
01345         Arguments:
01346         fun -- a function with two arguments, the coordinates of the
01347                clicked point on the canvas.
01348         num -- the number of the mouse-button, defaults to 1
01349 
01350         Example (for a TurtleScreen instance named screen
01351         and a Turtle instance named turtle):
01352 
01353         >>> screen.onclick(turtle.goto)
01354 
01355         ### Subsequently clicking into the TurtleScreen will
01356         ### make the turtle move to the clicked point.
01357         >>> screen.onclick(None)
01358 
01359         ### event-binding will be removed
01360         """
01361         self._onscreenclick(fun, btn, add)
01362 
01363     def onkey(self, fun, key):
01364         """Bind fun to key-release event of key.
01365 
01366         Arguments:
01367         fun -- a function with no arguments
01368         key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
01369 
01370         In order to be able to register key-events, TurtleScreen
01371         must have focus. (See method listen.)
01372 
01373         Example (for a TurtleScreen instance named screen
01374         and a Turtle instance named turtle):
01375 
01376         >>> def f():
01377                 fd(50)
01378                 lt(60)
01379 
01380 
01381         >>> screen.onkey(f, "Up")
01382         >>> screen.listen()
01383 
01384         ### Subsequently the turtle can be moved by
01385         ### repeatedly pressing the up-arrow key,
01386         ### consequently drawing a hexagon
01387         """
01388         if fun is None:
01389             if key in self._keys:
01390                 self._keys.remove(key)
01391         elif key not in self._keys:
01392             self._keys.append(key)
01393         self._onkeyrelease(fun, key)
01394 
01395     def onkeypress(self, fun, key=None):
01396         """Bind fun to key-press event of key if key is given,
01397         or to any key-press-event if no key is given.
01398 
01399         Arguments:
01400         fun -- a function with no arguments
01401         key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
01402 
01403         In order to be able to register key-events, TurtleScreen
01404         must have focus. (See method listen.)
01405 
01406         Example (for a TurtleScreen instance named screen
01407         and a Turtle instance named turtle):
01408 
01409         >>> def f():
01410                 fd(50)
01411 
01412 
01413         >>> screen.onkey(f, "Up")
01414         >>> screen.listen()
01415 
01416         ### Subsequently the turtle can be moved by
01417         ### repeatedly pressing the up-arrow key,
01418         ### or by keeping pressed the up-arrow key.
01419         ### consequently drawing a hexagon.
01420         """
01421         if fun is None:
01422             if key in self._keys:
01423                 self._keys.remove(key)
01424         elif key is not None and key not in self._keys:
01425             self._keys.append(key)
01426         self._onkeypress(fun, key)
01427 
01428     def listen(self, xdummy=None, ydummy=None):
01429         """Set focus on TurtleScreen (in order to collect key-events)
01430 
01431         No arguments.
01432         Dummy arguments are provided in order
01433         to be able to pass listen to the onclick method.
01434 
01435         Example (for a TurtleScreen instance named screen):
01436         >>> screen.listen()
01437         """
01438         self._listen()
01439 
01440     def ontimer(self, fun, t=0):
01441         """Install a timer, which calls fun after t milliseconds.
01442 
01443         Arguments:
01444         fun -- a function with no arguments.
01445         t -- a number >= 0
01446 
01447         Example (for a TurtleScreen instance named screen):
01448 
01449         >>> running = True
01450         >>> def f():
01451                 if running:
01452                         fd(50)
01453                         lt(60)
01454                         screen.ontimer(f, 250)
01455 
01456         >>> f()   ### makes the turtle marching around
01457         >>> running = False
01458         """
01459         self._ontimer(fun, t)
01460 
01461     def bgpic(self, picname=None):
01462         """Set background image or return name of current backgroundimage.
01463 
01464         Optional argument:
01465         picname -- a string, name of a gif-file or "nopic".
01466 
01467         If picname is a filename, set the corresponding image as background.
01468         If picname is "nopic", delete backgroundimage, if present.
01469         If picname is None, return the filename of the current backgroundimage.
01470 
01471         Example (for a TurtleScreen instance named screen):
01472         >>> screen.bgpic()
01473         'nopic'
01474         >>> screen.bgpic("landscape.gif")
01475         >>> screen.bgpic()
01476         'landscape.gif'
01477         """
01478         if picname is None:
01479             return self._bgpicname
01480         if picname not in self._bgpics:
01481             self._bgpics[picname] = self._image(picname)
01482         self._setbgpic(self._bgpic, self._bgpics[picname])
01483         self._bgpicname = picname
01484 
01485     def screensize(self, canvwidth=None, canvheight=None, bg=None):
01486         """Resize the canvas the turtles are drawing on.
01487 
01488         Optional arguments:
01489         canvwidth -- positive integer, new width of canvas in pixels
01490         canvheight --  positive integer, new height of canvas in pixels
01491         bg -- colorstring or color-tuple, new backgroundcolor
01492         If no arguments are given, return current (canvaswidth, canvasheight)
01493 
01494         Do not alter the drawing window. To observe hidden parts of
01495         the canvas use the scrollbars. (Can make visible those parts
01496         of a drawing, which were outside the canvas before!)
01497 
01498         Example (for a Turtle instance named turtle):
01499         >>> turtle.screensize(2000,1500)
01500             ### e. g. to search for an erroneously escaped turtle ;-)
01501         """
01502         return self._resize(canvwidth, canvheight, bg)
01503 
01504     onscreenclick = onclick
01505     resetscreen = reset
01506     clearscreen = clear
01507     addshape = register_shape
01508     onkeyrelease = onkey
01509 
01510 class TNavigator(object):
01511     """Navigation part of the RawTurtle.
01512     Implements methods for turtle movement.
01513     """
01514     START_ORIENTATION = {
01515         "standard": Vec2D(1.0, 0.0),
01516         "world"   : Vec2D(1.0, 0.0),
01517         "logo"    : Vec2D(0.0, 1.0)  }
01518     DEFAULT_MODE = "standard"
01519     DEFAULT_ANGLEOFFSET = 0
01520     DEFAULT_ANGLEORIENT = 1
01521 
01522     def __init__(self, mode=DEFAULT_MODE):
01523         self._angleOffset = self.DEFAULT_ANGLEOFFSET
01524         self._angleOrient = self.DEFAULT_ANGLEORIENT
01525         self._mode = mode
01526         self.undobuffer = None
01527         self.degrees()
01528         self._mode = None
01529         self._setmode(mode)
01530         TNavigator.reset(self)
01531 
01532     def reset(self):
01533         """reset turtle to its initial values
01534 
01535         Will be overwritten by parent class
01536         """
01537         self._position = Vec2D(0.0, 0.0)
01538         self._orient =  TNavigator.START_ORIENTATION[self._mode]
01539 
01540     def _setmode(self, mode=None):
01541         """Set turtle-mode to 'standard', 'world' or 'logo'.
01542         """
01543         if mode is None:
01544             return self._mode
01545         if mode not in ["standard", "logo", "world"]:
01546             return
01547         self._mode = mode
01548         if mode in ["standard", "world"]:
01549             self._angleOffset = 0
01550             self._angleOrient = 1
01551         else: # mode == "logo":
01552             self._angleOffset = self._fullcircle/4.
01553             self._angleOrient = -1
01554 
01555     def _setDegreesPerAU(self, fullcircle):
01556         """Helper function for degrees() and radians()"""
01557         self._fullcircle = fullcircle
01558         self._degreesPerAU = 360/fullcircle
01559         if self._mode == "standard":
01560             self._angleOffset = 0
01561         else:
01562             self._angleOffset = fullcircle/4.
01563 
01564     def degrees(self, fullcircle=360.0):
01565         """ Set angle measurement units to degrees.
01566 
01567         Optional argument:
01568         fullcircle -  a number
01569 
01570         Set angle measurement units, i. e. set number
01571         of 'degrees' for a full circle. Dafault value is
01572         360 degrees.
01573 
01574         Example (for a Turtle instance named turtle):
01575         >>> turtle.left(90)
01576         >>> turtle.heading()
01577         90
01578 
01579         Change angle measurement unit to grad (also known as gon,
01580         grade, or gradian and equals 1/100-th of the right angle.)
01581         >>> turtle.degrees(400.0)
01582         >>> turtle.heading()
01583         100
01584 
01585         """
01586         self._setDegreesPerAU(fullcircle)
01587 
01588     def radians(self):
01589         """ Set the angle measurement units to radians.
01590 
01591         No arguments.
01592 
01593         Example (for a Turtle instance named turtle):
01594         >>> turtle.heading()
01595         90
01596         >>> turtle.radians()
01597         >>> turtle.heading()
01598         1.5707963267948966
01599         """
01600         self._setDegreesPerAU(2*math.pi)
01601 
01602     def _go(self, distance):
01603         """move turtle forward by specified distance"""
01604         ende = self._position + self._orient * distance
01605         self._goto(ende)
01606 
01607     def _rotate(self, angle):
01608         """Turn turtle counterclockwise by specified angle if angle > 0."""
01609         angle *= self._degreesPerAU
01610         self._orient = self._orient.rotate(angle)
01611 
01612     def _goto(self, end):
01613         """move turtle to position end."""
01614         self._position = end
01615 
01616     def forward(self, distance):
01617         """Move the turtle forward by the specified distance.
01618 
01619         Aliases: forward | fd
01620 
01621         Argument:
01622         distance -- a number (integer or float)
01623 
01624         Move the turtle forward by the specified distance, in the direction
01625         the turtle is headed.
01626 
01627         Example (for a Turtle instance named turtle):
01628         >>> turtle.position()
01629         (0.00, 0.00)
01630         >>> turtle.forward(25)
01631         >>> turtle.position()
01632         (25.00,0.00)
01633         >>> turtle.forward(-75)
01634         >>> turtle.position()
01635         (-50.00,0.00)
01636         """
01637         self._go(distance)
01638 
01639     def back(self, distance):
01640         """Move the turtle backward by distance.
01641 
01642         Aliases: back | backward | bk
01643 
01644         Argument:
01645         distance -- a number
01646 
01647         Move the turtle backward by distance ,opposite to the direction the
01648         turtle is headed. Do not change the turtle's heading.
01649 
01650         Example (for a Turtle instance named turtle):
01651         >>> turtle.position()
01652         (0.00, 0.00)
01653         >>> turtle.backward(30)
01654         >>> turtle.position()
01655         (-30.00, 0.00)
01656         """
01657         self._go(-distance)
01658 
01659     def right(self, angle):
01660         """Turn turtle right by angle units.
01661 
01662         Aliases: right | rt
01663 
01664         Argument:
01665         angle -- a number (integer or float)
01666 
01667         Turn turtle right by angle units. (Units are by default degrees,
01668         but can be set via the degrees() and radians() functions.)
01669         Angle orientation depends on mode. (See this.)
01670 
01671         Example (for a Turtle instance named turtle):
01672         >>> turtle.heading()
01673         22.0
01674         >>> turtle.right(45)
01675         >>> turtle.heading()
01676         337.0
01677         """
01678         self._rotate(-angle)
01679 
01680     def left(self, angle):
01681         """Turn turtle left by angle units.
01682 
01683         Aliases: left | lt
01684 
01685         Argument:
01686         angle -- a number (integer or float)
01687 
01688         Turn turtle left by angle units. (Units are by default degrees,
01689         but can be set via the degrees() and radians() functions.)
01690         Angle orientation depends on mode. (See this.)
01691 
01692         Example (for a Turtle instance named turtle):
01693         >>> turtle.heading()
01694         22.0
01695         >>> turtle.left(45)
01696         >>> turtle.heading()
01697         67.0
01698         """
01699         self._rotate(angle)
01700 
01701     def pos(self):
01702         """Return the turtle's current location (x,y), as a Vec2D-vector.
01703 
01704         Aliases: pos | position
01705 
01706         No arguments.
01707 
01708         Example (for a Turtle instance named turtle):
01709         >>> turtle.pos()
01710         (0.00, 240.00)
01711         """
01712         return self._position
01713 
01714     def xcor(self):
01715         """ Return the turtle's x coordinate.
01716 
01717         No arguments.
01718 
01719         Example (for a Turtle instance named turtle):
01720         >>> reset()
01721         >>> turtle.left(60)
01722         >>> turtle.forward(100)
01723         >>> print turtle.xcor()
01724         50.0
01725         """
01726         return self._position[0]
01727 
01728     def ycor(self):
01729         """ Return the turtle's y coordinate
01730         ---
01731         No arguments.
01732 
01733         Example (for a Turtle instance named turtle):
01734         >>> reset()
01735         >>> turtle.left(60)
01736         >>> turtle.forward(100)
01737         >>> print turtle.ycor()
01738         86.6025403784
01739         """
01740         return self._position[1]
01741 
01742 
01743     def goto(self, x, y=None):
01744         """Move turtle to an absolute position.
01745 
01746         Aliases: setpos | setposition | goto:
01747 
01748         Arguments:
01749         x -- a number      or     a pair/vector of numbers
01750         y -- a number             None
01751 
01752         call: goto(x, y)         # two coordinates
01753         --or: goto((x, y))       # a pair (tuple) of coordinates
01754         --or: goto(vec)          # e.g. as returned by pos()
01755 
01756         Move turtle to an absolute position. If the pen is down,
01757         a line will be drawn. The turtle's orientation does not change.
01758 
01759         Example (for a Turtle instance named turtle):
01760         >>> tp = turtle.pos()
01761         >>> tp
01762         (0.00, 0.00)
01763         >>> turtle.setpos(60,30)
01764         >>> turtle.pos()
01765         (60.00,30.00)
01766         >>> turtle.setpos((20,80))
01767         >>> turtle.pos()
01768         (20.00,80.00)
01769         >>> turtle.setpos(tp)
01770         >>> turtle.pos()
01771         (0.00,0.00)
01772         """
01773         if y is None:
01774             self._goto(Vec2D(*x))
01775         else:
01776             self._goto(Vec2D(x, y))
01777 
01778     def home(self):
01779         """Move turtle to the origin - coordinates (0,0).
01780 
01781         No arguments.
01782 
01783         Move turtle to the origin - coordinates (0,0) and set its
01784         heading to its start-orientation (which depends on mode).
01785 
01786         Example (for a Turtle instance named turtle):
01787         >>> turtle.home()
01788         """
01789         self.goto(0, 0)
01790         self.setheading(0)
01791 
01792     def setx(self, x):
01793         """Set the turtle's first coordinate to x
01794 
01795         Argument:
01796         x -- a number (integer or float)
01797 
01798         Set the turtle's first coordinate to x, leave second coordinate
01799         unchanged.
01800 
01801         Example (for a Turtle instance named turtle):
01802         >>> turtle.position()
01803         (0.00, 240.00)
01804         >>> turtle.setx(10)
01805         >>> turtle.position()
01806         (10.00, 240.00)
01807         """
01808         self._goto(Vec2D(x, self._position[1]))
01809 
01810     def sety(self, y):
01811         """Set the turtle's second coordinate to y
01812 
01813         Argument:
01814         y -- a number (integer or float)
01815 
01816         Set the turtle's first coordinate to x, second coordinate remains
01817         unchanged.
01818 
01819         Example (for a Turtle instance named turtle):
01820         >>> turtle.position()
01821         (0.00, 40.00)
01822         >>> turtle.sety(-10)
01823         >>> turtle.position()
01824         (0.00, -10.00)
01825         """
01826         self._goto(Vec2D(self._position[0], y))
01827 
01828     def distance(self, x, y=None):
01829         """Return the distance from the turtle to (x,y) in turtle step units.
01830 
01831         Arguments:
01832         x -- a number   or  a pair/vector of numbers   or   a turtle instance
01833         y -- a number       None                            None
01834 
01835         call: distance(x, y)         # two coordinates
01836         --or: distance((x, y))       # a pair (tuple) of coordinates
01837         --or: distance(vec)          # e.g. as returned by pos()
01838         --or: distance(mypen)        # where mypen is another turtle
01839 
01840         Example (for a Turtle instance named turtle):
01841         >>> turtle.pos()
01842         (0.00, 0.00)
01843         >>> turtle.distance(30,40)
01844         50.0
01845         >>> pen = Turtle()
01846         >>> pen.forward(77)
01847         >>> turtle.distance(pen)
01848         77.0
01849         """
01850         if y is not None:
01851             pos = Vec2D(x, y)
01852         if isinstance(x, Vec2D):
01853             pos = x
01854         elif isinstance(x, tuple):
01855             pos = Vec2D(*x)
01856         elif isinstance(x, TNavigator):
01857             pos = x._position
01858         return abs(pos - self._position)
01859 
01860     def towards(self, x, y=None):
01861         """Return the angle of the line from the turtle's position to (x, y).
01862 
01863         Arguments:
01864         x -- a number   or  a pair/vector of numbers   or   a turtle instance
01865         y -- a number       None                            None
01866 
01867         call: distance(x, y)         # two coordinates
01868         --or: distance((x, y))       # a pair (tuple) of coordinates
01869         --or: distance(vec)          # e.g. as returned by pos()
01870         --or: distance(mypen)        # where mypen is another turtle
01871 
01872         Return the angle, between the line from turtle-position to position
01873         specified by x, y and the turtle's start orientation. (Depends on
01874         modes - "standard" or "logo")
01875 
01876         Example (for a Turtle instance named turtle):
01877         >>> turtle.pos()
01878         (10.00, 10.00)
01879         >>> turtle.towards(0,0)
01880         225.0
01881         """
01882         if y is not None:
01883             pos = Vec2D(x, y)
01884         if isinstance(x, Vec2D):
01885             pos = x
01886         elif isinstance(x, tuple):
01887             pos = Vec2D(*x)
01888         elif isinstance(x, TNavigator):
01889             pos = x._position
01890         x, y = pos - self._position
01891         result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
01892         result /= self._degreesPerAU
01893         return (self._angleOffset + self._angleOrient*result) % self._fullcircle
01894 
01895     def heading(self):
01896         """ Return the turtle's current heading.
01897 
01898         No arguments.
01899 
01900         Example (for a Turtle instance named turtle):
01901         >>> turtle.left(67)
01902         >>> turtle.heading()
01903         67.0
01904         """
01905         x, y = self._orient
01906         result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
01907         result /= self._degreesPerAU
01908         return (self._angleOffset + self._angleOrient*result) % self._fullcircle
01909 
01910     def setheading(self, to_angle):
01911         """Set the orientation of the turtle to to_angle.
01912 
01913         Aliases:  setheading | seth
01914 
01915         Argument:
01916         to_angle -- a number (integer or float)
01917 
01918         Set the orientation of the turtle to to_angle.
01919         Here are some common directions in degrees:
01920 
01921          standard - mode:          logo-mode:
01922         -------------------|--------------------
01923            0 - east                0 - north
01924           90 - north              90 - east
01925          180 - west              180 - south
01926          270 - south             270 - west
01927 
01928         Example (for a Turtle instance named turtle):
01929         >>> turtle.setheading(90)
01930         >>> turtle.heading()
01931         90
01932         """
01933         angle = (to_angle - self.heading())*self._angleOrient
01934         full = self._fullcircle
01935         angle = (angle+full/2.)%full - full/2.
01936         self._rotate(angle)
01937 
01938     def circle(self, radius, extent = None, steps = None):
01939         """ Draw a circle with given radius.
01940 
01941         Arguments:
01942         radius -- a number
01943         extent (optional) -- a number
01944         steps (optional) -- an integer
01945 
01946         Draw a circle with given radius. The center is radius units left
01947         of the turtle; extent - an angle - determines which part of the
01948         circle is drawn. If extent is not given, draw the entire circle.
01949         If extent is not a full circle, one endpoint of the arc is the
01950         current pen position. Draw the arc in counterclockwise direction
01951         if radius is positive, otherwise in clockwise direction. Finally
01952         the direction of the turtle is changed by the amount of extent.
01953 
01954         As the circle is approximated by an inscribed regular polygon,
01955         steps determines the number of steps to use. If not given,
01956         it will be calculated automatically. Maybe used to draw regular
01957         polygons.
01958 
01959         call: circle(radius)                  # full circle
01960         --or: circle(radius, extent)          # arc
01961         --or: circle(radius, extent, steps)
01962         --or: circle(radius, steps=6)         # 6-sided polygon
01963 
01964         Example (for a Turtle instance named turtle):
01965         >>> turtle.circle(50)
01966         >>> turtle.circle(120, 180)  # semicircle
01967         """
01968         if self.undobuffer:
01969             self.undobuffer.push(["seq"])
01970             self.undobuffer.cumulate = True
01971         speed = self.speed()
01972         if extent is None:
01973             extent = self._fullcircle
01974         if steps is None:
01975             frac = abs(extent)/self._fullcircle
01976             steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
01977         w = 1.0 * extent / steps
01978         w2 = 0.5 * w
01979         l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
01980         if radius < 0:
01981             l, w, w2 = -l, -w, -w2
01982         tr = self._tracer()
01983         dl = self._delay()
01984         if speed == 0:
01985             self._tracer(0, 0)
01986         else:
01987             self.speed(0)
01988         self._rotate(w2)
01989         for i in range(steps):
01990             self.speed(speed)
01991             self._go(l)
01992             self.speed(0)
01993             self._rotate(w)
01994         self._rotate(-w2)
01995         if speed == 0:
01996             self._tracer(tr, dl)
01997         self.speed(speed)
01998         if self.undobuffer:
01999             self.undobuffer.cumulate = False
02000 
02001 ## three dummy methods to be implemented by child class:
02002 
02003     def speed(self, s=0):
02004         """dummy method - to be overwritten by child class"""
02005     def _tracer(self, a=None, b=None):
02006         """dummy method - to be overwritten by child class"""
02007     def _delay(self, n=None):
02008         """dummy method - to be overwritten by child class"""
02009 
02010     fd = forward
02011     bk = back
02012     backward = back
02013     rt = right
02014     lt = left
02015     position = pos
02016     setpos = goto
02017     setposition = goto
02018     seth = setheading
02019 
02020 
02021 class TPen(object):
02022     """Drawing part of the RawTurtle.
02023     Implements drawing properties.
02024     """
02025     def __init__(self, resizemode=_CFG["resizemode"]):
02026         self._resizemode = resizemode # or "user" or "noresize"
02027         self.undobuffer = None
02028         TPen._reset(self)
02029 
02030     def _reset(self, pencolor=_CFG["pencolor"],
02031                      fillcolor=_CFG["fillcolor"]):
02032         self._pensize = 1
02033         self._shown = True
02034         self._pencolor = pencolor
02035         self._fillcolor = fillcolor
02036         self._drawing = True
02037         self._speed = 3
02038         self._stretchfactor = (1., 1.)
02039         self._shearfactor = 0.
02040         self._tilt = 0.
02041         self._shapetrafo = (1., 0., 0., 1.)
02042         self._outlinewidth = 1
02043 
02044     def resizemode(self, rmode=None):
02045         """Set resizemode to one of the values: "auto", "user", "noresize".
02046 
02047         (Optional) Argument:
02048         rmode -- one of the strings "auto", "user", "noresize"
02049 
02050         Different resizemodes have the following effects:
02051           - "auto" adapts the appearance of the turtle
02052                    corresponding to the value of pensize.
02053           - "user" adapts the appearance of the turtle according to the
02054                    values of stretchfactor and outlinewidth (outline),
02055                    which are set by shapesize()
02056           - "noresize" no adaption of the turtle's appearance takes place.
02057         If no argument is given, return current resizemode.
02058         resizemode("user") is called by a call of shapesize with arguments.
02059 
02060 
02061         Examples (for a Turtle instance named turtle):
02062         >>> turtle.resizemode("noresize")
02063         >>> turtle.resizemode()
02064         'noresize'
02065         """
02066         if rmode is None:
02067             return self._resizemode
02068         rmode = rmode.lower()
02069         if rmode in ["auto", "user", "noresize"]:
02070             self.pen(resizemode=rmode)
02071 
02072     def pensize(self, width=None):
02073         """Set or return the line thickness.
02074 
02075         Aliases:  pensize | width
02076 
02077         Argument:
02078         width -- positive number
02079 
02080         Set the line thickness to width or return it. If resizemode is set
02081         to "auto" and turtleshape is a polygon, that polygon is drawn with
02082         the same line thickness. If no argument is given, current pensize
02083         is returned.
02084 
02085         Example (for a Turtle instance named turtle):
02086         >>> turtle.pensize()
02087         1
02088         turtle.pensize(10)   # from here on lines of width 10 are drawn
02089         """
02090         if width is None:
02091             return self._pensize
02092         self.pen(pensize=width)
02093 
02094 
02095     def penup(self):
02096         """Pull the pen up -- no drawing when moving.
02097 
02098         Aliases: penup | pu | up
02099 
02100         No argument
02101 
02102         Example (for a Turtle instance named turtle):
02103         >>> turtle.penup()
02104         """
02105         if not self._drawing:
02106             return
02107         self.pen(pendown=False)
02108 
02109     def pendown(self):
02110         """Pull the pen down -- drawing when moving.
02111 
02112         Aliases: pendown | pd | down
02113 
02114         No argument.
02115 
02116         Example (for a Turtle instance named turtle):
02117         >>> turtle.pendown()
02118         """
02119         if self._drawing:
02120             return
02121         self.pen(pendown=True)
02122 
02123     def isdown(self):
02124         """Return True if pen is down, False if it's up.
02125 
02126         No argument.
02127 
02128         Example (for a Turtle instance named turtle):
02129         >>> turtle.penup()
02130         >>> turtle.isdown()
02131         False
02132         >>> turtle.pendown()
02133         >>> turtle.isdown()
02134         True
02135         """
02136         return self._drawing
02137 
02138     def speed(self, speed=None):
02139         """ Return or set the turtle's speed.
02140 
02141         Optional argument:
02142         speed -- an integer in the range 0..10 or a speedstring (see below)
02143 
02144         Set the turtle's speed to an integer value in the range 0 .. 10.
02145         If no argument is given: return current speed.
02146 
02147         If input is a number greater than 10 or smaller than 0.5,
02148         speed is set to 0.
02149         Speedstrings  are mapped to speedvalues in the following way:
02150             'fastest' :  0
02151             'fast'    :  10
02152             'normal'  :  6
02153             'slow'    :  3
02154             'slowest' :  1
02155         speeds from 1 to 10 enforce increasingly faster animation of
02156         line drawing and turtle turning.
02157 
02158         Attention:
02159         speed = 0 : *no* animation takes place. forward/back makes turtle jump
02160         and likewise left/right make the turtle turn instantly.
02161 
02162         Example (for a Turtle instance named turtle):
02163         >>> turtle.speed(3)
02164         """
02165         speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
02166         if speed is None:
02167             return self._speed
02168         if speed in speeds:
02169             speed = speeds[speed]
02170         elif 0.5 < speed < 10.5:
02171             speed = int(round(speed))
02172         else:
02173             speed = 0
02174         self.pen(speed=speed)
02175 
02176     def color(self, *args):
02177         """Return or set the pencolor and fillcolor.
02178 
02179         Arguments:
02180         Several input formats are allowed.
02181         They use 0, 1, 2, or 3 arguments as follows:
02182 
02183         color()
02184             Return the current pencolor and the current fillcolor
02185             as a pair of color specification strings as are returned
02186             by pencolor and fillcolor.
02187         color(colorstring), color((r,g,b)), color(r,g,b)
02188             inputs as in pencolor, set both, fillcolor and pencolor,
02189             to the given value.
02190         color(colorstring1, colorstring2),
02191         color((r1,g1,b1), (r2,g2,b2))
02192             equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
02193             and analogously, if the other input format is used.
02194 
02195         If turtleshape is a polygon, outline and interior of that polygon
02196         is drawn with the newly set colors.
02197         For mor info see: pencolor, fillcolor
02198 
02199         Example (for a Turtle instance named turtle):
02200         >>> turtle.color('red', 'green')
02201         >>> turtle.color()
02202         ('red', 'green')
02203         >>> colormode(255)
02204         >>> color((40, 80, 120), (160, 200, 240))
02205         >>> color()
02206         ('#285078', '#a0c8f0')
02207         """
02208         if args:
02209             l = len(args)
02210             if l == 1:
02211                 pcolor = fcolor = args[0]
02212             elif l == 2:
02213                 pcolor, fcolor = args
02214             elif l == 3:
02215                 pcolor = fcolor = args
02216             pcolor = self._colorstr(pcolor)
02217             fcolor = self._colorstr(fcolor)
02218             self.pen(pencolor=pcolor, fillcolor=fcolor)
02219         else:
02220             return self._color(self._pencolor), self._color(self._fillcolor)
02221 
02222     def pencolor(self, *args):
02223         """ Return or set the pencolor.
02224 
02225         Arguments:
02226         Four input formats are allowed:
02227           - pencolor()
02228             Return the current pencolor as color specification string,
02229             possibly in hex-number format (see example).
02230             May be used as input to another color/pencolor/fillcolor call.
02231           - pencolor(colorstring)
02232             s is a Tk color specification string, such as "red" or "yellow"
02233           - pencolor((r, g, b))
02234             *a tuple* of r, g, and b, which represent, an RGB color,
02235             and each of r, g, and b are in the range 0..colormode,
02236             where colormode is either 1.0 or 255
02237           - pencolor(r, g, b)
02238             r, g, and b represent an RGB color, and each of r, g, and b
02239             are in the range 0..colormode
02240 
02241         If turtleshape is a polygon, the outline of that polygon is drawn
02242         with the newly set pencolor.
02243 
02244         Example (for a Turtle instance named turtle):
02245         >>> turtle.pencolor('brown')
02246         >>> tup = (0.2, 0.8, 0.55)
02247         >>> turtle.pencolor(tup)
02248         >>> turtle.pencolor()
02249         '#33cc8c'
02250         """
02251         if args:
02252             color = self._colorstr(args)
02253             if color == self._pencolor:
02254                 return
02255             self.pen(pencolor=color)
02256         else:
02257             return self._color(self._pencolor)
02258 
02259     def fillcolor(self, *args):
02260         """ Return or set the fillcolor.
02261 
02262         Arguments:
02263         Four input formats are allowed:
02264           - fillcolor()
02265             Return the current fillcolor as color specification string,
02266             possibly in hex-number format (see example).
02267             May be used as input to another color/pencolor/fillcolor call.
02268           - fillcolor(colorstring)
02269             s is a Tk color specification string, such as "red" or "yellow"
02270           - fillcolor((r, g, b))
02271             *a tuple* of r, g, and b, which represent, an RGB color,
02272             and each of r, g, and b are in the range 0..colormode,
02273             where colormode is either 1.0 or 255
02274           - fillcolor(r, g, b)
02275             r, g, and b represent an RGB color, and each of r, g, and b
02276             are in the range 0..colormode
02277 
02278         If turtleshape is a polygon, the interior of that polygon is drawn
02279         with the newly set fillcolor.
02280 
02281         Example (for a Turtle instance named turtle):
02282         >>> turtle.fillcolor('violet')
02283         >>> col = turtle.pencolor()
02284         >>> turtle.fillcolor(col)
02285         >>> turtle.fillcolor(0, .5, 0)
02286         """
02287         if args:
02288             color = self._colorstr(args)
02289             if color == self._fillcolor:
02290                 return
02291             self.pen(fillcolor=color)
02292         else:
02293             return self._color(self._fillcolor)
02294 
02295     def showturtle(self):
02296         """Makes the turtle visible.
02297 
02298         Aliases: showturtle | st
02299 
02300         No argument.
02301 
02302         Example (for a Turtle instance named turtle):
02303         >>> turtle.hideturtle()
02304         >>> turtle.showturtle()
02305         """
02306         self.pen(shown=True)
02307 
02308     def hideturtle(self):
02309         """Makes the turtle invisible.
02310 
02311         Aliases: hideturtle | ht
02312 
02313         No argument.
02314 
02315         It's a good idea to do this while you're in the
02316         middle of a complicated drawing, because hiding
02317         the turtle speeds up the drawing observably.
02318 
02319         Example (for a Turtle instance named turtle):
02320         >>> turtle.hideturtle()
02321         """
02322         self.pen(shown=False)
02323 
02324     def isvisible(self):
02325         """Return True if the Turtle is shown, False if it's hidden.
02326 
02327         No argument.
02328 
02329         Example (for a Turtle instance named turtle):
02330         >>> turtle.hideturtle()
02331         >>> print turtle.isvisible():
02332         False
02333         """
02334         return self._shown
02335 
02336     def pen(self, pen=None, **pendict):
02337         """Return or set the pen's attributes.
02338 
02339         Arguments:
02340             pen -- a dictionary with some or all of the below listed keys.
02341             **pendict -- one or more keyword-arguments with the below
02342                          listed keys as keywords.
02343 
02344         Return or set the pen's attributes in a 'pen-dictionary'
02345         with the following key/value pairs:
02346            "shown"      :   True/False
02347            "pendown"    :   True/False
02348            "pencolor"   :   color-string or color-tuple
02349            "fillcolor"  :   color-string or color-tuple
02350            "pensize"    :   positive number
02351            "speed"      :   number in range 0..10
02352            "resizemode" :   "auto" or "user" or "noresize"
02353            "stretchfactor": (positive number, positive number)
02354            "shearfactor":   number
02355            "outline"    :   positive number
02356            "tilt"       :   number
02357 
02358         This dictionary can be used as argument for a subsequent
02359         pen()-call to restore the former pen-state. Moreover one
02360         or more of these attributes can be provided as keyword-arguments.
02361         This can be used to set several pen attributes in one statement.
02362 
02363 
02364         Examples (for a Turtle instance named turtle):
02365         >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
02366         >>> turtle.pen()
02367         {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
02368         'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
02369         'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
02370         >>> penstate=turtle.pen()
02371         >>> turtle.color("yellow","")
02372         >>> turtle.penup()
02373         >>> turtle.pen()
02374         {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
02375         'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
02376         'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
02377         >>> p.pen(penstate, fillcolor="green")
02378         >>> p.pen()
02379         {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
02380         'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
02381         'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
02382         """
02383         _pd =  {"shown"         : self._shown,
02384                 "pendown"       : self._drawing,
02385                 "pencolor"      : self._pencolor,
02386                 "fillcolor"     : self._fillcolor,
02387                 "pensize"       : self._pensize,
02388                 "speed"         : self._speed,
02389                 "resizemode"    : self._resizemode,
02390                 "stretchfactor" : self._stretchfactor,
02391                 "shearfactor"   : self._shearfactor,
02392                 "outline"       : self._outlinewidth,
02393                 "tilt"          : self._tilt
02394                }
02395 
02396         if not (pen or pendict):
02397             return _pd
02398 
02399         if isinstance(pen, dict):
02400             p = pen
02401         else:
02402             p = {}
02403         p.update(pendict)
02404 
02405         _p_buf = {}
02406         for key in p:
02407             _p_buf[key] = _pd[key]
02408 
02409         if self.undobuffer:
02410             self.undobuffer.push(("pen", _p_buf))
02411 
02412         newLine = False
02413         if "pendown" in p:
02414             if self._drawing != p["pendown"]:
02415                 newLine = True
02416         if "pencolor" in p:
02417             if isinstance(p["pencolor"], tuple):
02418                 p["pencolor"] = self._colorstr((p["pencolor"],))
02419             if self._pencolor != p["pencolor"]:
02420                 newLine = True
02421         if "pensize" in p:
02422             if self._pensize != p["pensize"]:
02423                 newLine = True
02424         if newLine:
02425             self._newLine()
02426         if "pendown" in p:
02427             self._drawing = p["pendown"]
02428         if "pencolor" in p:
02429             self._pencolor = p["pencolor"]
02430         if "pensize" in p:
02431             self._pensize = p["pensize"]
02432         if "fillcolor" in p:
02433             if isinstance(p["fillcolor"], tuple):
02434                 p["fillcolor"] = self._colorstr((p["fillcolor"],))
02435             self._fillcolor = p["fillcolor"]
02436         if "speed" in p:
02437             self._speed = p["speed"]
02438         if "resizemode" in p:
02439             self._resizemode = p["resizemode"]
02440         if "stretchfactor" in p:
02441             sf = p["stretchfactor"]
02442             if isinstance(sf, (int, float)):
02443                 sf = (sf, sf)
02444             self._stretchfactor = sf
02445         if "shearfactor" in p:
02446             self._shearfactor = p["shearfactor"]
02447         if "outline" in p:
02448             self._outlinewidth = p["outline"]
02449         if "shown" in p:
02450             self._shown = p["shown"]
02451         if "tilt" in p:
02452             self._tilt = p["tilt"]
02453         if "stretchfactor" in p or "tilt" in p or "shearfactor" in p:
02454             scx, scy = self._stretchfactor
02455             shf = self._shearfactor
02456             sa, ca = math.sin(self._tilt), math.cos(self._tilt)
02457             self._shapetrafo = ( scx*ca, scy*(shf*ca + sa),
02458                                 -scx*sa, scy*(ca - shf*sa))
02459         self._update()
02460 
02461 ## three dummy methods to be implemented by child class:
02462 
02463     def _newLine(self, usePos = True):
02464         """dummy method - to be overwritten by child class"""
02465     def _update(self, count=True, forced=False):
02466         """dummy method - to be overwritten by child class"""
02467     def _color(self, args):
02468         """dummy method - to be overwritten by child class"""
02469     def _colorstr(self, args):
02470         """dummy method - to be overwritten by child class"""
02471 
02472     width = pensize
02473     up = penup
02474     pu = penup
02475     pd = pendown
02476     down = pendown
02477     st = showturtle
02478     ht = hideturtle
02479 
02480 
02481 class _TurtleImage(object):
02482     """Helper class: Datatype to store Turtle attributes
02483     """
02484 
02485     def __init__(self, screen, shapeIndex):
02486         self.screen = screen
02487         self._type = None
02488         self._setshape(shapeIndex)
02489 
02490     def _setshape(self, shapeIndex):
02491         screen = self.screen
02492         self.shapeIndex = shapeIndex
02493         if self._type == "polygon" == screen._shapes[shapeIndex]._type:
02494             return
02495         if self._type == "image" == screen._shapes[shapeIndex]._type:
02496             return
02497         if self._type in ["image", "polygon"]:
02498             screen._delete(self._item)
02499         elif self._type == "compound":
02500             for item in self._item:
02501                 screen._delete(item)
02502         self._type = screen._shapes[shapeIndex]._type
02503         if self._type == "polygon":
02504             self._item = screen._createpoly()
02505         elif self._type == "image":
02506             self._item = screen._createimage(screen._shapes["blank"]._data)
02507         elif self._type == "compound":
02508             self._item = [screen._createpoly() for item in
02509                                           screen._shapes[shapeIndex]._data]
02510 
02511 
02512 class RawTurtle(TPen, TNavigator):
02513     """Animation part of the RawTurtle.
02514     Puts RawTurtle upon a TurtleScreen and provides tools for
02515     its animation.
02516     """
02517     screens = []
02518 
02519     def __init__(self, canvas=None,
02520                  shape=_CFG["shape"],
02521                  undobuffersize=_CFG["undobuffersize"],
02522                  visible=_CFG["visible"]):
02523         if isinstance(canvas, _Screen):
02524             self.screen = canvas
02525         elif isinstance(canvas, TurtleScreen):
02526             if canvas not in RawTurtle.screens:
02527                 RawTurtle.screens.append(canvas)
02528             self.screen = canvas
02529         elif isinstance(canvas, (ScrolledCanvas, Canvas)):
02530             for screen in RawTurtle.screens:
02531                 if screen.cv == canvas:
02532                     self.screen = screen
02533                     break
02534             else:
02535                 self.screen = TurtleScreen(canvas)
02536                 RawTurtle.screens.append(self.screen)
02537         else:
02538             raise TurtleGraphicsError("bad cavas argument %s" % canvas)
02539 
02540         screen = self.screen
02541         TNavigator.__init__(self, screen.mode())
02542         TPen.__init__(self)
02543         screen._turtles.append(self)
02544         self.drawingLineItem = screen._createline()
02545         self.turtle = _TurtleImage(screen, shape)
02546         self._poly = None
02547         self._creatingPoly = False
02548         self._fillitem = self._fillpath = None
02549         self._shown = visible
02550         self._hidden_from_screen = False
02551         self.currentLineItem = screen._createline()
02552         self.currentLine = [self._position]
02553         self.items = [self.currentLineItem]
02554         self.stampItems = []
02555         self._undobuffersize = undobuffersize
02556         self.undobuffer = Tbuffer(undobuffersize)
02557         self._update()
02558 
02559     def reset(self):
02560         """Delete the turtle's drawings and restore its default values.
02561 
02562         No argument.
02563 ,
02564         Delete the turtle's drawings from the screen, re-center the turtle
02565         and set variables to the default values.
02566 
02567         Example (for a Turtle instance named turtle):
02568         >>> turtle.position()
02569         (0.00,-22.00)
02570         >>> turtle.heading()
02571         100.0
02572         >>> turtle.reset()
02573         >>> turtle.position()
02574         (0.00,0.00)
02575         >>> turtle.heading()
02576         0.0
02577         """
02578         TNavigator.reset(self)
02579         TPen._reset(self)
02580         self._clear()
02581         self._drawturtle()
02582         self._update()
02583 
02584     def setundobuffer(self, size):
02585         """Set or disable undobuffer.
02586 
02587         Argument:
02588         size -- an integer or None
02589 
02590         If size is an integer an empty undobuffer of given size is installed.
02591         Size gives the maximum number of turtle-actions that can be undone
02592         by the undo() function.
02593         If size is None, no undobuffer is present.
02594 
02595         Example (for a Turtle instance named turtle):
02596         >>> turtle.setundobuffer(42)
02597         """
02598         if size is None:
02599             self.undobuffer = None
02600         else:
02601             self.undobuffer = Tbuffer(size)
02602 
02603     def undobufferentries(self):
02604         """Return count of entries in the undobuffer.
02605 
02606         No argument.
02607 
02608         Example (for a Turtle instance named turtle):
02609         >>> while undobufferentries():
02610                 undo()
02611         """
02612         if self.undobuffer is None:
02613             return 0
02614         return self.undobuffer.nr_of_items()
02615 
02616     def _clear(self):
02617         """Delete all of pen's drawings"""
02618         self._fillitem = self._fillpath = None
02619         for item in self.items:
02620             self.screen._delete(item)
02621         self.currentLineItem = self.screen._createline()
02622         self.currentLine = []
02623         if self._drawing:
02624             self.currentLine.append(self._position)
02625         self.items = [self.currentLineItem]
02626         self.clearstamps()
02627         self.setundobuffer(self._undobuffersize)
02628 
02629 
02630     def clear(self):
02631         """Delete the turtle's drawings from the screen. Do not move turtle.
02632 
02633         No arguments.
02634 
02635         Delete the turtle's drawings from the screen. Do not move turtle.
02636         State and position of the turtle as well as drawings of other
02637         turtles are not affected.
02638 
02639         Examples (for a Turtle instance named turtle):
02640         >>> turtle.clear()
02641         """
02642         self._clear()
02643         self._update()
02644 
02645     def _update_data(self):
02646         self.screen._incrementudc()
02647         if self.screen._updatecounter != 0:
02648             return
02649         if len(self.currentLine)>1:
02650             self.screen._drawline(self.currentLineItem, self.currentLine,
02651                                   self._pencolor, self._pensize)
02652 
02653     def _update(self):
02654         """Perform a Turtle-data update.
02655         """
02656         screen = self.screen
02657         if screen._tracing == 0:
02658             return
02659         elif screen._tracing == 1:
02660             self._update_data()
02661             self._drawturtle()
02662             screen._update()                  # TurtleScreenBase
02663             screen._delay(screen._delayvalue) # TurtleScreenBase
02664         else:
02665             self._update_data()
02666             if screen._updatecounter == 0:
02667                 for t in screen.turtles():
02668                     t._drawturtle()
02669                 screen._update()
02670 
02671     def _tracer(self, flag=None, delay=None):
02672         """Turns turtle animation on/off and set delay for update drawings.
02673 
02674         Optional arguments:
02675         n -- nonnegative  integer
02676         delay -- nonnegative  integer
02677 
02678         If n is given, only each n-th regular screen update is really performed.
02679         (Can be used to accelerate the drawing of complex graphics.)
02680         Second arguments sets delay value (see RawTurtle.delay())
02681 
02682         Example (for a Turtle instance named turtle):
02683         >>> turtle.tracer(8, 25)
02684         >>> dist = 2
02685         >>> for i in range(200):
02686                 turtle.fd(dist)
02687                 turtle.rt(90)
02688                 dist += 2
02689         """
02690         return self.screen.tracer(flag, delay)
02691 
02692     def _color(self, args):
02693         return self.screen._color(args)
02694 
02695     def _colorstr(self, args):
02696         return self.screen._colorstr(args)
02697 
02698     def _cc(self, args):
02699         """Convert colortriples to hexstrings.
02700         """
02701         if isinstance(args, str):
02702             return args
02703         try:
02704             r, g, b = args
02705         except:
02706             raise TurtleGraphicsError("bad color arguments: %s" % str(args))
02707         if self.screen._colormode == 1.0:
02708             r, g, b = [round(255.0*x) for x in (r, g, b)]
02709         if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
02710             raise TurtleGraphicsError("bad color sequence: %s" % str(args))
02711         return "#%02x%02x%02x" % (r, g, b)
02712 
02713     def clone(self):
02714         """Create and return a clone of the turtle.
02715 
02716         No argument.
02717 
02718         Create and return a clone of the turtle with same position, heading
02719         and turtle properties.
02720 
02721         Example (for a Turtle instance named mick):
02722         mick = Turtle()
02723         joe = mick.clone()
02724         """
02725         screen = self.screen
02726         self._newLine(self._drawing)
02727 
02728         turtle = self.turtle
02729         self.screen = None
02730         self.turtle = None  # too make self deepcopy-able
02731 
02732         q = deepcopy(self)
02733 
02734         self.screen = screen
02735         self.turtle = turtle
02736 
02737         q.screen = screen
02738         q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
02739 
02740         screen._turtles.append(q)
02741         ttype = screen._shapes[self.turtle.shapeIndex]._type
02742         if ttype == "polygon":
02743             q.turtle._item = screen._createpoly()
02744         elif ttype == "image":
02745             q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
02746         elif ttype == "compound":
02747             q.turtle._item = [screen._createpoly() for item in
02748                               screen._shapes[self.turtle.shapeIndex]._data]
02749         q.currentLineItem = screen._createline()
02750         q._update()
02751         return q
02752 
02753     def shape(self, name=None):
02754         """Set turtle shape to shape with given name / return current shapename.
02755 
02756         Optional argument:
02757         name -- a string, which is a valid shapename
02758 
02759         Set turtle shape to shape with given name or, if name is not given,
02760         return name of current shape.
02761         Shape with name must exist in the TurtleScreen's shape dictionary.
02762         Initially there are the following polygon shapes:
02763         'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
02764         To learn about how to deal with shapes see Screen-method register_shape.
02765 
02766         Example (for a Turtle instance named turtle):
02767         >>> turtle.shape()
02768         'arrow'
02769         >>> turtle.shape("turtle")
02770         >>> turtle.shape()
02771         'turtle'
02772         """
02773         if name is None:
02774             return self.turtle.shapeIndex
02775         if not name in self.screen.getshapes():
02776             raise TurtleGraphicsError("There is no shape named %s" % name)
02777         self.turtle._setshape(name)
02778         self._update()
02779 
02780     def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
02781         """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
02782 
02783         Optinonal arguments:
02784            stretch_wid : positive number
02785            stretch_len : positive number
02786            outline  : positive number
02787 
02788         Return or set the pen's attributes x/y-stretchfactors and/or outline.
02789         Set resizemode to "user".
02790         If and only if resizemode is set to "user", the turtle will be displayed
02791         stretched according to its stretchfactors:
02792         stretch_wid is stretchfactor perpendicular to orientation
02793         stretch_len is stretchfactor in direction of turtles orientation.
02794         outline determines the width of the shapes's outline.
02795 
02796         Examples (for a Turtle instance named turtle):
02797         >>> turtle.resizemode("user")
02798         >>> turtle.shapesize(5, 5, 12)
02799         >>> turtle.shapesize(outline=8)
02800         """
02801         if stretch_wid is stretch_len is outline is None:
02802             stretch_wid, stretch_len = self._stretchfactor
02803             return stretch_wid, stretch_len, self._outlinewidth
02804         if stretch_wid == 0 or stretch_len == 0:
02805             raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero")
02806         if stretch_wid is not None:
02807             if stretch_len is None:
02808                 stretchfactor = stretch_wid, stretch_wid
02809             else:
02810                 stretchfactor = stretch_wid, stretch_len
02811         elif stretch_len is not None:
02812             stretchfactor = self._stretchfactor[0], stretch_len
02813         else:
02814             stretchfactor = self._stretchfactor
02815         if outline is None:
02816             outline = self._outlinewidth
02817         self.pen(resizemode="user",
02818                  stretchfactor=stretchfactor, outline=outline)
02819 
02820     def shearfactor(self, shear=None):
02821         """Set or return the current shearfactor.
02822 
02823         Optional argument: shear -- number, tangent of the shear angle
02824 
02825         Shear the turtleshape according to the given shearfactor shear,
02826         which is the tangent of the shear angle. DO NOT change the
02827         turtle's heading (direction of movement).
02828         If shear is not given: return the current shearfactor, i. e. the
02829         tangent of the shear angle, by which lines parallel to the
02830         heading of the turtle are sheared.
02831 
02832         Examples (for a Turtle instance named turtle):
02833         >>> turtle.shape("circle")
02834         >>> turtle.shapesize(5,2)
02835         >>> turtle.shearfactor(0.5)
02836         >>> turtle.shearfactor()
02837         >>> 0.5
02838         """
02839         if shear is None:
02840             return self._shearfactor
02841         self.pen(resizemode="user", shearfactor=shear)
02842 
02843     def settiltangle(self, angle):
02844         """Rotate the turtleshape to point in the specified direction
02845 
02846         Argument: angle -- number
02847 
02848         Rotate the turtleshape to point in the direction specified by angle,
02849         regardless of its current tilt-angle. DO NOT change the turtle's
02850         heading (direction of movement).
02851 
02852 
02853         Examples (for a Turtle instance named turtle):
02854         >>> turtle.shape("circle")
02855         >>> turtle.shapesize(5,2)
02856         >>> turtle.settiltangle(45)
02857         >>> stamp()
02858         >>> turtle.fd(50)
02859         >>> turtle.settiltangle(-45)
02860         >>> stamp()
02861         >>> turtle.fd(50)
02862         """
02863         tilt = -angle * self._degreesPerAU * self._angleOrient
02864         tilt = (tilt * math.pi / 180.0) % (2*math.pi)
02865         self.pen(resizemode="user", tilt=tilt)
02866 
02867     def tiltangle(self, angle=None):
02868         """Set or return the current tilt-angle.
02869 
02870         Optional argument: angle -- number
02871 
02872         Rotate the turtleshape to point in the direction specified by angle,
02873         regardless of its current tilt-angle. DO NOT change the turtle's
02874         heading (direction of movement).
02875         If angle is not given: return the current tilt-angle, i. e. the angle
02876         between the orientation of the turtleshape and the heading of the
02877         turtle (its direction of movement).
02878 
02879         Deprecated since Python 3.1
02880 
02881         Examples (for a Turtle instance named turtle):
02882         >>> turtle.shape("circle")
02883         >>> turtle.shapesize(5,2)
02884         >>> turtle.tilt(45)
02885         >>> turtle.tiltangle()
02886         >>>
02887         """
02888         if angle is None:
02889             tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
02890             return (tilt / self._degreesPerAU) % self._fullcircle
02891         else:
02892             self.settiltangle(angle)
02893 
02894     def tilt(self, angle):
02895         """Rotate the turtleshape by angle.
02896 
02897         Argument:
02898         angle - a number
02899 
02900         Rotate the turtleshape by angle from its current tilt-angle,
02901         but do NOT change the turtle's heading (direction of movement).
02902 
02903         Examples (for a Turtle instance named turtle):
02904         >>> turtle.shape("circle")
02905         >>> turtle.shapesize(5,2)
02906         >>> turtle.tilt(30)
02907         >>> turtle.fd(50)
02908         >>> turtle.tilt(30)
02909         >>> turtle.fd(50)
02910         """
02911         self.settiltangle(angle + self.tiltangle())
02912 
02913     def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
02914         """Set or return the current transformation matrix of the turtle shape.
02915 
02916         Optional arguments: t11, t12, t21, t22 -- numbers.
02917 
02918         If none of the matrix elements are given, return the transformation
02919         matrix.
02920         Otherwise set the given elements and transform the turtleshape
02921         according to the matrix consisting of first row t11, t12 and
02922         second row t21, 22.
02923         Modify stretchfactor, shearfactor and tiltangle according to the
02924         given matrix.
02925 
02926         Examples (for a Turtle instance named turtle):
02927         >>> turtle.shape("square")
02928         >>> turtle.shapesize(4,2)
02929         >>> turtle.shearfactor(-0.5)
02930         >>> turtle.shapetransform()
02931         >>> (4.0, -1.0, -0.0, 2.0)
02932         """
02933         if t11 is t12 is t21 is t22 is None:
02934             return self._shapetrafo
02935         m11, m12, m21, m22 = self._shapetrafo
02936         if t11 is not None: m11 = t11
02937         if t12 is not None: m12 = t12
02938         if t21 is not None: m21 = t21
02939         if t22 is not None: m22 = t22
02940         if t11 * t22 - t12 * t21 == 0:
02941             raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
02942         self._shapetrafo = (m11, m12, m21, m22)
02943         alfa = math.atan2(-m21, m11) % (2 * math.pi)
02944         sa, ca = math.sin(alfa), math.cos(alfa)
02945         a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
02946                               sa*m11 + ca*m21, sa*m12 + ca*m22)
02947         self._stretchfactor = a11, a22
02948         self._shearfactor = a12/a22
02949         self._tilt = alfa
02950         self._update()
02951 
02952 
02953     def _polytrafo(self, poly):
02954         """Computes transformed polygon shapes from a shape
02955         according to current position and heading.
02956         """
02957         screen = self.screen
02958         p0, p1 = self._position
02959         e0, e1 = self._orient
02960         e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
02961         e0, e1 = (1.0 / abs(e)) * e
02962         return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
02963                                                            for (x, y) in poly]
02964 
02965     def get_shapepoly(self):
02966         """Return the current shape polygon as tuple of coordinate pairs.
02967 
02968         No argument.
02969 
02970         Examples (for a Turtle instance named turtle):
02971         >>> turtle.shape("square")
02972         >>> turtle.shapetransform(4, -1, 0, 2)
02973         >>> turtle.get_shapepoly()
02974         ((50, -20), (30, 20), (-50, 20), (-30, -20))
02975 
02976         """
02977         shape = self.screen._shapes[self.turtle.shapeIndex]
02978         if shape._type == "polygon":
02979             return self._getshapepoly(shape._data, shape._type == "compound")
02980         # else return None
02981 
02982     def _getshapepoly(self, polygon, compound=False):
02983         """Calculate transformed shape polygon according to resizemode
02984         and shapetransform.
02985         """
02986         if self._resizemode == "user" or compound:
02987             t11, t12, t21, t22 = self._shapetrafo
02988         elif self._resizemode == "auto":
02989             l = max(1, self._pensize/5.0)
02990             t11, t12, t21, t22 = l, 0, 0, l
02991         elif self._resizemode == "noresize":
02992             return polygon
02993         return tuple([(t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon])
02994 
02995     def _drawturtle(self):
02996         """Manages the correct rendering of the turtle with respect to
02997         its shape, resizemode, stretch and tilt etc."""
02998         screen = self.screen
02999         shape = screen._shapes[self.turtle.shapeIndex]
03000         ttype = shape._type
03001         titem = self.turtle._item
03002         if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
03003             self._hidden_from_screen = False
03004             tshape = shape._data
03005             if ttype == "polygon":
03006                 if self._resizemode == "noresize": w = 1
03007                 elif self._resizemode == "auto": w = self._pensize
03008                 else: w =self._outlinewidth
03009                 shape = self._polytrafo(self._getshapepoly(tshape))
03010                 fc, oc = self._fillcolor, self._pencolor
03011                 screen._drawpoly(titem, shape, fill=fc, outline=oc,
03012                                                       width=w, top=True)
03013             elif ttype == "image":
03014                 screen._drawimage(titem, self._position, tshape)
03015             elif ttype == "compound":
03016                 for item, (poly, fc, oc) in zip(titem, tshape):
03017                     poly = self._polytrafo(self._getshapepoly(poly, True))
03018                     screen._drawpoly(item, poly, fill=self._cc(fc),
03019                                      outline=self._cc(oc), width=self._outlinewidth, top=True)
03020         else:
03021             if self._hidden_from_screen:
03022                 return
03023             if ttype == "polygon":
03024                 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
03025             elif ttype == "image":
03026                 screen._drawimage(titem, self._position,
03027                                           screen._shapes["blank"]._data)
03028             elif ttype == "compound":
03029                 for item in titem:
03030                     screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
03031             self._hidden_from_screen = True
03032 
03033 ##############################  stamp stuff  ###############################
03034 
03035     def stamp(self):
03036         """Stamp a copy of the turtleshape onto the canvas and return its id.
03037 
03038         No argument.
03039 
03040         Stamp a copy of the turtle shape onto the canvas at the current
03041         turtle position. Return a stamp_id for that stamp, which can be
03042         used to delete it by calling clearstamp(stamp_id).
03043 
03044         Example (for a Turtle instance named turtle):
03045         >>> turtle.color("blue")
03046         >>> turtle.stamp()
03047         13
03048         >>> turtle.fd(50)
03049         """
03050         screen = self.screen
03051         shape = screen._shapes[self.turtle.shapeIndex]
03052         ttype = shape._type
03053         tshape = shape._data
03054         if ttype == "polygon":
03055             stitem = screen._createpoly()
03056             if self._resizemode == "noresize": w = 1
03057             elif self._resizemode == "auto": w = self._pensize
03058             else: w =self._outlinewidth
03059             shape = self._polytrafo(self._getshapepoly(tshape))
03060             fc, oc = self._fillcolor, self._pencolor
03061             screen._drawpoly(stitem, shape, fill=fc, outline=oc,
03062                                                   width=w, top=True)
03063         elif ttype == "image":
03064             stitem = screen._createimage("")
03065             screen._drawimage(stitem, self._position, tshape)
03066         elif ttype == "compound":
03067             stitem = []
03068             for element in tshape:
03069                 item = screen._createpoly()
03070                 stitem.append(item)
03071             stitem = tuple(stitem)
03072             for item, (poly, fc, oc) in zip(stitem, tshape):
03073                 poly = self._polytrafo(self._getshapepoly(poly, True))
03074                 screen._drawpoly(item, poly, fill=self._cc(fc),
03075                                  outline=self._cc(oc), width=self._outlinewidth, top=True)
03076         self.stampItems.append(stitem)
03077         self.undobuffer.push(("stamp", stitem))
03078         return stitem
03079 
03080     def _clearstamp(self, stampid):
03081         """does the work for clearstamp() and clearstamps()
03082         """
03083         if stampid in self.stampItems:
03084             if isinstance(stampid, tuple):
03085                 for subitem in stampid:
03086                     self.screen._delete(subitem)
03087             else:
03088                 self.screen._delete(stampid)
03089             self.stampItems.remove(stampid)
03090         # Delete stampitem from undobuffer if necessary
03091         # if clearstamp is called directly.
03092         item = ("stamp", stampid)
03093         buf = self.undobuffer
03094         if item not in buf.buffer:
03095             return
03096         index = buf.buffer.index(item)
03097         buf.buffer.remove(item)
03098         if index <= buf.ptr:
03099             buf.ptr = (buf.ptr - 1) % buf.bufsize
03100         buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
03101 
03102     def clearstamp(self, stampid):
03103         """Delete stamp with given stampid
03104 
03105         Argument:
03106         stampid - an integer, must be return value of previous stamp() call.
03107 
03108         Example (for a Turtle instance named turtle):
03109         >>> turtle.color("blue")
03110         >>> astamp = turtle.stamp()
03111         >>> turtle.fd(50)
03112         >>> turtle.clearstamp(astamp)
03113         """
03114         self._clearstamp(stampid)
03115         self._update()
03116 
03117     def clearstamps(self, n=None):
03118         """Delete all or first/last n of turtle's stamps.
03119 
03120         Optional argument:
03121         n -- an integer
03122 
03123         If n is None, delete all of pen's stamps,
03124         else if n > 0 delete first n stamps
03125         else if n < 0 delete last n stamps.
03126 
03127         Example (for a Turtle instance named turtle):
03128         >>> for i in range(8):
03129                 turtle.stamp(); turtle.fd(30)
03130         ...
03131         >>> turtle.clearstamps(2)
03132         >>> turtle.clearstamps(-2)
03133         >>> turtle.clearstamps()
03134         """
03135         if n is None:
03136             toDelete = self.stampItems[:]
03137         elif n >= 0:
03138             toDelete = self.stampItems[:n]
03139         else:
03140             toDelete = self.stampItems[n:]
03141         for item in toDelete:
03142             self._clearstamp(item)
03143         self._update()
03144 
03145     def _goto(self, end):
03146         """Move the pen to the point end, thereby drawing a line
03147         if pen is down. All other methodes for turtle movement depend
03148         on this one.
03149         """
03150         ## Version with undo-stuff
03151         go_modes = ( self._drawing,
03152                      self._pencolor,
03153                      self._pensize,
03154                      isinstance(self._fillpath, list))
03155         screen = self.screen
03156         undo_entry = ("go", self._position, end, go_modes,
03157                       (self.currentLineItem,
03158                       self.currentLine[:],
03159                       screen._pointlist(self.currentLineItem),
03160                       self.items[:])
03161                       )
03162         if self.undobuffer:
03163             self.undobuffer.push(undo_entry)
03164         start = self._position
03165         if self._speed and screen._tracing == 1:
03166             diff = (end-start)
03167             diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
03168             nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
03169             delta = diff * (1.0/nhops)
03170             for n in range(1, nhops):
03171                 if n == 1:
03172                     top = True
03173                 else:
03174                     top = False
03175                 self._position = start + delta * n
03176                 if self._drawing:
03177                     screen._drawline(self.drawingLineItem,
03178                                      (start, self._position),
03179                                      self._pencolor, self._pensize, top)
03180                 self._update()
03181             if self._drawing:
03182                 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
03183                                                fill="", width=self._pensize)
03184         # Turtle now at end,
03185         if self._drawing: # now update currentLine
03186             self.currentLine.append(end)
03187         if isinstance(self._fillpath, list):
03188             self._fillpath.append(end)
03189         ######    vererbung!!!!!!!!!!!!!!!!!!!!!!
03190         self._position = end
03191         if self._creatingPoly:
03192             self._poly.append(end)
03193         if len(self.currentLine) > 42: # 42! answer to the ultimate question
03194                                        # of life, the universe and everything
03195             self._newLine()
03196         self._update() #count=True)
03197 
03198     def _undogoto(self, entry):
03199         """Reverse a _goto. Used for undo()
03200         """
03201         old, new, go_modes, coodata = entry
03202         drawing, pc, ps, filling = go_modes
03203         cLI, cL, pl, items = coodata
03204         screen = self.screen
03205         if abs(self._position - new) > 0.5:
03206             print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
03207         # restore former situation
03208         self.currentLineItem = cLI
03209         self.currentLine = cL
03210 
03211         if pl == [(0, 0), (0, 0)]:
03212             usepc = ""
03213         else:
03214             usepc = pc
03215         screen._drawline(cLI, pl, fill=usepc, width=ps)
03216 
03217         todelete = [i for i in self.items if (i not in items) and
03218                                        (screen._type(i) == "line")]
03219         for i in todelete:
03220             screen._delete(i)
03221             self.items.remove(i)
03222 
03223         start = old
03224         if self._speed and screen._tracing == 1:
03225             diff = old - new
03226             diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
03227             nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
03228             delta = diff * (1.0/nhops)
03229             for n in range(1, nhops):
03230                 if n == 1:
03231                     top = True
03232                 else:
03233                     top = False
03234                 self._position = new + delta * n
03235                 if drawing:
03236                     screen._drawline(self.drawingLineItem,
03237                                      (start, self._position),
03238                                      pc, ps, top)
03239                 self._update()
03240             if drawing:
03241                 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
03242                                                fill="", width=ps)
03243         # Turtle now at position old,
03244         self._position = old
03245         ##  if undo is done during creating a polygon, the last vertex
03246         ##  will be deleted. if the polygon is entirely deleted,
03247         ##  creatingPoly will be set to False.
03248         ##  Polygons created before the last one will not be affected by undo()
03249         if self._creatingPoly:
03250             if len(self._poly) > 0:
03251                 self._poly.pop()
03252             if self._poly == []:
03253                 self._creatingPoly = False
03254                 self._poly = None
03255         if filling:
03256             if self._fillpath == []:
03257                 self._fillpath = None
03258                 print("Unwahrscheinlich in _undogoto!")
03259             elif self._fillpath is not None:
03260                 self._fillpath.pop()
03261         self._update() #count=True)
03262 
03263     def _rotate(self, angle):
03264         """Turns pen clockwise by angle.
03265         """
03266         if self.undobuffer:
03267             self.undobuffer.push(("rot", angle, self._degreesPerAU))
03268         angle *= self._degreesPerAU
03269         neworient = self._orient.rotate(angle)
03270         tracing = self.screen._tracing
03271         if tracing == 1 and self._speed > 0:
03272             anglevel = 3.0 * self._speed
03273             steps = 1 + int(abs(angle)/anglevel)
03274             delta = 1.0*angle/steps
03275             for _ in range(steps):
03276                 self._orient = self._orient.rotate(delta)
03277                 self._update()
03278         self._orient = neworient
03279         self._update()
03280 
03281     def _newLine(self, usePos=True):
03282         """Closes current line item and starts a new one.
03283            Remark: if current line became too long, animation
03284            performance (via _drawline) slowed down considerably.
03285         """
03286         if len(self.currentLine) > 1:
03287             self.screen._drawline(self.currentLineItem, self.currentLine,
03288                                       self._pencolor, self._pensize)
03289             self.currentLineItem = self.screen._createline()
03290             self.items.append(self.currentLineItem)
03291         else:
03292             self.screen._drawline(self.currentLineItem, top=True)
03293         self.currentLine = []
03294         if usePos:
03295             self.currentLine = [self._position]
03296 
03297     def filling(self):
03298         """Return fillstate (True if filling, False else).
03299 
03300         No argument.
03301 
03302         Example (for a Turtle instance named turtle):
03303         >>> turtle.begin_fill()
03304         >>> if turtle.filling():
03305                 turtle.pensize(5)
03306         else:
03307                 turtle.pensize(3)
03308         """
03309         return isinstance(self._fillpath, list)
03310 
03311     def begin_fill(self):
03312         """Called just before drawing a shape to be filled.
03313 
03314         No argument.
03315 
03316         Example (for a Turtle instance named turtle):
03317         >>> turtle.color("black", "red")
03318         >>> turtle.begin_fill()
03319         >>> turtle.circle(60)
03320         >>> turtle.end_fill()
03321         """
03322         if not self.filling():
03323             self._fillitem = self.screen._createpoly()
03324             self.items.append(self._fillitem)
03325         self._fillpath = [self._position]
03326         self._newLine()
03327         if self.undobuffer:
03328             self.undobuffer.push(("beginfill", self._fillitem))
03329         self._update()
03330 
03331 
03332     def end_fill(self):
03333         """Fill the shape drawn after the call begin_fill().
03334 
03335         No argument.
03336 
03337         Example (for a Turtle instance named turtle):
03338         >>> turtle.color("black", "red")
03339         >>> turtle.begin_fill()
03340         >>> turtle.circle(60)
03341         >>> turtle.end_fill()
03342         """
03343         if self.filling():
03344             if len(self._fillpath) > 2:
03345                 self.screen._drawpoly(self._fillitem, self._fillpath,
03346                                       fill=self._fillcolor)
03347                 if self.undobuffer:
03348                     self.undobuffer.push(("dofill", self._fillitem))
03349             self._fillitem = self._fillpath = None
03350             self._update()
03351 
03352     def dot(self, size=None, *color):
03353         """Draw a dot with diameter size, using color.
03354 
03355         Optional arguments:
03356         size -- an integer >= 1 (if given)
03357         color -- a colorstring or a numeric color tuple
03358 
03359         Draw a circular dot with diameter size, using color.
03360         If size is not given, the maximum of pensize+4 and 2*pensize is used.
03361 
03362         Example (for a Turtle instance named turtle):
03363         >>> turtle.dot()
03364         >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
03365         """
03366         if not color:
03367             if isinstance(size, (str, tuple)):
03368                 color = self._colorstr(size)
03369                 size = self._pensize + max(self._pensize, 4)
03370             else:
03371                 color = self._pencolor
03372                 if not size:
03373                     size = self._pensize + max(self._pensize, 4)
03374         else:
03375             if size is None:
03376                 size = self._pensize + max(self._pensize, 4)
03377             color = self._colorstr(color)
03378         if hasattr(self.screen, "_dot"):
03379             item = self.screen._dot(self._position, size, color)
03380             self.items.append(item)
03381             if self.undobuffer:
03382                 self.undobuffer.push(("dot", item))
03383         else:
03384             pen = self.pen()
03385             if self.undobuffer:
03386                 self.undobuffer.push(["seq"])
03387                 self.undobuffer.cumulate = True
03388             try:
03389                 if self.resizemode() == 'auto':
03390                     self.ht()
03391                 self.pendown()
03392                 self.pensize(size)
03393                 self.pencolor(color)
03394                 self.forward(0)
03395             finally:
03396                 self.pen(pen)
03397             if self.undobuffer:
03398                 self.undobuffer.cumulate = False
03399 
03400     def _write(self, txt, align, font):
03401         """Performs the writing for write()
03402         """
03403         item, end = self.screen._write(self._position, txt, align, font,
03404                                                           self._pencolor)
03405         self.items.append(item)
03406         if self.undobuffer:
03407             self.undobuffer.push(("wri", item))
03408         return end
03409 
03410     def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
03411         """Write text at the current turtle position.
03412 
03413         Arguments:
03414         arg -- info, which is to be written to the TurtleScreen
03415         move (optional) -- True/False
03416         align (optional) -- one of the strings "left", "center" or right"
03417         font (optional) -- a triple (fontname, fontsize, fonttype)
03418 
03419         Write text - the string representation of arg - at the current
03420         turtle position according to align ("left", "center" or right")
03421         and with the given font.
03422         If move is True, the pen is moved to the bottom-right corner
03423         of the text. By default, move is False.
03424 
03425         Example (for a Turtle instance named turtle):
03426         >>> turtle.write('Home = ', True, align="center")
03427         >>> turtle.write((0,0), True)
03428         """
03429         if self.undobuffer:
03430             self.undobuffer.push(["seq"])
03431             self.undobuffer.cumulate = True
03432         end = self._write(str(arg), align.lower(), font)
03433         if move:
03434             x, y = self.pos()
03435             self.setpos(end, y)
03436         if self.undobuffer:
03437             self.undobuffer.cumulate = False
03438 
03439     def begin_poly(self):
03440         """Start recording the vertices of a polygon.
03441 
03442         No argument.
03443 
03444         Start recording the vertices of a polygon. Current turtle position
03445         is first point of polygon.
03446 
03447         Example (for a Turtle instance named turtle):
03448         >>> turtle.begin_poly()
03449         """
03450         self._poly = [self._position]
03451         self._creatingPoly = True
03452 
03453     def end_poly(self):
03454         """Stop recording the vertices of a polygon.
03455 
03456         No argument.
03457 
03458         Stop recording the vertices of a polygon. Current turtle position is
03459         last point of polygon. This will be connected with the first point.
03460 
03461         Example (for a Turtle instance named turtle):
03462         >>> turtle.end_poly()
03463         """
03464         self._creatingPoly = False
03465 
03466     def get_poly(self):
03467         """Return the lastly recorded polygon.
03468 
03469         No argument.
03470 
03471         Example (for a Turtle instance named turtle):
03472         >>> p = turtle.get_poly()
03473         >>> turtle.register_shape("myFavouriteShape", p)
03474         """
03475         ## check if there is any poly?
03476         if self._poly is not None:
03477             return tuple(self._poly)
03478 
03479     def getscreen(self):
03480         """Return the TurtleScreen object, the turtle is drawing  on.
03481 
03482         No argument.
03483 
03484         Return the TurtleScreen object, the turtle is drawing  on.
03485         So TurtleScreen-methods can be called for that object.
03486 
03487         Example (for a Turtle instance named turtle):
03488         >>> ts = turtle.getscreen()
03489         >>> ts
03490         <turtle.TurtleScreen object at 0x0106B770>
03491         >>> ts.bgcolor("pink")
03492         """
03493         return self.screen
03494 
03495     def getturtle(self):
03496         """Return the Turtleobject itself.
03497 
03498         No argument.
03499 
03500         Only reasonable use: as a function to return the 'anonymous turtle':
03501 
03502         Example:
03503         >>> pet = getturtle()
03504         >>> pet.fd(50)
03505         >>> pet
03506         <turtle.Turtle object at 0x0187D810>
03507         >>> turtles()
03508         [<turtle.Turtle object at 0x0187D810>]
03509         """
03510         return self
03511 
03512     getpen = getturtle
03513 
03514 
03515     ################################################################
03516     ### screen oriented methods recurring to methods of TurtleScreen
03517     ################################################################
03518 
03519     def _delay(self, delay=None):
03520         """Set delay value which determines speed of turtle animation.
03521         """
03522         return self.screen.delay(delay)
03523 
03524     def onclick(self, fun, btn=1, add=None):
03525         """Bind fun to mouse-click event on this turtle on canvas.
03526 
03527         Arguments:
03528         fun --  a function with two arguments, to which will be assigned
03529                 the coordinates of the clicked point on the canvas.
03530         num --  number of the mouse-button defaults to 1 (left mouse button).
03531         add --  True or False. If True, new binding will be added, otherwise
03532                 it will replace a former binding.
03533 
03534         Example for the anonymous turtle, i. e. the procedural way:
03535 
03536         >>> def turn(x, y):
03537                 left(360)
03538 
03539         >>> onclick(turn) # Now clicking into the turtle will turn it.
03540         >>> onclick(None)  # event-binding will be removed
03541         """
03542         self.screen._onclick(self.turtle._item, fun, btn, add)
03543         self._update()
03544 
03545     def onrelease(self, fun, btn=1, add=None):
03546         """Bind fun to mouse-button-release event on this turtle on canvas.
03547 
03548         Arguments:
03549         fun -- a function with two arguments, to which will be assigned
03550                 the coordinates of the clicked point on the canvas.
03551         num --  number of the mouse-button defaults to 1 (left mouse button).
03552 
03553         Example (for a MyTurtle instance named joe):
03554         >>> class MyTurtle(Turtle):
03555                 def glow(self,x,y):
03556                         self.fillcolor("red")
03557                 def unglow(self,x,y):
03558                         self.fillcolor("")
03559 
03560         >>> joe = MyTurtle()
03561         >>> joe.onclick(joe.glow)
03562         >>> joe.onrelease(joe.unglow)
03563         ### clicking on joe turns fillcolor red,
03564         ### unclicking turns it to transparent.
03565         """
03566         self.screen._onrelease(self.turtle._item, fun, btn, add)
03567         self._update()
03568 
03569     def ondrag(self, fun, btn=1, add=None):
03570         """Bind fun to mouse-move event on this turtle on canvas.
03571 
03572         Arguments:
03573         fun -- a function with two arguments, to which will be assigned
03574                the coordinates of the clicked point on the canvas.
03575         num -- number of the mouse-button defaults to 1 (left mouse button).
03576 
03577         Every sequence of mouse-move-events on a turtle is preceded by a
03578         mouse-click event on that turtle.
03579 
03580         Example (for a Turtle instance named turtle):
03581         >>> turtle.ondrag(turtle.goto)
03582 
03583         ### Subsequently clicking and dragging a Turtle will
03584         ### move it across the screen thereby producing handdrawings
03585         ### (if pen is down).
03586         """
03587         self.screen._ondrag(self.turtle._item, fun, btn, add)
03588 
03589 
03590     def _undo(self, action, data):
03591         """Does the main part of the work for undo()
03592         """
03593         if self.undobuffer is None:
03594             return
03595         if action == "rot":
03596             angle, degPAU = data
03597             self._rotate(-angle*degPAU/self._degreesPerAU)
03598             dummy = self.undobuffer.pop()
03599         elif action == "stamp":
03600             stitem = data[0]
03601             self.clearstamp(stitem)
03602         elif action == "go":
03603             self._undogoto(data)
03604         elif action in ["wri", "dot"]:
03605             item = data[0]
03606             self.screen._delete(item)
03607             self.items.remove(item)
03608         elif action == "dofill":
03609             item = data[0]
03610             self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
03611                                   fill="", outline="")
03612         elif action == "beginfill":
03613             item = data[0]
03614             self._fillitem = self._fillpath = None
03615             if item in self.items:
03616                 self.screen._delete(item)
03617                 self.items.remove(item)
03618         elif action == "pen":
03619             TPen.pen(self, data[0])
03620             self.undobuffer.pop()
03621 
03622     def undo(self):
03623         """undo (repeatedly) the last turtle action.
03624 
03625         No argument.
03626 
03627         undo (repeatedly) the last turtle action.
03628         Number of available undo actions is determined by the size of
03629         the undobuffer.
03630 
03631         Example (for a Turtle instance named turtle):
03632         >>> for i in range(4):
03633                 turtle.fd(50); turtle.lt(80)
03634 
03635         >>> for i in range(8):
03636                 turtle.undo()
03637         """
03638         if self.undobuffer is None:
03639             return
03640         item = self.undobuffer.pop()
03641         action = item[0]
03642         data = item[1:]
03643         if action == "seq":
03644             while data:
03645                 item = data.pop()
03646                 self._undo(item[0], item[1:])
03647         else:
03648             self._undo(action, data)
03649 
03650     turtlesize = shapesize
03651 
03652 RawPen = RawTurtle
03653 
03654 ###  Screen - Singleton  ########################
03655 
03656 def Screen():
03657     """Return the singleton screen object.
03658     If none exists at the moment, create a new one and return it,
03659     else return the existing one."""
03660     if Turtle._screen is None:
03661         Turtle._screen = _Screen()
03662     return Turtle._screen
03663 
03664 class _Screen(TurtleScreen):
03665 
03666     _root = None
03667     _canvas = None
03668     _title = _CFG["title"]
03669 
03670     def __init__(self):
03671         # XXX there is no need for this code to be conditional,
03672         # as there will be only a single _Screen instance, anyway
03673         # XXX actually, the turtle demo is injecting root window,
03674         # so perhaps the conditional creation of a root should be
03675         # preserved (perhaps by passing it as an optional parameter)
03676         if _Screen._root is None:
03677             _Screen._root = self._root = _Root()
03678             self._root.title(_Screen._title)
03679             self._root.ondestroy(self._destroy)
03680         if _Screen._canvas is None:
03681             width = _CFG["width"]
03682             height = _CFG["height"]
03683             canvwidth = _CFG["canvwidth"]
03684             canvheight = _CFG["canvheight"]
03685             leftright = _CFG["leftright"]
03686             topbottom = _CFG["topbottom"]
03687             self._root.setupcanvas(width, height, canvwidth, canvheight)
03688             _Screen._canvas = self._root._getcanvas()
03689             TurtleScreen.__init__(self, _Screen._canvas)
03690             self.setup(width, height, leftright, topbottom)
03691 
03692     def setup(self, width=_CFG["width"], height=_CFG["height"],
03693               startx=_CFG["leftright"], starty=_CFG["topbottom"]):
03694         """ Set the size and position of the main window.
03695 
03696         Arguments:
03697         width: as integer a size in pixels, as float a fraction of the screen.
03698           Default is 50% of screen.
03699         height: as integer the height in pixels, as float a fraction of the
03700           screen. Default is 75% of screen.
03701         startx: if positive, starting position in pixels from the left
03702           edge of the screen, if negative from the right edge
03703           Default, startx=None is to center window horizontally.
03704         starty: if positive, starting position in pixels from the top
03705           edge of the screen, if negative from the bottom edge
03706           Default, starty=None is to center window vertically.
03707 
03708         Examples (for a Screen instance named screen):
03709         >>> screen.setup (width=200, height=200, startx=0, starty=0)
03710 
03711         sets window to 200x200 pixels, in upper left of screen
03712 
03713         >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
03714 
03715         sets window to 75% of screen by 50% of screen and centers
03716         """
03717         if not hasattr(self._root, "set_geometry"):
03718             return
03719         sw = self._root.win_width()
03720         sh = self._root.win_height()
03721         if isinstance(width, float) and 0 <= width <= 1:
03722             width = sw*width
03723         if startx is None:
03724             startx = (sw - width) / 2
03725         if isinstance(height, float) and 0 <= height <= 1:
03726             height = sh*height
03727         if starty is None:
03728             starty = (sh - height) / 2
03729         self._root.set_geometry(width, height, startx, starty)
03730         self.update()
03731 
03732     def title(self, titlestring):
03733         """Set title of turtle-window
03734 
03735         Argument:
03736         titlestring -- a string, to appear in the titlebar of the
03737                        turtle graphics window.
03738 
03739         This is a method of Screen-class. Not available for TurtleScreen-
03740         objects.
03741 
03742         Example (for a Screen instance named screen):
03743         >>> screen.title("Welcome to the turtle-zoo!")
03744         """
03745         if _Screen._root is not None:
03746             _Screen._root.title(titlestring)
03747         _Screen._title = titlestring
03748 
03749     def _destroy(self):
03750         root = self._root
03751         if root is _Screen._root:
03752             Turtle._pen = None
03753             Turtle._screen = None
03754             _Screen._root = None
03755             _Screen._canvas = None
03756         TurtleScreen._RUNNING = True
03757         root.destroy()
03758 
03759     def bye(self):
03760         """Shut the turtlegraphics window.
03761 
03762         Example (for a TurtleScreen instance named screen):
03763         >>> screen.bye()
03764         """
03765         self._destroy()
03766 
03767     def exitonclick(self):
03768         """Go into mainloop until the mouse is clicked.
03769 
03770         No arguments.
03771 
03772         Bind bye() method to mouseclick on TurtleScreen.
03773         If "using_IDLE" - value in configuration dictionary is False
03774         (default value), enter mainloop.
03775         If IDLE with -n switch (no subprocess) is used, this value should be
03776         set to True in turtle.cfg. In this case IDLE's mainloop
03777         is active also for the client script.
03778 
03779         This is a method of the Screen-class and not available for
03780         TurtleScreen instances.
03781 
03782         Example (for a Screen instance named screen):
03783         >>> screen.exitonclick()
03784 
03785         """
03786         def exitGracefully(x, y):
03787             """Screen.bye() with two dummy-parameters"""
03788             self.bye()
03789         self.onclick(exitGracefully)
03790         if _CFG["using_IDLE"]:
03791             return
03792         try:
03793             mainloop()
03794         except AttributeError:
03795             exit(0)
03796 
03797 
03798 class Turtle(RawTurtle):
03799     """RawTurtle auto-creating (scrolled) canvas.
03800 
03801     When a Turtle object is created or a function derived from some
03802     Turtle method is called a TurtleScreen object is automatically created.
03803     """
03804     _pen = None
03805     _screen = None
03806 
03807     def __init__(self,
03808                  shape=_CFG["shape"],
03809                  undobuffersize=_CFG["undobuffersize"],
03810                  visible=_CFG["visible"]):
03811         if Turtle._screen is None:
03812             Turtle._screen = Screen()
03813         RawTurtle.__init__(self, Turtle._screen,
03814                            shape=shape,
03815                            undobuffersize=undobuffersize,
03816                            visible=visible)
03817 
03818 Pen = Turtle
03819 
03820 def _getpen():
03821     """Create the 'anonymous' turtle if not already present."""
03822     if Turtle._pen is None:
03823         Turtle._pen = Turtle()
03824     return Turtle._pen
03825 
03826 def _getscreen():
03827     """Create a TurtleScreen if not already present."""
03828     if Turtle._screen is None:
03829         Turtle._screen = Screen()
03830     return Turtle._screen
03831 
03832 def write_docstringdict(filename="turtle_docstringdict"):
03833     """Create and write docstring-dictionary to file.
03834 
03835     Optional argument:
03836     filename -- a string, used as filename
03837                 default value is turtle_docstringdict
03838 
03839     Has to be called explicitly, (not used by the turtle-graphics classes)
03840     The docstring dictionary will be written to the Python script <filname>.py
03841     It is intended to serve as a template for translation of the docstrings
03842     into different languages.
03843     """
03844     docsdict = {}
03845 
03846     for methodname in _tg_screen_functions:
03847         key = "_Screen."+methodname
03848         docsdict[key] = eval(key).__doc__
03849     for methodname in _tg_turtle_functions:
03850         key = "Turtle."+methodname
03851         docsdict[key] = eval(key).__doc__
03852 
03853     f = open("%s.py" % filename,"w")
03854     keys = sorted([x for x in docsdict.keys()
03855                         if x.split('.')[1] not in _alias_list])
03856     f.write('docsdict = {\n\n')
03857     for key in keys[:-1]:
03858         f.write('%s :\n' % repr(key))
03859         f.write('        """%s\n""",\n\n' % docsdict[key])
03860     key = keys[-1]
03861     f.write('%s :\n' % repr(key))
03862     f.write('        """%s\n"""\n\n' % docsdict[key])
03863     f.write("}\n")
03864     f.close()
03865 
03866 def read_docstrings(lang):
03867     """Read in docstrings from lang-specific docstring dictionary.
03868 
03869     Transfer docstrings, translated to lang, from a dictionary-file
03870     to the methods of classes Screen and Turtle and - in revised form -
03871     to the corresponding functions.
03872     """
03873     modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
03874     module = __import__(modname)
03875     docsdict = module.docsdict
03876     for key in docsdict:
03877         try:
03878 #            eval(key).im_func.__doc__ = docsdict[key]
03879             eval(key).__doc__ = docsdict[key]
03880         except:
03881             print("Bad docstring-entry: %s" % key)
03882 
03883 _LANGUAGE = _CFG["language"]
03884 
03885 try:
03886     if _LANGUAGE != "english":
03887         read_docstrings(_LANGUAGE)
03888 except ImportError:
03889     print("Cannot find docsdict for", _LANGUAGE)
03890 except:
03891     print ("Unknown Error when trying to import %s-docstring-dictionary" %
03892                                                                   _LANGUAGE)
03893 
03894 
03895 def getmethparlist(ob):
03896     """Get strings describing the arguments for the given object
03897 
03898     Returns a pair of strings representing function parameter lists
03899     including parenthesis.  The first string is suitable for use in
03900     function definition and the second is suitable for use in function
03901     call.  The "self" parameter is not included.
03902     """
03903     defText = callText = ""
03904     # bit of a hack for methods - turn it into a function
03905     # but we drop the "self" param.
03906     # Try and build one for Python defined functions
03907     args, varargs, varkw = inspect.getargs(ob.__code__)
03908     items2 = args[1:]
03909     realArgs = args[1:]
03910     defaults = ob.__defaults__ or []
03911     defaults = ["=%r" % (value,) for value in defaults]
03912     defaults = [""] * (len(realArgs)-len(defaults)) + defaults
03913     items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
03914     if varargs is not None:
03915         items1.append("*" + varargs)
03916         items2.append("*" + varargs)
03917     if varkw is not None:
03918         items1.append("**" + varkw)
03919         items2.append("**" + varkw)
03920     defText = ", ".join(items1)
03921     defText = "(%s)" % defText
03922     callText = ", ".join(items2)
03923     callText = "(%s)" % callText
03924     return defText, callText
03925 
03926 def _turtle_docrevise(docstr):
03927     """To reduce docstrings from RawTurtle class for functions
03928     """
03929     import re
03930     if docstr is None:
03931         return None
03932     turtlename = _CFG["exampleturtle"]
03933     newdocstr = docstr.replace("%s." % turtlename,"")
03934     parexp = re.compile(r' \(.+ %s\):' % turtlename)
03935     newdocstr = parexp.sub(":", newdocstr)
03936     return newdocstr
03937 
03938 def _screen_docrevise(docstr):
03939     """To reduce docstrings from TurtleScreen class for functions
03940     """
03941     import re
03942     if docstr is None:
03943         return None
03944     screenname = _CFG["examplescreen"]
03945     newdocstr = docstr.replace("%s." % screenname,"")
03946     parexp = re.compile(r' \(.+ %s\):' % screenname)
03947     newdocstr = parexp.sub(":", newdocstr)
03948     return newdocstr
03949 
03950 ## The following mechanism makes all methods of RawTurtle and Turtle available
03951 ## as functions. So we can enhance, change, add, delete methods to these
03952 ## classes and do not need to change anything here.
03953 
03954 
03955 for methodname in _tg_screen_functions:
03956     pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
03957     if pl1 == "":
03958         print(">>>>>>", pl1, pl2)
03959         continue
03960     defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
03961                                    {'key':methodname, 'pl1':pl1, 'pl2':pl2})
03962     exec(defstr)
03963     eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
03964 
03965 for methodname in _tg_turtle_functions:
03966     pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
03967     if pl1 == "":
03968         print(">>>>>>", pl1, pl2)
03969         continue
03970     defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
03971                                    {'key':methodname, 'pl1':pl1, 'pl2':pl2})
03972     exec(defstr)
03973     eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
03974 
03975 
03976 done = mainloop
03977 
03978 if __name__ == "__main__":
03979     def switchpen():
03980         if isdown():
03981             pu()
03982         else:
03983             pd()
03984 
03985     def demo1():
03986         """Demo of old turtle.py - module"""
03987         reset()
03988         tracer(True)
03989         up()
03990         backward(100)
03991         down()
03992         # draw 3 squares; the last filled
03993         width(3)
03994         for i in range(3):
03995             if i == 2:
03996                 begin_fill()
03997             for _ in range(4):
03998                 forward(20)
03999                 left(90)
04000             if i == 2:
04001                 color("maroon")
04002                 end_fill()
04003             up()
04004             forward(30)
04005             down()
04006         width(1)
04007         color("black")
04008         # move out of the way
04009         tracer(False)
04010         up()
04011         right(90)
04012         forward(100)
04013         right(90)
04014         forward(100)
04015         right(180)
04016         down()
04017         # some text
04018         write("startstart", 1)
04019         write("start", 1)
04020         color("red")
04021         # staircase
04022         for i in range(5):
04023             forward(20)
04024             left(90)
04025             forward(20)
04026             right(90)
04027         # filled staircase
04028         tracer(True)
04029         begin_fill()
04030         for i in range(5):
04031             forward(20)
04032             left(90)
04033             forward(20)
04034             right(90)
04035         end_fill()
04036         # more text
04037 
04038     def demo2():
04039         """Demo of some new features."""
04040         speed(1)
04041         st()
04042         pensize(3)
04043         setheading(towards(0, 0))
04044         radius = distance(0, 0)/2.0
04045         rt(90)
04046         for _ in range(18):
04047             switchpen()
04048             circle(radius, 10)
04049         write("wait a moment...")
04050         while undobufferentries():
04051             undo()
04052         reset()
04053         lt(90)
04054         colormode(255)
04055         laenge = 10
04056         pencolor("green")
04057         pensize(3)
04058         lt(180)
04059         for i in range(-2, 16):
04060             if i > 0:
04061                 begin_fill()
04062                 fillcolor(255-15*i, 0, 15*i)
04063             for _ in range(3):
04064                 fd(laenge)
04065                 lt(120)
04066             end_fill()
04067             laenge += 10
04068             lt(15)
04069             speed((speed()+1)%12)
04070         #end_fill()
04071 
04072         lt(120)
04073         pu()
04074         fd(70)
04075         rt(30)
04076         pd()
04077         color("red","yellow")
04078         speed(0)
04079         begin_fill()
04080         for _ in range(4):
04081             circle(50, 90)
04082             rt(90)
04083             fd(30)
04084             rt(90)
04085         end_fill()
04086         lt(90)
04087         pu()
04088         fd(30)
04089         pd()
04090         shape("turtle")
04091 
04092         tri = getturtle()
04093         tri.resizemode("auto")
04094         turtle = Turtle()
04095         turtle.resizemode("auto")
04096         turtle.shape("turtle")
04097         turtle.reset()
04098         turtle.left(90)
04099         turtle.speed(0)
04100         turtle.up()
04101         turtle.goto(280, 40)
04102         turtle.lt(30)
04103         turtle.down()
04104         turtle.speed(6)
04105         turtle.color("blue","orange")
04106         turtle.pensize(2)
04107         tri.speed(6)
04108         setheading(towards(turtle))
04109         count = 1
04110         while tri.distance(turtle) > 4:
04111             turtle.fd(3.5)
04112             turtle.lt(0.6)
04113             tri.setheading(tri.towards(turtle))
04114             tri.fd(4)
04115             if count % 20 == 0:
04116                 turtle.stamp()
04117                 tri.stamp()
04118                 switchpen()
04119             count += 1
04120         tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
04121         tri.pencolor("black")
04122         tri.pencolor("red")
04123 
04124         def baba(xdummy, ydummy):
04125             clearscreen()
04126             bye()
04127 
04128         time.sleep(2)
04129 
04130         while undobufferentries():
04131             tri.undo()
04132             turtle.undo()
04133         tri.fd(50)
04134         tri.write("  Click me!", font = ("Courier", 12, "bold") )
04135         tri.onclick(baba, 1)
04136 
04137     demo1()
04138     demo2()
04139     exitonclick()