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