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