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