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