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