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