• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #
2 # turtle.py: a Tkinter based turtle graphics module for Python
3 # Version 1.1b - 4. 5. 2009
4 #
5 # Copyright (C) 2006 - 2010  Gregor Lingl
6 # email: glingl@aon.at
7 #
8 # This software is provided 'as-is', without any express or implied
9 # warranty.  In no event will the authors be held liable for any damages
10 # arising from the use of this software.
11 #
12 # Permission is granted to anyone to use this software for any purpose,
13 # including commercial applications, and to alter it and redistribute it
14 # freely, subject to the following restrictions:
15 #
16 # 1. The origin of this software must not be misrepresented; you must not
17 #    claim that you wrote the original software. If you use this software
18 #    in a product, an acknowledgment in the product documentation would be
19 #    appreciated but is not required.
20 # 2. Altered source versions must be plainly marked as such, and must not be
21 #    misrepresented as being the original software.
22 # 3. This notice may not be removed or altered from any source distribution.
23 
24 
25 """
26 Turtle graphics is a popular way for introducing programming to
27 kids. It was part of the original Logo programming language developed
28 by Wally Feurzig and Seymour Papert in 1966.
29 
30 Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it
31 the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
32 the direction it is facing, drawing a line as it moves. Give it the
33 command turtle.right(25), and it rotates in-place 25 degrees clockwise.
34 
35 By combining together these and similar commands, intricate shapes and
36 pictures can easily be drawn.
37 
38 ----- turtle.py
39 
40 This module is an extended reimplementation of turtle.py from the
41 Python standard distribution up to Python 2.5. (See: https://www.python.org)
42 
43 It tries to keep the merits of turtle.py and to be (nearly) 100%
44 compatible with it. This means in the first place to enable the
45 learning programmer to use all the commands, classes and methods
46 interactively when using the module from within IDLE run with
47 the -n switch.
48 
49 Roughly 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 
98 Behind the scenes there are some features included with possible
99 extensions 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 
107 import tkinter as TK
108 import types
109 import math
110 import time
111 import inspect
112 import sys
113 
114 from os.path import isfile, split, join
115 from copy import deepcopy
116 from tkinter import simpledialog
117 
118 _tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
119                'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
120 _tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
121         'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
122         'getshapes', 'listen', 'mainloop', 'mode', 'numinput',
123         'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer',
124         'register_shape', 'resetscreen', 'screensize', 'setup',
125         'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update',
126         'window_height', 'window_width']
127 _tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
128         'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
129         'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
130         'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly',
131         'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
132         'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
133         'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
134         'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
135         'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
136         'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle',
137         'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards',
138         'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
139         'write', 'xcor', 'ycor']
140 _tg_utilities = ['write_docstringdict', 'done']
141 
142 __all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
143            _tg_utilities + ['Terminator']) # + _math_functions)
144 
145 _alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
146                'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
147                'turtlesize', 'up', 'width']
148 
149 _CFG = {"width" : 0.5,               # Screen
150         "height" : 0.75,
151         "canvwidth" : 400,
152         "canvheight": 300,
153         "leftright": None,
154         "topbottom": None,
155         "mode": "standard",          # TurtleScreen
156         "colormode": 1.0,
157         "delay": 10,
158         "undobuffersize": 1000,      # RawTurtle
159         "shape": "classic",
160         "pencolor" : "black",
161         "fillcolor" : "black",
162         "resizemode" : "noresize",
163         "visible" : True,
164         "language": "english",        # docstrings
165         "exampleturtle": "turtle",
166         "examplescreen": "screen",
167         "title": "Python Turtle Graphics",
168         "using_IDLE": False
169        }
170 
171 def config_dict(filename):
172     """Convert content of config-file into dictionary."""
173     with open(filename, "r") as f:
174         cfglines = f.readlines()
175     cfgdict = {}
176     for line in cfglines:
177         line = line.strip()
178         if not line or line.startswith("#"):
179             continue
180         try:
181             key, value = line.split("=")
182         except ValueError:
183             print("Bad line in config-file %s:\n%s" % (filename,line))
184             continue
185         key = key.strip()
186         value = value.strip()
187         if value in ["True", "False", "None", "''", '""']:
188             value = eval(value)
189         else:
190             try:
191                 if "." in value:
192                     value = float(value)
193                 else:
194                     value = int(value)
195             except ValueError:
196                 pass # value need not be converted
197         cfgdict[key] = value
198     return cfgdict
199 
200 def readconfig(cfgdict):
201     """Read config-files, change configuration-dict accordingly.
202 
203     If there is a turtle.cfg file in the current working directory,
204     read it from there. If this contains an importconfig-value,
205     say 'myway', construct filename turtle_mayway.cfg else use
206     turtle.cfg and read it from the import-directory, where
207     turtle.py is located.
208     Update configuration dictionary first according to config-file,
209     in the import directory, then according to config-file in the
210     current working directory.
211     If no config-file is found, the default configuration is used.
212     """
213     default_cfg = "turtle.cfg"
214     cfgdict1 = {}
215     cfgdict2 = {}
216     if isfile(default_cfg):
217         cfgdict1 = config_dict(default_cfg)
218     if "importconfig" in cfgdict1:
219         default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
220     try:
221         head, tail = split(__file__)
222         cfg_file2 = join(head, default_cfg)
223     except Exception:
224         cfg_file2 = ""
225     if isfile(cfg_file2):
226         cfgdict2 = config_dict(cfg_file2)
227     _CFG.update(cfgdict2)
228     _CFG.update(cfgdict1)
229 
230 try:
231     readconfig(_CFG)
232 except Exception:
233     print ("No configfile read, reason unknown")
234 
235 
236 class Vec2D(tuple):
237     """A 2 dimensional vector class, used as a helper class
238     for implementing turtle graphics.
239     May be useful for turtle graphics programs also.
240     Derived from tuple, so a vector is a tuple!
241 
242     Provides (for a, b vectors, k number):
243        a+b vector addition
244        a-b vector subtraction
245        a*b inner product
246        k*a and a*k multiplication with scalar
247        |a| absolute value of a
248        a.rotate(angle) rotation
249     """
250     def __new__(cls, x, y):
251         return tuple.__new__(cls, (x, y))
252     def __add__(self, other):
253         return Vec2D(self[0]+other[0], self[1]+other[1])
254     def __mul__(self, other):
255         if isinstance(other, Vec2D):
256             return self[0]*other[0]+self[1]*other[1]
257         return Vec2D(self[0]*other, self[1]*other)
258     def __rmul__(self, other):
259         if isinstance(other, int) or isinstance(other, float):
260             return Vec2D(self[0]*other, self[1]*other)
261         return NotImplemented
262     def __sub__(self, other):
263         return Vec2D(self[0]-other[0], self[1]-other[1])
264     def __neg__(self):
265         return Vec2D(-self[0], -self[1])
266     def __abs__(self):
267         return math.hypot(*self)
268     def rotate(self, angle):
269         """rotate self counterclockwise by angle
270         """
271         perp = Vec2D(-self[1], self[0])
272         angle = math.radians(angle)
273         c, s = math.cos(angle), math.sin(angle)
274         return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
275     def __getnewargs__(self):
276         return (self[0], self[1])
277     def __repr__(self):
278         return "(%.2f,%.2f)" % self
279 
280 
281 ##############################################################################
282 ### From here up to line    : Tkinter - Interface for turtle.py            ###
283 ### May be replaced by an interface to some different graphics toolkit     ###
284 ##############################################################################
285 
286 ## helper functions for Scrolled Canvas, to forward Canvas-methods
287 ## to ScrolledCanvas class
288 
289 def __methodDict(cls, _dict):
290     """helper function for Scrolled Canvas"""
291     baseList = list(cls.__bases__)
292     baseList.reverse()
293     for _super in baseList:
294         __methodDict(_super, _dict)
295     for key, value in cls.__dict__.items():
296         if type(value) == types.FunctionType:
297             _dict[key] = value
298 
299 def __methods(cls):
300     """helper function for Scrolled Canvas"""
301     _dict = {}
302     __methodDict(cls, _dict)
303     return _dict.keys()
304 
305 __stringBody = (
306     'def %(method)s(self, *args, **kw): return ' +
307     'self.%(attribute)s.%(method)s(*args, **kw)')
308 
309 def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
310     ### MANY CHANGES ###
311     _dict_1 = {}
312     __methodDict(toClass, _dict_1)
313     _dict = {}
314     mfc = __methods(fromClass)
315     for ex in _dict_1.keys():
316         if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
317             pass
318         else:
319             _dict[ex] = _dict_1[ex]
320 
321     for method, func in _dict.items():
322         d = {'method': method, 'func': func}
323         if isinstance(toPart, str):
324             execString = \
325                 __stringBody % {'method' : method, 'attribute' : toPart}
326         exec(execString, d)
327         setattr(fromClass, method, d[method])   ### NEWU!
328 
329 
330 class ScrolledCanvas(TK.Frame):
331     """Modeled after the scrolled canvas class from Grayons's Tkinter book.
332 
333     Used as the default canvas, which pops up automatically when
334     using turtle graphics functions or the Turtle class.
335     """
336     def __init__(self, master, width=500, height=350,
337                                           canvwidth=600, canvheight=500):
338         TK.Frame.__init__(self, master, width=width, height=height)
339         self._rootwindow = self.winfo_toplevel()
340         self.width, self.height = width, height
341         self.canvwidth, self.canvheight = canvwidth, canvheight
342         self.bg = "white"
343         self._canvas = TK.Canvas(master, width=width, height=height,
344                                  bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
345         self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
346                                     orient=TK.HORIZONTAL)
347         self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
348         self._canvas.configure(xscrollcommand=self.hscroll.set,
349                                yscrollcommand=self.vscroll.set)
350         self.rowconfigure(0, weight=1, minsize=0)
351         self.columnconfigure(0, weight=1, minsize=0)
352         self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
353                 column=0, rowspan=1, columnspan=1, sticky='news')
354         self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
355                 column=1, rowspan=1, columnspan=1, sticky='news')
356         self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
357                 column=0, rowspan=1, columnspan=1, sticky='news')
358         self.reset()
359         self._rootwindow.bind('<Configure>', self.onResize)
360 
361     def reset(self, canvwidth=None, canvheight=None, bg = None):
362         """Adjust canvas and scrollbars according to given canvas size."""
363         if canvwidth:
364             self.canvwidth = canvwidth
365         if canvheight:
366             self.canvheight = canvheight
367         if bg:
368             self.bg = bg
369         self._canvas.config(bg=bg,
370                         scrollregion=(-self.canvwidth//2, -self.canvheight//2,
371                                        self.canvwidth//2, self.canvheight//2))
372         self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
373                                                                self.canvwidth)
374         self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
375                                                               self.canvheight)
376         self.adjustScrolls()
377 
378 
379     def adjustScrolls(self):
380         """ Adjust scrollbars according to window- and canvas-size.
381         """
382         cwidth = self._canvas.winfo_width()
383         cheight = self._canvas.winfo_height()
384         self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
385         self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
386         if cwidth < self.canvwidth or cheight < self.canvheight:
387             self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
388                               column=0, rowspan=1, columnspan=1, sticky='news')
389             self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
390                               column=1, rowspan=1, columnspan=1, sticky='news')
391         else:
392             self.hscroll.grid_forget()
393             self.vscroll.grid_forget()
394 
395     def onResize(self, event):
396         """self-explanatory"""
397         self.adjustScrolls()
398 
399     def bbox(self, *args):
400         """ 'forward' method, which canvas itself has inherited...
401         """
402         return self._canvas.bbox(*args)
403 
404     def cget(self, *args, **kwargs):
405         """ 'forward' method, which canvas itself has inherited...
406         """
407         return self._canvas.cget(*args, **kwargs)
408 
409     def config(self, *args, **kwargs):
410         """ 'forward' method, which canvas itself has inherited...
411         """
412         self._canvas.config(*args, **kwargs)
413 
414     def bind(self, *args, **kwargs):
415         """ 'forward' method, which canvas itself has inherited...
416         """
417         self._canvas.bind(*args, **kwargs)
418 
419     def unbind(self, *args, **kwargs):
420         """ 'forward' method, which canvas itself has inherited...
421         """
422         self._canvas.unbind(*args, **kwargs)
423 
424     def focus_force(self):
425         """ 'forward' method, which canvas itself has inherited...
426         """
427         self._canvas.focus_force()
428 
429 __forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
430 
431 
432 class _Root(TK.Tk):
433     """Root class for Screen based on Tkinter."""
434     def __init__(self):
435         TK.Tk.__init__(self)
436 
437     def setupcanvas(self, width, height, cwidth, cheight):
438         self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
439         self._canvas.pack(expand=1, fill="both")
440 
441     def _getcanvas(self):
442         return self._canvas
443 
444     def set_geometry(self, width, height, startx, starty):
445         self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
446 
447     def ondestroy(self, destroy):
448         self.wm_protocol("WM_DELETE_WINDOW", destroy)
449 
450     def win_width(self):
451         return self.winfo_screenwidth()
452 
453     def win_height(self):
454         return self.winfo_screenheight()
455 
456 Canvas = TK.Canvas
457 
458 
459 class TurtleScreenBase(object):
460     """Provide the basic graphics functionality.
461        Interface between Tkinter and turtle.py.
462 
463        To port turtle.py to some different graphics toolkit
464        a corresponding TurtleScreenBase class has to be implemented.
465     """
466 
467     def _blankimage(self):
468         """return a blank image object
469         """
470         img = TK.PhotoImage(width=1, height=1, master=self.cv)
471         img.blank()
472         return img
473 
474     def _image(self, filename):
475         """return an image object containing the
476         imagedata from a gif-file named filename.
477         """
478         return TK.PhotoImage(file=filename, master=self.cv)
479 
480     def __init__(self, cv):
481         self.cv = cv
482         if isinstance(cv, ScrolledCanvas):
483             w = self.cv.canvwidth
484             h = self.cv.canvheight
485         else:  # expected: ordinary TK.Canvas
486             w = int(self.cv.cget("width"))
487             h = int(self.cv.cget("height"))
488             self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
489         self.canvwidth = w
490         self.canvheight = h
491         self.xscale = self.yscale = 1.0
492 
493     def _createpoly(self):
494         """Create an invisible polygon item on canvas self.cv)
495         """
496         return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
497 
498     def _drawpoly(self, polyitem, coordlist, fill=None,
499                   outline=None, width=None, top=False):
500         """Configure polygonitem polyitem according to provided
501         arguments:
502         coordlist is sequence of coordinates
503         fill is filling color
504         outline is outline color
505         top is a boolean value, which specifies if polyitem
506         will be put on top of the canvas' displaylist so it
507         will not be covered by other items.
508         """
509         cl = []
510         for x, y in coordlist:
511             cl.append(x * self.xscale)
512             cl.append(-y * self.yscale)
513         self.cv.coords(polyitem, *cl)
514         if fill is not None:
515             self.cv.itemconfigure(polyitem, fill=fill)
516         if outline is not None:
517             self.cv.itemconfigure(polyitem, outline=outline)
518         if width is not None:
519             self.cv.itemconfigure(polyitem, width=width)
520         if top:
521             self.cv.tag_raise(polyitem)
522 
523     def _createline(self):
524         """Create an invisible line item on canvas self.cv)
525         """
526         return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
527                                    capstyle = TK.ROUND)
528 
529     def _drawline(self, lineitem, coordlist=None,
530                   fill=None, width=None, top=False):
531         """Configure lineitem according to provided arguments:
532         coordlist is sequence of coordinates
533         fill is drawing color
534         width is width of drawn line.
535         top is a boolean value, which specifies if polyitem
536         will be put on top of the canvas' displaylist so it
537         will not be covered by other items.
538         """
539         if coordlist is not None:
540             cl = []
541             for x, y in coordlist:
542                 cl.append(x * self.xscale)
543                 cl.append(-y * self.yscale)
544             self.cv.coords(lineitem, *cl)
545         if fill is not None:
546             self.cv.itemconfigure(lineitem, fill=fill)
547         if width is not None:
548             self.cv.itemconfigure(lineitem, width=width)
549         if top:
550             self.cv.tag_raise(lineitem)
551 
552     def _delete(self, item):
553         """Delete graphics item from canvas.
554         If item is"all" delete all graphics items.
555         """
556         self.cv.delete(item)
557 
558     def _update(self):
559         """Redraw graphics items on canvas
560         """
561         self.cv.update()
562 
563     def _delay(self, delay):
564         """Delay subsequent canvas actions for delay ms."""
565         self.cv.after(delay)
566 
567     def _iscolorstring(self, color):
568         """Check if the string color is a legal Tkinter color string.
569         """
570         try:
571             rgb = self.cv.winfo_rgb(color)
572             ok = True
573         except TK.TclError:
574             ok = False
575         return ok
576 
577     def _bgcolor(self, color=None):
578         """Set canvas' backgroundcolor if color is not None,
579         else return backgroundcolor."""
580         if color is not None:
581             self.cv.config(bg = color)
582             self._update()
583         else:
584             return self.cv.cget("bg")
585 
586     def _write(self, pos, txt, align, font, pencolor):
587         """Write txt at pos in canvas with specified font
588         and color.
589         Return text item and x-coord of right bottom corner
590         of text's bounding box."""
591         x, y = pos
592         x = x * self.xscale
593         y = y * self.yscale
594         anchor = {"left":"sw", "center":"s", "right":"se" }
595         item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
596                                         fill = pencolor, font = font)
597         x0, y0, x1, y1 = self.cv.bbox(item)
598         self.cv.update()
599         return item, x1-1
600 
601 ##    def _dot(self, pos, size, color):
602 ##        """may be implemented for some other graphics toolkit"""
603 
604     def _onclick(self, item, fun, num=1, add=None):
605         """Bind fun to mouse-click event on turtle.
606         fun must be a function with two arguments, the coordinates
607         of the clicked point on the canvas.
608         num, the number of the mouse-button defaults to 1
609         """
610         if fun is None:
611             self.cv.tag_unbind(item, "<Button-%s>" % num)
612         else:
613             def eventfun(event):
614                 x, y = (self.cv.canvasx(event.x)/self.xscale,
615                         -self.cv.canvasy(event.y)/self.yscale)
616                 fun(x, y)
617             self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
618 
619     def _onrelease(self, item, fun, num=1, add=None):
620         """Bind fun to mouse-button-release event on turtle.
621         fun must be a function with two arguments, the coordinates
622         of the point on the canvas where mouse button is released.
623         num, the number of the mouse-button defaults to 1
624 
625         If a turtle is clicked, first _onclick-event will be performed,
626         then _onscreensclick-event.
627         """
628         if fun is None:
629             self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
630         else:
631             def eventfun(event):
632                 x, y = (self.cv.canvasx(event.x)/self.xscale,
633                         -self.cv.canvasy(event.y)/self.yscale)
634                 fun(x, y)
635             self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
636                              eventfun, add)
637 
638     def _ondrag(self, item, fun, num=1, add=None):
639         """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
640         fun must be a function with two arguments, the coordinates of the
641         actual mouse position on the canvas.
642         num, the number of the mouse-button defaults to 1
643 
644         Every sequence of mouse-move-events on a turtle is preceded by a
645         mouse-click event on that turtle.
646         """
647         if fun is None:
648             self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
649         else:
650             def eventfun(event):
651                 try:
652                     x, y = (self.cv.canvasx(event.x)/self.xscale,
653                            -self.cv.canvasy(event.y)/self.yscale)
654                     fun(x, y)
655                 except 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 
857 class 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 
866 class TurtleGraphicsError(Exception):
867     """Some TurtleGraphics Error
868     """
869 
870 
871 class 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 
918 class 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 
956 class TurtleScreen(TurtleScreenBase):
957     """Provides screen oriented methods like setbg etc.
958 
959     Only relies upon the methods of TurtleScreenBase and NOT
960     upon components of the underlying graphics toolkit -
961     which is Tkinter in this case.
962     """
963     _RUNNING = True
964 
965     def __init__(self, cv, mode=_CFG["mode"],
966                  colormode=_CFG["colormode"], delay=_CFG["delay"]):
967         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 
1511 class 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 
2022 class 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 
2482 class _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 
2513 class 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 
2854         Examples (for a Turtle instance named turtle):
2855         >>> turtle.shape("circle")
2856         >>> turtle.shapesize(5,2)
2857         >>> turtle.settiltangle(45)
2858         >>> stamp()
2859         >>> turtle.fd(50)
2860         >>> turtle.settiltangle(-45)
2861         >>> stamp()
2862         >>> turtle.fd(50)
2863         """
2864         tilt = -angle * self._degreesPerAU * self._angleOrient
2865         tilt = math.radians(tilt) % math.tau
2866         self.pen(resizemode="user", tilt=tilt)
2867 
2868     def tiltangle(self, angle=None):
2869         """Set or return the current tilt-angle.
2870 
2871         Optional argument: angle -- number
2872 
2873         Rotate the turtleshape to point in the direction specified by angle,
2874         regardless of its current tilt-angle. DO NOT change the turtle's
2875         heading (direction of movement).
2876         If angle is not given: return the current tilt-angle, i. e. the angle
2877         between the orientation of the turtleshape and the heading of the
2878         turtle (its direction of movement).
2879 
2880         (Incorrectly marked as deprecated since Python 3.1, it is really
2881         settiltangle that is deprecated.)
2882 
2883         Examples (for a Turtle instance named turtle):
2884         >>> turtle.shape("circle")
2885         >>> turtle.shapesize(5,2)
2886         >>> turtle.tilt(45)
2887         >>> turtle.tiltangle()
2888         """
2889         if angle is None:
2890             tilt = -math.degrees(self._tilt) * self._angleOrient
2891             return (tilt / self._degreesPerAU) % self._fullcircle
2892         else:
2893             self.settiltangle(angle)
2894 
2895     def tilt(self, angle):
2896         """Rotate the turtleshape by angle.
2897 
2898         Argument:
2899         angle - a number
2900 
2901         Rotate the turtleshape by angle from its current tilt-angle,
2902         but do NOT change the turtle's heading (direction of movement).
2903 
2904         Examples (for a Turtle instance named turtle):
2905         >>> turtle.shape("circle")
2906         >>> turtle.shapesize(5,2)
2907         >>> turtle.tilt(30)
2908         >>> turtle.fd(50)
2909         >>> turtle.tilt(30)
2910         >>> turtle.fd(50)
2911         """
2912         self.settiltangle(angle + self.tiltangle())
2913 
2914     def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2915         """Set or return the current transformation matrix of the turtle shape.
2916 
2917         Optional arguments: t11, t12, t21, t22 -- numbers.
2918 
2919         If none of the matrix elements are given, return the transformation
2920         matrix.
2921         Otherwise set the given elements and transform the turtleshape
2922         according to the matrix consisting of first row t11, t12 and
2923         second row t21, 22.
2924         Modify stretchfactor, shearfactor and tiltangle according to the
2925         given matrix.
2926 
2927         Examples (for a Turtle instance named turtle):
2928         >>> turtle.shape("square")
2929         >>> turtle.shapesize(4,2)
2930         >>> turtle.shearfactor(-0.5)
2931         >>> turtle.shapetransform()
2932         (4.0, -1.0, -0.0, 2.0)
2933         """
2934         if t11 is t12 is t21 is t22 is None:
2935             return self._shapetrafo
2936         m11, m12, m21, m22 = self._shapetrafo
2937         if t11 is not None: m11 = t11
2938         if t12 is not None: m12 = t12
2939         if t21 is not None: m21 = t21
2940         if t22 is not None: m22 = t22
2941         if t11 * t22 - t12 * t21 == 0:
2942             raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2943         self._shapetrafo = (m11, m12, m21, m22)
2944         alfa = math.atan2(-m21, m11) % math.tau
2945         sa, ca = math.sin(alfa), math.cos(alfa)
2946         a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2947                               sa*m11 + ca*m21, sa*m12 + ca*m22)
2948         self._stretchfactor = a11, a22
2949         self._shearfactor = a12/a22
2950         self._tilt = alfa
2951         self.pen(resizemode="user")
2952 
2953 
2954     def _polytrafo(self, poly):
2955         """Computes transformed polygon shapes from a shape
2956         according to current position and heading.
2957         """
2958         screen = self.screen
2959         p0, p1 = self._position
2960         e0, e1 = self._orient
2961         e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2962         e0, e1 = (1.0 / abs(e)) * e
2963         return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2964                                                            for (x, y) in poly]
2965 
2966     def get_shapepoly(self):
2967         """Return the current shape polygon as tuple of coordinate pairs.
2968 
2969         No argument.
2970 
2971         Examples (for a Turtle instance named turtle):
2972         >>> turtle.shape("square")
2973         >>> turtle.shapetransform(4, -1, 0, 2)
2974         >>> turtle.get_shapepoly()
2975         ((50, -20), (30, 20), (-50, 20), (-30, -20))
2976 
2977         """
2978         shape = self.screen._shapes[self.turtle.shapeIndex]
2979         if shape._type == "polygon":
2980             return self._getshapepoly(shape._data, shape._type == "compound")
2981         # else return None
2982 
2983     def _getshapepoly(self, polygon, compound=False):
2984         """Calculate transformed shape polygon according to resizemode
2985         and shapetransform.
2986         """
2987         if self._resizemode == "user" or compound:
2988             t11, t12, t21, t22 = self._shapetrafo
2989         elif self._resizemode == "auto":
2990             l = max(1, self._pensize/5.0)
2991             t11, t12, t21, t22 = l, 0, 0, l
2992         elif self._resizemode == "noresize":
2993             return polygon
2994         return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon)
2995 
2996     def _drawturtle(self):
2997         """Manages the correct rendering of the turtle with respect to
2998         its shape, resizemode, stretch and tilt etc."""
2999         screen = self.screen
3000         shape = screen._shapes[self.turtle.shapeIndex]
3001         ttype = shape._type
3002         titem = self.turtle._item
3003         if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3004             self._hidden_from_screen = False
3005             tshape = shape._data
3006             if ttype == "polygon":
3007                 if self._resizemode == "noresize": w = 1
3008                 elif self._resizemode == "auto": w = self._pensize
3009                 else: w =self._outlinewidth
3010                 shape = self._polytrafo(self._getshapepoly(tshape))
3011                 fc, oc = self._fillcolor, self._pencolor
3012                 screen._drawpoly(titem, shape, fill=fc, outline=oc,
3013                                                       width=w, top=True)
3014             elif ttype == "image":
3015                 screen._drawimage(titem, self._position, tshape)
3016             elif ttype == "compound":
3017                 for item, (poly, fc, oc) in zip(titem, tshape):
3018                     poly = self._polytrafo(self._getshapepoly(poly, True))
3019                     screen._drawpoly(item, poly, fill=self._cc(fc),
3020                                      outline=self._cc(oc), width=self._outlinewidth, top=True)
3021         else:
3022             if self._hidden_from_screen:
3023                 return
3024             if ttype == "polygon":
3025                 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3026             elif ttype == "image":
3027                 screen._drawimage(titem, self._position,
3028                                           screen._shapes["blank"]._data)
3029             elif ttype == "compound":
3030                 for item in titem:
3031                     screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3032             self._hidden_from_screen = True
3033 
3034 ##############################  stamp stuff  ###############################
3035 
3036     def stamp(self):
3037         """Stamp a copy of the turtleshape onto the canvas and return its id.
3038 
3039         No argument.
3040 
3041         Stamp a copy of the turtle shape onto the canvas at the current
3042         turtle position. Return a stamp_id for that stamp, which can be
3043         used to delete it by calling clearstamp(stamp_id).
3044 
3045         Example (for a Turtle instance named turtle):
3046         >>> turtle.color("blue")
3047         >>> turtle.stamp()
3048         13
3049         >>> turtle.fd(50)
3050         """
3051         screen = self.screen
3052         shape = screen._shapes[self.turtle.shapeIndex]
3053         ttype = shape._type
3054         tshape = shape._data
3055         if ttype == "polygon":
3056             stitem = screen._createpoly()
3057             if self._resizemode == "noresize": w = 1
3058             elif self._resizemode == "auto": w = self._pensize
3059             else: w =self._outlinewidth
3060             shape = self._polytrafo(self._getshapepoly(tshape))
3061             fc, oc = self._fillcolor, self._pencolor
3062             screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3063                                                   width=w, top=True)
3064         elif ttype == "image":
3065             stitem = screen._createimage("")
3066             screen._drawimage(stitem, self._position, tshape)
3067         elif ttype == "compound":
3068             stitem = []
3069             for element in tshape:
3070                 item = screen._createpoly()
3071                 stitem.append(item)
3072             stitem = tuple(stitem)
3073             for item, (poly, fc, oc) in zip(stitem, tshape):
3074                 poly = self._polytrafo(self._getshapepoly(poly, True))
3075                 screen._drawpoly(item, poly, fill=self._cc(fc),
3076                                  outline=self._cc(oc), width=self._outlinewidth, top=True)
3077         self.stampItems.append(stitem)
3078         self.undobuffer.push(("stamp", stitem))
3079         return stitem
3080 
3081     def _clearstamp(self, stampid):
3082         """does the work for clearstamp() and clearstamps()
3083         """
3084         if stampid in self.stampItems:
3085             if isinstance(stampid, tuple):
3086                 for subitem in stampid:
3087                     self.screen._delete(subitem)
3088             else:
3089                 self.screen._delete(stampid)
3090             self.stampItems.remove(stampid)
3091         # Delete stampitem from undobuffer if necessary
3092         # if clearstamp is called directly.
3093         item = ("stamp", stampid)
3094         buf = self.undobuffer
3095         if item not in buf.buffer:
3096             return
3097         index = buf.buffer.index(item)
3098         buf.buffer.remove(item)
3099         if index <= buf.ptr:
3100             buf.ptr = (buf.ptr - 1) % buf.bufsize
3101         buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3102 
3103     def clearstamp(self, stampid):
3104         """Delete stamp with given stampid
3105 
3106         Argument:
3107         stampid - an integer, must be return value of previous stamp() call.
3108 
3109         Example (for a Turtle instance named turtle):
3110         >>> turtle.color("blue")
3111         >>> astamp = turtle.stamp()
3112         >>> turtle.fd(50)
3113         >>> turtle.clearstamp(astamp)
3114         """
3115         self._clearstamp(stampid)
3116         self._update()
3117 
3118     def clearstamps(self, n=None):
3119         """Delete all or first/last n of turtle's stamps.
3120 
3121         Optional argument:
3122         n -- an integer
3123 
3124         If n is None, delete all of pen's stamps,
3125         else if n > 0 delete first n stamps
3126         else if n < 0 delete last n stamps.
3127 
3128         Example (for a Turtle instance named turtle):
3129         >>> for i in range(8):
3130         ...     turtle.stamp(); turtle.fd(30)
3131         ...
3132         >>> turtle.clearstamps(2)
3133         >>> turtle.clearstamps(-2)
3134         >>> turtle.clearstamps()
3135         """
3136         if n is None:
3137             toDelete = self.stampItems[:]
3138         elif n >= 0:
3139             toDelete = self.stampItems[:n]
3140         else:
3141             toDelete = self.stampItems[n:]
3142         for item in toDelete:
3143             self._clearstamp(item)
3144         self._update()
3145 
3146     def _goto(self, end):
3147         """Move the pen to the point end, thereby drawing a line
3148         if pen is down. All other methods for turtle movement depend
3149         on this one.
3150         """
3151         ## Version with undo-stuff
3152         go_modes = ( self._drawing,
3153                      self._pencolor,
3154                      self._pensize,
3155                      isinstance(self._fillpath, list))
3156         screen = self.screen
3157         undo_entry = ("go", self._position, end, go_modes,
3158                       (self.currentLineItem,
3159                       self.currentLine[:],
3160                       screen._pointlist(self.currentLineItem),
3161                       self.items[:])
3162                       )
3163         if self.undobuffer:
3164             self.undobuffer.push(undo_entry)
3165         start = self._position
3166         if self._speed and screen._tracing == 1:
3167             diff = (end-start)
3168             diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3169             nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3170             delta = diff * (1.0/nhops)
3171             for n in range(1, nhops):
3172                 if n == 1:
3173                     top = True
3174                 else:
3175                     top = False
3176                 self._position = start + delta * n
3177                 if self._drawing:
3178                     screen._drawline(self.drawingLineItem,
3179                                      (start, self._position),
3180                                      self._pencolor, self._pensize, top)
3181                 self._update()
3182             if self._drawing:
3183                 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3184                                                fill="", width=self._pensize)
3185         # Turtle now at end,
3186         if self._drawing: # now update currentLine
3187             self.currentLine.append(end)
3188         if isinstance(self._fillpath, list):
3189             self._fillpath.append(end)
3190         ######    vererbung!!!!!!!!!!!!!!!!!!!!!!
3191         self._position = end
3192         if self._creatingPoly:
3193             self._poly.append(end)
3194         if len(self.currentLine) > 42: # 42! answer to the ultimate question
3195                                        # of life, the universe and everything
3196             self._newLine()
3197         self._update() #count=True)
3198 
3199     def _undogoto(self, entry):
3200         """Reverse a _goto. Used for undo()
3201         """
3202         old, new, go_modes, coodata = entry
3203         drawing, pc, ps, filling = go_modes
3204         cLI, cL, pl, items = coodata
3205         screen = self.screen
3206         if abs(self._position - new) > 0.5:
3207             print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3208         # restore former situation
3209         self.currentLineItem = cLI
3210         self.currentLine = cL
3211 
3212         if pl == [(0, 0), (0, 0)]:
3213             usepc = ""
3214         else:
3215             usepc = pc
3216         screen._drawline(cLI, pl, fill=usepc, width=ps)
3217 
3218         todelete = [i for i in self.items if (i not in items) and
3219                                        (screen._type(i) == "line")]
3220         for i in todelete:
3221             screen._delete(i)
3222             self.items.remove(i)
3223 
3224         start = old
3225         if self._speed and screen._tracing == 1:
3226             diff = old - new
3227             diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3228             nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3229             delta = diff * (1.0/nhops)
3230             for n in range(1, nhops):
3231                 if n == 1:
3232                     top = True
3233                 else:
3234                     top = False
3235                 self._position = new + delta * n
3236                 if drawing:
3237                     screen._drawline(self.drawingLineItem,
3238                                      (start, self._position),
3239                                      pc, ps, top)
3240                 self._update()
3241             if drawing:
3242                 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3243                                                fill="", width=ps)
3244         # Turtle now at position old,
3245         self._position = old
3246         ##  if undo is done during creating a polygon, the last vertex
3247         ##  will be deleted. if the polygon is entirely deleted,
3248         ##  creatingPoly will be set to False.
3249         ##  Polygons created before the last one will not be affected by undo()
3250         if self._creatingPoly:
3251             if len(self._poly) > 0:
3252                 self._poly.pop()
3253             if self._poly == []:
3254                 self._creatingPoly = False
3255                 self._poly = None
3256         if filling:
3257             if self._fillpath == []:
3258                 self._fillpath = None
3259                 print("Unwahrscheinlich in _undogoto!")
3260             elif self._fillpath is not None:
3261                 self._fillpath.pop()
3262         self._update() #count=True)
3263 
3264     def _rotate(self, angle):
3265         """Turns pen clockwise by angle.
3266         """
3267         if self.undobuffer:
3268             self.undobuffer.push(("rot", angle, self._degreesPerAU))
3269         angle *= self._degreesPerAU
3270         neworient = self._orient.rotate(angle)
3271         tracing = self.screen._tracing
3272         if tracing == 1 and self._speed > 0:
3273             anglevel = 3.0 * self._speed
3274             steps = 1 + int(abs(angle)/anglevel)
3275             delta = 1.0*angle/steps
3276             for _ in range(steps):
3277                 self._orient = self._orient.rotate(delta)
3278                 self._update()
3279         self._orient = neworient
3280         self._update()
3281 
3282     def _newLine(self, usePos=True):
3283         """Closes current line item and starts a new one.
3284            Remark: if current line became too long, animation
3285            performance (via _drawline) slowed down considerably.
3286         """
3287         if len(self.currentLine) > 1:
3288             self.screen._drawline(self.currentLineItem, self.currentLine,
3289                                       self._pencolor, self._pensize)
3290             self.currentLineItem = self.screen._createline()
3291             self.items.append(self.currentLineItem)
3292         else:
3293             self.screen._drawline(self.currentLineItem, top=True)
3294         self.currentLine = []
3295         if usePos:
3296             self.currentLine = [self._position]
3297 
3298     def filling(self):
3299         """Return fillstate (True if filling, False else).
3300 
3301         No argument.
3302 
3303         Example (for a Turtle instance named turtle):
3304         >>> turtle.begin_fill()
3305         >>> if turtle.filling():
3306         ...     turtle.pensize(5)
3307         ... else:
3308         ...     turtle.pensize(3)
3309         """
3310         return isinstance(self._fillpath, list)
3311 
3312     def begin_fill(self):
3313         """Called just before drawing a shape to be filled.
3314 
3315         No argument.
3316 
3317         Example (for a Turtle instance named turtle):
3318         >>> turtle.color("black", "red")
3319         >>> turtle.begin_fill()
3320         >>> turtle.circle(60)
3321         >>> turtle.end_fill()
3322         """
3323         if not self.filling():
3324             self._fillitem = self.screen._createpoly()
3325             self.items.append(self._fillitem)
3326         self._fillpath = [self._position]
3327         self._newLine()
3328         if self.undobuffer:
3329             self.undobuffer.push(("beginfill", self._fillitem))
3330         self._update()
3331 
3332 
3333     def end_fill(self):
3334         """Fill the shape drawn after the call begin_fill().
3335 
3336         No argument.
3337 
3338         Example (for a Turtle instance named turtle):
3339         >>> turtle.color("black", "red")
3340         >>> turtle.begin_fill()
3341         >>> turtle.circle(60)
3342         >>> turtle.end_fill()
3343         """
3344         if self.filling():
3345             if len(self._fillpath) > 2:
3346                 self.screen._drawpoly(self._fillitem, self._fillpath,
3347                                       fill=self._fillcolor)
3348                 if self.undobuffer:
3349                     self.undobuffer.push(("dofill", self._fillitem))
3350             self._fillitem = self._fillpath = None
3351             self._update()
3352 
3353     def dot(self, size=None, *color):
3354         """Draw a dot with diameter size, using color.
3355 
3356         Optional arguments:
3357         size -- an integer >= 1 (if given)
3358         color -- a colorstring or a numeric color tuple
3359 
3360         Draw a circular dot with diameter size, using color.
3361         If size is not given, the maximum of pensize+4 and 2*pensize is used.
3362 
3363         Example (for a Turtle instance named turtle):
3364         >>> turtle.dot()
3365         >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3366         """
3367         if not color:
3368             if isinstance(size, (str, tuple)):
3369                 color = self._colorstr(size)
3370                 size = self._pensize + max(self._pensize, 4)
3371             else:
3372                 color = self._pencolor
3373                 if not size:
3374                     size = self._pensize + max(self._pensize, 4)
3375         else:
3376             if size is None:
3377                 size = self._pensize + max(self._pensize, 4)
3378             color = self._colorstr(color)
3379         if hasattr(self.screen, "_dot"):
3380             item = self.screen._dot(self._position, size, color)
3381             self.items.append(item)
3382             if self.undobuffer:
3383                 self.undobuffer.push(("dot", item))
3384         else:
3385             pen = self.pen()
3386             if self.undobuffer:
3387                 self.undobuffer.push(["seq"])
3388                 self.undobuffer.cumulate = True
3389             try:
3390                 if self.resizemode() == 'auto':
3391                     self.ht()
3392                 self.pendown()
3393                 self.pensize(size)
3394                 self.pencolor(color)
3395                 self.forward(0)
3396             finally:
3397                 self.pen(pen)
3398             if self.undobuffer:
3399                 self.undobuffer.cumulate = False
3400 
3401     def _write(self, txt, align, font):
3402         """Performs the writing for write()
3403         """
3404         item, end = self.screen._write(self._position, txt, align, font,
3405                                                           self._pencolor)
3406         self.items.append(item)
3407         if self.undobuffer:
3408             self.undobuffer.push(("wri", item))
3409         return end
3410 
3411     def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3412         """Write text at the current turtle position.
3413 
3414         Arguments:
3415         arg -- info, which is to be written to the TurtleScreen
3416         move (optional) -- True/False
3417         align (optional) -- one of the strings "left", "center" or right"
3418         font (optional) -- a triple (fontname, fontsize, fonttype)
3419 
3420         Write text - the string representation of arg - at the current
3421         turtle position according to align ("left", "center" or right")
3422         and with the given font.
3423         If move is True, the pen is moved to the bottom-right corner
3424         of the text. By default, move is False.
3425 
3426         Example (for a Turtle instance named turtle):
3427         >>> turtle.write('Home = ', True, align="center")
3428         >>> turtle.write((0,0), True)
3429         """
3430         if self.undobuffer:
3431             self.undobuffer.push(["seq"])
3432             self.undobuffer.cumulate = True
3433         end = self._write(str(arg), align.lower(), font)
3434         if move:
3435             x, y = self.pos()
3436             self.setpos(end, y)
3437         if self.undobuffer:
3438             self.undobuffer.cumulate = False
3439 
3440     def begin_poly(self):
3441         """Start recording the vertices of a polygon.
3442 
3443         No argument.
3444 
3445         Start recording the vertices of a polygon. Current turtle position
3446         is first point of polygon.
3447 
3448         Example (for a Turtle instance named turtle):
3449         >>> turtle.begin_poly()
3450         """
3451         self._poly = [self._position]
3452         self._creatingPoly = True
3453 
3454     def end_poly(self):
3455         """Stop recording the vertices of a polygon.
3456 
3457         No argument.
3458 
3459         Stop recording the vertices of a polygon. Current turtle position is
3460         last point of polygon. This will be connected with the first point.
3461 
3462         Example (for a Turtle instance named turtle):
3463         >>> turtle.end_poly()
3464         """
3465         self._creatingPoly = False
3466 
3467     def get_poly(self):
3468         """Return the lastly recorded polygon.
3469 
3470         No argument.
3471 
3472         Example (for a Turtle instance named turtle):
3473         >>> p = turtle.get_poly()
3474         >>> turtle.register_shape("myFavouriteShape", p)
3475         """
3476         ## check if there is any poly?
3477         if self._poly is not None:
3478             return tuple(self._poly)
3479 
3480     def getscreen(self):
3481         """Return the TurtleScreen object, the turtle is drawing  on.
3482 
3483         No argument.
3484 
3485         Return the TurtleScreen object, the turtle is drawing  on.
3486         So TurtleScreen-methods can be called for that object.
3487 
3488         Example (for a Turtle instance named turtle):
3489         >>> ts = turtle.getscreen()
3490         >>> ts
3491         <turtle.TurtleScreen object at 0x0106B770>
3492         >>> ts.bgcolor("pink")
3493         """
3494         return self.screen
3495 
3496     def getturtle(self):
3497         """Return the Turtleobject itself.
3498 
3499         No argument.
3500 
3501         Only reasonable use: as a function to return the 'anonymous turtle':
3502 
3503         Example:
3504         >>> pet = getturtle()
3505         >>> pet.fd(50)
3506         >>> pet
3507         <turtle.Turtle object at 0x0187D810>
3508         >>> turtles()
3509         [<turtle.Turtle object at 0x0187D810>]
3510         """
3511         return self
3512 
3513     getpen = getturtle
3514 
3515 
3516     ################################################################
3517     ### screen oriented methods recurring to methods of TurtleScreen
3518     ################################################################
3519 
3520     def _delay(self, delay=None):
3521         """Set delay value which determines speed of turtle animation.
3522         """
3523         return self.screen.delay(delay)
3524 
3525     def onclick(self, fun, btn=1, add=None):
3526         """Bind fun to mouse-click event on this turtle on canvas.
3527 
3528         Arguments:
3529         fun --  a function with two arguments, to which will be assigned
3530                 the coordinates of the clicked point on the canvas.
3531         btn --  number of the mouse-button defaults to 1 (left mouse button).
3532         add --  True or False. If True, new binding will be added, otherwise
3533                 it will replace a former binding.
3534 
3535         Example for the anonymous turtle, i. e. the procedural way:
3536 
3537         >>> def turn(x, y):
3538         ...     left(360)
3539         ...
3540         >>> onclick(turn)  # Now clicking into the turtle will turn it.
3541         >>> onclick(None)  # event-binding will be removed
3542         """
3543         self.screen._onclick(self.turtle._item, fun, btn, add)
3544         self._update()
3545 
3546     def onrelease(self, fun, btn=1, add=None):
3547         """Bind fun to mouse-button-release event on this turtle on canvas.
3548 
3549         Arguments:
3550         fun -- a function with two arguments, to which will be assigned
3551                 the coordinates of the clicked point on the canvas.
3552         btn --  number of the mouse-button defaults to 1 (left mouse button).
3553 
3554         Example (for a MyTurtle instance named joe):
3555         >>> class MyTurtle(Turtle):
3556         ...     def glow(self,x,y):
3557         ...             self.fillcolor("red")
3558         ...     def unglow(self,x,y):
3559         ...             self.fillcolor("")
3560         ...
3561         >>> joe = MyTurtle()
3562         >>> joe.onclick(joe.glow)
3563         >>> joe.onrelease(joe.unglow)
3564 
3565         Clicking on joe turns fillcolor red, unclicking turns it to
3566         transparent.
3567         """
3568         self.screen._onrelease(self.turtle._item, fun, btn, add)
3569         self._update()
3570 
3571     def ondrag(self, fun, btn=1, add=None):
3572         """Bind fun to mouse-move event on this turtle on canvas.
3573 
3574         Arguments:
3575         fun -- a function with two arguments, to which will be assigned
3576                the coordinates of the clicked point on the canvas.
3577         btn -- number of the mouse-button defaults to 1 (left mouse button).
3578 
3579         Every sequence of mouse-move-events on a turtle is preceded by a
3580         mouse-click event on that turtle.
3581 
3582         Example (for a Turtle instance named turtle):
3583         >>> turtle.ondrag(turtle.goto)
3584 
3585         Subsequently clicking and dragging a Turtle will move it
3586         across the screen thereby producing handdrawings (if pen is
3587         down).
3588         """
3589         self.screen._ondrag(self.turtle._item, fun, btn, add)
3590 
3591 
3592     def _undo(self, action, data):
3593         """Does the main part of the work for undo()
3594         """
3595         if self.undobuffer is None:
3596             return
3597         if action == "rot":
3598             angle, degPAU = data
3599             self._rotate(-angle*degPAU/self._degreesPerAU)
3600             dummy = self.undobuffer.pop()
3601         elif action == "stamp":
3602             stitem = data[0]
3603             self.clearstamp(stitem)
3604         elif action == "go":
3605             self._undogoto(data)
3606         elif action in ["wri", "dot"]:
3607             item = data[0]
3608             self.screen._delete(item)
3609             self.items.remove(item)
3610         elif action == "dofill":
3611             item = data[0]
3612             self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3613                                   fill="", outline="")
3614         elif action == "beginfill":
3615             item = data[0]
3616             self._fillitem = self._fillpath = None
3617             if item in self.items:
3618                 self.screen._delete(item)
3619                 self.items.remove(item)
3620         elif action == "pen":
3621             TPen.pen(self, data[0])
3622             self.undobuffer.pop()
3623 
3624     def undo(self):
3625         """undo (repeatedly) the last turtle action.
3626 
3627         No argument.
3628 
3629         undo (repeatedly) the last turtle action.
3630         Number of available undo actions is determined by the size of
3631         the undobuffer.
3632 
3633         Example (for a Turtle instance named turtle):
3634         >>> for i in range(4):
3635         ...     turtle.fd(50); turtle.lt(80)
3636         ...
3637         >>> for i in range(8):
3638         ...     turtle.undo()
3639         ...
3640         """
3641         if self.undobuffer is None:
3642             return
3643         item = self.undobuffer.pop()
3644         action = item[0]
3645         data = item[1:]
3646         if action == "seq":
3647             while data:
3648                 item = data.pop()
3649                 self._undo(item[0], item[1:])
3650         else:
3651             self._undo(action, data)
3652 
3653     turtlesize = shapesize
3654 
3655 RawPen = RawTurtle
3656 
3657 ###  Screen - Singleton  ########################
3658 
3659 def Screen():
3660     """Return the singleton screen object.
3661     If none exists at the moment, create a new one and return it,
3662     else return the existing one."""
3663     if Turtle._screen is None:
3664         Turtle._screen = _Screen()
3665     return Turtle._screen
3666 
3667 class _Screen(TurtleScreen):
3668 
3669     _root = None
3670     _canvas = None
3671     _title = _CFG["title"]
3672 
3673     def __init__(self):
3674         # XXX there is no need for this code to be conditional,
3675         # as there will be only a single _Screen instance, anyway
3676         # XXX actually, the turtle demo is injecting root window,
3677         # so perhaps the conditional creation of a root should be
3678         # preserved (perhaps by passing it as an optional parameter)
3679         if _Screen._root is None:
3680             _Screen._root = self._root = _Root()
3681             self._root.title(_Screen._title)
3682             self._root.ondestroy(self._destroy)
3683         if _Screen._canvas is None:
3684             width = _CFG["width"]
3685             height = _CFG["height"]
3686             canvwidth = _CFG["canvwidth"]
3687             canvheight = _CFG["canvheight"]
3688             leftright = _CFG["leftright"]
3689             topbottom = _CFG["topbottom"]
3690             self._root.setupcanvas(width, height, canvwidth, canvheight)
3691             _Screen._canvas = self._root._getcanvas()
3692             TurtleScreen.__init__(self, _Screen._canvas)
3693             self.setup(width, height, leftright, topbottom)
3694 
3695     def setup(self, width=_CFG["width"], height=_CFG["height"],
3696               startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3697         """ Set the size and position of the main window.
3698 
3699         Arguments:
3700         width: as integer a size in pixels, as float a fraction of the screen.
3701           Default is 50% of screen.
3702         height: as integer the height in pixels, as float a fraction of the
3703           screen. Default is 75% of screen.
3704         startx: if positive, starting position in pixels from the left
3705           edge of the screen, if negative from the right edge
3706           Default, startx=None is to center window horizontally.
3707         starty: if positive, starting position in pixels from the top
3708           edge of the screen, if negative from the bottom edge
3709           Default, starty=None is to center window vertically.
3710 
3711         Examples (for a Screen instance named screen):
3712         >>> screen.setup (width=200, height=200, startx=0, starty=0)
3713 
3714         sets window to 200x200 pixels, in upper left of screen
3715 
3716         >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3717 
3718         sets window to 75% of screen by 50% of screen and centers
3719         """
3720         if not hasattr(self._root, "set_geometry"):
3721             return
3722         sw = self._root.win_width()
3723         sh = self._root.win_height()
3724         if isinstance(width, float) and 0 <= width <= 1:
3725             width = sw*width
3726         if startx is None:
3727             startx = (sw - width) / 2
3728         if isinstance(height, float) and 0 <= height <= 1:
3729             height = sh*height
3730         if starty is None:
3731             starty = (sh - height) / 2
3732         self._root.set_geometry(width, height, startx, starty)
3733         self.update()
3734 
3735     def title(self, titlestring):
3736         """Set title of turtle-window
3737 
3738         Argument:
3739         titlestring -- a string, to appear in the titlebar of the
3740                        turtle graphics window.
3741 
3742         This is a method of Screen-class. Not available for TurtleScreen-
3743         objects.
3744 
3745         Example (for a Screen instance named screen):
3746         >>> screen.title("Welcome to the turtle-zoo!")
3747         """
3748         if _Screen._root is not None:
3749             _Screen._root.title(titlestring)
3750         _Screen._title = titlestring
3751 
3752     def _destroy(self):
3753         root = self._root
3754         if root is _Screen._root:
3755             Turtle._pen = None
3756             Turtle._screen = None
3757             _Screen._root = None
3758             _Screen._canvas = None
3759         TurtleScreen._RUNNING = False
3760         root.destroy()
3761 
3762     def bye(self):
3763         """Shut the turtlegraphics window.
3764 
3765         Example (for a TurtleScreen instance named screen):
3766         >>> screen.bye()
3767         """
3768         self._destroy()
3769 
3770     def exitonclick(self):
3771         """Go into mainloop until the mouse is clicked.
3772 
3773         No arguments.
3774 
3775         Bind bye() method to mouseclick on TurtleScreen.
3776         If "using_IDLE" - value in configuration dictionary is False
3777         (default value), enter mainloop.
3778         If IDLE with -n switch (no subprocess) is used, this value should be
3779         set to True in turtle.cfg. In this case IDLE's mainloop
3780         is active also for the client script.
3781 
3782         This is a method of the Screen-class and not available for
3783         TurtleScreen instances.
3784 
3785         Example (for a Screen instance named screen):
3786         >>> screen.exitonclick()
3787 
3788         """
3789         def exitGracefully(x, y):
3790             """Screen.bye() with two dummy-parameters"""
3791             self.bye()
3792         self.onclick(exitGracefully)
3793         if _CFG["using_IDLE"]:
3794             return
3795         try:
3796             mainloop()
3797         except AttributeError:
3798             exit(0)
3799 
3800 class Turtle(RawTurtle):
3801     """RawTurtle auto-creating (scrolled) canvas.
3802 
3803     When a Turtle object is created or a function derived from some
3804     Turtle method is called a TurtleScreen object is automatically created.
3805     """
3806     _pen = None
3807     _screen = None
3808 
3809     def __init__(self,
3810                  shape=_CFG["shape"],
3811                  undobuffersize=_CFG["undobuffersize"],
3812                  visible=_CFG["visible"]):
3813         if Turtle._screen is None:
3814             Turtle._screen = Screen()
3815         RawTurtle.__init__(self, Turtle._screen,
3816                            shape=shape,
3817                            undobuffersize=undobuffersize,
3818                            visible=visible)
3819 
3820 Pen = Turtle
3821 
3822 def write_docstringdict(filename="turtle_docstringdict"):
3823     """Create and write docstring-dictionary to file.
3824 
3825     Optional argument:
3826     filename -- a string, used as filename
3827                 default value is turtle_docstringdict
3828 
3829     Has to be called explicitly, (not used by the turtle-graphics classes)
3830     The docstring dictionary will be written to the Python script <filname>.py
3831     It is intended to serve as a template for translation of the docstrings
3832     into different languages.
3833     """
3834     docsdict = {}
3835 
3836     for methodname in _tg_screen_functions:
3837         key = "_Screen."+methodname
3838         docsdict[key] = eval(key).__doc__
3839     for methodname in _tg_turtle_functions:
3840         key = "Turtle."+methodname
3841         docsdict[key] = eval(key).__doc__
3842 
3843     with open("%s.py" % filename,"w") as f:
3844         keys = sorted(x for x in docsdict
3845                       if x.split('.')[1] not in _alias_list)
3846         f.write('docsdict = {\n\n')
3847         for key in keys[:-1]:
3848             f.write('%s :\n' % repr(key))
3849             f.write('        """%s\n""",\n\n' % docsdict[key])
3850         key = keys[-1]
3851         f.write('%s :\n' % repr(key))
3852         f.write('        """%s\n"""\n\n' % docsdict[key])
3853         f.write("}\n")
3854         f.close()
3855 
3856 def read_docstrings(lang):
3857     """Read in docstrings from lang-specific docstring dictionary.
3858 
3859     Transfer docstrings, translated to lang, from a dictionary-file
3860     to the methods of classes Screen and Turtle and - in revised form -
3861     to the corresponding functions.
3862     """
3863     modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3864     module = __import__(modname)
3865     docsdict = module.docsdict
3866     for key in docsdict:
3867         try:
3868 #            eval(key).im_func.__doc__ = docsdict[key]
3869             eval(key).__doc__ = docsdict[key]
3870         except Exception:
3871             print("Bad docstring-entry: %s" % key)
3872 
3873 _LANGUAGE = _CFG["language"]
3874 
3875 try:
3876     if _LANGUAGE != "english":
3877         read_docstrings(_LANGUAGE)
3878 except ImportError:
3879     print("Cannot find docsdict for", _LANGUAGE)
3880 except Exception:
3881     print ("Unknown Error when trying to import %s-docstring-dictionary" %
3882                                                                   _LANGUAGE)
3883 
3884 
3885 def getmethparlist(ob):
3886     """Get strings describing the arguments for the given object
3887 
3888     Returns a pair of strings representing function parameter lists
3889     including parenthesis.  The first string is suitable for use in
3890     function definition and the second is suitable for use in function
3891     call.  The "self" parameter is not included.
3892     """
3893     defText = callText = ""
3894     # bit of a hack for methods - turn it into a function
3895     # but we drop the "self" param.
3896     # Try and build one for Python defined functions
3897     args, varargs, varkw = inspect.getargs(ob.__code__)
3898     items2 = args[1:]
3899     realArgs = args[1:]
3900     defaults = ob.__defaults__ or []
3901     defaults = ["=%r" % (value,) for value in defaults]
3902     defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3903     items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3904     if varargs is not None:
3905         items1.append("*" + varargs)
3906         items2.append("*" + varargs)
3907     if varkw is not None:
3908         items1.append("**" + varkw)
3909         items2.append("**" + varkw)
3910     defText = ", ".join(items1)
3911     defText = "(%s)" % defText
3912     callText = ", ".join(items2)
3913     callText = "(%s)" % callText
3914     return defText, callText
3915 
3916 def _turtle_docrevise(docstr):
3917     """To reduce docstrings from RawTurtle class for functions
3918     """
3919     import re
3920     if docstr is None:
3921         return None
3922     turtlename = _CFG["exampleturtle"]
3923     newdocstr = docstr.replace("%s." % turtlename,"")
3924     parexp = re.compile(r' \(.+ %s\):' % turtlename)
3925     newdocstr = parexp.sub(":", newdocstr)
3926     return newdocstr
3927 
3928 def _screen_docrevise(docstr):
3929     """To reduce docstrings from TurtleScreen class for functions
3930     """
3931     import re
3932     if docstr is None:
3933         return None
3934     screenname = _CFG["examplescreen"]
3935     newdocstr = docstr.replace("%s." % screenname,"")
3936     parexp = re.compile(r' \(.+ %s\):' % screenname)
3937     newdocstr = parexp.sub(":", newdocstr)
3938     return newdocstr
3939 
3940 ## The following mechanism makes all methods of RawTurtle and Turtle available
3941 ## as functions. So we can enhance, change, add, delete methods to these
3942 ## classes and do not need to change anything here.
3943 
3944 __func_body = """\
3945 def {name}{paramslist}:
3946     if {obj} is None:
3947         if not TurtleScreen._RUNNING:
3948             TurtleScreen._RUNNING = True
3949             raise Terminator
3950         {obj} = {init}
3951     try:
3952         return {obj}.{name}{argslist}
3953     except TK.TclError:
3954         if not TurtleScreen._RUNNING:
3955             TurtleScreen._RUNNING = True
3956             raise Terminator
3957         raise
3958 """
3959 
3960 def _make_global_funcs(functions, cls, obj, init, docrevise):
3961     for methodname in functions:
3962         method = getattr(cls, methodname)
3963         pl1, pl2 = getmethparlist(method)
3964         if pl1 == "":
3965             print(">>>>>>", pl1, pl2)
3966             continue
3967         defstr = __func_body.format(obj=obj, init=init, name=methodname,
3968                                     paramslist=pl1, argslist=pl2)
3969         exec(defstr, globals())
3970         globals()[methodname].__doc__ = docrevise(method.__doc__)
3971 
3972 _make_global_funcs(_tg_screen_functions, _Screen,
3973                    'Turtle._screen', 'Screen()', _screen_docrevise)
3974 _make_global_funcs(_tg_turtle_functions, Turtle,
3975                    'Turtle._pen', 'Turtle()', _turtle_docrevise)
3976 
3977 
3978 done = mainloop
3979 
3980 if __name__ == "__main__":
3981     def switchpen():
3982         if isdown():
3983             pu()
3984         else:
3985             pd()
3986 
3987     def demo1():
3988         """Demo of old turtle.py - module"""
3989         reset()
3990         tracer(True)
3991         up()
3992         backward(100)
3993         down()
3994         # draw 3 squares; the last filled
3995         width(3)
3996         for i in range(3):
3997             if i == 2:
3998                 begin_fill()
3999             for _ in range(4):
4000                 forward(20)
4001                 left(90)
4002             if i == 2:
4003                 color("maroon")
4004                 end_fill()
4005             up()
4006             forward(30)
4007             down()
4008         width(1)
4009         color("black")
4010         # move out of the way
4011         tracer(False)
4012         up()
4013         right(90)
4014         forward(100)
4015         right(90)
4016         forward(100)
4017         right(180)
4018         down()
4019         # some text
4020         write("startstart", 1)
4021         write("start", 1)
4022         color("red")
4023         # staircase
4024         for i in range(5):
4025             forward(20)
4026             left(90)
4027             forward(20)
4028             right(90)
4029         # filled staircase
4030         tracer(True)
4031         begin_fill()
4032         for i in range(5):
4033             forward(20)
4034             left(90)
4035             forward(20)
4036             right(90)
4037         end_fill()
4038         # more text
4039 
4040     def demo2():
4041         """Demo of some new features."""
4042         speed(1)
4043         st()
4044         pensize(3)
4045         setheading(towards(0, 0))
4046         radius = distance(0, 0)/2.0
4047         rt(90)
4048         for _ in range(18):
4049             switchpen()
4050             circle(radius, 10)
4051         write("wait a moment...")
4052         while undobufferentries():
4053             undo()
4054         reset()
4055         lt(90)
4056         colormode(255)
4057         laenge = 10
4058         pencolor("green")
4059         pensize(3)
4060         lt(180)
4061         for i in range(-2, 16):
4062             if i > 0:
4063                 begin_fill()
4064                 fillcolor(255-15*i, 0, 15*i)
4065             for _ in range(3):
4066                 fd(laenge)
4067                 lt(120)
4068             end_fill()
4069             laenge += 10
4070             lt(15)
4071             speed((speed()+1)%12)
4072         #end_fill()
4073 
4074         lt(120)
4075         pu()
4076         fd(70)
4077         rt(30)
4078         pd()
4079         color("red","yellow")
4080         speed(0)
4081         begin_fill()
4082         for _ in range(4):
4083             circle(50, 90)
4084             rt(90)
4085             fd(30)
4086             rt(90)
4087         end_fill()
4088         lt(90)
4089         pu()
4090         fd(30)
4091         pd()
4092         shape("turtle")
4093 
4094         tri = getturtle()
4095         tri.resizemode("auto")
4096         turtle = Turtle()
4097         turtle.resizemode("auto")
4098         turtle.shape("turtle")
4099         turtle.reset()
4100         turtle.left(90)
4101         turtle.speed(0)
4102         turtle.up()
4103         turtle.goto(280, 40)
4104         turtle.lt(30)
4105         turtle.down()
4106         turtle.speed(6)
4107         turtle.color("blue","orange")
4108         turtle.pensize(2)
4109         tri.speed(6)
4110         setheading(towards(turtle))
4111         count = 1
4112         while tri.distance(turtle) > 4:
4113             turtle.fd(3.5)
4114             turtle.lt(0.6)
4115             tri.setheading(tri.towards(turtle))
4116             tri.fd(4)
4117             if count % 20 == 0:
4118                 turtle.stamp()
4119                 tri.stamp()
4120                 switchpen()
4121             count += 1
4122         tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4123         tri.pencolor("black")
4124         tri.pencolor("red")
4125 
4126         def baba(xdummy, ydummy):
4127             clearscreen()
4128             bye()
4129 
4130         time.sleep(2)
4131 
4132         while undobufferentries():
4133             tri.undo()
4134             turtle.undo()
4135         tri.fd(50)
4136         tri.write("  Click me!", font = ("Courier", 12, "bold") )
4137         tri.onclick(baba, 1)
4138 
4139     demo1()
4140     demo2()
4141     exitonclick()
4142