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