turtle.py revision 4969f709cc07088a40574c05724d83cddc8fafc7
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 = 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 corresponding image as background.
1468        If picname is "nopic", delete backgroundimage, if present.
1469        If picname is None, return the filename of the current backgroundimage.
1470
1471        Example (for a TurtleScreen instance named screen):
1472        >>> screen.bgpic()
1473        'nopic'
1474        >>> screen.bgpic("landscape.gif")
1475        >>> screen.bgpic()
1476        'landscape.gif'
1477        """
1478        if picname is None:
1479            return self._bgpicname
1480        if picname not in self._bgpics:
1481            self._bgpics[picname] = self._image(picname)
1482        self._setbgpic(self._bgpic, self._bgpics[picname])
1483        self._bgpicname = picname
1484
1485    def screensize(self, canvwidth=None, canvheight=None, bg=None):
1486        """Resize the canvas the turtles are drawing on.
1487
1488        Optional arguments:
1489        canvwidth -- positive integer, new width of canvas in pixels
1490        canvheight --  positive integer, new height of canvas in pixels
1491        bg -- colorstring or color-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
1579        Change angle measurement unit to grad (also known as gon,
1580        grade, or gradian and equals 1/100-th of the right angle.)
1581        >>> turtle.degrees(400.0)
1582        >>> turtle.heading()
1583        100
1584
1585        """
1586        self._setDegreesPerAU(fullcircle)
1587
1588    def radians(self):
1589        """ Set the angle measurement units to radians.
1590
1591        No arguments.
1592
1593        Example (for a Turtle instance named turtle):
1594        >>> turtle.heading()
1595        90
1596        >>> turtle.radians()
1597        >>> turtle.heading()
1598        1.5707963267948966
1599        """
1600        self._setDegreesPerAU(2*math.pi)
1601
1602    def _go(self, distance):
1603        """move turtle forward by specified distance"""
1604        ende = self._position + self._orient * distance
1605        self._goto(ende)
1606
1607    def _rotate(self, angle):
1608        """Turn turtle counterclockwise by specified angle if angle > 0."""
1609        angle *= self._degreesPerAU
1610        self._orient = self._orient.rotate(angle)
1611
1612    def _goto(self, end):
1613        """move turtle to position end."""
1614        self._position = end
1615
1616    def forward(self, distance):
1617        """Move the turtle forward by the specified distance.
1618
1619        Aliases: forward | fd
1620
1621        Argument:
1622        distance -- a number (integer or float)
1623
1624        Move the turtle forward by the specified distance, in the direction
1625        the turtle is headed.
1626
1627        Example (for a Turtle instance named turtle):
1628        >>> turtle.position()
1629        (0.00, 0.00)
1630        >>> turtle.forward(25)
1631        >>> turtle.position()
1632        (25.00,0.00)
1633        >>> turtle.forward(-75)
1634        >>> turtle.position()
1635        (-50.00,0.00)
1636        """
1637        self._go(distance)
1638
1639    def back(self, distance):
1640        """Move the turtle backward by distance.
1641
1642        Aliases: back | backward | bk
1643
1644        Argument:
1645        distance -- a number
1646
1647        Move the turtle backward by distance ,opposite to the direction the
1648        turtle is headed. Do not change the turtle's heading.
1649
1650        Example (for a Turtle instance named turtle):
1651        >>> turtle.position()
1652        (0.00, 0.00)
1653        >>> turtle.backward(30)
1654        >>> turtle.position()
1655        (-30.00, 0.00)
1656        """
1657        self._go(-distance)
1658
1659    def right(self, angle):
1660        """Turn turtle right by angle units.
1661
1662        Aliases: right | rt
1663
1664        Argument:
1665        angle -- a number (integer or float)
1666
1667        Turn turtle right by angle units. (Units are by default degrees,
1668        but can be set via the degrees() and radians() functions.)
1669        Angle orientation depends on mode. (See this.)
1670
1671        Example (for a Turtle instance named turtle):
1672        >>> turtle.heading()
1673        22.0
1674        >>> turtle.right(45)
1675        >>> turtle.heading()
1676        337.0
1677        """
1678        self._rotate(-angle)
1679
1680    def left(self, angle):
1681        """Turn turtle left by angle units.
1682
1683        Aliases: left | lt
1684
1685        Argument:
1686        angle -- a number (integer or float)
1687
1688        Turn turtle left by angle units. (Units are by default degrees,
1689        but can be set via the degrees() and radians() functions.)
1690        Angle orientation depends on mode. (See this.)
1691
1692        Example (for a Turtle instance named turtle):
1693        >>> turtle.heading()
1694        22.0
1695        >>> turtle.left(45)
1696        >>> turtle.heading()
1697        67.0
1698        """
1699        self._rotate(angle)
1700
1701    def pos(self):
1702        """Return the turtle's current location (x,y), as a Vec2D-vector.
1703
1704        Aliases: pos | position
1705
1706        No arguments.
1707
1708        Example (for a Turtle instance named turtle):
1709        >>> turtle.pos()
1710        (0.00, 240.00)
1711        """
1712        return self._position
1713
1714    def xcor(self):
1715        """ Return the turtle's x coordinate.
1716
1717        No arguments.
1718
1719        Example (for a Turtle instance named turtle):
1720        >>> reset()
1721        >>> turtle.left(60)
1722        >>> turtle.forward(100)
1723        >>> print turtle.xcor()
1724        50.0
1725        """
1726        return self._position[0]
1727
1728    def ycor(self):
1729        """ Return the turtle's y coordinate
1730        ---
1731        No arguments.
1732
1733        Example (for a Turtle instance named turtle):
1734        >>> reset()
1735        >>> turtle.left(60)
1736        >>> turtle.forward(100)
1737        >>> print turtle.ycor()
1738        86.6025403784
1739        """
1740        return self._position[1]
1741
1742
1743    def goto(self, x, y=None):
1744        """Move turtle to an absolute position.
1745
1746        Aliases: setpos | setposition | goto:
1747
1748        Arguments:
1749        x -- a number      or     a pair/vector of numbers
1750        y -- a number             None
1751
1752        call: goto(x, y)         # two coordinates
1753        --or: goto((x, y))       # a pair (tuple) of coordinates
1754        --or: goto(vec)          # e.g. as returned by pos()
1755
1756        Move turtle to an absolute position. If the pen is down,
1757        a line will be drawn. The turtle's orientation does not change.
1758
1759        Example (for a Turtle instance named turtle):
1760        >>> tp = turtle.pos()
1761        >>> tp
1762        (0.00, 0.00)
1763        >>> turtle.setpos(60,30)
1764        >>> turtle.pos()
1765        (60.00,30.00)
1766        >>> turtle.setpos((20,80))
1767        >>> turtle.pos()
1768        (20.00,80.00)
1769        >>> turtle.setpos(tp)
1770        >>> turtle.pos()
1771        (0.00,0.00)
1772        """
1773        if y is None:
1774            self._goto(Vec2D(*x))
1775        else:
1776            self._goto(Vec2D(x, y))
1777
1778    def home(self):
1779        """Move turtle to the origin - coordinates (0,0).
1780
1781        No arguments.
1782
1783        Move turtle to the origin - coordinates (0,0) and set its
1784        heading to its start-orientation (which depends on mode).
1785
1786        Example (for a Turtle instance named turtle):
1787        >>> turtle.home()
1788        """
1789        self.goto(0, 0)
1790        self.setheading(0)
1791
1792    def setx(self, x):
1793        """Set the turtle's first coordinate to x
1794
1795        Argument:
1796        x -- a number (integer or float)
1797
1798        Set the turtle's first coordinate to x, leave second coordinate
1799        unchanged.
1800
1801        Example (for a Turtle instance named turtle):
1802        >>> turtle.position()
1803        (0.00, 240.00)
1804        >>> turtle.setx(10)
1805        >>> turtle.position()
1806        (10.00, 240.00)
1807        """
1808        self._goto(Vec2D(x, self._position[1]))
1809
1810    def sety(self, y):
1811        """Set the turtle's second coordinate to y
1812
1813        Argument:
1814        y -- a number (integer or float)
1815
1816        Set the turtle's first coordinate to x, second coordinate remains
1817        unchanged.
1818
1819        Example (for a Turtle instance named turtle):
1820        >>> turtle.position()
1821        (0.00, 40.00)
1822        >>> turtle.sety(-10)
1823        >>> turtle.position()
1824        (0.00, -10.00)
1825        """
1826        self._goto(Vec2D(self._position[0], y))
1827
1828    def distance(self, x, y=None):
1829        """Return the distance from the turtle to (x,y) in turtle step units.
1830
1831        Arguments:
1832        x -- a number   or  a pair/vector of numbers   or   a turtle instance
1833        y -- a number       None                            None
1834
1835        call: distance(x, y)         # two coordinates
1836        --or: distance((x, y))       # a pair (tuple) of coordinates
1837        --or: distance(vec)          # e.g. as returned by pos()
1838        --or: distance(mypen)        # where mypen is another turtle
1839
1840        Example (for a Turtle instance named turtle):
1841        >>> turtle.pos()
1842        (0.00, 0.00)
1843        >>> turtle.distance(30,40)
1844        50.0
1845        >>> pen = Turtle()
1846        >>> pen.forward(77)
1847        >>> turtle.distance(pen)
1848        77.0
1849        """
1850        if y is not None:
1851            pos = Vec2D(x, y)
1852        if isinstance(x, Vec2D):
1853            pos = x
1854        elif isinstance(x, tuple):
1855            pos = Vec2D(*x)
1856        elif isinstance(x, TNavigator):
1857            pos = x._position
1858        return abs(pos - self._position)
1859
1860    def towards(self, x, y=None):
1861        """Return the angle of the line from the turtle's position to (x, y).
1862
1863        Arguments:
1864        x -- a number   or  a pair/vector of numbers   or   a turtle instance
1865        y -- a number       None                            None
1866
1867        call: distance(x, y)         # two coordinates
1868        --or: distance((x, y))       # a pair (tuple) of coordinates
1869        --or: distance(vec)          # e.g. as returned by pos()
1870        --or: distance(mypen)        # where mypen is another turtle
1871
1872        Return the angle, between the line from turtle-position to position
1873        specified by x, y and the turtle's start orientation. (Depends on
1874        modes - "standard" or "logo")
1875
1876        Example (for a Turtle instance named turtle):
1877        >>> turtle.pos()
1878        (10.00, 10.00)
1879        >>> turtle.towards(0,0)
1880        225.0
1881        """
1882        if y is not None:
1883            pos = Vec2D(x, y)
1884        if isinstance(x, Vec2D):
1885            pos = x
1886        elif isinstance(x, tuple):
1887            pos = Vec2D(*x)
1888        elif isinstance(x, TNavigator):
1889            pos = x._position
1890        x, y = pos - self._position
1891        result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1892        result /= self._degreesPerAU
1893        return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1894
1895    def heading(self):
1896        """ Return the turtle's current heading.
1897
1898        No arguments.
1899
1900        Example (for a Turtle instance named turtle):
1901        >>> turtle.left(67)
1902        >>> turtle.heading()
1903        67.0
1904        """
1905        x, y = self._orient
1906        result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1907        result /= self._degreesPerAU
1908        return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1909
1910    def setheading(self, to_angle):
1911        """Set the orientation of the turtle to to_angle.
1912
1913        Aliases:  setheading | seth
1914
1915        Argument:
1916        to_angle -- a number (integer or float)
1917
1918        Set the orientation of the turtle to to_angle.
1919        Here are some common directions in degrees:
1920
1921         standard - mode:          logo-mode:
1922        -------------------|--------------------
1923           0 - east                0 - north
1924          90 - north              90 - east
1925         180 - west              180 - south
1926         270 - south             270 - west
1927
1928        Example (for a Turtle instance named turtle):
1929        >>> turtle.setheading(90)
1930        >>> turtle.heading()
1931        90
1932        """
1933        angle = (to_angle - self.heading())*self._angleOrient
1934        full = self._fullcircle
1935        angle = (angle+full/2.)%full - full/2.
1936        self._rotate(angle)
1937
1938    def circle(self, radius, extent = None, steps = None):
1939        """ Draw a circle with given radius.
1940
1941        Arguments:
1942        radius -- a number
1943        extent (optional) -- a number
1944        steps (optional) -- an integer
1945
1946        Draw a circle with given radius. The center is radius units left
1947        of the turtle; extent - an angle - determines which part of the
1948        circle is drawn. If extent is not given, draw the entire circle.
1949        If extent is not a full circle, one endpoint of the arc is the
1950        current pen position. Draw the arc in counterclockwise direction
1951        if radius is positive, otherwise in clockwise direction. Finally
1952        the direction of the turtle is changed by the amount of extent.
1953
1954        As the circle is approximated by an inscribed regular polygon,
1955        steps determines the number of steps to use. If not given,
1956        it will be calculated automatically. Maybe used to draw regular
1957        polygons.
1958
1959        call: circle(radius)                  # full circle
1960        --or: circle(radius, extent)          # arc
1961        --or: circle(radius, extent, steps)
1962        --or: circle(radius, steps=6)         # 6-sided polygon
1963
1964        Example (for a Turtle instance named turtle):
1965        >>> turtle.circle(50)
1966        >>> turtle.circle(120, 180)  # semicircle
1967        """
1968        if self.undobuffer:
1969            self.undobuffer.push(["seq"])
1970            self.undobuffer.cumulate = True
1971        speed = self.speed()
1972        if extent is None:
1973            extent = self._fullcircle
1974        if steps is None:
1975            frac = abs(extent)/self._fullcircle
1976            steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1977        w = 1.0 * extent / steps
1978        w2 = 0.5 * w
1979        l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1980        if radius < 0:
1981            l, w, w2 = -l, -w, -w2
1982        tr = self._tracer()
1983        dl = self._delay()
1984        if speed == 0:
1985            self._tracer(0, 0)
1986        else:
1987            self.speed(0)
1988        self._rotate(w2)
1989        for i in range(steps):
1990            self.speed(speed)
1991            self._go(l)
1992            self.speed(0)
1993            self._rotate(w)
1994        self._rotate(-w2)
1995        if speed == 0:
1996            self._tracer(tr, dl)
1997        self.speed(speed)
1998        if self.undobuffer:
1999            self.undobuffer.cumulate = False
2000
2001## three dummy methods to be implemented by child class:
2002
2003    def speed(self, s=0):
2004        """dummy method - to be overwritten by child class"""
2005    def _tracer(self, a=None, b=None):
2006        """dummy method - to be overwritten by child class"""
2007    def _delay(self, n=None):
2008        """dummy method - to be overwritten by child class"""
2009
2010    fd = forward
2011    bk = back
2012    backward = back
2013    rt = right
2014    lt = left
2015    position = pos
2016    setpos = goto
2017    setposition = goto
2018    seth = setheading
2019
2020
2021class TPen(object):
2022    """Drawing part of the RawTurtle.
2023    Implements drawing properties.
2024    """
2025    def __init__(self, resizemode=_CFG["resizemode"]):
2026        self._resizemode = resizemode # or "user" or "noresize"
2027        self.undobuffer = None
2028        TPen._reset(self)
2029
2030    def _reset(self, pencolor=_CFG["pencolor"],
2031                     fillcolor=_CFG["fillcolor"]):
2032        self._pensize = 1
2033        self._shown = True
2034        self._pencolor = pencolor
2035        self._fillcolor = fillcolor
2036        self._drawing = True
2037        self._speed = 3
2038        self._stretchfactor = (1., 1.)
2039        self._shearfactor = 0.
2040        self._tilt = 0.
2041        self._shapetrafo = (1., 0., 0., 1.)
2042        self._outlinewidth = 1
2043
2044    def resizemode(self, rmode=None):
2045        """Set resizemode to one of the values: "auto", "user", "noresize".
2046
2047        (Optional) Argument:
2048        rmode -- one of the strings "auto", "user", "noresize"
2049
2050        Different resizemodes have the following effects:
2051          - "auto" adapts the appearance of the turtle
2052                   corresponding to the value of pensize.
2053          - "user" adapts the appearance of the turtle according to the
2054                   values of stretchfactor and outlinewidth (outline),
2055                   which are set by shapesize()
2056          - "noresize" no adaption of the turtle's appearance takes place.
2057        If no argument is given, return current resizemode.
2058        resizemode("user") is called by a call of shapesize with arguments.
2059
2060
2061        Examples (for a Turtle instance named turtle):
2062        >>> turtle.resizemode("noresize")
2063        >>> turtle.resizemode()
2064        'noresize'
2065        """
2066        if rmode is None:
2067            return self._resizemode
2068        rmode = rmode.lower()
2069        if rmode in ["auto", "user", "noresize"]:
2070            self.pen(resizemode=rmode)
2071
2072    def pensize(self, width=None):
2073        """Set or return the line thickness.
2074
2075        Aliases:  pensize | width
2076
2077        Argument:
2078        width -- positive number
2079
2080        Set the line thickness to width or return it. If resizemode is set
2081        to "auto" and turtleshape is a polygon, that polygon is drawn with
2082        the same line thickness. If no argument is given, current pensize
2083        is returned.
2084
2085        Example (for a Turtle instance named turtle):
2086        >>> turtle.pensize()
2087        1
2088        turtle.pensize(10)   # from here on lines of width 10 are drawn
2089        """
2090        if width is None:
2091            return self._pensize
2092        self.pen(pensize=width)
2093
2094
2095    def penup(self):
2096        """Pull the pen up -- no drawing when moving.
2097
2098        Aliases: penup | pu | up
2099
2100        No argument
2101
2102        Example (for a Turtle instance named turtle):
2103        >>> turtle.penup()
2104        """
2105        if not self._drawing:
2106            return
2107        self.pen(pendown=False)
2108
2109    def pendown(self):
2110        """Pull the pen down -- drawing when moving.
2111
2112        Aliases: pendown | pd | down
2113
2114        No argument.
2115
2116        Example (for a Turtle instance named turtle):
2117        >>> turtle.pendown()
2118        """
2119        if self._drawing:
2120            return
2121        self.pen(pendown=True)
2122
2123    def isdown(self):
2124        """Return True if pen is down, False if it's up.
2125
2126        No argument.
2127
2128        Example (for a Turtle instance named turtle):
2129        >>> turtle.penup()
2130        >>> turtle.isdown()
2131        False
2132        >>> turtle.pendown()
2133        >>> turtle.isdown()
2134        True
2135        """
2136        return self._drawing
2137
2138    def speed(self, speed=None):
2139        """ Return or set the turtle's speed.
2140
2141        Optional argument:
2142        speed -- an integer in the range 0..10 or a speedstring (see below)
2143
2144        Set the turtle's speed to an integer value in the range 0 .. 10.
2145        If no argument is given: return current speed.
2146
2147        If input is a number greater than 10 or smaller than 0.5,
2148        speed is set to 0.
2149        Speedstrings  are mapped to speedvalues in the following way:
2150            'fastest' :  0
2151            'fast'    :  10
2152            'normal'  :  6
2153            'slow'    :  3
2154            'slowest' :  1
2155        speeds from 1 to 10 enforce increasingly faster animation of
2156        line drawing and turtle turning.
2157
2158        Attention:
2159        speed = 0 : *no* animation takes place. forward/back makes turtle jump
2160        and likewise left/right make the turtle turn instantly.
2161
2162        Example (for a Turtle instance named turtle):
2163        >>> turtle.speed(3)
2164        """
2165        speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2166        if speed is None:
2167            return self._speed
2168        if speed in speeds:
2169            speed = speeds[speed]
2170        elif 0.5 < speed < 10.5:
2171            speed = int(round(speed))
2172        else:
2173            speed = 0
2174        self.pen(speed=speed)
2175
2176    def color(self, *args):
2177        """Return or set the pencolor and fillcolor.
2178
2179        Arguments:
2180        Several input formats are allowed.
2181        They use 0, 1, 2, or 3 arguments as follows:
2182
2183        color()
2184            Return the current pencolor and the current fillcolor
2185            as a pair of color specification strings as are returned
2186            by pencolor and fillcolor.
2187        color(colorstring), color((r,g,b)), color(r,g,b)
2188            inputs as in pencolor, set both, fillcolor and pencolor,
2189            to the given value.
2190        color(colorstring1, colorstring2),
2191        color((r1,g1,b1), (r2,g2,b2))
2192            equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2193            and analogously, if the other input format is used.
2194
2195        If turtleshape is a polygon, outline and interior of that polygon
2196        is drawn with the newly set colors.
2197        For mor info see: pencolor, fillcolor
2198
2199        Example (for a Turtle instance named turtle):
2200        >>> turtle.color('red', 'green')
2201        >>> turtle.color()
2202        ('red', 'green')
2203        >>> colormode(255)
2204        >>> color((40, 80, 120), (160, 200, 240))
2205        >>> color()
2206        ('#285078', '#a0c8f0')
2207        """
2208        if args:
2209            l = len(args)
2210            if l == 1:
2211                pcolor = fcolor = args[0]
2212            elif l == 2:
2213                pcolor, fcolor = args
2214            elif l == 3:
2215                pcolor = fcolor = args
2216            pcolor = self._colorstr(pcolor)
2217            fcolor = self._colorstr(fcolor)
2218            self.pen(pencolor=pcolor, fillcolor=fcolor)
2219        else:
2220            return self._color(self._pencolor), self._color(self._fillcolor)
2221
2222    def pencolor(self, *args):
2223        """ Return or set the pencolor.
2224
2225        Arguments:
2226        Four input formats are allowed:
2227          - pencolor()
2228            Return the current pencolor as color specification string,
2229            possibly in hex-number format (see example).
2230            May be used as input to another color/pencolor/fillcolor call.
2231          - pencolor(colorstring)
2232            s is a Tk color specification string, such as "red" or "yellow"
2233          - pencolor((r, g, b))
2234            *a tuple* of r, g, and b, which represent, an RGB color,
2235            and each of r, g, and b are in the range 0..colormode,
2236            where colormode is either 1.0 or 255
2237          - pencolor(r, g, b)
2238            r, g, and b represent an RGB color, and each of r, g, and b
2239            are in the range 0..colormode
2240
2241        If turtleshape is a polygon, the outline of that polygon is drawn
2242        with the newly set pencolor.
2243
2244        Example (for a Turtle instance named turtle):
2245        >>> turtle.pencolor('brown')
2246        >>> tup = (0.2, 0.8, 0.55)
2247        >>> turtle.pencolor(tup)
2248        >>> turtle.pencolor()
2249        '#33cc8c'
2250        """
2251        if args:
2252            color = self._colorstr(args)
2253            if color == self._pencolor:
2254                return
2255            self.pen(pencolor=color)
2256        else:
2257            return self._color(self._pencolor)
2258
2259    def fillcolor(self, *args):
2260        """ Return or set the fillcolor.
2261
2262        Arguments:
2263        Four input formats are allowed:
2264          - fillcolor()
2265            Return the current fillcolor as color specification string,
2266            possibly in hex-number format (see example).
2267            May be used as input to another color/pencolor/fillcolor call.
2268          - fillcolor(colorstring)
2269            s is a Tk color specification string, such as "red" or "yellow"
2270          - fillcolor((r, g, b))
2271            *a tuple* of r, g, and b, which represent, an RGB color,
2272            and each of r, g, and b are in the range 0..colormode,
2273            where colormode is either 1.0 or 255
2274          - fillcolor(r, g, b)
2275            r, g, and b represent an RGB color, and each of r, g, and b
2276            are in the range 0..colormode
2277
2278        If turtleshape is a polygon, the interior of that polygon is drawn
2279        with the newly set fillcolor.
2280
2281        Example (for a Turtle instance named turtle):
2282        >>> turtle.fillcolor('violet')
2283        >>> col = turtle.pencolor()
2284        >>> turtle.fillcolor(col)
2285        >>> turtle.fillcolor(0, .5, 0)
2286        """
2287        if args:
2288            color = self._colorstr(args)
2289            if color == self._fillcolor:
2290                return
2291            self.pen(fillcolor=color)
2292        else:
2293            return self._color(self._fillcolor)
2294
2295    def showturtle(self):
2296        """Makes the turtle visible.
2297
2298        Aliases: showturtle | st
2299
2300        No argument.
2301
2302        Example (for a Turtle instance named turtle):
2303        >>> turtle.hideturtle()
2304        >>> turtle.showturtle()
2305        """
2306        self.pen(shown=True)
2307
2308    def hideturtle(self):
2309        """Makes the turtle invisible.
2310
2311        Aliases: hideturtle | ht
2312
2313        No argument.
2314
2315        It's a good idea to do this while you're in the
2316        middle of a complicated drawing, because hiding
2317        the turtle speeds up the drawing observably.
2318
2319        Example (for a Turtle instance named turtle):
2320        >>> turtle.hideturtle()
2321        """
2322        self.pen(shown=False)
2323
2324    def isvisible(self):
2325        """Return True if the Turtle is shown, False if it's hidden.
2326
2327        No argument.
2328
2329        Example (for a Turtle instance named turtle):
2330        >>> turtle.hideturtle()
2331        >>> print turtle.isvisible():
2332        False
2333        """
2334        return self._shown
2335
2336    def pen(self, pen=None, **pendict):
2337        """Return or set the pen's attributes.
2338
2339        Arguments:
2340            pen -- a dictionary with some or all of the below listed keys.
2341            **pendict -- one or more keyword-arguments with the below
2342                         listed keys as keywords.
2343
2344        Return or set the pen's attributes in a 'pen-dictionary'
2345        with the following key/value pairs:
2346           "shown"      :   True/False
2347           "pendown"    :   True/False
2348           "pencolor"   :   color-string or color-tuple
2349           "fillcolor"  :   color-string or color-tuple
2350           "pensize"    :   positive number
2351           "speed"      :   number in range 0..10
2352           "resizemode" :   "auto" or "user" or "noresize"
2353           "stretchfactor": (positive number, positive number)
2354           "shearfactor":   number
2355           "outline"    :   positive number
2356           "tilt"       :   number
2357
2358        This dictionary can be used as argument for a subsequent
2359        pen()-call to restore the former pen-state. Moreover one
2360        or more of these attributes can be provided as keyword-arguments.
2361        This can be used to set several pen attributes in one statement.
2362
2363
2364        Examples (for a Turtle instance named turtle):
2365        >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2366        >>> turtle.pen()
2367        {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2368        'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
2369        'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
2370        >>> penstate=turtle.pen()
2371        >>> turtle.color("yellow","")
2372        >>> turtle.penup()
2373        >>> turtle.pen()
2374        {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2375        'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
2376        'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
2377        >>> p.pen(penstate, fillcolor="green")
2378        >>> p.pen()
2379        {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2380        'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
2381        'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
2382        """
2383        _pd =  {"shown"         : self._shown,
2384                "pendown"       : self._drawing,
2385                "pencolor"      : self._pencolor,
2386                "fillcolor"     : self._fillcolor,
2387                "pensize"       : self._pensize,
2388                "speed"         : self._speed,
2389                "resizemode"    : self._resizemode,
2390                "stretchfactor" : self._stretchfactor,
2391                "shearfactor"   : self._shearfactor,
2392                "outline"       : self._outlinewidth,
2393                "tilt"          : self._tilt
2394               }
2395
2396        if not (pen or pendict):
2397            return _pd
2398
2399        if isinstance(pen, dict):
2400            p = pen
2401        else:
2402            p = {}
2403        p.update(pendict)
2404
2405        _p_buf = {}
2406        for key in p:
2407            _p_buf[key] = _pd[key]
2408
2409        if self.undobuffer:
2410            self.undobuffer.push(("pen", _p_buf))
2411
2412        newLine = False
2413        if "pendown" in p:
2414            if self._drawing != p["pendown"]:
2415                newLine = True
2416        if "pencolor" in p:
2417            if isinstance(p["pencolor"], tuple):
2418                p["pencolor"] = self._colorstr((p["pencolor"],))
2419            if self._pencolor != p["pencolor"]:
2420                newLine = True
2421        if "pensize" in p:
2422            if self._pensize != p["pensize"]:
2423                newLine = True
2424        if newLine:
2425            self._newLine()
2426        if "pendown" in p:
2427            self._drawing = p["pendown"]
2428        if "pencolor" in p:
2429            self._pencolor = p["pencolor"]
2430        if "pensize" in p:
2431            self._pensize = p["pensize"]
2432        if "fillcolor" in p:
2433            if isinstance(p["fillcolor"], tuple):
2434                p["fillcolor"] = self._colorstr((p["fillcolor"],))
2435            self._fillcolor = p["fillcolor"]
2436        if "speed" in p:
2437            self._speed = p["speed"]
2438        if "resizemode" in p:
2439            self._resizemode = p["resizemode"]
2440        if "stretchfactor" in p:
2441            sf = p["stretchfactor"]
2442            if isinstance(sf, (int, float)):
2443                sf = (sf, sf)
2444            self._stretchfactor = sf
2445        if "shearfactor" in p:
2446            self._shearfactor = p["shearfactor"]
2447        if "outline" in p:
2448            self._outlinewidth = p["outline"]
2449        if "shown" in p:
2450            self._shown = p["shown"]
2451        if "tilt" in p:
2452            self._tilt = p["tilt"]
2453        if "stretchfactor" in p or "tilt" in p or "shearfactor" in p:
2454            scx, scy = self._stretchfactor
2455            shf = self._shearfactor
2456            sa, ca = math.sin(self._tilt), math.cos(self._tilt)
2457            self._shapetrafo = ( scx*ca, scy*(shf*ca + sa),
2458                                -scx*sa, scy*(ca - shf*sa))
2459        self._update()
2460
2461## three dummy methods to be implemented by child class:
2462
2463    def _newLine(self, usePos = True):
2464        """dummy method - to be overwritten by child class"""
2465    def _update(self, count=True, forced=False):
2466        """dummy method - to be overwritten by child class"""
2467    def _color(self, args):
2468        """dummy method - to be overwritten by child class"""
2469    def _colorstr(self, args):
2470        """dummy method - to be overwritten by child class"""
2471
2472    width = pensize
2473    up = penup
2474    pu = penup
2475    pd = pendown
2476    down = pendown
2477    st = showturtle
2478    ht = hideturtle
2479
2480
2481class _TurtleImage(object):
2482    """Helper class: Datatype to store Turtle attributes
2483    """
2484
2485    def __init__(self, screen, shapeIndex):
2486        self.screen = screen
2487        self._type = None
2488        self._setshape(shapeIndex)
2489
2490    def _setshape(self, shapeIndex):
2491        screen = self.screen
2492        self.shapeIndex = shapeIndex
2493        if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2494            return
2495        if self._type == "image" == screen._shapes[shapeIndex]._type:
2496            return
2497        if self._type in ["image", "polygon"]:
2498            screen._delete(self._item)
2499        elif self._type == "compound":
2500            for item in self._item:
2501                screen._delete(item)
2502        self._type = screen._shapes[shapeIndex]._type
2503        if self._type == "polygon":
2504            self._item = screen._createpoly()
2505        elif self._type == "image":
2506            self._item = screen._createimage(screen._shapes["blank"]._data)
2507        elif self._type == "compound":
2508            self._item = [screen._createpoly() for item in
2509                                          screen._shapes[shapeIndex]._data]
2510
2511
2512class RawTurtle(TPen, TNavigator):
2513    """Animation part of the RawTurtle.
2514    Puts RawTurtle upon a TurtleScreen and provides tools for
2515    its animation.
2516    """
2517    screens = []
2518
2519    def __init__(self, canvas=None,
2520                 shape=_CFG["shape"],
2521                 undobuffersize=_CFG["undobuffersize"],
2522                 visible=_CFG["visible"]):
2523        if isinstance(canvas, _Screen):
2524            self.screen = canvas
2525        elif isinstance(canvas, TurtleScreen):
2526            if canvas not in RawTurtle.screens:
2527                RawTurtle.screens.append(canvas)
2528            self.screen = canvas
2529        elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2530            for screen in RawTurtle.screens:
2531                if screen.cv == canvas:
2532                    self.screen = screen
2533                    break
2534            else:
2535                self.screen = TurtleScreen(canvas)
2536                RawTurtle.screens.append(self.screen)
2537        else:
2538            raise TurtleGraphicsError("bad cavas argument %s" % canvas)
2539
2540        screen = self.screen
2541        TNavigator.__init__(self, screen.mode())
2542        TPen.__init__(self)
2543        screen._turtles.append(self)
2544        self.drawingLineItem = screen._createline()
2545        self.turtle = _TurtleImage(screen, shape)
2546        self._poly = None
2547        self._creatingPoly = False
2548        self._fillitem = self._fillpath = None
2549        self._shown = visible
2550        self._hidden_from_screen = False
2551        self.currentLineItem = screen._createline()
2552        self.currentLine = [self._position]
2553        self.items = [self.currentLineItem]
2554        self.stampItems = []
2555        self._undobuffersize = undobuffersize
2556        self.undobuffer = Tbuffer(undobuffersize)
2557        self._update()
2558
2559    def reset(self):
2560        """Delete the turtle's drawings and restore its default values.
2561
2562        No argument.
2563,
2564        Delete the turtle's drawings from the screen, re-center the turtle
2565        and set variables to the default values.
2566
2567        Example (for a Turtle instance named turtle):
2568        >>> turtle.position()
2569        (0.00,-22.00)
2570        >>> turtle.heading()
2571        100.0
2572        >>> turtle.reset()
2573        >>> turtle.position()
2574        (0.00,0.00)
2575        >>> turtle.heading()
2576        0.0
2577        """
2578        TNavigator.reset(self)
2579        TPen._reset(self)
2580        self._clear()
2581        self._drawturtle()
2582        self._update()
2583
2584    def setundobuffer(self, size):
2585        """Set or disable undobuffer.
2586
2587        Argument:
2588        size -- an integer or None
2589
2590        If size is an integer an empty undobuffer of given size is installed.
2591        Size gives the maximum number of turtle-actions that can be undone
2592        by the undo() function.
2593        If size is None, no undobuffer is present.
2594
2595        Example (for a Turtle instance named turtle):
2596        >>> turtle.setundobuffer(42)
2597        """
2598        if size is None:
2599            self.undobuffer = None
2600        else:
2601            self.undobuffer = Tbuffer(size)
2602
2603    def undobufferentries(self):
2604        """Return count of entries in the undobuffer.
2605
2606        No argument.
2607
2608        Example (for a Turtle instance named turtle):
2609        >>> while undobufferentries():
2610                undo()
2611        """
2612        if self.undobuffer is None:
2613            return 0
2614        return self.undobuffer.nr_of_items()
2615
2616    def _clear(self):
2617        """Delete all of pen's drawings"""
2618        self._fillitem = self._fillpath = None
2619        for item in self.items:
2620            self.screen._delete(item)
2621        self.currentLineItem = self.screen._createline()
2622        self.currentLine = []
2623        if self._drawing:
2624            self.currentLine.append(self._position)
2625        self.items = [self.currentLineItem]
2626        self.clearstamps()
2627        self.setundobuffer(self._undobuffersize)
2628
2629
2630    def clear(self):
2631        """Delete the turtle's drawings from the screen. Do not move turtle.
2632
2633        No arguments.
2634
2635        Delete the turtle's drawings from the screen. Do not move turtle.
2636        State and position of the turtle as well as drawings of other
2637        turtles are not affected.
2638
2639        Examples (for a Turtle instance named turtle):
2640        >>> turtle.clear()
2641        """
2642        self._clear()
2643        self._update()
2644
2645    def _update_data(self):
2646        self.screen._incrementudc()
2647        if self.screen._updatecounter != 0:
2648            return
2649        if len(self.currentLine)>1:
2650            self.screen._drawline(self.currentLineItem, self.currentLine,
2651                                  self._pencolor, self._pensize)
2652
2653    def _update(self):
2654        """Perform a Turtle-data update.
2655        """
2656        screen = self.screen
2657        if screen._tracing == 0:
2658            return
2659        elif screen._tracing == 1:
2660            self._update_data()
2661            self._drawturtle()
2662            screen._update()                  # TurtleScreenBase
2663            screen._delay(screen._delayvalue) # TurtleScreenBase
2664        else:
2665            self._update_data()
2666            if screen._updatecounter == 0:
2667                for t in screen.turtles():
2668                    t._drawturtle()
2669                screen._update()
2670
2671    def _tracer(self, flag=None, delay=None):
2672        """Turns turtle animation on/off and set delay for update drawings.
2673
2674        Optional arguments:
2675        n -- nonnegative  integer
2676        delay -- nonnegative  integer
2677
2678        If n is given, only each n-th regular screen update is really performed.
2679        (Can be used to accelerate the drawing of complex graphics.)
2680        Second arguments sets delay value (see RawTurtle.delay())
2681
2682        Example (for a Turtle instance named turtle):
2683        >>> turtle.tracer(8, 25)
2684        >>> dist = 2
2685        >>> for i in range(200):
2686                turtle.fd(dist)
2687                turtle.rt(90)
2688                dist += 2
2689        """
2690        return self.screen.tracer(flag, delay)
2691
2692    def _color(self, args):
2693        return self.screen._color(args)
2694
2695    def _colorstr(self, args):
2696        return self.screen._colorstr(args)
2697
2698    def _cc(self, args):
2699        """Convert colortriples to hexstrings.
2700        """
2701        if isinstance(args, str):
2702            return args
2703        try:
2704            r, g, b = args
2705        except:
2706            raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2707        if self.screen._colormode == 1.0:
2708            r, g, b = [round(255.0*x) for x in (r, g, b)]
2709        if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2710            raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2711        return "#%02x%02x%02x" % (r, g, b)
2712
2713    def clone(self):
2714        """Create and return a clone of the turtle.
2715
2716        No argument.
2717
2718        Create and return a clone of the turtle with same position, heading
2719        and turtle properties.
2720
2721        Example (for a Turtle instance named mick):
2722        mick = Turtle()
2723        joe = mick.clone()
2724        """
2725        screen = self.screen
2726        self._newLine(self._drawing)
2727
2728        turtle = self.turtle
2729        self.screen = None
2730        self.turtle = None  # too make self deepcopy-able
2731
2732        q = deepcopy(self)
2733
2734        self.screen = screen
2735        self.turtle = turtle
2736
2737        q.screen = screen
2738        q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2739
2740        screen._turtles.append(q)
2741        ttype = screen._shapes[self.turtle.shapeIndex]._type
2742        if ttype == "polygon":
2743            q.turtle._item = screen._createpoly()
2744        elif ttype == "image":
2745            q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2746        elif ttype == "compound":
2747            q.turtle._item = [screen._createpoly() for item in
2748                              screen._shapes[self.turtle.shapeIndex]._data]
2749        q.currentLineItem = screen._createline()
2750        q._update()
2751        return q
2752
2753    def shape(self, name=None):
2754        """Set turtle shape to shape with given name / return current shapename.
2755
2756        Optional argument:
2757        name -- a string, which is a valid shapename
2758
2759        Set turtle shape to shape with given name or, if name is not given,
2760        return name of current shape.
2761        Shape with name must exist in the TurtleScreen's shape dictionary.
2762        Initially there are the following polygon shapes:
2763        'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2764        To learn about how to deal with shapes see Screen-method register_shape.
2765
2766        Example (for a Turtle instance named turtle):
2767        >>> turtle.shape()
2768        'arrow'
2769        >>> turtle.shape("turtle")
2770        >>> turtle.shape()
2771        'turtle'
2772        """
2773        if name is None:
2774            return self.turtle.shapeIndex
2775        if not name in self.screen.getshapes():
2776            raise TurtleGraphicsError("There is no shape named %s" % name)
2777        self.turtle._setshape(name)
2778        self._update()
2779
2780    def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2781        """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2782
2783        Optinonal arguments:
2784           stretch_wid : positive number
2785           stretch_len : positive number
2786           outline  : positive number
2787
2788        Return or set the pen's attributes x/y-stretchfactors and/or outline.
2789        Set resizemode to "user".
2790        If and only if resizemode is set to "user", the turtle will be displayed
2791        stretched according to its stretchfactors:
2792        stretch_wid is stretchfactor perpendicular to orientation
2793        stretch_len is stretchfactor in direction of turtles orientation.
2794        outline determines the width of the shapes's outline.
2795
2796        Examples (for a Turtle instance named turtle):
2797        >>> turtle.resizemode("user")
2798        >>> turtle.shapesize(5, 5, 12)
2799        >>> turtle.shapesize(outline=8)
2800        """
2801        if stretch_wid is stretch_len is outline is None:
2802            stretch_wid, stretch_len = self._stretchfactor
2803            return stretch_wid, stretch_len, self._outlinewidth
2804        if stretch_wid == 0 or stretch_len == 0:
2805            raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero")
2806        if stretch_wid is not None:
2807            if stretch_len is None:
2808                stretchfactor = stretch_wid, stretch_wid
2809            else:
2810                stretchfactor = stretch_wid, stretch_len
2811        elif stretch_len is not None:
2812            stretchfactor = self._stretchfactor[0], stretch_len
2813        else:
2814            stretchfactor = self._stretchfactor
2815        if outline is None:
2816            outline = self._outlinewidth
2817        self.pen(resizemode="user",
2818                 stretchfactor=stretchfactor, outline=outline)
2819
2820    def shearfactor(self, shear=None):
2821        """Set or return the current shearfactor.
2822
2823        Optional argument: shear -- number, tangent of the shear angle
2824
2825        Shear the turtleshape according to the given shearfactor shear,
2826        which is the tangent of the shear angle. DO NOT change the
2827        turtle's heading (direction of movement).
2828        If shear is not given: return the current shearfactor, i. e. the
2829        tangent of the shear angle, by which lines parallel to the
2830        heading of the turtle are sheared.
2831
2832        Examples (for a Turtle instance named turtle):
2833        >>> turtle.shape("circle")
2834        >>> turtle.shapesize(5,2)
2835        >>> turtle.shearfactor(0.5)
2836        >>> turtle.shearfactor()
2837        >>> 0.5
2838        """
2839        if shear is None:
2840            return self._shearfactor
2841        self.pen(resizemode="user", shearfactor=shear)
2842
2843    def settiltangle(self, angle):
2844        """Rotate the turtleshape to point in the specified direction
2845
2846        Argument: angle -- number
2847
2848        Rotate the turtleshape to point in the direction specified by angle,
2849        regardless of its current tilt-angle. DO NOT change the turtle's
2850        heading (direction of movement).
2851
2852
2853        Examples (for a Turtle instance named turtle):
2854        >>> turtle.shape("circle")
2855        >>> turtle.shapesize(5,2)
2856        >>> turtle.settiltangle(45)
2857        >>> stamp()
2858        >>> turtle.fd(50)
2859        >>> turtle.settiltangle(-45)
2860        >>> stamp()
2861        >>> turtle.fd(50)
2862        """
2863        tilt = -angle * self._degreesPerAU * self._angleOrient
2864        tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2865        self.pen(resizemode="user", tilt=tilt)
2866
2867    def tiltangle(self, angle=None):
2868        """Set or return the current tilt-angle.
2869
2870        Optional argument: angle -- number
2871
2872        Rotate the turtleshape to point in the direction specified by angle,
2873        regardless of its current tilt-angle. DO NOT change the turtle's
2874        heading (direction of movement).
2875        If angle is not given: return the current tilt-angle, i. e. the angle
2876        between the orientation of the turtleshape and the heading of the
2877        turtle (its direction of movement).
2878
2879        Deprecated since Python 3.1
2880
2881        Examples (for a Turtle instance named turtle):
2882        >>> turtle.shape("circle")
2883        >>> turtle.shapesize(5,2)
2884        >>> turtle.tilt(45)
2885        >>> turtle.tiltangle()
2886        >>>
2887        """
2888        if angle is None:
2889            tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2890            return (tilt / self._degreesPerAU) % self._fullcircle
2891        else:
2892            self.settiltangle(angle)
2893
2894    def tilt(self, angle):
2895        """Rotate the turtleshape by angle.
2896
2897        Argument:
2898        angle - a number
2899
2900        Rotate the turtleshape by angle from its current tilt-angle,
2901        but do NOT change the turtle's heading (direction of movement).
2902
2903        Examples (for a Turtle instance named turtle):
2904        >>> turtle.shape("circle")
2905        >>> turtle.shapesize(5,2)
2906        >>> turtle.tilt(30)
2907        >>> turtle.fd(50)
2908        >>> turtle.tilt(30)
2909        >>> turtle.fd(50)
2910        """
2911        self.settiltangle(angle + self.tiltangle())
2912
2913    def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2914        """Set or return the current transformation matrix of the turtle shape.
2915
2916        Optional arguments: t11, t12, t21, t22 -- numbers.
2917
2918        If none of the matrix elements are given, return the transformation
2919        matrix.
2920        Otherwise set the given elements and transform the turtleshape
2921        according to the matrix consisting of first row t11, t12 and
2922        second row t21, 22.
2923        Modify stretchfactor, shearfactor and tiltangle according to the
2924        given matrix.
2925
2926        Examples (for a Turtle instance named turtle):
2927        >>> turtle.shape("square")
2928        >>> turtle.shapesize(4,2)
2929        >>> turtle.shearfactor(-0.5)
2930        >>> turtle.shapetransform()
2931        >>> (4.0, -1.0, -0.0, 2.0)
2932        """
2933        if t11 is t12 is t21 is t22 is None:
2934            return self._shapetrafo
2935        m11, m12, m21, m22 = self._shapetrafo
2936        if t11 is not None: m11 = t11
2937        if t12 is not None: m12 = t12
2938        if t21 is not None: m21 = t21
2939        if t22 is not None: m22 = t22
2940        if t11 * t22 - t12 * t21 == 0:
2941            raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2942        self._shapetrafo = (m11, m12, m21, m22)
2943        alfa = math.atan2(-m21, m11) % (2 * math.pi)
2944        sa, ca = math.sin(alfa), math.cos(alfa)
2945        a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2946                              sa*m11 + ca*m21, sa*m12 + ca*m22)
2947        self._stretchfactor = a11, a22
2948        self._shearfactor = a12/a22
2949        self._tilt = alfa
2950        self._update()
2951
2952
2953    def _polytrafo(self, poly):
2954        """Computes transformed polygon shapes from a shape
2955        according to current position and heading.
2956        """
2957        screen = self.screen
2958        p0, p1 = self._position
2959        e0, e1 = self._orient
2960        e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2961        e0, e1 = (1.0 / abs(e)) * e
2962        return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2963                                                           for (x, y) in poly]
2964
2965    def get_shapepoly(self):
2966        """Return the current shape polygon as tuple of coordinate pairs.
2967
2968        No argument.
2969
2970        Examples (for a Turtle instance named turtle):
2971        >>> turtle.shape("square")
2972        >>> turtle.shapetransform(4, -1, 0, 2)
2973        >>> turtle.get_shapepoly()
2974        ((50, -20), (30, 20), (-50, 20), (-30, -20))
2975
2976        """
2977        shape = self.screen._shapes[self.turtle.shapeIndex]
2978        if shape._type == "polygon":
2979            return self._getshapepoly(shape._data, shape._type == "compound")
2980        # else return None
2981
2982    def _getshapepoly(self, polygon, compound=False):
2983        """Calculate transformed shape polygon according to resizemode
2984        and shapetransform.
2985        """
2986        if self._resizemode == "user" or compound:
2987            t11, t12, t21, t22 = self._shapetrafo
2988        elif self._resizemode == "auto":
2989            l = max(1, self._pensize/5.0)
2990            t11, t12, t21, t22 = l, 0, 0, l
2991        elif self._resizemode == "noresize":
2992            return polygon
2993        return tuple([(t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon])
2994
2995    def _drawturtle(self):
2996        """Manages the correct rendering of the turtle with respect to
2997        its shape, resizemode, stretch and tilt etc."""
2998        screen = self.screen
2999        shape = screen._shapes[self.turtle.shapeIndex]
3000        ttype = shape._type
3001        titem = self.turtle._item
3002        if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3003            self._hidden_from_screen = False
3004            tshape = shape._data
3005            if ttype == "polygon":
3006                if self._resizemode == "noresize": w = 1
3007                elif self._resizemode == "auto": w = self._pensize
3008                else: w =self._outlinewidth
3009                shape = self._polytrafo(self._getshapepoly(tshape))
3010                fc, oc = self._fillcolor, self._pencolor
3011                screen._drawpoly(titem, shape, fill=fc, outline=oc,
3012                                                      width=w, top=True)
3013            elif ttype == "image":
3014                screen._drawimage(titem, self._position, tshape)
3015            elif ttype == "compound":
3016                for item, (poly, fc, oc) in zip(titem, tshape):
3017                    poly = self._polytrafo(self._getshapepoly(poly, True))
3018                    screen._drawpoly(item, poly, fill=self._cc(fc),
3019                                     outline=self._cc(oc), width=self._outlinewidth, top=True)
3020        else:
3021            if self._hidden_from_screen:
3022                return
3023            if ttype == "polygon":
3024                screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3025            elif ttype == "image":
3026                screen._drawimage(titem, self._position,
3027                                          screen._shapes["blank"]._data)
3028            elif ttype == "compound":
3029                for item in titem:
3030                    screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3031            self._hidden_from_screen = True
3032
3033##############################  stamp stuff  ###############################
3034
3035    def stamp(self):
3036        """Stamp a copy of the turtleshape onto the canvas and return its id.
3037
3038        No argument.
3039
3040        Stamp a copy of the turtle shape onto the canvas at the current
3041        turtle position. Return a stamp_id for that stamp, which can be
3042        used to delete it by calling clearstamp(stamp_id).
3043
3044        Example (for a Turtle instance named turtle):
3045        >>> turtle.color("blue")
3046        >>> turtle.stamp()
3047        13
3048        >>> turtle.fd(50)
3049        """
3050        screen = self.screen
3051        shape = screen._shapes[self.turtle.shapeIndex]
3052        ttype = shape._type
3053        tshape = shape._data
3054        if ttype == "polygon":
3055            stitem = screen._createpoly()
3056            if self._resizemode == "noresize": w = 1
3057            elif self._resizemode == "auto": w = self._pensize
3058            else: w =self._outlinewidth
3059            shape = self._polytrafo(self._getshapepoly(tshape))
3060            fc, oc = self._fillcolor, self._pencolor
3061            screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3062                                                  width=w, top=True)
3063        elif ttype == "image":
3064            stitem = screen._createimage("")
3065            screen._drawimage(stitem, self._position, tshape)
3066        elif ttype == "compound":
3067            stitem = []
3068            for element in tshape:
3069                item = screen._createpoly()
3070                stitem.append(item)
3071            stitem = tuple(stitem)
3072            for item, (poly, fc, oc) in zip(stitem, tshape):
3073                poly = self._polytrafo(self._getshapepoly(poly, True))
3074                screen._drawpoly(item, poly, fill=self._cc(fc),
3075                                 outline=self._cc(oc), width=self._outlinewidth, top=True)
3076        self.stampItems.append(stitem)
3077        self.undobuffer.push(("stamp", stitem))
3078        return stitem
3079
3080    def _clearstamp(self, stampid):
3081        """does the work for clearstamp() and clearstamps()
3082        """
3083        if stampid in self.stampItems:
3084            if isinstance(stampid, tuple):
3085                for subitem in stampid:
3086                    self.screen._delete(subitem)
3087            else:
3088                self.screen._delete(stampid)
3089            self.stampItems.remove(stampid)
3090        # Delete stampitem from undobuffer if necessary
3091        # if clearstamp is called directly.
3092        item = ("stamp", stampid)
3093        buf = self.undobuffer
3094        if item not in buf.buffer:
3095            return
3096        index = buf.buffer.index(item)
3097        buf.buffer.remove(item)
3098        if index <= buf.ptr:
3099            buf.ptr = (buf.ptr - 1) % buf.bufsize
3100        buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3101
3102    def clearstamp(self, stampid):
3103        """Delete stamp with given stampid
3104
3105        Argument:
3106        stampid - an integer, must be return value of previous stamp() call.
3107
3108        Example (for a Turtle instance named turtle):
3109        >>> turtle.color("blue")
3110        >>> astamp = turtle.stamp()
3111        >>> turtle.fd(50)
3112        >>> turtle.clearstamp(astamp)
3113        """
3114        self._clearstamp(stampid)
3115        self._update()
3116
3117    def clearstamps(self, n=None):
3118        """Delete all or first/last n of turtle's stamps.
3119
3120        Optional argument:
3121        n -- an integer
3122
3123        If n is None, delete all of pen's stamps,
3124        else if n > 0 delete first n stamps
3125        else if n < 0 delete last n stamps.
3126
3127        Example (for a Turtle instance named turtle):
3128        >>> for i in range(8):
3129                turtle.stamp(); turtle.fd(30)
3130        ...
3131        >>> turtle.clearstamps(2)
3132        >>> turtle.clearstamps(-2)
3133        >>> turtle.clearstamps()
3134        """
3135        if n is None:
3136            toDelete = self.stampItems[:]
3137        elif n >= 0:
3138            toDelete = self.stampItems[:n]
3139        else:
3140            toDelete = self.stampItems[n:]
3141        for item in toDelete:
3142            self._clearstamp(item)
3143        self._update()
3144
3145    def _goto(self, end):
3146        """Move the pen to the point end, thereby drawing a line
3147        if pen is down. All other methodes for turtle movement depend
3148        on this one.
3149        """
3150        ## Version with undo-stuff
3151        go_modes = ( self._drawing,
3152                     self._pencolor,
3153                     self._pensize,
3154                     isinstance(self._fillpath, list))
3155        screen = self.screen
3156        undo_entry = ("go", self._position, end, go_modes,
3157                      (self.currentLineItem,
3158                      self.currentLine[:],
3159                      screen._pointlist(self.currentLineItem),
3160                      self.items[:])
3161                      )
3162        if self.undobuffer:
3163            self.undobuffer.push(undo_entry)
3164        start = self._position
3165        if self._speed and screen._tracing == 1:
3166            diff = (end-start)
3167            diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3168            nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3169            delta = diff * (1.0/nhops)
3170            for n in range(1, nhops):
3171                if n == 1:
3172                    top = True
3173                else:
3174                    top = False
3175                self._position = start + delta * n
3176                if self._drawing:
3177                    screen._drawline(self.drawingLineItem,
3178                                     (start, self._position),
3179                                     self._pencolor, self._pensize, top)
3180                self._update()
3181            if self._drawing:
3182                screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3183                                               fill="", width=self._pensize)
3184        # Turtle now at end,
3185        if self._drawing: # now update currentLine
3186            self.currentLine.append(end)
3187        if isinstance(self._fillpath, list):
3188            self._fillpath.append(end)
3189        ######    vererbung!!!!!!!!!!!!!!!!!!!!!!
3190        self._position = end
3191        if self._creatingPoly:
3192            self._poly.append(end)
3193        if len(self.currentLine) > 42: # 42! answer to the ultimate question
3194                                       # of life, the universe and everything
3195            self._newLine()
3196        self._update() #count=True)
3197
3198    def _undogoto(self, entry):
3199        """Reverse a _goto. Used for undo()
3200        """
3201        old, new, go_modes, coodata = entry
3202        drawing, pc, ps, filling = go_modes
3203        cLI, cL, pl, items = coodata
3204        screen = self.screen
3205        if abs(self._position - new) > 0.5:
3206            print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3207        # restore former situation
3208        self.currentLineItem = cLI
3209        self.currentLine = cL
3210
3211        if pl == [(0, 0), (0, 0)]:
3212            usepc = ""
3213        else:
3214            usepc = pc
3215        screen._drawline(cLI, pl, fill=usepc, width=ps)
3216
3217        todelete = [i for i in self.items if (i not in items) and
3218                                       (screen._type(i) == "line")]
3219        for i in todelete:
3220            screen._delete(i)
3221            self.items.remove(i)
3222
3223        start = old
3224        if self._speed and screen._tracing == 1:
3225            diff = old - new
3226            diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3227            nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3228            delta = diff * (1.0/nhops)
3229            for n in range(1, nhops):
3230                if n == 1:
3231                    top = True
3232                else:
3233                    top = False
3234                self._position = new + delta * n
3235                if drawing:
3236                    screen._drawline(self.drawingLineItem,
3237                                     (start, self._position),
3238                                     pc, ps, top)
3239                self._update()
3240            if drawing:
3241                screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3242                                               fill="", width=ps)
3243        # Turtle now at position old,
3244        self._position = old
3245        ##  if undo is done during crating a polygon, the last vertex
3246        ##  will be deleted. if the polygon is entirel deleted,
3247        ##  creatigPoly will be set to False.
3248        ##  Polygons created before the last one will not be affected by undo()
3249        if self._creatingPoly:
3250            if len(self._poly) > 0:
3251                self._poly.pop()
3252            if self._poly == []:
3253                self._creatingPoly = False
3254                self._poly = None
3255        if filling:
3256            if self._fillpath == []:
3257                self._fillpath = None
3258                print("Unwahrscheinlich in _undogoto!")
3259            elif self._fillpath is not None:
3260                self._fillpath.pop()
3261        self._update() #count=True)
3262
3263    def _rotate(self, angle):
3264        """Turns pen clockwise by angle.
3265        """
3266        if self.undobuffer:
3267            self.undobuffer.push(("rot", angle, self._degreesPerAU))
3268        angle *= self._degreesPerAU
3269        neworient = self._orient.rotate(angle)
3270        tracing = self.screen._tracing
3271        if tracing == 1 and self._speed > 0:
3272            anglevel = 3.0 * self._speed
3273            steps = 1 + int(abs(angle)/anglevel)
3274            delta = 1.0*angle/steps
3275            for _ in range(steps):
3276                self._orient = self._orient.rotate(delta)
3277                self._update()
3278        self._orient = neworient
3279        self._update()
3280
3281    def _newLine(self, usePos=True):
3282        """Closes current line item and starts a new one.
3283           Remark: if current line became too long, animation
3284           performance (via _drawline) slowed down considerably.
3285        """
3286        if len(self.currentLine) > 1:
3287            self.screen._drawline(self.currentLineItem, self.currentLine,
3288                                      self._pencolor, self._pensize)
3289            self.currentLineItem = self.screen._createline()
3290            self.items.append(self.currentLineItem)
3291        else:
3292            self.screen._drawline(self.currentLineItem, top=True)
3293        self.currentLine = []
3294        if usePos:
3295            self.currentLine = [self._position]
3296
3297    def filling(self):
3298        """Return fillstate (True if filling, False else).
3299
3300        No argument.
3301
3302        Example (for a Turtle instance named turtle):
3303        >>> turtle.begin_fill()
3304        >>> if turtle.filling():
3305                turtle.pensize(5)
3306        else:
3307                turtle.pensize(3)
3308        """
3309        return isinstance(self._fillpath, list)
3310
3311    def begin_fill(self):
3312        """Called just before drawing a shape to be filled.
3313
3314        No argument.
3315
3316        Example (for a Turtle instance named turtle):
3317        >>> turtle.color("black", "red")
3318        >>> turtle.begin_fill()
3319        >>> turtle.circle(60)
3320        >>> turtle.end_fill()
3321        """
3322        if not self.filling():
3323            self._fillitem = self.screen._createpoly()
3324            self.items.append(self._fillitem)
3325        self._fillpath = [self._position]
3326        self._newLine()
3327        if self.undobuffer:
3328            self.undobuffer.push(("beginfill", self._fillitem))
3329        self._update()
3330
3331
3332    def end_fill(self):
3333        """Fill the shape drawn after the call begin_fill().
3334
3335        No argument.
3336
3337        Example (for a Turtle instance named turtle):
3338        >>> turtle.color("black", "red")
3339        >>> turtle.begin_fill()
3340        >>> turtle.circle(60)
3341        >>> turtle.end_fill()
3342        """
3343        if self.filling():
3344            if len(self._fillpath) > 2:
3345                self.screen._drawpoly(self._fillitem, self._fillpath,
3346                                      fill=self._fillcolor)
3347                if self.undobuffer:
3348                    self.undobuffer.push(("dofill", self._fillitem))
3349            self._fillitem = self._fillpath = None
3350            self._update()
3351
3352    def dot(self, size=None, *color):
3353        """Draw a dot with diameter size, using color.
3354
3355        Optional arguments:
3356        size -- an integer >= 1 (if given)
3357        color -- a colorstring or a numeric color tuple
3358
3359        Draw a circular dot with diameter size, using color.
3360        If size is not given, the maximum of pensize+4 and 2*pensize is used.
3361
3362        Example (for a Turtle instance named turtle):
3363        >>> turtle.dot()
3364        >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3365        """
3366        if not color:
3367            if isinstance(size, (str, tuple)):
3368                color = self._colorstr(size)
3369                size = self._pensize + max(self._pensize, 4)
3370            else:
3371                color = self._pencolor
3372                if not size:
3373                    size = self._pensize + max(self._pensize, 4)
3374        else:
3375            if size is None:
3376                size = self._pensize + max(self._pensize, 4)
3377            color = self._colorstr(color)
3378        if hasattr(self.screen, "_dot"):
3379            item = self.screen._dot(self._position, size, color)
3380            self.items.append(item)
3381            if self.undobuffer:
3382                self.undobuffer.push(("dot", item))
3383        else:
3384            pen = self.pen()
3385            if self.undobuffer:
3386                self.undobuffer.push(["seq"])
3387                self.undobuffer.cumulate = True
3388            try:
3389                if self.resizemode() == 'auto':
3390                    self.ht()
3391                self.pendown()
3392                self.pensize(size)
3393                self.pencolor(color)
3394                self.forward(0)
3395            finally:
3396                self.pen(pen)
3397            if self.undobuffer:
3398                self.undobuffer.cumulate = False
3399
3400    def _write(self, txt, align, font):
3401        """Performs the writing for write()
3402        """
3403        item, end = self.screen._write(self._position, txt, align, font,
3404                                                          self._pencolor)
3405        self.items.append(item)
3406        if self.undobuffer:
3407            self.undobuffer.push(("wri", item))
3408        return end
3409
3410    def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3411        """Write text at the current turtle position.
3412
3413        Arguments:
3414        arg -- info, which is to be written to the TurtleScreen
3415        move (optional) -- True/False
3416        align (optional) -- one of the strings "left", "center" or right"
3417        font (optional) -- a triple (fontname, fontsize, fonttype)
3418
3419        Write text - the string representation of arg - at the current
3420        turtle position according to align ("left", "center" or right")
3421        and with the given font.
3422        If move is True, the pen is moved to the bottom-right corner
3423        of the text. By default, move is False.
3424
3425        Example (for a Turtle instance named turtle):
3426        >>> turtle.write('Home = ', True, align="center")
3427        >>> turtle.write((0,0), True)
3428        """
3429        if self.undobuffer:
3430            self.undobuffer.push(["seq"])
3431            self.undobuffer.cumulate = True
3432        end = self._write(str(arg), align.lower(), font)
3433        if move:
3434            x, y = self.pos()
3435            self.setpos(end, y)
3436        if self.undobuffer:
3437            self.undobuffer.cumulate = False
3438
3439    def begin_poly(self):
3440        """Start recording the vertices of a polygon.
3441
3442        No argument.
3443
3444        Start recording the vertices of a polygon. Current turtle position
3445        is first point of polygon.
3446
3447        Example (for a Turtle instance named turtle):
3448        >>> turtle.begin_poly()
3449        """
3450        self._poly = [self._position]
3451        self._creatingPoly = True
3452
3453    def end_poly(self):
3454        """Stop recording the vertices of a polygon.
3455
3456        No argument.
3457
3458        Stop recording the vertices of a polygon. Current turtle position is
3459        last point of polygon. This will be connected with the first point.
3460
3461        Example (for a Turtle instance named turtle):
3462        >>> turtle.end_poly()
3463        """
3464        self._creatingPoly = False
3465
3466    def get_poly(self):
3467        """Return the lastly recorded polygon.
3468
3469        No argument.
3470
3471        Example (for a Turtle instance named turtle):
3472        >>> p = turtle.get_poly()
3473        >>> turtle.register_shape("myFavouriteShape", p)
3474        """
3475        ## check if there is any poly?
3476        if self._poly is not None:
3477            return tuple(self._poly)
3478
3479    def getscreen(self):
3480        """Return the TurtleScreen object, the turtle is drawing  on.
3481
3482        No argument.
3483
3484        Return the TurtleScreen object, the turtle is drawing  on.
3485        So TurtleScreen-methods can be called for that object.
3486
3487        Example (for a Turtle instance named turtle):
3488        >>> ts = turtle.getscreen()
3489        >>> ts
3490        <turtle.TurtleScreen object at 0x0106B770>
3491        >>> ts.bgcolor("pink")
3492        """
3493        return self.screen
3494
3495    def getturtle(self):
3496        """Return the Turtleobject itself.
3497
3498        No argument.
3499
3500        Only reasonable use: as a function to return the 'anonymous turtle':
3501
3502        Example:
3503        >>> pet = getturtle()
3504        >>> pet.fd(50)
3505        >>> pet
3506        <turtle.Turtle object at 0x0187D810>
3507        >>> turtles()
3508        [<turtle.Turtle object at 0x0187D810>]
3509        """
3510        return self
3511
3512    getpen = getturtle
3513
3514
3515    ################################################################
3516    ### screen oriented methods recurring to methods of TurtleScreen
3517    ################################################################
3518
3519    def _delay(self, delay=None):
3520        """Set delay value which determines speed of turtle animation.
3521        """
3522        return self.screen.delay(delay)
3523
3524    def onclick(self, fun, btn=1, add=None):
3525        """Bind fun to mouse-click event on this turtle on canvas.
3526
3527        Arguments:
3528        fun --  a function with two arguments, to which will be assigned
3529                the coordinates of the clicked point on the canvas.
3530        num --  number of the mouse-button defaults to 1 (left mouse button).
3531        add --  True or False. If True, new binding will be added, otherwise
3532                it will replace a former binding.
3533
3534        Example for the anonymous turtle, i. e. the procedural way:
3535
3536        >>> def turn(x, y):
3537                left(360)
3538
3539        >>> onclick(turn) # Now clicking into the turtle will turn it.
3540        >>> onclick(None)  # event-binding will be removed
3541        """
3542        self.screen._onclick(self.turtle._item, fun, btn, add)
3543        self._update()
3544
3545    def onrelease(self, fun, btn=1, add=None):
3546        """Bind fun to mouse-button-release event on this turtle on canvas.
3547
3548        Arguments:
3549        fun -- a function with two arguments, to which will be assigned
3550                the coordinates of the clicked point on the canvas.
3551        num --  number of the mouse-button defaults to 1 (left mouse button).
3552
3553        Example (for a MyTurtle instance named joe):
3554        >>> class MyTurtle(Turtle):
3555                def glow(self,x,y):
3556                        self.fillcolor("red")
3557                def unglow(self,x,y):
3558                        self.fillcolor("")
3559
3560        >>> joe = MyTurtle()
3561        >>> joe.onclick(joe.glow)
3562        >>> joe.onrelease(joe.unglow)
3563        ### clicking on joe turns fillcolor red,
3564        ### unclicking turns it to transparent.
3565        """
3566        self.screen._onrelease(self.turtle._item, fun, btn, add)
3567        self._update()
3568
3569    def ondrag(self, fun, btn=1, add=None):
3570        """Bind fun to mouse-move event on this turtle on canvas.
3571
3572        Arguments:
3573        fun -- a function with two arguments, to which will be assigned
3574               the coordinates of the clicked point on the canvas.
3575        num -- number of the mouse-button defaults to 1 (left mouse button).
3576
3577        Every sequence of mouse-move-events on a turtle is preceded by a
3578        mouse-click event on that turtle.
3579
3580        Example (for a Turtle instance named turtle):
3581        >>> turtle.ondrag(turtle.goto)
3582
3583        ### Subsequently clicking and dragging a Turtle will
3584        ### move it across the screen thereby producing handdrawings
3585        ### (if pen is down).
3586        """
3587        self.screen._ondrag(self.turtle._item, fun, btn, add)
3588
3589
3590    def _undo(self, action, data):
3591        """Does the main part of the work for undo()
3592        """
3593        if self.undobuffer is None:
3594            return
3595        if action == "rot":
3596            angle, degPAU = data
3597            self._rotate(-angle*degPAU/self._degreesPerAU)
3598            dummy = self.undobuffer.pop()
3599        elif action == "stamp":
3600            stitem = data[0]
3601            self.clearstamp(stitem)
3602        elif action == "go":
3603            self._undogoto(data)
3604        elif action in ["wri", "dot"]:
3605            item = data[0]
3606            self.screen._delete(item)
3607            self.items.remove(item)
3608        elif action == "dofill":
3609            item = data[0]
3610            self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3611                                  fill="", outline="")
3612        elif action == "beginfill":
3613            item = data[0]
3614            self._fillitem = self._fillpath = None
3615            if item in self.items:
3616                self.screen._delete(item)
3617                self.items.remove(item)
3618        elif action == "pen":
3619            TPen.pen(self, data[0])
3620            self.undobuffer.pop()
3621
3622    def undo(self):
3623        """undo (repeatedly) the last turtle action.
3624
3625        No argument.
3626
3627        undo (repeatedly) the last turtle action.
3628        Number of available undo actions is determined by the size of
3629        the undobuffer.
3630
3631        Example (for a Turtle instance named turtle):
3632        >>> for i in range(4):
3633                turtle.fd(50); turtle.lt(80)
3634
3635        >>> for i in range(8):
3636                turtle.undo()
3637        """
3638        if self.undobuffer is None:
3639            return
3640        item = self.undobuffer.pop()
3641        action = item[0]
3642        data = item[1:]
3643        if action == "seq":
3644            while data:
3645                item = data.pop()
3646                self._undo(item[0], item[1:])
3647        else:
3648            self._undo(action, data)
3649
3650    turtlesize = shapesize
3651
3652RawPen = RawTurtle
3653
3654###  Screen - Singleton  ########################
3655
3656def Screen():
3657    """Return the singleton screen object.
3658    If none exists at the moment, create a new one and return it,
3659    else return the existing one."""
3660    if Turtle._screen is None:
3661        Turtle._screen = _Screen()
3662    return Turtle._screen
3663
3664class _Screen(TurtleScreen):
3665
3666    _root = None
3667    _canvas = None
3668    _title = _CFG["title"]
3669
3670    def __init__(self):
3671        # XXX there is no need for this code to be conditional,
3672        # as there will be only a single _Screen instance, anyway
3673        # XXX actually, the turtle demo is injecting root window,
3674        # so perhaps the conditional creation of a root should be
3675        # preserved (perhaps by passing it as an optional parameter)
3676        if _Screen._root is None:
3677            _Screen._root = self._root = _Root()
3678            self._root.title(_Screen._title)
3679            self._root.ondestroy(self._destroy)
3680        if _Screen._canvas is None:
3681            width = _CFG["width"]
3682            height = _CFG["height"]
3683            canvwidth = _CFG["canvwidth"]
3684            canvheight = _CFG["canvheight"]
3685            leftright = _CFG["leftright"]
3686            topbottom = _CFG["topbottom"]
3687            self._root.setupcanvas(width, height, canvwidth, canvheight)
3688            _Screen._canvas = self._root._getcanvas()
3689            TurtleScreen.__init__(self, _Screen._canvas)
3690            self.setup(width, height, leftright, topbottom)
3691
3692    def setup(self, width=_CFG["width"], height=_CFG["height"],
3693              startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3694        """ Set the size and position of the main window.
3695
3696        Arguments:
3697        width: as integer a size in pixels, as float a fraction of the screen.
3698          Default is 50% of screen.
3699        height: as integer the height in pixels, as float a fraction of the
3700          screen. Default is 75% of screen.
3701        startx: if positive, starting position in pixels from the left
3702          edge of the screen, if negative from the right edge
3703          Default, startx=None is to center window horizontally.
3704        starty: if positive, starting position in pixels from the top
3705          edge of the screen, if negative from the bottom edge
3706          Default, starty=None is to center window vertically.
3707
3708        Examples (for a Screen instance named screen):
3709        >>> screen.setup (width=200, height=200, startx=0, starty=0)
3710
3711        sets window to 200x200 pixels, in upper left of screen
3712
3713        >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3714
3715        sets window to 75% of screen by 50% of screen and centers
3716        """
3717        if not hasattr(self._root, "set_geometry"):
3718            return
3719        sw = self._root.win_width()
3720        sh = self._root.win_height()
3721        if isinstance(width, float) and 0 <= width <= 1:
3722            width = sw*width
3723        if startx is None:
3724            startx = (sw - width) / 2
3725        if isinstance(height, float) and 0 <= height <= 1:
3726            height = sh*height
3727        if starty is None:
3728            starty = (sh - height) / 2
3729        self._root.set_geometry(width, height, startx, starty)
3730        self.update()
3731
3732    def title(self, titlestring):
3733        """Set title of turtle-window
3734
3735        Argument:
3736        titlestring -- a string, to appear in the titlebar of the
3737                       turtle graphics window.
3738
3739        This is a method of Screen-class. Not available for TurtleScreen-
3740        objects.
3741
3742        Example (for a Screen instance named screen):
3743        >>> screen.title("Welcome to the turtle-zoo!")
3744        """
3745        if _Screen._root is not None:
3746            _Screen._root.title(titlestring)
3747        _Screen._title = titlestring
3748
3749    def _destroy(self):
3750        root = self._root
3751        if root is _Screen._root:
3752            Turtle._pen = None
3753            Turtle._screen = None
3754            _Screen._root = None
3755            _Screen._canvas = None
3756        TurtleScreen._RUNNING = True
3757        root.destroy()
3758
3759    def bye(self):
3760        """Shut the turtlegraphics window.
3761
3762        Example (for a TurtleScreen instance named screen):
3763        >>> screen.bye()
3764        """
3765        self._destroy()
3766
3767    def exitonclick(self):
3768        """Go into mainloop until the mouse is clicked.
3769
3770        No arguments.
3771
3772        Bind bye() method to mouseclick on TurtleScreen.
3773        If "using_IDLE" - value in configuration dictionary is False
3774        (default value), enter mainloop.
3775        If IDLE with -n switch (no subprocess) is used, this value should be
3776        set to True in turtle.cfg. In this case IDLE's mainloop
3777        is active also for the client script.
3778
3779        This is a method of the Screen-class and not available for
3780        TurtleScreen instances.
3781
3782        Example (for a Screen instance named screen):
3783        >>> screen.exitonclick()
3784
3785        """
3786        def exitGracefully(x, y):
3787            """Screen.bye() with two dummy-parameters"""
3788            self.bye()
3789        self.onclick(exitGracefully)
3790        if _CFG["using_IDLE"]:
3791            return
3792        try:
3793            mainloop()
3794        except AttributeError:
3795            exit(0)
3796
3797
3798class Turtle(RawTurtle):
3799    """RawTurtle auto-crating (scrolled) canvas.
3800
3801    When a Turtle object is created or a function derived from some
3802    Turtle method is called a TurtleScreen object is automatically created.
3803    """
3804    _pen = None
3805    _screen = None
3806
3807    def __init__(self,
3808                 shape=_CFG["shape"],
3809                 undobuffersize=_CFG["undobuffersize"],
3810                 visible=_CFG["visible"]):
3811        if Turtle._screen is None:
3812            Turtle._screen = Screen()
3813        RawTurtle.__init__(self, Turtle._screen,
3814                           shape=shape,
3815                           undobuffersize=undobuffersize,
3816                           visible=visible)
3817
3818Pen = Turtle
3819
3820def _getpen():
3821    """Create the 'anonymous' turtle if not already present."""
3822    if Turtle._pen is None:
3823        Turtle._pen = Turtle()
3824    return Turtle._pen
3825
3826def _getscreen():
3827    """Create a TurtleScreen if not already present."""
3828    if Turtle._screen is None:
3829        Turtle._screen = Screen()
3830    return Turtle._screen
3831
3832def write_docstringdict(filename="turtle_docstringdict"):
3833    """Create and write docstring-dictionary to file.
3834
3835    Optional argument:
3836    filename -- a string, used as filename
3837                default value is turtle_docstringdict
3838
3839    Has to be called explicitely, (not used by the turtle-graphics classes)
3840    The docstring dictionary will be written to the Python script <filname>.py
3841    It is intended to serve as a template for translation of the docstrings
3842    into different languages.
3843    """
3844    docsdict = {}
3845
3846    for methodname in _tg_screen_functions:
3847        key = "_Screen."+methodname
3848        docsdict[key] = eval(key).__doc__
3849    for methodname in _tg_turtle_functions:
3850        key = "Turtle."+methodname
3851        docsdict[key] = eval(key).__doc__
3852
3853    f = open("%s.py" % filename,"w")
3854    keys = sorted([x for x in docsdict.keys()
3855                        if x.split('.')[1] not in _alias_list])
3856    f.write('docsdict = {\n\n')
3857    for key in keys[:-1]:
3858        f.write('%s :\n' % repr(key))
3859        f.write('        """%s\n""",\n\n' % docsdict[key])
3860    key = keys[-1]
3861    f.write('%s :\n' % repr(key))
3862    f.write('        """%s\n"""\n\n' % docsdict[key])
3863    f.write("}\n")
3864    f.close()
3865
3866def read_docstrings(lang):
3867    """Read in docstrings from lang-specific docstring dictionary.
3868
3869    Transfer docstrings, translated to lang, from a dictionary-file
3870    to the methods of classes Screen and Turtle and - in revised form -
3871    to the corresponding functions.
3872    """
3873    modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3874    module = __import__(modname)
3875    docsdict = module.docsdict
3876    for key in docsdict:
3877        try:
3878#            eval(key).im_func.__doc__ = docsdict[key]
3879            eval(key).__doc__ = docsdict[key]
3880        except:
3881            print("Bad docstring-entry: %s" % key)
3882
3883_LANGUAGE = _CFG["language"]
3884
3885try:
3886    if _LANGUAGE != "english":
3887        read_docstrings(_LANGUAGE)
3888except ImportError:
3889    print("Cannot find docsdict for", _LANGUAGE)
3890except:
3891    print ("Unknown Error when trying to import %s-docstring-dictionary" %
3892                                                                  _LANGUAGE)
3893
3894
3895def getmethparlist(ob):
3896    """Get strings describing the arguments for the given object
3897
3898    Returns a pair of strings representing function parameter lists
3899    including parenthesis.  The first string is suitable for use in
3900    function definition and the second is suitable for use in function
3901    call.  The "self" parameter is not included.
3902    """
3903    defText = callText = ""
3904    # bit of a hack for methods - turn it into a function
3905    # but we drop the "self" param.
3906    # Try and build one for Python defined functions
3907    args, varargs, varkw = inspect.getargs(ob.__code__)
3908    items2 = args[1:]
3909    realArgs = args[1:]
3910    defaults = ob.__defaults__ or []
3911    defaults = ["=%r" % (value,) for value in defaults]
3912    defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3913    items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3914    if varargs is not None:
3915        items1.append("*" + varargs)
3916        items2.append("*" + varargs)
3917    if varkw is not None:
3918        items1.append("**" + varkw)
3919        items2.append("**" + varkw)
3920    defText = ", ".join(items1)
3921    defText = "(%s)" % defText
3922    callText = ", ".join(items2)
3923    callText = "(%s)" % callText
3924    return defText, callText
3925
3926def _turtle_docrevise(docstr):
3927    """To reduce docstrings from RawTurtle class for functions
3928    """
3929    import re
3930    if docstr is None:
3931        return None
3932    turtlename = _CFG["exampleturtle"]
3933    newdocstr = docstr.replace("%s." % turtlename,"")
3934    parexp = re.compile(r' \(.+ %s\):' % turtlename)
3935    newdocstr = parexp.sub(":", newdocstr)
3936    return newdocstr
3937
3938def _screen_docrevise(docstr):
3939    """To reduce docstrings from TurtleScreen class for functions
3940    """
3941    import re
3942    if docstr is None:
3943        return None
3944    screenname = _CFG["examplescreen"]
3945    newdocstr = docstr.replace("%s." % screenname,"")
3946    parexp = re.compile(r' \(.+ %s\):' % screenname)
3947    newdocstr = parexp.sub(":", newdocstr)
3948    return newdocstr
3949
3950## The following mechanism makes all methods of RawTurtle and Turtle available
3951## as functions. So we can enhance, change, add, delete methods to these
3952## classes and do not need to change anything here.
3953
3954
3955for methodname in _tg_screen_functions:
3956    pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
3957    if pl1 == "":
3958        print(">>>>>>", pl1, pl2)
3959        continue
3960    defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
3961                                   {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3962    exec(defstr)
3963    eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
3964
3965for methodname in _tg_turtle_functions:
3966    pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
3967    if pl1 == "":
3968        print(">>>>>>", pl1, pl2)
3969        continue
3970    defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
3971                                   {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3972    exec(defstr)
3973    eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
3974
3975
3976done = mainloop
3977
3978if __name__ == "__main__":
3979    def switchpen():
3980        if isdown():
3981            pu()
3982        else:
3983            pd()
3984
3985    def demo1():
3986        """Demo of old turtle.py - module"""
3987        reset()
3988        tracer(True)
3989        up()
3990        backward(100)
3991        down()
3992        # draw 3 squares; the last filled
3993        width(3)
3994        for i in range(3):
3995            if i == 2:
3996                begin_fill()
3997            for _ in range(4):
3998                forward(20)
3999                left(90)
4000            if i == 2:
4001                color("maroon")
4002                end_fill()
4003            up()
4004            forward(30)
4005            down()
4006        width(1)
4007        color("black")
4008        # move out of the way
4009        tracer(False)
4010        up()
4011        right(90)
4012        forward(100)
4013        right(90)
4014        forward(100)
4015        right(180)
4016        down()
4017        # some text
4018        write("startstart", 1)
4019        write("start", 1)
4020        color("red")
4021        # staircase
4022        for i in range(5):
4023            forward(20)
4024            left(90)
4025            forward(20)
4026            right(90)
4027        # filled staircase
4028        tracer(True)
4029        begin_fill()
4030        for i in range(5):
4031            forward(20)
4032            left(90)
4033            forward(20)
4034            right(90)
4035        end_fill()
4036        # more text
4037
4038    def demo2():
4039        """Demo of some new features."""
4040        speed(1)
4041        st()
4042        pensize(3)
4043        setheading(towards(0, 0))
4044        radius = distance(0, 0)/2.0
4045        rt(90)
4046        for _ in range(18):
4047            switchpen()
4048            circle(radius, 10)
4049        write("wait a moment...")
4050        while undobufferentries():
4051            undo()
4052        reset()
4053        lt(90)
4054        colormode(255)
4055        laenge = 10
4056        pencolor("green")
4057        pensize(3)
4058        lt(180)
4059        for i in range(-2, 16):
4060            if i > 0:
4061                begin_fill()
4062                fillcolor(255-15*i, 0, 15*i)
4063            for _ in range(3):
4064                fd(laenge)
4065                lt(120)
4066            end_fill()
4067            laenge += 10
4068            lt(15)
4069            speed((speed()+1)%12)
4070        #end_fill()
4071
4072        lt(120)
4073        pu()
4074        fd(70)
4075        rt(30)
4076        pd()
4077        color("red","yellow")
4078        speed(0)
4079        begin_fill()
4080        for _ in range(4):
4081            circle(50, 90)
4082            rt(90)
4083            fd(30)
4084            rt(90)
4085        end_fill()
4086        lt(90)
4087        pu()
4088        fd(30)
4089        pd()
4090        shape("turtle")
4091
4092        tri = getturtle()
4093        tri.resizemode("auto")
4094        turtle = Turtle()
4095        turtle.resizemode("auto")
4096        turtle.shape("turtle")
4097        turtle.reset()
4098        turtle.left(90)
4099        turtle.speed(0)
4100        turtle.up()
4101        turtle.goto(280, 40)
4102        turtle.lt(30)
4103        turtle.down()
4104        turtle.speed(6)
4105        turtle.color("blue","orange")
4106        turtle.pensize(2)
4107        tri.speed(6)
4108        setheading(towards(turtle))
4109        count = 1
4110        while tri.distance(turtle) > 4:
4111            turtle.fd(3.5)
4112            turtle.lt(0.6)
4113            tri.setheading(tri.towards(turtle))
4114            tri.fd(4)
4115            if count % 20 == 0:
4116                turtle.stamp()
4117                tri.stamp()
4118                switchpen()
4119            count += 1
4120        tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4121        tri.pencolor("black")
4122        tri.pencolor("red")
4123
4124        def baba(xdummy, ydummy):
4125            clearscreen()
4126            bye()
4127
4128        time.sleep(2)
4129
4130        while undobufferentries():
4131            tri.undo()
4132            turtle.undo()
4133        tri.fd(50)
4134        tri.write("  Click me!", font = ("Courier", 12, "bold") )
4135        tri.onclick(baba, 1)
4136
4137    demo1()
4138    demo2()
4139    exitonclick()
4140