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: https://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 math.hypot(*self)
268    def rotate(self, angle):
269        """rotate self counterclockwise by angle
270        """
271        perp = Vec2D(-self[1], self[0])
272        angle = math.radians(angle)
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(math.tau)
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.degrees(math.atan2(y, x)), 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.degrees(math.atan2(y, x)), 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(math.radians(w2)*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 = math.radians(tilt) % math.tau
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        (Incorrectly marked as deprecated since Python 3.1, it is really
2881        settiltangle that is deprecated.)
2882
2883        Examples (for a Turtle instance named turtle):
2884        >>> turtle.shape("circle")
2885        >>> turtle.shapesize(5,2)
2886        >>> turtle.tilt(45)
2887        >>> turtle.tiltangle()
2888        """
2889        if angle is None:
2890            tilt = -math.degrees(self._tilt) * self._angleOrient
2891            return (tilt / self._degreesPerAU) % self._fullcircle
2892        else:
2893            self.settiltangle(angle)
2894
2895    def tilt(self, angle):
2896        """Rotate the turtleshape by angle.
2897
2898        Argument:
2899        angle - a number
2900
2901        Rotate the turtleshape by angle from its current tilt-angle,
2902        but do NOT change the turtle's heading (direction of movement).
2903
2904        Examples (for a Turtle instance named turtle):
2905        >>> turtle.shape("circle")
2906        >>> turtle.shapesize(5,2)
2907        >>> turtle.tilt(30)
2908        >>> turtle.fd(50)
2909        >>> turtle.tilt(30)
2910        >>> turtle.fd(50)
2911        """
2912        self.settiltangle(angle + self.tiltangle())
2913
2914    def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2915        """Set or return the current transformation matrix of the turtle shape.
2916
2917        Optional arguments: t11, t12, t21, t22 -- numbers.
2918
2919        If none of the matrix elements are given, return the transformation
2920        matrix.
2921        Otherwise set the given elements and transform the turtleshape
2922        according to the matrix consisting of first row t11, t12 and
2923        second row t21, 22.
2924        Modify stretchfactor, shearfactor and tiltangle according to the
2925        given matrix.
2926
2927        Examples (for a Turtle instance named turtle):
2928        >>> turtle.shape("square")
2929        >>> turtle.shapesize(4,2)
2930        >>> turtle.shearfactor(-0.5)
2931        >>> turtle.shapetransform()
2932        (4.0, -1.0, -0.0, 2.0)
2933        """
2934        if t11 is t12 is t21 is t22 is None:
2935            return self._shapetrafo
2936        m11, m12, m21, m22 = self._shapetrafo
2937        if t11 is not None: m11 = t11
2938        if t12 is not None: m12 = t12
2939        if t21 is not None: m21 = t21
2940        if t22 is not None: m22 = t22
2941        if t11 * t22 - t12 * t21 == 0:
2942            raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2943        self._shapetrafo = (m11, m12, m21, m22)
2944        alfa = math.atan2(-m21, m11) % math.tau
2945        sa, ca = math.sin(alfa), math.cos(alfa)
2946        a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2947                              sa*m11 + ca*m21, sa*m12 + ca*m22)
2948        self._stretchfactor = a11, a22
2949        self._shearfactor = a12/a22
2950        self._tilt = alfa
2951        self.pen(resizemode="user")
2952
2953
2954    def _polytrafo(self, poly):
2955        """Computes transformed polygon shapes from a shape
2956        according to current position and heading.
2957        """
2958        screen = self.screen
2959        p0, p1 = self._position
2960        e0, e1 = self._orient
2961        e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2962        e0, e1 = (1.0 / abs(e)) * e
2963        return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2964                                                           for (x, y) in poly]
2965
2966    def get_shapepoly(self):
2967        """Return the current shape polygon as tuple of coordinate pairs.
2968
2969        No argument.
2970
2971        Examples (for a Turtle instance named turtle):
2972        >>> turtle.shape("square")
2973        >>> turtle.shapetransform(4, -1, 0, 2)
2974        >>> turtle.get_shapepoly()
2975        ((50, -20), (30, 20), (-50, 20), (-30, -20))
2976
2977        """
2978        shape = self.screen._shapes[self.turtle.shapeIndex]
2979        if shape._type == "polygon":
2980            return self._getshapepoly(shape._data, shape._type == "compound")
2981        # else return None
2982
2983    def _getshapepoly(self, polygon, compound=False):
2984        """Calculate transformed shape polygon according to resizemode
2985        and shapetransform.
2986        """
2987        if self._resizemode == "user" or compound:
2988            t11, t12, t21, t22 = self._shapetrafo
2989        elif self._resizemode == "auto":
2990            l = max(1, self._pensize/5.0)
2991            t11, t12, t21, t22 = l, 0, 0, l
2992        elif self._resizemode == "noresize":
2993            return polygon
2994        return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon)
2995
2996    def _drawturtle(self):
2997        """Manages the correct rendering of the turtle with respect to
2998        its shape, resizemode, stretch and tilt etc."""
2999        screen = self.screen
3000        shape = screen._shapes[self.turtle.shapeIndex]
3001        ttype = shape._type
3002        titem = self.turtle._item
3003        if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3004            self._hidden_from_screen = False
3005            tshape = shape._data
3006            if ttype == "polygon":
3007                if self._resizemode == "noresize": w = 1
3008                elif self._resizemode == "auto": w = self._pensize
3009                else: w =self._outlinewidth
3010                shape = self._polytrafo(self._getshapepoly(tshape))
3011                fc, oc = self._fillcolor, self._pencolor
3012                screen._drawpoly(titem, shape, fill=fc, outline=oc,
3013                                                      width=w, top=True)
3014            elif ttype == "image":
3015                screen._drawimage(titem, self._position, tshape)
3016            elif ttype == "compound":
3017                for item, (poly, fc, oc) in zip(titem, tshape):
3018                    poly = self._polytrafo(self._getshapepoly(poly, True))
3019                    screen._drawpoly(item, poly, fill=self._cc(fc),
3020                                     outline=self._cc(oc), width=self._outlinewidth, top=True)
3021        else:
3022            if self._hidden_from_screen:
3023                return
3024            if ttype == "polygon":
3025                screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3026            elif ttype == "image":
3027                screen._drawimage(titem, self._position,
3028                                          screen._shapes["blank"]._data)
3029            elif ttype == "compound":
3030                for item in titem:
3031                    screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3032            self._hidden_from_screen = True
3033
3034##############################  stamp stuff  ###############################
3035
3036    def stamp(self):
3037        """Stamp a copy of the turtleshape onto the canvas and return its id.
3038
3039        No argument.
3040
3041        Stamp a copy of the turtle shape onto the canvas at the current
3042        turtle position. Return a stamp_id for that stamp, which can be
3043        used to delete it by calling clearstamp(stamp_id).
3044
3045        Example (for a Turtle instance named turtle):
3046        >>> turtle.color("blue")
3047        >>> turtle.stamp()
3048        13
3049        >>> turtle.fd(50)
3050        """
3051        screen = self.screen
3052        shape = screen._shapes[self.turtle.shapeIndex]
3053        ttype = shape._type
3054        tshape = shape._data
3055        if ttype == "polygon":
3056            stitem = screen._createpoly()
3057            if self._resizemode == "noresize": w = 1
3058            elif self._resizemode == "auto": w = self._pensize
3059            else: w =self._outlinewidth
3060            shape = self._polytrafo(self._getshapepoly(tshape))
3061            fc, oc = self._fillcolor, self._pencolor
3062            screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3063                                                  width=w, top=True)
3064        elif ttype == "image":
3065            stitem = screen._createimage("")
3066            screen._drawimage(stitem, self._position, tshape)
3067        elif ttype == "compound":
3068            stitem = []
3069            for element in tshape:
3070                item = screen._createpoly()
3071                stitem.append(item)
3072            stitem = tuple(stitem)
3073            for item, (poly, fc, oc) in zip(stitem, tshape):
3074                poly = self._polytrafo(self._getshapepoly(poly, True))
3075                screen._drawpoly(item, poly, fill=self._cc(fc),
3076                                 outline=self._cc(oc), width=self._outlinewidth, top=True)
3077        self.stampItems.append(stitem)
3078        self.undobuffer.push(("stamp", stitem))
3079        return stitem
3080
3081    def _clearstamp(self, stampid):
3082        """does the work for clearstamp() and clearstamps()
3083        """
3084        if stampid in self.stampItems:
3085            if isinstance(stampid, tuple):
3086                for subitem in stampid:
3087                    self.screen._delete(subitem)
3088            else:
3089                self.screen._delete(stampid)
3090            self.stampItems.remove(stampid)
3091        # Delete stampitem from undobuffer if necessary
3092        # if clearstamp is called directly.
3093        item = ("stamp", stampid)
3094        buf = self.undobuffer
3095        if item not in buf.buffer:
3096            return
3097        index = buf.buffer.index(item)
3098        buf.buffer.remove(item)
3099        if index <= buf.ptr:
3100            buf.ptr = (buf.ptr - 1) % buf.bufsize
3101        buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3102
3103    def clearstamp(self, stampid):
3104        """Delete stamp with given stampid
3105
3106        Argument:
3107        stampid - an integer, must be return value of previous stamp() call.
3108
3109        Example (for a Turtle instance named turtle):
3110        >>> turtle.color("blue")
3111        >>> astamp = turtle.stamp()
3112        >>> turtle.fd(50)
3113        >>> turtle.clearstamp(astamp)
3114        """
3115        self._clearstamp(stampid)
3116        self._update()
3117
3118    def clearstamps(self, n=None):
3119        """Delete all or first/last n of turtle's stamps.
3120
3121        Optional argument:
3122        n -- an integer
3123
3124        If n is None, delete all of pen's stamps,
3125        else if n > 0 delete first n stamps
3126        else if n < 0 delete last n stamps.
3127
3128        Example (for a Turtle instance named turtle):
3129        >>> for i in range(8):
3130        ...     turtle.stamp(); turtle.fd(30)
3131        ...
3132        >>> turtle.clearstamps(2)
3133        >>> turtle.clearstamps(-2)
3134        >>> turtle.clearstamps()
3135        """
3136        if n is None:
3137            toDelete = self.stampItems[:]
3138        elif n >= 0:
3139            toDelete = self.stampItems[:n]
3140        else:
3141            toDelete = self.stampItems[n:]
3142        for item in toDelete:
3143            self._clearstamp(item)
3144        self._update()
3145
3146    def _goto(self, end):
3147        """Move the pen to the point end, thereby drawing a line
3148        if pen is down. All other methods for turtle movement depend
3149        on this one.
3150        """
3151        ## Version with undo-stuff
3152        go_modes = ( self._drawing,
3153                     self._pencolor,
3154                     self._pensize,
3155                     isinstance(self._fillpath, list))
3156        screen = self.screen
3157        undo_entry = ("go", self._position, end, go_modes,
3158                      (self.currentLineItem,
3159                      self.currentLine[:],
3160                      screen._pointlist(self.currentLineItem),
3161                      self.items[:])
3162                      )
3163        if self.undobuffer:
3164            self.undobuffer.push(undo_entry)
3165        start = self._position
3166        if self._speed and screen._tracing == 1:
3167            diff = (end-start)
3168            diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3169            nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3170            delta = diff * (1.0/nhops)
3171            for n in range(1, nhops):
3172                if n == 1:
3173                    top = True
3174                else:
3175                    top = False
3176                self._position = start + delta * n
3177                if self._drawing:
3178                    screen._drawline(self.drawingLineItem,
3179                                     (start, self._position),
3180                                     self._pencolor, self._pensize, top)
3181                self._update()
3182            if self._drawing:
3183                screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3184                                               fill="", width=self._pensize)
3185        # Turtle now at end,
3186        if self._drawing: # now update currentLine
3187            self.currentLine.append(end)
3188        if isinstance(self._fillpath, list):
3189            self._fillpath.append(end)
3190        ######    vererbung!!!!!!!!!!!!!!!!!!!!!!
3191        self._position = end
3192        if self._creatingPoly:
3193            self._poly.append(end)
3194        if len(self.currentLine) > 42: # 42! answer to the ultimate question
3195                                       # of life, the universe and everything
3196            self._newLine()
3197        self._update() #count=True)
3198
3199    def _undogoto(self, entry):
3200        """Reverse a _goto. Used for undo()
3201        """
3202        old, new, go_modes, coodata = entry
3203        drawing, pc, ps, filling = go_modes
3204        cLI, cL, pl, items = coodata
3205        screen = self.screen
3206        if abs(self._position - new) > 0.5:
3207            print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3208        # restore former situation
3209        self.currentLineItem = cLI
3210        self.currentLine = cL
3211
3212        if pl == [(0, 0), (0, 0)]:
3213            usepc = ""
3214        else:
3215            usepc = pc
3216        screen._drawline(cLI, pl, fill=usepc, width=ps)
3217
3218        todelete = [i for i in self.items if (i not in items) and
3219                                       (screen._type(i) == "line")]
3220        for i in todelete:
3221            screen._delete(i)
3222            self.items.remove(i)
3223
3224        start = old
3225        if self._speed and screen._tracing == 1:
3226            diff = old - new
3227            diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3228            nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3229            delta = diff * (1.0/nhops)
3230            for n in range(1, nhops):
3231                if n == 1:
3232                    top = True
3233                else:
3234                    top = False
3235                self._position = new + delta * n
3236                if drawing:
3237                    screen._drawline(self.drawingLineItem,
3238                                     (start, self._position),
3239                                     pc, ps, top)
3240                self._update()
3241            if drawing:
3242                screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3243                                               fill="", width=ps)
3244        # Turtle now at position old,
3245        self._position = old
3246        ##  if undo is done during creating a polygon, the last vertex
3247        ##  will be deleted. if the polygon is entirely deleted,
3248        ##  creatingPoly will be set to False.
3249        ##  Polygons created before the last one will not be affected by undo()
3250        if self._creatingPoly:
3251            if len(self._poly) > 0:
3252                self._poly.pop()
3253            if self._poly == []:
3254                self._creatingPoly = False
3255                self._poly = None
3256        if filling:
3257            if self._fillpath == []:
3258                self._fillpath = None
3259                print("Unwahrscheinlich in _undogoto!")
3260            elif self._fillpath is not None:
3261                self._fillpath.pop()
3262        self._update() #count=True)
3263
3264    def _rotate(self, angle):
3265        """Turns pen clockwise by angle.
3266        """
3267        if self.undobuffer:
3268            self.undobuffer.push(("rot", angle, self._degreesPerAU))
3269        angle *= self._degreesPerAU
3270        neworient = self._orient.rotate(angle)
3271        tracing = self.screen._tracing
3272        if tracing == 1 and self._speed > 0:
3273            anglevel = 3.0 * self._speed
3274            steps = 1 + int(abs(angle)/anglevel)
3275            delta = 1.0*angle/steps
3276            for _ in range(steps):
3277                self._orient = self._orient.rotate(delta)
3278                self._update()
3279        self._orient = neworient
3280        self._update()
3281
3282    def _newLine(self, usePos=True):
3283        """Closes current line item and starts a new one.
3284           Remark: if current line became too long, animation
3285           performance (via _drawline) slowed down considerably.
3286        """
3287        if len(self.currentLine) > 1:
3288            self.screen._drawline(self.currentLineItem, self.currentLine,
3289                                      self._pencolor, self._pensize)
3290            self.currentLineItem = self.screen._createline()
3291            self.items.append(self.currentLineItem)
3292        else:
3293            self.screen._drawline(self.currentLineItem, top=True)
3294        self.currentLine = []
3295        if usePos:
3296            self.currentLine = [self._position]
3297
3298    def filling(self):
3299        """Return fillstate (True if filling, False else).
3300
3301        No argument.
3302
3303        Example (for a Turtle instance named turtle):
3304        >>> turtle.begin_fill()
3305        >>> if turtle.filling():
3306        ...     turtle.pensize(5)
3307        ... else:
3308        ...     turtle.pensize(3)
3309        """
3310        return isinstance(self._fillpath, list)
3311
3312    def begin_fill(self):
3313        """Called just before drawing a shape to be filled.
3314
3315        No argument.
3316
3317        Example (for a Turtle instance named turtle):
3318        >>> turtle.color("black", "red")
3319        >>> turtle.begin_fill()
3320        >>> turtle.circle(60)
3321        >>> turtle.end_fill()
3322        """
3323        if not self.filling():
3324            self._fillitem = self.screen._createpoly()
3325            self.items.append(self._fillitem)
3326        self._fillpath = [self._position]
3327        self._newLine()
3328        if self.undobuffer:
3329            self.undobuffer.push(("beginfill", self._fillitem))
3330        self._update()
3331
3332
3333    def end_fill(self):
3334        """Fill the shape drawn after the call begin_fill().
3335
3336        No argument.
3337
3338        Example (for a Turtle instance named turtle):
3339        >>> turtle.color("black", "red")
3340        >>> turtle.begin_fill()
3341        >>> turtle.circle(60)
3342        >>> turtle.end_fill()
3343        """
3344        if self.filling():
3345            if len(self._fillpath) > 2:
3346                self.screen._drawpoly(self._fillitem, self._fillpath,
3347                                      fill=self._fillcolor)
3348                if self.undobuffer:
3349                    self.undobuffer.push(("dofill", self._fillitem))
3350            self._fillitem = self._fillpath = None
3351            self._update()
3352
3353    def dot(self, size=None, *color):
3354        """Draw a dot with diameter size, using color.
3355
3356        Optional arguments:
3357        size -- an integer >= 1 (if given)
3358        color -- a colorstring or a numeric color tuple
3359
3360        Draw a circular dot with diameter size, using color.
3361        If size is not given, the maximum of pensize+4 and 2*pensize is used.
3362
3363        Example (for a Turtle instance named turtle):
3364        >>> turtle.dot()
3365        >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3366        """
3367        if not color:
3368            if isinstance(size, (str, tuple)):
3369                color = self._colorstr(size)
3370                size = self._pensize + max(self._pensize, 4)
3371            else:
3372                color = self._pencolor
3373                if not size:
3374                    size = self._pensize + max(self._pensize, 4)
3375        else:
3376            if size is None:
3377                size = self._pensize + max(self._pensize, 4)
3378            color = self._colorstr(color)
3379        if hasattr(self.screen, "_dot"):
3380            item = self.screen._dot(self._position, size, color)
3381            self.items.append(item)
3382            if self.undobuffer:
3383                self.undobuffer.push(("dot", item))
3384        else:
3385            pen = self.pen()
3386            if self.undobuffer:
3387                self.undobuffer.push(["seq"])
3388                self.undobuffer.cumulate = True
3389            try:
3390                if self.resizemode() == 'auto':
3391                    self.ht()
3392                self.pendown()
3393                self.pensize(size)
3394                self.pencolor(color)
3395                self.forward(0)
3396            finally:
3397                self.pen(pen)
3398            if self.undobuffer:
3399                self.undobuffer.cumulate = False
3400
3401    def _write(self, txt, align, font):
3402        """Performs the writing for write()
3403        """
3404        item, end = self.screen._write(self._position, txt, align, font,
3405                                                          self._pencolor)
3406        self.items.append(item)
3407        if self.undobuffer:
3408            self.undobuffer.push(("wri", item))
3409        return end
3410
3411    def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3412        """Write text at the current turtle position.
3413
3414        Arguments:
3415        arg -- info, which is to be written to the TurtleScreen
3416        move (optional) -- True/False
3417        align (optional) -- one of the strings "left", "center" or right"
3418        font (optional) -- a triple (fontname, fontsize, fonttype)
3419
3420        Write text - the string representation of arg - at the current
3421        turtle position according to align ("left", "center" or right")
3422        and with the given font.
3423        If move is True, the pen is moved to the bottom-right corner
3424        of the text. By default, move is False.
3425
3426        Example (for a Turtle instance named turtle):
3427        >>> turtle.write('Home = ', True, align="center")
3428        >>> turtle.write((0,0), True)
3429        """
3430        if self.undobuffer:
3431            self.undobuffer.push(["seq"])
3432            self.undobuffer.cumulate = True
3433        end = self._write(str(arg), align.lower(), font)
3434        if move:
3435            x, y = self.pos()
3436            self.setpos(end, y)
3437        if self.undobuffer:
3438            self.undobuffer.cumulate = False
3439
3440    def begin_poly(self):
3441        """Start recording the vertices of a polygon.
3442
3443        No argument.
3444
3445        Start recording the vertices of a polygon. Current turtle position
3446        is first point of polygon.
3447
3448        Example (for a Turtle instance named turtle):
3449        >>> turtle.begin_poly()
3450        """
3451        self._poly = [self._position]
3452        self._creatingPoly = True
3453
3454    def end_poly(self):
3455        """Stop recording the vertices of a polygon.
3456
3457        No argument.
3458
3459        Stop recording the vertices of a polygon. Current turtle position is
3460        last point of polygon. This will be connected with the first point.
3461
3462        Example (for a Turtle instance named turtle):
3463        >>> turtle.end_poly()
3464        """
3465        self._creatingPoly = False
3466
3467    def get_poly(self):
3468        """Return the lastly recorded polygon.
3469
3470        No argument.
3471
3472        Example (for a Turtle instance named turtle):
3473        >>> p = turtle.get_poly()
3474        >>> turtle.register_shape("myFavouriteShape", p)
3475        """
3476        ## check if there is any poly?
3477        if self._poly is not None:
3478            return tuple(self._poly)
3479
3480    def getscreen(self):
3481        """Return the TurtleScreen object, the turtle is drawing  on.
3482
3483        No argument.
3484
3485        Return the TurtleScreen object, the turtle is drawing  on.
3486        So TurtleScreen-methods can be called for that object.
3487
3488        Example (for a Turtle instance named turtle):
3489        >>> ts = turtle.getscreen()
3490        >>> ts
3491        <turtle.TurtleScreen object at 0x0106B770>
3492        >>> ts.bgcolor("pink")
3493        """
3494        return self.screen
3495
3496    def getturtle(self):
3497        """Return the Turtleobject itself.
3498
3499        No argument.
3500
3501        Only reasonable use: as a function to return the 'anonymous turtle':
3502
3503        Example:
3504        >>> pet = getturtle()
3505        >>> pet.fd(50)
3506        >>> pet
3507        <turtle.Turtle object at 0x0187D810>
3508        >>> turtles()
3509        [<turtle.Turtle object at 0x0187D810>]
3510        """
3511        return self
3512
3513    getpen = getturtle
3514
3515
3516    ################################################################
3517    ### screen oriented methods recurring to methods of TurtleScreen
3518    ################################################################
3519
3520    def _delay(self, delay=None):
3521        """Set delay value which determines speed of turtle animation.
3522        """
3523        return self.screen.delay(delay)
3524
3525    def onclick(self, fun, btn=1, add=None):
3526        """Bind fun to mouse-click event on this turtle on canvas.
3527
3528        Arguments:
3529        fun --  a function with two arguments, to which will be assigned
3530                the coordinates of the clicked point on the canvas.
3531        btn --  number of the mouse-button defaults to 1 (left mouse button).
3532        add --  True or False. If True, new binding will be added, otherwise
3533                it will replace a former binding.
3534
3535        Example for the anonymous turtle, i. e. the procedural way:
3536
3537        >>> def turn(x, y):
3538        ...     left(360)
3539        ...
3540        >>> onclick(turn)  # Now clicking into the turtle will turn it.
3541        >>> onclick(None)  # event-binding will be removed
3542        """
3543        self.screen._onclick(self.turtle._item, fun, btn, add)
3544        self._update()
3545
3546    def onrelease(self, fun, btn=1, add=None):
3547        """Bind fun to mouse-button-release event on this turtle on canvas.
3548
3549        Arguments:
3550        fun -- a function with two arguments, to which will be assigned
3551                the coordinates of the clicked point on the canvas.
3552        btn --  number of the mouse-button defaults to 1 (left mouse button).
3553
3554        Example (for a MyTurtle instance named joe):
3555        >>> class MyTurtle(Turtle):
3556        ...     def glow(self,x,y):
3557        ...             self.fillcolor("red")
3558        ...     def unglow(self,x,y):
3559        ...             self.fillcolor("")
3560        ...
3561        >>> joe = MyTurtle()
3562        >>> joe.onclick(joe.glow)
3563        >>> joe.onrelease(joe.unglow)
3564
3565        Clicking on joe turns fillcolor red, unclicking turns it to
3566        transparent.
3567        """
3568        self.screen._onrelease(self.turtle._item, fun, btn, add)
3569        self._update()
3570
3571    def ondrag(self, fun, btn=1, add=None):
3572        """Bind fun to mouse-move event on this turtle on canvas.
3573
3574        Arguments:
3575        fun -- a function with two arguments, to which will be assigned
3576               the coordinates of the clicked point on the canvas.
3577        btn -- number of the mouse-button defaults to 1 (left mouse button).
3578
3579        Every sequence of mouse-move-events on a turtle is preceded by a
3580        mouse-click event on that turtle.
3581
3582        Example (for a Turtle instance named turtle):
3583        >>> turtle.ondrag(turtle.goto)
3584
3585        Subsequently clicking and dragging a Turtle will move it
3586        across the screen thereby producing handdrawings (if pen is
3587        down).
3588        """
3589        self.screen._ondrag(self.turtle._item, fun, btn, add)
3590
3591
3592    def _undo(self, action, data):
3593        """Does the main part of the work for undo()
3594        """
3595        if self.undobuffer is None:
3596            return
3597        if action == "rot":
3598            angle, degPAU = data
3599            self._rotate(-angle*degPAU/self._degreesPerAU)
3600            dummy = self.undobuffer.pop()
3601        elif action == "stamp":
3602            stitem = data[0]
3603            self.clearstamp(stitem)
3604        elif action == "go":
3605            self._undogoto(data)
3606        elif action in ["wri", "dot"]:
3607            item = data[0]
3608            self.screen._delete(item)
3609            self.items.remove(item)
3610        elif action == "dofill":
3611            item = data[0]
3612            self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3613                                  fill="", outline="")
3614        elif action == "beginfill":
3615            item = data[0]
3616            self._fillitem = self._fillpath = None
3617            if item in self.items:
3618                self.screen._delete(item)
3619                self.items.remove(item)
3620        elif action == "pen":
3621            TPen.pen(self, data[0])
3622            self.undobuffer.pop()
3623
3624    def undo(self):
3625        """undo (repeatedly) the last turtle action.
3626
3627        No argument.
3628
3629        undo (repeatedly) the last turtle action.
3630        Number of available undo actions is determined by the size of
3631        the undobuffer.
3632
3633        Example (for a Turtle instance named turtle):
3634        >>> for i in range(4):
3635        ...     turtle.fd(50); turtle.lt(80)
3636        ...
3637        >>> for i in range(8):
3638        ...     turtle.undo()
3639        ...
3640        """
3641        if self.undobuffer is None:
3642            return
3643        item = self.undobuffer.pop()
3644        action = item[0]
3645        data = item[1:]
3646        if action == "seq":
3647            while data:
3648                item = data.pop()
3649                self._undo(item[0], item[1:])
3650        else:
3651            self._undo(action, data)
3652
3653    turtlesize = shapesize
3654
3655RawPen = RawTurtle
3656
3657###  Screen - Singleton  ########################
3658
3659def Screen():
3660    """Return the singleton screen object.
3661    If none exists at the moment, create a new one and return it,
3662    else return the existing one."""
3663    if Turtle._screen is None:
3664        Turtle._screen = _Screen()
3665    return Turtle._screen
3666
3667class _Screen(TurtleScreen):
3668
3669    _root = None
3670    _canvas = None
3671    _title = _CFG["title"]
3672
3673    def __init__(self):
3674        # XXX there is no need for this code to be conditional,
3675        # as there will be only a single _Screen instance, anyway
3676        # XXX actually, the turtle demo is injecting root window,
3677        # so perhaps the conditional creation of a root should be
3678        # preserved (perhaps by passing it as an optional parameter)
3679        if _Screen._root is None:
3680            _Screen._root = self._root = _Root()
3681            self._root.title(_Screen._title)
3682            self._root.ondestroy(self._destroy)
3683        if _Screen._canvas is None:
3684            width = _CFG["width"]
3685            height = _CFG["height"]
3686            canvwidth = _CFG["canvwidth"]
3687            canvheight = _CFG["canvheight"]
3688            leftright = _CFG["leftright"]
3689            topbottom = _CFG["topbottom"]
3690            self._root.setupcanvas(width, height, canvwidth, canvheight)
3691            _Screen._canvas = self._root._getcanvas()
3692            TurtleScreen.__init__(self, _Screen._canvas)
3693            self.setup(width, height, leftright, topbottom)
3694
3695    def setup(self, width=_CFG["width"], height=_CFG["height"],
3696              startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3697        """ Set the size and position of the main window.
3698
3699        Arguments:
3700        width: as integer a size in pixels, as float a fraction of the screen.
3701          Default is 50% of screen.
3702        height: as integer the height in pixels, as float a fraction of the
3703          screen. Default is 75% of screen.
3704        startx: if positive, starting position in pixels from the left
3705          edge of the screen, if negative from the right edge
3706          Default, startx=None is to center window horizontally.
3707        starty: if positive, starting position in pixels from the top
3708          edge of the screen, if negative from the bottom edge
3709          Default, starty=None is to center window vertically.
3710
3711        Examples (for a Screen instance named screen):
3712        >>> screen.setup (width=200, height=200, startx=0, starty=0)
3713
3714        sets window to 200x200 pixels, in upper left of screen
3715
3716        >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3717
3718        sets window to 75% of screen by 50% of screen and centers
3719        """
3720        if not hasattr(self._root, "set_geometry"):
3721            return
3722        sw = self._root.win_width()
3723        sh = self._root.win_height()
3724        if isinstance(width, float) and 0 <= width <= 1:
3725            width = sw*width
3726        if startx is None:
3727            startx = (sw - width) / 2
3728        if isinstance(height, float) and 0 <= height <= 1:
3729            height = sh*height
3730        if starty is None:
3731            starty = (sh - height) / 2
3732        self._root.set_geometry(width, height, startx, starty)
3733        self.update()
3734
3735    def title(self, titlestring):
3736        """Set title of turtle-window
3737
3738        Argument:
3739        titlestring -- a string, to appear in the titlebar of the
3740                       turtle graphics window.
3741
3742        This is a method of Screen-class. Not available for TurtleScreen-
3743        objects.
3744
3745        Example (for a Screen instance named screen):
3746        >>> screen.title("Welcome to the turtle-zoo!")
3747        """
3748        if _Screen._root is not None:
3749            _Screen._root.title(titlestring)
3750        _Screen._title = titlestring
3751
3752    def _destroy(self):
3753        root = self._root
3754        if root is _Screen._root:
3755            Turtle._pen = None
3756            Turtle._screen = None
3757            _Screen._root = None
3758            _Screen._canvas = None
3759        TurtleScreen._RUNNING = False
3760        root.destroy()
3761
3762    def bye(self):
3763        """Shut the turtlegraphics window.
3764
3765        Example (for a TurtleScreen instance named screen):
3766        >>> screen.bye()
3767        """
3768        self._destroy()
3769
3770    def exitonclick(self):
3771        """Go into mainloop until the mouse is clicked.
3772
3773        No arguments.
3774
3775        Bind bye() method to mouseclick on TurtleScreen.
3776        If "using_IDLE" - value in configuration dictionary is False
3777        (default value), enter mainloop.
3778        If IDLE with -n switch (no subprocess) is used, this value should be
3779        set to True in turtle.cfg. In this case IDLE's mainloop
3780        is active also for the client script.
3781
3782        This is a method of the Screen-class and not available for
3783        TurtleScreen instances.
3784
3785        Example (for a Screen instance named screen):
3786        >>> screen.exitonclick()
3787
3788        """
3789        def exitGracefully(x, y):
3790            """Screen.bye() with two dummy-parameters"""
3791            self.bye()
3792        self.onclick(exitGracefully)
3793        if _CFG["using_IDLE"]:
3794            return
3795        try:
3796            mainloop()
3797        except AttributeError:
3798            exit(0)
3799
3800class Turtle(RawTurtle):
3801    """RawTurtle auto-creating (scrolled) canvas.
3802
3803    When a Turtle object is created or a function derived from some
3804    Turtle method is called a TurtleScreen object is automatically created.
3805    """
3806    _pen = None
3807    _screen = None
3808
3809    def __init__(self,
3810                 shape=_CFG["shape"],
3811                 undobuffersize=_CFG["undobuffersize"],
3812                 visible=_CFG["visible"]):
3813        if Turtle._screen is None:
3814            Turtle._screen = Screen()
3815        RawTurtle.__init__(self, Turtle._screen,
3816                           shape=shape,
3817                           undobuffersize=undobuffersize,
3818                           visible=visible)
3819
3820Pen = Turtle
3821
3822def write_docstringdict(filename="turtle_docstringdict"):
3823    """Create and write docstring-dictionary to file.
3824
3825    Optional argument:
3826    filename -- a string, used as filename
3827                default value is turtle_docstringdict
3828
3829    Has to be called explicitly, (not used by the turtle-graphics classes)
3830    The docstring dictionary will be written to the Python script <filname>.py
3831    It is intended to serve as a template for translation of the docstrings
3832    into different languages.
3833    """
3834    docsdict = {}
3835
3836    for methodname in _tg_screen_functions:
3837        key = "_Screen."+methodname
3838        docsdict[key] = eval(key).__doc__
3839    for methodname in _tg_turtle_functions:
3840        key = "Turtle."+methodname
3841        docsdict[key] = eval(key).__doc__
3842
3843    with open("%s.py" % filename,"w") as f:
3844        keys = sorted(x for x in docsdict
3845                      if x.split('.')[1] not in _alias_list)
3846        f.write('docsdict = {\n\n')
3847        for key in keys[:-1]:
3848            f.write('%s :\n' % repr(key))
3849            f.write('        """%s\n""",\n\n' % docsdict[key])
3850        key = keys[-1]
3851        f.write('%s :\n' % repr(key))
3852        f.write('        """%s\n"""\n\n' % docsdict[key])
3853        f.write("}\n")
3854        f.close()
3855
3856def read_docstrings(lang):
3857    """Read in docstrings from lang-specific docstring dictionary.
3858
3859    Transfer docstrings, translated to lang, from a dictionary-file
3860    to the methods of classes Screen and Turtle and - in revised form -
3861    to the corresponding functions.
3862    """
3863    modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3864    module = __import__(modname)
3865    docsdict = module.docsdict
3866    for key in docsdict:
3867        try:
3868#            eval(key).im_func.__doc__ = docsdict[key]
3869            eval(key).__doc__ = docsdict[key]
3870        except Exception:
3871            print("Bad docstring-entry: %s" % key)
3872
3873_LANGUAGE = _CFG["language"]
3874
3875try:
3876    if _LANGUAGE != "english":
3877        read_docstrings(_LANGUAGE)
3878except ImportError:
3879    print("Cannot find docsdict for", _LANGUAGE)
3880except Exception:
3881    print ("Unknown Error when trying to import %s-docstring-dictionary" %
3882                                                                  _LANGUAGE)
3883
3884
3885def getmethparlist(ob):
3886    """Get strings describing the arguments for the given object
3887
3888    Returns a pair of strings representing function parameter lists
3889    including parenthesis.  The first string is suitable for use in
3890    function definition and the second is suitable for use in function
3891    call.  The "self" parameter is not included.
3892    """
3893    defText = callText = ""
3894    # bit of a hack for methods - turn it into a function
3895    # but we drop the "self" param.
3896    # Try and build one for Python defined functions
3897    args, varargs, varkw = inspect.getargs(ob.__code__)
3898    items2 = args[1:]
3899    realArgs = args[1:]
3900    defaults = ob.__defaults__ or []
3901    defaults = ["=%r" % (value,) for value in defaults]
3902    defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3903    items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3904    if varargs is not None:
3905        items1.append("*" + varargs)
3906        items2.append("*" + varargs)
3907    if varkw is not None:
3908        items1.append("**" + varkw)
3909        items2.append("**" + varkw)
3910    defText = ", ".join(items1)
3911    defText = "(%s)" % defText
3912    callText = ", ".join(items2)
3913    callText = "(%s)" % callText
3914    return defText, callText
3915
3916def _turtle_docrevise(docstr):
3917    """To reduce docstrings from RawTurtle class for functions
3918    """
3919    import re
3920    if docstr is None:
3921        return None
3922    turtlename = _CFG["exampleturtle"]
3923    newdocstr = docstr.replace("%s." % turtlename,"")
3924    parexp = re.compile(r' \(.+ %s\):' % turtlename)
3925    newdocstr = parexp.sub(":", newdocstr)
3926    return newdocstr
3927
3928def _screen_docrevise(docstr):
3929    """To reduce docstrings from TurtleScreen class for functions
3930    """
3931    import re
3932    if docstr is None:
3933        return None
3934    screenname = _CFG["examplescreen"]
3935    newdocstr = docstr.replace("%s." % screenname,"")
3936    parexp = re.compile(r' \(.+ %s\):' % screenname)
3937    newdocstr = parexp.sub(":", newdocstr)
3938    return newdocstr
3939
3940## The following mechanism makes all methods of RawTurtle and Turtle available
3941## as functions. So we can enhance, change, add, delete methods to these
3942## classes and do not need to change anything here.
3943
3944__func_body = """\
3945def {name}{paramslist}:
3946    if {obj} is None:
3947        if not TurtleScreen._RUNNING:
3948            TurtleScreen._RUNNING = True
3949            raise Terminator
3950        {obj} = {init}
3951    try:
3952        return {obj}.{name}{argslist}
3953    except TK.TclError:
3954        if not TurtleScreen._RUNNING:
3955            TurtleScreen._RUNNING = True
3956            raise Terminator
3957        raise
3958"""
3959
3960def _make_global_funcs(functions, cls, obj, init, docrevise):
3961    for methodname in functions:
3962        method = getattr(cls, methodname)
3963        pl1, pl2 = getmethparlist(method)
3964        if pl1 == "":
3965            print(">>>>>>", pl1, pl2)
3966            continue
3967        defstr = __func_body.format(obj=obj, init=init, name=methodname,
3968                                    paramslist=pl1, argslist=pl2)
3969        exec(defstr, globals())
3970        globals()[methodname].__doc__ = docrevise(method.__doc__)
3971
3972_make_global_funcs(_tg_screen_functions, _Screen,
3973                   'Turtle._screen', 'Screen()', _screen_docrevise)
3974_make_global_funcs(_tg_turtle_functions, Turtle,
3975                   'Turtle._pen', 'Turtle()', _turtle_docrevise)
3976
3977
3978done = mainloop
3979
3980if __name__ == "__main__":
3981    def switchpen():
3982        if isdown():
3983            pu()
3984        else:
3985            pd()
3986
3987    def demo1():
3988        """Demo of old turtle.py - module"""
3989        reset()
3990        tracer(True)
3991        up()
3992        backward(100)
3993        down()
3994        # draw 3 squares; the last filled
3995        width(3)
3996        for i in range(3):
3997            if i == 2:
3998                begin_fill()
3999            for _ in range(4):
4000                forward(20)
4001                left(90)
4002            if i == 2:
4003                color("maroon")
4004                end_fill()
4005            up()
4006            forward(30)
4007            down()
4008        width(1)
4009        color("black")
4010        # move out of the way
4011        tracer(False)
4012        up()
4013        right(90)
4014        forward(100)
4015        right(90)
4016        forward(100)
4017        right(180)
4018        down()
4019        # some text
4020        write("startstart", 1)
4021        write("start", 1)
4022        color("red")
4023        # staircase
4024        for i in range(5):
4025            forward(20)
4026            left(90)
4027            forward(20)
4028            right(90)
4029        # filled staircase
4030        tracer(True)
4031        begin_fill()
4032        for i in range(5):
4033            forward(20)
4034            left(90)
4035            forward(20)
4036            right(90)
4037        end_fill()
4038        # more text
4039
4040    def demo2():
4041        """Demo of some new features."""
4042        speed(1)
4043        st()
4044        pensize(3)
4045        setheading(towards(0, 0))
4046        radius = distance(0, 0)/2.0
4047        rt(90)
4048        for _ in range(18):
4049            switchpen()
4050            circle(radius, 10)
4051        write("wait a moment...")
4052        while undobufferentries():
4053            undo()
4054        reset()
4055        lt(90)
4056        colormode(255)
4057        laenge = 10
4058        pencolor("green")
4059        pensize(3)
4060        lt(180)
4061        for i in range(-2, 16):
4062            if i > 0:
4063                begin_fill()
4064                fillcolor(255-15*i, 0, 15*i)
4065            for _ in range(3):
4066                fd(laenge)
4067                lt(120)
4068            end_fill()
4069            laenge += 10
4070            lt(15)
4071            speed((speed()+1)%12)
4072        #end_fill()
4073
4074        lt(120)
4075        pu()
4076        fd(70)
4077        rt(30)
4078        pd()
4079        color("red","yellow")
4080        speed(0)
4081        begin_fill()
4082        for _ in range(4):
4083            circle(50, 90)
4084            rt(90)
4085            fd(30)
4086            rt(90)
4087        end_fill()
4088        lt(90)
4089        pu()
4090        fd(30)
4091        pd()
4092        shape("turtle")
4093
4094        tri = getturtle()
4095        tri.resizemode("auto")
4096        turtle = Turtle()
4097        turtle.resizemode("auto")
4098        turtle.shape("turtle")
4099        turtle.reset()
4100        turtle.left(90)
4101        turtle.speed(0)
4102        turtle.up()
4103        turtle.goto(280, 40)
4104        turtle.lt(30)
4105        turtle.down()
4106        turtle.speed(6)
4107        turtle.color("blue","orange")
4108        turtle.pensize(2)
4109        tri.speed(6)
4110        setheading(towards(turtle))
4111        count = 1
4112        while tri.distance(turtle) > 4:
4113            turtle.fd(3.5)
4114            turtle.lt(0.6)
4115            tri.setheading(tri.towards(turtle))
4116            tri.fd(4)
4117            if count % 20 == 0:
4118                turtle.stamp()
4119                tri.stamp()
4120                switchpen()
4121            count += 1
4122        tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4123        tri.pencolor("black")
4124        tri.pencolor("red")
4125
4126        def baba(xdummy, ydummy):
4127            clearscreen()
4128            bye()
4129
4130        time.sleep(2)
4131
4132        while undobufferentries():
4133            tri.undo()
4134            turtle.undo()
4135        tri.fd(50)
4136        tri.write("  Click me!", font = ("Courier", 12, "bold") )
4137        tri.onclick(baba, 1)
4138
4139    demo1()
4140    demo2()
4141    exitonclick()
4142