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