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