• 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: http://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 (self[0]**2 + self[1]**2)**0.5
268     def rotate(self, angle):
269         """rotate self counterclockwise by angle
270         """
271         perp = Vec2D(-self[1], self[0])
272         angle = angle * math.pi / 180.0
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     @staticmethod
468     def _blankimage():
469         """return a blank image object
470         """
471         img = TK.PhotoImage(width=1, height=1)
472         img.blank()
473         return img
474 
475     @staticmethod
476     def _image(filename):
477         """return an image object containing the
478         imagedata from a gif-file named filename.
479         """
480         return TK.PhotoImage(file=filename)
481 
482     def __init__(self, cv):
483         self.cv = cv
484         if isinstance(cv, ScrolledCanvas):
485             w = self.cv.canvwidth
486             h = self.cv.canvheight
487         else:  # expected: ordinary TK.Canvas
488             w = int(self.cv.cget("width"))
489             h = int(self.cv.cget("height"))
490             self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
491         self.canvwidth = w
492         self.canvheight = h
493         self.xscale = self.yscale = 1.0
494 
495     def _createpoly(self):
496         """Create an invisible polygon item on canvas self.cv)
497         """
498         return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
499 
500     def _drawpoly(self, polyitem, coordlist, fill=None,
501                   outline=None, width=None, top=False):
502         """Configure polygonitem polyitem according to provided
503         arguments:
504         coordlist is sequence of coordinates
505         fill is filling color
506         outline is outline color
507         top is a boolean value, which specifies if polyitem
508         will be put on top of the canvas' displaylist so it
509         will not be covered by other items.
510         """
511         cl = []
512         for x, y in coordlist:
513             cl.append(x * self.xscale)
514             cl.append(-y * self.yscale)
515         self.cv.coords(polyitem, *cl)
516         if fill is not None:
517             self.cv.itemconfigure(polyitem, fill=fill)
518         if outline is not None:
519             self.cv.itemconfigure(polyitem, outline=outline)
520         if width is not None:
521             self.cv.itemconfigure(polyitem, width=width)
522         if top:
523             self.cv.tag_raise(polyitem)
524 
525     def _createline(self):
526         """Create an invisible line item on canvas self.cv)
527         """
528         return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
529                                    capstyle = TK.ROUND)
530 
531     def _drawline(self, lineitem, coordlist=None,
532                   fill=None, width=None, top=False):
533         """Configure lineitem according to provided arguments:
534         coordlist is sequence of coordinates
535         fill is drawing color
536         width is width of drawn line.
537         top is a boolean value, which specifies if polyitem
538         will be put on top of the canvas' displaylist so it
539         will not be covered by other items.
540         """
541         if coordlist is not None:
542             cl = []
543             for x, y in coordlist:
544                 cl.append(x * self.xscale)
545                 cl.append(-y * self.yscale)
546             self.cv.coords(lineitem, *cl)
547         if fill is not None:
548             self.cv.itemconfigure(lineitem, fill=fill)
549         if width is not None:
550             self.cv.itemconfigure(lineitem, width=width)
551         if top:
552             self.cv.tag_raise(lineitem)
553 
554     def _delete(self, item):
555         """Delete graphics item from canvas.
556         If item is"all" delete all graphics items.
557         """
558         self.cv.delete(item)
559 
560     def _update(self):
561         """Redraw graphics items on canvas
562         """
563         self.cv.update()
564 
565     def _delay(self, delay):
566         """Delay subsequent canvas actions for delay ms."""
567         self.cv.after(delay)
568 
569     def _iscolorstring(self, color):
570         """Check if the string color is a legal Tkinter color string.
571         """
572         try:
573             rgb = self.cv.winfo_rgb(color)
574             ok = True
575         except TK.TclError:
576             ok = False
577         return ok
578 
579     def _bgcolor(self, color=None):
580         """Set canvas' backgroundcolor if color is not None,
581         else return backgroundcolor."""
582         if color is not None:
583             self.cv.config(bg = color)
584             self._update()
585         else:
586             return self.cv.cget("bg")
587 
588     def _write(self, pos, txt, align, font, pencolor):
589         """Write txt at pos in canvas with specified font
590         and color.
591         Return text item and x-coord of right bottom corner
592         of text's bounding box."""
593         x, y = pos
594         x = x * self.xscale
595         y = y * self.yscale
596         anchor = {"left":"sw", "center":"s", "right":"se" }
597         item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
598                                         fill = pencolor, font = font)
599         x0, y0, x1, y1 = self.cv.bbox(item)
600         self.cv.update()
601         return item, x1-1
602 
603 ##    def _dot(self, pos, size, color):
604 ##        """may be implemented for some other graphics toolkit"""
605 
606     def _onclick(self, item, fun, num=1, add=None):
607         """Bind fun to mouse-click event on turtle.
608         fun must be a function with two arguments, the coordinates
609         of the clicked point on the canvas.
610         num, the number of the mouse-button defaults to 1
611         """
612         if fun is None:
613             self.cv.tag_unbind(item, "<Button-%s>" % num)
614         else:
615             def eventfun(event):
616                 x, y = (self.cv.canvasx(event.x)/self.xscale,
617                         -self.cv.canvasy(event.y)/self.yscale)
618                 fun(x, y)
619             self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
620 
621     def _onrelease(self, item, fun, num=1, add=None):
622         """Bind fun to mouse-button-release event on turtle.
623         fun must be a function with two arguments, the coordinates
624         of the point on the canvas where mouse button is released.
625         num, the number of the mouse-button defaults to 1
626 
627         If a turtle is clicked, first _onclick-event will be performed,
628         then _onscreensclick-event.
629         """
630         if fun is None:
631             self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
632         else:
633             def eventfun(event):
634                 x, y = (self.cv.canvasx(event.x)/self.xscale,
635                         -self.cv.canvasy(event.y)/self.yscale)
636                 fun(x, y)
637             self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
638                              eventfun, add)
639 
640     def _ondrag(self, item, fun, num=1, add=None):
641         """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
642         fun must be a function with two arguments, the coordinates of the
643         actual mouse position on the canvas.
644         num, the number of the mouse-button defaults to 1
645 
646         Every sequence of mouse-move-events on a turtle is preceded by a
647         mouse-click event on that turtle.
648         """
649         if fun is None:
650             self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
651         else:
652             def eventfun(event):
653                 try:
654                     x, y = (self.cv.canvasx(event.x)/self.xscale,
655                            -self.cv.canvasy(event.y)/self.yscale)
656                     fun(x, y)
657                 except Exception:
658                     pass
659             self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
660 
661     def _onscreenclick(self, fun, num=1, add=None):
662         """Bind fun to mouse-click event on canvas.
663         fun must be a function with two arguments, the coordinates
664         of the clicked point on the canvas.
665         num, the number of the mouse-button defaults to 1
666 
667         If a turtle is clicked, first _onclick-event will be performed,
668         then _onscreensclick-event.
669         """
670         if fun is None:
671             self.cv.unbind("<Button-%s>" % num)
672         else:
673             def eventfun(event):
674                 x, y = (self.cv.canvasx(event.x)/self.xscale,
675                         -self.cv.canvasy(event.y)/self.yscale)
676                 fun(x, y)
677             self.cv.bind("<Button-%s>" % num, eventfun, add)
678 
679     def _onkeyrelease(self, fun, key):
680         """Bind fun to key-release event of key.
681         Canvas must have focus. See method listen
682         """
683         if fun is None:
684             self.cv.unbind("<KeyRelease-%s>" % key, None)
685         else:
686             def eventfun(event):
687                 fun()
688             self.cv.bind("<KeyRelease-%s>" % key, eventfun)
689 
690     def _onkeypress(self, fun, key=None):
691         """If key is given, bind fun to key-press event of key.
692         Otherwise bind fun to any key-press.
693         Canvas must have focus. See method listen.
694         """
695         if fun is None:
696             if key is None:
697                 self.cv.unbind("<KeyPress>", None)
698             else:
699                 self.cv.unbind("<KeyPress-%s>" % key, None)
700         else:
701             def eventfun(event):
702                 fun()
703             if key is None:
704                 self.cv.bind("<KeyPress>", eventfun)
705             else:
706                 self.cv.bind("<KeyPress-%s>" % key, eventfun)
707 
708     def _listen(self):
709         """Set focus on canvas (in order to collect key-events)
710         """
711         self.cv.focus_force()
712 
713     def _ontimer(self, fun, t):
714         """Install a timer, which calls fun after t milliseconds.
715         """
716         if t == 0:
717             self.cv.after_idle(fun)
718         else:
719             self.cv.after(t, fun)
720 
721     def _createimage(self, image):
722         """Create and return image item on canvas.
723         """
724         return self.cv.create_image(0, 0, image=image)
725 
726     def _drawimage(self, item, pos, image):
727         """Configure image item as to draw image object
728         at position (x,y) on canvas)
729         """
730         x, y = pos
731         self.cv.coords(item, (x * self.xscale, -y * self.yscale))
732         self.cv.itemconfig(item, image=image)
733 
734     def _setbgpic(self, item, image):
735         """Configure image item as to draw image object
736         at center of canvas. Set item to the first item
737         in the displaylist, so it will be drawn below
738         any other item ."""
739         self.cv.itemconfig(item, image=image)
740         self.cv.tag_lower(item)
741 
742     def _type(self, item):
743         """Return 'line' or 'polygon' or 'image' depending on
744         type of item.
745         """
746         return self.cv.type(item)
747 
748     def _pointlist(self, item):
749         """returns list of coordinate-pairs of points of item
750         Example (for insiders):
751         >>> from turtle import *
752         >>> getscreen()._pointlist(getturtle().turtle._item)
753         [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
754         (9.9999999999999982, 0.0)]
755         >>> """
756         cl = self.cv.coords(item)
757         pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
758         return  pl
759 
760     def _setscrollregion(self, srx1, sry1, srx2, sry2):
761         self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
762 
763     def _rescale(self, xscalefactor, yscalefactor):
764         items = self.cv.find_all()
765         for item in items:
766             coordinates = list(self.cv.coords(item))
767             newcoordlist = []
768             while coordinates:
769                 x, y = coordinates[:2]
770                 newcoordlist.append(x * xscalefactor)
771                 newcoordlist.append(y * yscalefactor)
772                 coordinates = coordinates[2:]
773             self.cv.coords(item, *newcoordlist)
774 
775     def _resize(self, canvwidth=None, canvheight=None, bg=None):
776         """Resize the canvas the turtles are drawing on. Does
777         not alter the drawing window.
778         """
779         # needs amendment
780         if not isinstance(self.cv, ScrolledCanvas):
781             return self.canvwidth, self.canvheight
782         if canvwidth is canvheight is bg is None:
783             return self.cv.canvwidth, self.cv.canvheight
784         if canvwidth is not None:
785             self.canvwidth = canvwidth
786         if canvheight is not None:
787             self.canvheight = canvheight
788         self.cv.reset(canvwidth, canvheight, bg)
789 
790     def _window_size(self):
791         """ Return the width and height of the turtle window.
792         """
793         width = self.cv.winfo_width()
794         if width <= 1:  # the window isn't managed by a geometry manager
795             width = self.cv['width']
796         height = self.cv.winfo_height()
797         if height <= 1: # the window isn't managed by a geometry manager
798             height = self.cv['height']
799         return width, height
800 
801     def mainloop(self):
802         """Starts event loop - calling Tkinter's mainloop function.
803 
804         No argument.
805 
806         Must be last statement in a turtle graphics program.
807         Must NOT be used if a script is run from within IDLE in -n mode
808         (No subprocess) - for interactive use of turtle graphics.
809 
810         Example (for a TurtleScreen instance named screen):
811         >>> screen.mainloop()
812 
813         """
814         TK.mainloop()
815 
816     def textinput(self, title, prompt):
817         """Pop up a dialog window for input of a string.
818 
819         Arguments: title is the title of the dialog window,
820         prompt is a text mostly describing what information to input.
821 
822         Return the string input
823         If the dialog is canceled, return None.
824 
825         Example (for a TurtleScreen instance named screen):
826         >>> screen.textinput("NIM", "Name of first player:")
827 
828         """
829         return simpledialog.askstring(title, prompt)
830 
831     def numinput(self, title, prompt, default=None, minval=None, maxval=None):
832         """Pop up a dialog window for input of a number.
833 
834         Arguments: title is the title of the dialog window,
835         prompt is a text mostly describing what numerical information to input.
836         default: default value
837         minval: minimum value for input
838         maxval: maximum value for input
839 
840         The number input must be in the range minval .. maxval if these are
841         given. If not, a hint is issued and the dialog remains open for
842         correction. Return the number input.
843         If the dialog is canceled,  return None.
844 
845         Example (for a TurtleScreen instance named screen):
846         >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000)
847 
848         """
849         return simpledialog.askfloat(title, prompt, initialvalue=default,
850                                      minvalue=minval, maxvalue=maxval)
851 
852 
853 ##############################################################################
854 ###                  End of Tkinter - interface                            ###
855 ##############################################################################
856 
857 
858 class Terminator (Exception):
859     """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
860 
861     This stops execution of a turtle graphics script.
862     Main purpose: use in the Demo-Viewer turtle.Demo.py.
863     """
864     pass
865 
866 
867 class TurtleGraphicsError(Exception):
868     """Some TurtleGraphics Error
869     """
870 
871 
872 class Shape(object):
873     """Data structure modeling shapes.
874 
875     attribute _type is one of "polygon", "image", "compound"
876     attribute _data is - depending on _type a poygon-tuple,
877     an image or a list constructed using the addcomponent method.
878     """
879     def __init__(self, type_, data=None):
880         self._type = type_
881         if type_ == "polygon":
882             if isinstance(data, list):
883                 data = tuple(data)
884         elif type_ == "image":
885             if isinstance(data, str):
886                 if data.lower().endswith(".gif") and isfile(data):
887                     data = TurtleScreen._image(data)
888                 # else data assumed to be Photoimage
889         elif type_ == "compound":
890             data = []
891         else:
892             raise TurtleGraphicsError("There is no shape type %s" % type_)
893         self._data = data
894 
895     def addcomponent(self, poly, fill, outline=None):
896         """Add component to a shape of type compound.
897 
898         Arguments: poly is a polygon, i. e. a tuple of number pairs.
899         fill is the fillcolor of the component,
900         outline is the outline color of the component.
901 
902         call (for a Shapeobject namend s):
903         --   s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
904 
905         Example:
906         >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
907         >>> s = Shape("compound")
908         >>> s.addcomponent(poly, "red", "blue")
909         >>> # .. add more components and then use register_shape()
910         """
911         if self._type != "compound":
912             raise TurtleGraphicsError("Cannot add component to %s Shape"
913                                                                 % self._type)
914         if outline is None:
915             outline = fill
916         self._data.append([poly, fill, outline])
917 
918 
919 class Tbuffer(object):
920     """Ring buffer used as undobuffer for RawTurtle objects."""
921     def __init__(self, bufsize=10):
922         self.bufsize = bufsize
923         self.buffer = [[None]] * bufsize
924         self.ptr = -1
925         self.cumulate = False
926     def reset(self, bufsize=None):
927         if bufsize is None:
928             for i in range(self.bufsize):
929                 self.buffer[i] = [None]
930         else:
931             self.bufsize = bufsize
932             self.buffer = [[None]] * bufsize
933         self.ptr = -1
934     def push(self, item):
935         if self.bufsize > 0:
936             if not self.cumulate:
937                 self.ptr = (self.ptr + 1) % self.bufsize
938                 self.buffer[self.ptr] = item
939             else:
940                 self.buffer[self.ptr].append(item)
941     def pop(self):
942         if self.bufsize > 0:
943             item = self.buffer[self.ptr]
944             if item is None:
945                 return None
946             else:
947                 self.buffer[self.ptr] = [None]
948                 self.ptr = (self.ptr - 1) % self.bufsize
949                 return (item)
950     def nr_of_items(self):
951         return self.bufsize - self.buffer.count([None])
952     def __repr__(self):
953         return str(self.buffer) + " " + str(self.ptr)
954 
955 
956 
957 class TurtleScreen(TurtleScreenBase):
958     """Provides screen oriented methods like setbg etc.
959 
960     Only relies upon the methods of TurtleScreenBase and NOT
961     upon components of the underlying graphics toolkit -
962     which is Tkinter in this case.
963     """
964     _RUNNING = True
965 
966     def __init__(self, cv, mode=_CFG["mode"],
967                  colormode=_CFG["colormode"], delay=_CFG["delay"]):
968         self._shapes = {
969                    "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
970                   "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
971                               (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
972                               (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
973                               (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
974                               (2,14))),
975                   "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
976                               (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
977                               (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
978                               (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
979                               (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
980                               (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
981                   "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
982                               (-10,-10))),
983                 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
984                               (-10,-5.77))),
985                   "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
986                    "blank" : Shape("image", self._blankimage())
987                   }
988 
989         self._bgpics = {"nopic" : ""}
990 
991         TurtleScreenBase.__init__(self, cv)
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(2*math.pi)
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.atan2(y, x)*180.0/math.pi, 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.atan2(y, x)*180.0/math.pi, 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(w2*math.pi/180.0*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 = (tilt * math.pi / 180.0) % (2*math.pi)
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         Deprecated since Python 3.1
2881 
2882         Examples (for a Turtle instance named turtle):
2883         >>> turtle.shape("circle")
2884         >>> turtle.shapesize(5,2)
2885         >>> turtle.tilt(45)
2886         >>> turtle.tiltangle()
2887         """
2888         if angle is None:
2889             tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2890             return (tilt / self._degreesPerAU) % self._fullcircle
2891         else:
2892             self.settiltangle(angle)
2893 
2894     def tilt(self, angle):
2895         """Rotate the turtleshape by angle.
2896 
2897         Argument:
2898         angle - a number
2899 
2900         Rotate the turtleshape by angle from its current tilt-angle,
2901         but do NOT change the turtle's heading (direction of movement).
2902 
2903         Examples (for a Turtle instance named turtle):
2904         >>> turtle.shape("circle")
2905         >>> turtle.shapesize(5,2)
2906         >>> turtle.tilt(30)
2907         >>> turtle.fd(50)
2908         >>> turtle.tilt(30)
2909         >>> turtle.fd(50)
2910         """
2911         self.settiltangle(angle + self.tiltangle())
2912 
2913     def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2914         """Set or return the current transformation matrix of the turtle shape.
2915 
2916         Optional arguments: t11, t12, t21, t22 -- numbers.
2917 
2918         If none of the matrix elements are given, return the transformation
2919         matrix.
2920         Otherwise set the given elements and transform the turtleshape
2921         according to the matrix consisting of first row t11, t12 and
2922         second row t21, 22.
2923         Modify stretchfactor, shearfactor and tiltangle according to the
2924         given matrix.
2925 
2926         Examples (for a Turtle instance named turtle):
2927         >>> turtle.shape("square")
2928         >>> turtle.shapesize(4,2)
2929         >>> turtle.shearfactor(-0.5)
2930         >>> turtle.shapetransform()
2931         (4.0, -1.0, -0.0, 2.0)
2932         """
2933         if t11 is t12 is t21 is t22 is None:
2934             return self._shapetrafo
2935         m11, m12, m21, m22 = self._shapetrafo
2936         if t11 is not None: m11 = t11
2937         if t12 is not None: m12 = t12
2938         if t21 is not None: m21 = t21
2939         if t22 is not None: m22 = t22
2940         if t11 * t22 - t12 * t21 == 0:
2941             raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2942         self._shapetrafo = (m11, m12, m21, m22)
2943         alfa = math.atan2(-m21, m11) % (2 * math.pi)
2944         sa, ca = math.sin(alfa), math.cos(alfa)
2945         a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2946                               sa*m11 + ca*m21, sa*m12 + ca*m22)
2947         self._stretchfactor = a11, a22
2948         self._shearfactor = a12/a22
2949         self._tilt = alfa
2950         self.pen(resizemode="user")
2951 
2952 
2953     def _polytrafo(self, poly):
2954         """Computes transformed polygon shapes from a shape
2955         according to current position and heading.
2956         """
2957         screen = self.screen
2958         p0, p1 = self._position
2959         e0, e1 = self._orient
2960         e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2961         e0, e1 = (1.0 / abs(e)) * e
2962         return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2963                                                            for (x, y) in poly]
2964 
2965     def get_shapepoly(self):
2966         """Return the current shape polygon as tuple of coordinate pairs.
2967 
2968         No argument.
2969 
2970         Examples (for a Turtle instance named turtle):
2971         >>> turtle.shape("square")
2972         >>> turtle.shapetransform(4, -1, 0, 2)
2973         >>> turtle.get_shapepoly()
2974         ((50, -20), (30, 20), (-50, 20), (-30, -20))
2975 
2976         """
2977         shape = self.screen._shapes[self.turtle.shapeIndex]
2978         if shape._type == "polygon":
2979             return self._getshapepoly(shape._data, shape._type == "compound")
2980         # else return None
2981 
2982     def _getshapepoly(self, polygon, compound=False):
2983         """Calculate transformed shape polygon according to resizemode
2984         and shapetransform.
2985         """
2986         if self._resizemode == "user" or compound:
2987             t11, t12, t21, t22 = self._shapetrafo
2988         elif self._resizemode == "auto":
2989             l = max(1, self._pensize/5.0)
2990             t11, t12, t21, t22 = l, 0, 0, l
2991         elif self._resizemode == "noresize":
2992             return polygon
2993         return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon)
2994 
2995     def _drawturtle(self):
2996         """Manages the correct rendering of the turtle with respect to
2997         its shape, resizemode, stretch and tilt etc."""
2998         screen = self.screen
2999         shape = screen._shapes[self.turtle.shapeIndex]
3000         ttype = shape._type
3001         titem = self.turtle._item
3002         if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3003             self._hidden_from_screen = False
3004             tshape = shape._data
3005             if ttype == "polygon":
3006                 if self._resizemode == "noresize": w = 1
3007                 elif self._resizemode == "auto": w = self._pensize
3008                 else: w =self._outlinewidth
3009                 shape = self._polytrafo(self._getshapepoly(tshape))
3010                 fc, oc = self._fillcolor, self._pencolor
3011                 screen._drawpoly(titem, shape, fill=fc, outline=oc,
3012                                                       width=w, top=True)
3013             elif ttype == "image":
3014                 screen._drawimage(titem, self._position, tshape)
3015             elif ttype == "compound":
3016                 for item, (poly, fc, oc) in zip(titem, tshape):
3017                     poly = self._polytrafo(self._getshapepoly(poly, True))
3018                     screen._drawpoly(item, poly, fill=self._cc(fc),
3019                                      outline=self._cc(oc), width=self._outlinewidth, top=True)
3020         else:
3021             if self._hidden_from_screen:
3022                 return
3023             if ttype == "polygon":
3024                 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3025             elif ttype == "image":
3026                 screen._drawimage(titem, self._position,
3027                                           screen._shapes["blank"]._data)
3028             elif ttype == "compound":
3029                 for item in titem:
3030                     screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3031             self._hidden_from_screen = True
3032 
3033 ##############################  stamp stuff  ###############################
3034 
3035     def stamp(self):
3036         """Stamp a copy of the turtleshape onto the canvas and return its id.
3037 
3038         No argument.
3039 
3040         Stamp a copy of the turtle shape onto the canvas at the current
3041         turtle position. Return a stamp_id for that stamp, which can be
3042         used to delete it by calling clearstamp(stamp_id).
3043 
3044         Example (for a Turtle instance named turtle):
3045         >>> turtle.color("blue")
3046         >>> turtle.stamp()
3047         13
3048         >>> turtle.fd(50)
3049         """
3050         screen = self.screen
3051         shape = screen._shapes[self.turtle.shapeIndex]
3052         ttype = shape._type
3053         tshape = shape._data
3054         if ttype == "polygon":
3055             stitem = screen._createpoly()
3056             if self._resizemode == "noresize": w = 1
3057             elif self._resizemode == "auto": w = self._pensize
3058             else: w =self._outlinewidth
3059             shape = self._polytrafo(self._getshapepoly(tshape))
3060             fc, oc = self._fillcolor, self._pencolor
3061             screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3062                                                   width=w, top=True)
3063         elif ttype == "image":
3064             stitem = screen._createimage("")
3065             screen._drawimage(stitem, self._position, tshape)
3066         elif ttype == "compound":
3067             stitem = []
3068             for element in tshape:
3069                 item = screen._createpoly()
3070                 stitem.append(item)
3071             stitem = tuple(stitem)
3072             for item, (poly, fc, oc) in zip(stitem, tshape):
3073                 poly = self._polytrafo(self._getshapepoly(poly, True))
3074                 screen._drawpoly(item, poly, fill=self._cc(fc),
3075                                  outline=self._cc(oc), width=self._outlinewidth, top=True)
3076         self.stampItems.append(stitem)
3077         self.undobuffer.push(("stamp", stitem))
3078         return stitem
3079 
3080     def _clearstamp(self, stampid):
3081         """does the work for clearstamp() and clearstamps()
3082         """
3083         if stampid in self.stampItems:
3084             if isinstance(stampid, tuple):
3085                 for subitem in stampid:
3086                     self.screen._delete(subitem)
3087             else:
3088                 self.screen._delete(stampid)
3089             self.stampItems.remove(stampid)
3090         # Delete stampitem from undobuffer if necessary
3091         # if clearstamp is called directly.
3092         item = ("stamp", stampid)
3093         buf = self.undobuffer
3094         if item not in buf.buffer:
3095             return
3096         index = buf.buffer.index(item)
3097         buf.buffer.remove(item)
3098         if index <= buf.ptr:
3099             buf.ptr = (buf.ptr - 1) % buf.bufsize
3100         buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3101 
3102     def clearstamp(self, stampid):
3103         """Delete stamp with given stampid
3104 
3105         Argument:
3106         stampid - an integer, must be return value of previous stamp() call.
3107 
3108         Example (for a Turtle instance named turtle):
3109         >>> turtle.color("blue")
3110         >>> astamp = turtle.stamp()
3111         >>> turtle.fd(50)
3112         >>> turtle.clearstamp(astamp)
3113         """
3114         self._clearstamp(stampid)
3115         self._update()
3116 
3117     def clearstamps(self, n=None):
3118         """Delete all or first/last n of turtle's stamps.
3119 
3120         Optional argument:
3121         n -- an integer
3122 
3123         If n is None, delete all of pen's stamps,
3124         else if n > 0 delete first n stamps
3125         else if n < 0 delete last n stamps.
3126 
3127         Example (for a Turtle instance named turtle):
3128         >>> for i in range(8):
3129         ...     turtle.stamp(); turtle.fd(30)
3130         ...
3131         >>> turtle.clearstamps(2)
3132         >>> turtle.clearstamps(-2)
3133         >>> turtle.clearstamps()
3134         """
3135         if n is None:
3136             toDelete = self.stampItems[:]
3137         elif n >= 0:
3138             toDelete = self.stampItems[:n]
3139         else:
3140             toDelete = self.stampItems[n:]
3141         for item in toDelete:
3142             self._clearstamp(item)
3143         self._update()
3144 
3145     def _goto(self, end):
3146         """Move the pen to the point end, thereby drawing a line
3147         if pen is down. All other methods for turtle movement depend
3148         on this one.
3149         """
3150         ## Version with undo-stuff
3151         go_modes = ( self._drawing,
3152                      self._pencolor,
3153                      self._pensize,
3154                      isinstance(self._fillpath, list))
3155         screen = self.screen
3156         undo_entry = ("go", self._position, end, go_modes,
3157                       (self.currentLineItem,
3158                       self.currentLine[:],
3159                       screen._pointlist(self.currentLineItem),
3160                       self.items[:])
3161                       )
3162         if self.undobuffer:
3163             self.undobuffer.push(undo_entry)
3164         start = self._position
3165         if self._speed and screen._tracing == 1:
3166             diff = (end-start)
3167             diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3168             nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3169             delta = diff * (1.0/nhops)
3170             for n in range(1, nhops):
3171                 if n == 1:
3172                     top = True
3173                 else:
3174                     top = False
3175                 self._position = start + delta * n
3176                 if self._drawing:
3177                     screen._drawline(self.drawingLineItem,
3178                                      (start, self._position),
3179                                      self._pencolor, self._pensize, top)
3180                 self._update()
3181             if self._drawing:
3182                 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3183                                                fill="", width=self._pensize)
3184         # Turtle now at end,
3185         if self._drawing: # now update currentLine
3186             self.currentLine.append(end)
3187         if isinstance(self._fillpath, list):
3188             self._fillpath.append(end)
3189         ######    vererbung!!!!!!!!!!!!!!!!!!!!!!
3190         self._position = end
3191         if self._creatingPoly:
3192             self._poly.append(end)
3193         if len(self.currentLine) > 42: # 42! answer to the ultimate question
3194                                        # of life, the universe and everything
3195             self._newLine()
3196         self._update() #count=True)
3197 
3198     def _undogoto(self, entry):
3199         """Reverse a _goto. Used for undo()
3200         """
3201         old, new, go_modes, coodata = entry
3202         drawing, pc, ps, filling = go_modes
3203         cLI, cL, pl, items = coodata
3204         screen = self.screen
3205         if abs(self._position - new) > 0.5:
3206             print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3207         # restore former situation
3208         self.currentLineItem = cLI
3209         self.currentLine = cL
3210 
3211         if pl == [(0, 0), (0, 0)]:
3212             usepc = ""
3213         else:
3214             usepc = pc
3215         screen._drawline(cLI, pl, fill=usepc, width=ps)
3216 
3217         todelete = [i for i in self.items if (i not in items) and
3218                                        (screen._type(i) == "line")]
3219         for i in todelete:
3220             screen._delete(i)
3221             self.items.remove(i)
3222 
3223         start = old
3224         if self._speed and screen._tracing == 1:
3225             diff = old - new
3226             diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3227             nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3228             delta = diff * (1.0/nhops)
3229             for n in range(1, nhops):
3230                 if n == 1:
3231                     top = True
3232                 else:
3233                     top = False
3234                 self._position = new + delta * n
3235                 if drawing:
3236                     screen._drawline(self.drawingLineItem,
3237                                      (start, self._position),
3238                                      pc, ps, top)
3239                 self._update()
3240             if drawing:
3241                 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3242                                                fill="", width=ps)
3243         # Turtle now at position old,
3244         self._position = old
3245         ##  if undo is done during creating a polygon, the last vertex
3246         ##  will be deleted. if the polygon is entirely deleted,
3247         ##  creatingPoly will be set to False.
3248         ##  Polygons created before the last one will not be affected by undo()
3249         if self._creatingPoly:
3250             if len(self._poly) > 0:
3251                 self._poly.pop()
3252             if self._poly == []:
3253                 self._creatingPoly = False
3254                 self._poly = None
3255         if filling:
3256             if self._fillpath == []:
3257                 self._fillpath = None
3258                 print("Unwahrscheinlich in _undogoto!")
3259             elif self._fillpath is not None:
3260                 self._fillpath.pop()
3261         self._update() #count=True)
3262 
3263     def _rotate(self, angle):
3264         """Turns pen clockwise by angle.
3265         """
3266         if self.undobuffer:
3267             self.undobuffer.push(("rot", angle, self._degreesPerAU))
3268         angle *= self._degreesPerAU
3269         neworient = self._orient.rotate(angle)
3270         tracing = self.screen._tracing
3271         if tracing == 1 and self._speed > 0:
3272             anglevel = 3.0 * self._speed
3273             steps = 1 + int(abs(angle)/anglevel)
3274             delta = 1.0*angle/steps
3275             for _ in range(steps):
3276                 self._orient = self._orient.rotate(delta)
3277                 self._update()
3278         self._orient = neworient
3279         self._update()
3280 
3281     def _newLine(self, usePos=True):
3282         """Closes current line item and starts a new one.
3283            Remark: if current line became too long, animation
3284            performance (via _drawline) slowed down considerably.
3285         """
3286         if len(self.currentLine) > 1:
3287             self.screen._drawline(self.currentLineItem, self.currentLine,
3288                                       self._pencolor, self._pensize)
3289             self.currentLineItem = self.screen._createline()
3290             self.items.append(self.currentLineItem)
3291         else:
3292             self.screen._drawline(self.currentLineItem, top=True)
3293         self.currentLine = []
3294         if usePos:
3295             self.currentLine = [self._position]
3296 
3297     def filling(self):
3298         """Return fillstate (True if filling, False else).
3299 
3300         No argument.
3301 
3302         Example (for a Turtle instance named turtle):
3303         >>> turtle.begin_fill()
3304         >>> if turtle.filling():
3305         ...     turtle.pensize(5)
3306         ... else:
3307         ...     turtle.pensize(3)
3308         """
3309         return isinstance(self._fillpath, list)
3310 
3311     def begin_fill(self):
3312         """Called just before drawing a shape to be filled.
3313 
3314         No argument.
3315 
3316         Example (for a Turtle instance named turtle):
3317         >>> turtle.color("black", "red")
3318         >>> turtle.begin_fill()
3319         >>> turtle.circle(60)
3320         >>> turtle.end_fill()
3321         """
3322         if not self.filling():
3323             self._fillitem = self.screen._createpoly()
3324             self.items.append(self._fillitem)
3325         self._fillpath = [self._position]
3326         self._newLine()
3327         if self.undobuffer:
3328             self.undobuffer.push(("beginfill", self._fillitem))
3329         self._update()
3330 
3331 
3332     def end_fill(self):
3333         """Fill the shape drawn after the call begin_fill().
3334 
3335         No argument.
3336 
3337         Example (for a Turtle instance named turtle):
3338         >>> turtle.color("black", "red")
3339         >>> turtle.begin_fill()
3340         >>> turtle.circle(60)
3341         >>> turtle.end_fill()
3342         """
3343         if self.filling():
3344             if len(self._fillpath) > 2:
3345                 self.screen._drawpoly(self._fillitem, self._fillpath,
3346                                       fill=self._fillcolor)
3347                 if self.undobuffer:
3348                     self.undobuffer.push(("dofill", self._fillitem))
3349             self._fillitem = self._fillpath = None
3350             self._update()
3351 
3352     def dot(self, size=None, *color):
3353         """Draw a dot with diameter size, using color.
3354 
3355         Optional arguments:
3356         size -- an integer >= 1 (if given)
3357         color -- a colorstring or a numeric color tuple
3358 
3359         Draw a circular dot with diameter size, using color.
3360         If size is not given, the maximum of pensize+4 and 2*pensize is used.
3361 
3362         Example (for a Turtle instance named turtle):
3363         >>> turtle.dot()
3364         >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3365         """
3366         if not color:
3367             if isinstance(size, (str, tuple)):
3368                 color = self._colorstr(size)
3369                 size = self._pensize + max(self._pensize, 4)
3370             else:
3371                 color = self._pencolor
3372                 if not size:
3373                     size = self._pensize + max(self._pensize, 4)
3374         else:
3375             if size is None:
3376                 size = self._pensize + max(self._pensize, 4)
3377             color = self._colorstr(color)
3378         if hasattr(self.screen, "_dot"):
3379             item = self.screen._dot(self._position, size, color)
3380             self.items.append(item)
3381             if self.undobuffer:
3382                 self.undobuffer.push(("dot", item))
3383         else:
3384             pen = self.pen()
3385             if self.undobuffer:
3386                 self.undobuffer.push(["seq"])
3387                 self.undobuffer.cumulate = True
3388             try:
3389                 if self.resizemode() == 'auto':
3390                     self.ht()
3391                 self.pendown()
3392                 self.pensize(size)
3393                 self.pencolor(color)
3394                 self.forward(0)
3395             finally:
3396                 self.pen(pen)
3397             if self.undobuffer:
3398                 self.undobuffer.cumulate = False
3399 
3400     def _write(self, txt, align, font):
3401         """Performs the writing for write()
3402         """
3403         item, end = self.screen._write(self._position, txt, align, font,
3404                                                           self._pencolor)
3405         self.items.append(item)
3406         if self.undobuffer:
3407             self.undobuffer.push(("wri", item))
3408         return end
3409 
3410     def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3411         """Write text at the current turtle position.
3412 
3413         Arguments:
3414         arg -- info, which is to be written to the TurtleScreen
3415         move (optional) -- True/False
3416         align (optional) -- one of the strings "left", "center" or right"
3417         font (optional) -- a triple (fontname, fontsize, fonttype)
3418 
3419         Write text - the string representation of arg - at the current
3420         turtle position according to align ("left", "center" or right")
3421         and with the given font.
3422         If move is True, the pen is moved to the bottom-right corner
3423         of the text. By default, move is False.
3424 
3425         Example (for a Turtle instance named turtle):
3426         >>> turtle.write('Home = ', True, align="center")
3427         >>> turtle.write((0,0), True)
3428         """
3429         if self.undobuffer:
3430             self.undobuffer.push(["seq"])
3431             self.undobuffer.cumulate = True
3432         end = self._write(str(arg), align.lower(), font)
3433         if move:
3434             x, y = self.pos()
3435             self.setpos(end, y)
3436         if self.undobuffer:
3437             self.undobuffer.cumulate = False
3438 
3439     def begin_poly(self):
3440         """Start recording the vertices of a polygon.
3441 
3442         No argument.
3443 
3444         Start recording the vertices of a polygon. Current turtle position
3445         is first point of polygon.
3446 
3447         Example (for a Turtle instance named turtle):
3448         >>> turtle.begin_poly()
3449         """
3450         self._poly = [self._position]
3451         self._creatingPoly = True
3452 
3453     def end_poly(self):
3454         """Stop recording the vertices of a polygon.
3455 
3456         No argument.
3457 
3458         Stop recording the vertices of a polygon. Current turtle position is
3459         last point of polygon. This will be connected with the first point.
3460 
3461         Example (for a Turtle instance named turtle):
3462         >>> turtle.end_poly()
3463         """
3464         self._creatingPoly = False
3465 
3466     def get_poly(self):
3467         """Return the lastly recorded polygon.
3468 
3469         No argument.
3470 
3471         Example (for a Turtle instance named turtle):
3472         >>> p = turtle.get_poly()
3473         >>> turtle.register_shape("myFavouriteShape", p)
3474         """
3475         ## check if there is any poly?
3476         if self._poly is not None:
3477             return tuple(self._poly)
3478 
3479     def getscreen(self):
3480         """Return the TurtleScreen object, the turtle is drawing  on.
3481 
3482         No argument.
3483 
3484         Return the TurtleScreen object, the turtle is drawing  on.
3485         So TurtleScreen-methods can be called for that object.
3486 
3487         Example (for a Turtle instance named turtle):
3488         >>> ts = turtle.getscreen()
3489         >>> ts
3490         <turtle.TurtleScreen object at 0x0106B770>
3491         >>> ts.bgcolor("pink")
3492         """
3493         return self.screen
3494 
3495     def getturtle(self):
3496         """Return the Turtleobject itself.
3497 
3498         No argument.
3499 
3500         Only reasonable use: as a function to return the 'anonymous turtle':
3501 
3502         Example:
3503         >>> pet = getturtle()
3504         >>> pet.fd(50)
3505         >>> pet
3506         <turtle.Turtle object at 0x0187D810>
3507         >>> turtles()
3508         [<turtle.Turtle object at 0x0187D810>]
3509         """
3510         return self
3511 
3512     getpen = getturtle
3513 
3514 
3515     ################################################################
3516     ### screen oriented methods recurring to methods of TurtleScreen
3517     ################################################################
3518 
3519     def _delay(self, delay=None):
3520         """Set delay value which determines speed of turtle animation.
3521         """
3522         return self.screen.delay(delay)
3523 
3524     def onclick(self, fun, btn=1, add=None):
3525         """Bind fun to mouse-click event on this turtle on canvas.
3526 
3527         Arguments:
3528         fun --  a function with two arguments, to which will be assigned
3529                 the coordinates of the clicked point on the canvas.
3530         btn --  number of the mouse-button defaults to 1 (left mouse button).
3531         add --  True or False. If True, new binding will be added, otherwise
3532                 it will replace a former binding.
3533 
3534         Example for the anonymous turtle, i. e. the procedural way:
3535 
3536         >>> def turn(x, y):
3537         ...     left(360)
3538         ...
3539         >>> onclick(turn)  # Now clicking into the turtle will turn it.
3540         >>> onclick(None)  # event-binding will be removed
3541         """
3542         self.screen._onclick(self.turtle._item, fun, btn, add)
3543         self._update()
3544 
3545     def onrelease(self, fun, btn=1, add=None):
3546         """Bind fun to mouse-button-release event on this turtle on canvas.
3547 
3548         Arguments:
3549         fun -- a function with two arguments, to which will be assigned
3550                 the coordinates of the clicked point on the canvas.
3551         btn --  number of the mouse-button defaults to 1 (left mouse button).
3552 
3553         Example (for a MyTurtle instance named joe):
3554         >>> class MyTurtle(Turtle):
3555         ...     def glow(self,x,y):
3556         ...             self.fillcolor("red")
3557         ...     def unglow(self,x,y):
3558         ...             self.fillcolor("")
3559         ...
3560         >>> joe = MyTurtle()
3561         >>> joe.onclick(joe.glow)
3562         >>> joe.onrelease(joe.unglow)
3563 
3564         Clicking on joe turns fillcolor red, unclicking turns it to
3565         transparent.
3566         """
3567         self.screen._onrelease(self.turtle._item, fun, btn, add)
3568         self._update()
3569 
3570     def ondrag(self, fun, btn=1, add=None):
3571         """Bind fun to mouse-move event on this turtle on canvas.
3572 
3573         Arguments:
3574         fun -- a function with two arguments, to which will be assigned
3575                the coordinates of the clicked point on the canvas.
3576         btn -- number of the mouse-button defaults to 1 (left mouse button).
3577 
3578         Every sequence of mouse-move-events on a turtle is preceded by a
3579         mouse-click event on that turtle.
3580 
3581         Example (for a Turtle instance named turtle):
3582         >>> turtle.ondrag(turtle.goto)
3583 
3584         Subsequently clicking and dragging a Turtle will move it
3585         across the screen thereby producing handdrawings (if pen is
3586         down).
3587         """
3588         self.screen._ondrag(self.turtle._item, fun, btn, add)
3589 
3590 
3591     def _undo(self, action, data):
3592         """Does the main part of the work for undo()
3593         """
3594         if self.undobuffer is None:
3595             return
3596         if action == "rot":
3597             angle, degPAU = data
3598             self._rotate(-angle*degPAU/self._degreesPerAU)
3599             dummy = self.undobuffer.pop()
3600         elif action == "stamp":
3601             stitem = data[0]
3602             self.clearstamp(stitem)
3603         elif action == "go":
3604             self._undogoto(data)
3605         elif action in ["wri", "dot"]:
3606             item = data[0]
3607             self.screen._delete(item)
3608             self.items.remove(item)
3609         elif action == "dofill":
3610             item = data[0]
3611             self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3612                                   fill="", outline="")
3613         elif action == "beginfill":
3614             item = data[0]
3615             self._fillitem = self._fillpath = None
3616             if item in self.items:
3617                 self.screen._delete(item)
3618                 self.items.remove(item)
3619         elif action == "pen":
3620             TPen.pen(self, data[0])
3621             self.undobuffer.pop()
3622 
3623     def undo(self):
3624         """undo (repeatedly) the last turtle action.
3625 
3626         No argument.
3627 
3628         undo (repeatedly) the last turtle action.
3629         Number of available undo actions is determined by the size of
3630         the undobuffer.
3631 
3632         Example (for a Turtle instance named turtle):
3633         >>> for i in range(4):
3634         ...     turtle.fd(50); turtle.lt(80)
3635         ...
3636         >>> for i in range(8):
3637         ...     turtle.undo()
3638         ...
3639         """
3640         if self.undobuffer is None:
3641             return
3642         item = self.undobuffer.pop()
3643         action = item[0]
3644         data = item[1:]
3645         if action == "seq":
3646             while data:
3647                 item = data.pop()
3648                 self._undo(item[0], item[1:])
3649         else:
3650             self._undo(action, data)
3651 
3652     turtlesize = shapesize
3653 
3654 RawPen = RawTurtle
3655 
3656 ###  Screen - Singleton  ########################
3657 
3658 def Screen():
3659     """Return the singleton screen object.
3660     If none exists at the moment, create a new one and return it,
3661     else return the existing one."""
3662     if Turtle._screen is None:
3663         Turtle._screen = _Screen()
3664     return Turtle._screen
3665 
3666 class _Screen(TurtleScreen):
3667 
3668     _root = None
3669     _canvas = None
3670     _title = _CFG["title"]
3671 
3672     def __init__(self):
3673         # XXX there is no need for this code to be conditional,
3674         # as there will be only a single _Screen instance, anyway
3675         # XXX actually, the turtle demo is injecting root window,
3676         # so perhaps the conditional creation of a root should be
3677         # preserved (perhaps by passing it as an optional parameter)
3678         if _Screen._root is None:
3679             _Screen._root = self._root = _Root()
3680             self._root.title(_Screen._title)
3681             self._root.ondestroy(self._destroy)
3682         if _Screen._canvas is None:
3683             width = _CFG["width"]
3684             height = _CFG["height"]
3685             canvwidth = _CFG["canvwidth"]
3686             canvheight = _CFG["canvheight"]
3687             leftright = _CFG["leftright"]
3688             topbottom = _CFG["topbottom"]
3689             self._root.setupcanvas(width, height, canvwidth, canvheight)
3690             _Screen._canvas = self._root._getcanvas()
3691             TurtleScreen.__init__(self, _Screen._canvas)
3692             self.setup(width, height, leftright, topbottom)
3693 
3694     def setup(self, width=_CFG["width"], height=_CFG["height"],
3695               startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3696         """ Set the size and position of the main window.
3697 
3698         Arguments:
3699         width: as integer a size in pixels, as float a fraction of the screen.
3700           Default is 50% of screen.
3701         height: as integer the height in pixels, as float a fraction of the
3702           screen. Default is 75% of screen.
3703         startx: if positive, starting position in pixels from the left
3704           edge of the screen, if negative from the right edge
3705           Default, startx=None is to center window horizontally.
3706         starty: if positive, starting position in pixels from the top
3707           edge of the screen, if negative from the bottom edge
3708           Default, starty=None is to center window vertically.
3709 
3710         Examples (for a Screen instance named screen):
3711         >>> screen.setup (width=200, height=200, startx=0, starty=0)
3712 
3713         sets window to 200x200 pixels, in upper left of screen
3714 
3715         >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3716 
3717         sets window to 75% of screen by 50% of screen and centers
3718         """
3719         if not hasattr(self._root, "set_geometry"):
3720             return
3721         sw = self._root.win_width()
3722         sh = self._root.win_height()
3723         if isinstance(width, float) and 0 <= width <= 1:
3724             width = sw*width
3725         if startx is None:
3726             startx = (sw - width) / 2
3727         if isinstance(height, float) and 0 <= height <= 1:
3728             height = sh*height
3729         if starty is None:
3730             starty = (sh - height) / 2
3731         self._root.set_geometry(width, height, startx, starty)
3732         self.update()
3733 
3734     def title(self, titlestring):
3735         """Set title of turtle-window
3736 
3737         Argument:
3738         titlestring -- a string, to appear in the titlebar of the
3739                        turtle graphics window.
3740 
3741         This is a method of Screen-class. Not available for TurtleScreen-
3742         objects.
3743 
3744         Example (for a Screen instance named screen):
3745         >>> screen.title("Welcome to the turtle-zoo!")
3746         """
3747         if _Screen._root is not None:
3748             _Screen._root.title(titlestring)
3749         _Screen._title = titlestring
3750 
3751     def _destroy(self):
3752         root = self._root
3753         if root is _Screen._root:
3754             Turtle._pen = None
3755             Turtle._screen = None
3756             _Screen._root = None
3757             _Screen._canvas = None
3758         TurtleScreen._RUNNING = False
3759         root.destroy()
3760 
3761     def bye(self):
3762         """Shut the turtlegraphics window.
3763 
3764         Example (for a TurtleScreen instance named screen):
3765         >>> screen.bye()
3766         """
3767         self._destroy()
3768 
3769     def exitonclick(self):
3770         """Go into mainloop until the mouse is clicked.
3771 
3772         No arguments.
3773 
3774         Bind bye() method to mouseclick on TurtleScreen.
3775         If "using_IDLE" - value in configuration dictionary is False
3776         (default value), enter mainloop.
3777         If IDLE with -n switch (no subprocess) is used, this value should be
3778         set to True in turtle.cfg. In this case IDLE's mainloop
3779         is active also for the client script.
3780 
3781         This is a method of the Screen-class and not available for
3782         TurtleScreen instances.
3783 
3784         Example (for a Screen instance named screen):
3785         >>> screen.exitonclick()
3786 
3787         """
3788         def exitGracefully(x, y):
3789             """Screen.bye() with two dummy-parameters"""
3790             self.bye()
3791         self.onclick(exitGracefully)
3792         if _CFG["using_IDLE"]:
3793             return
3794         try:
3795             mainloop()
3796         except AttributeError:
3797             exit(0)
3798 
3799 class Turtle(RawTurtle):
3800     """RawTurtle auto-creating (scrolled) canvas.
3801 
3802     When a Turtle object is created or a function derived from some
3803     Turtle method is called a TurtleScreen object is automatically created.
3804     """
3805     _pen = None
3806     _screen = None
3807 
3808     def __init__(self,
3809                  shape=_CFG["shape"],
3810                  undobuffersize=_CFG["undobuffersize"],
3811                  visible=_CFG["visible"]):
3812         if Turtle._screen is None:
3813             Turtle._screen = Screen()
3814         RawTurtle.__init__(self, Turtle._screen,
3815                            shape=shape,
3816                            undobuffersize=undobuffersize,
3817                            visible=visible)
3818 
3819 Pen = Turtle
3820 
3821 def write_docstringdict(filename="turtle_docstringdict"):
3822     """Create and write docstring-dictionary to file.
3823 
3824     Optional argument:
3825     filename -- a string, used as filename
3826                 default value is turtle_docstringdict
3827 
3828     Has to be called explicitly, (not used by the turtle-graphics classes)
3829     The docstring dictionary will be written to the Python script <filname>.py
3830     It is intended to serve as a template for translation of the docstrings
3831     into different languages.
3832     """
3833     docsdict = {}
3834 
3835     for methodname in _tg_screen_functions:
3836         key = "_Screen."+methodname
3837         docsdict[key] = eval(key).__doc__
3838     for methodname in _tg_turtle_functions:
3839         key = "Turtle."+methodname
3840         docsdict[key] = eval(key).__doc__
3841 
3842     with open("%s.py" % filename,"w") as f:
3843         keys = sorted(x for x in docsdict
3844                       if x.split('.')[1] not in _alias_list)
3845         f.write('docsdict = {\n\n')
3846         for key in keys[:-1]:
3847             f.write('%s :\n' % repr(key))
3848             f.write('        """%s\n""",\n\n' % docsdict[key])
3849         key = keys[-1]
3850         f.write('%s :\n' % repr(key))
3851         f.write('        """%s\n"""\n\n' % docsdict[key])
3852         f.write("}\n")
3853         f.close()
3854 
3855 def read_docstrings(lang):
3856     """Read in docstrings from lang-specific docstring dictionary.
3857 
3858     Transfer docstrings, translated to lang, from a dictionary-file
3859     to the methods of classes Screen and Turtle and - in revised form -
3860     to the corresponding functions.
3861     """
3862     modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3863     module = __import__(modname)
3864     docsdict = module.docsdict
3865     for key in docsdict:
3866         try:
3867 #            eval(key).im_func.__doc__ = docsdict[key]
3868             eval(key).__doc__ = docsdict[key]
3869         except Exception:
3870             print("Bad docstring-entry: %s" % key)
3871 
3872 _LANGUAGE = _CFG["language"]
3873 
3874 try:
3875     if _LANGUAGE != "english":
3876         read_docstrings(_LANGUAGE)
3877 except ImportError:
3878     print("Cannot find docsdict for", _LANGUAGE)
3879 except Exception:
3880     print ("Unknown Error when trying to import %s-docstring-dictionary" %
3881                                                                   _LANGUAGE)
3882 
3883 
3884 def getmethparlist(ob):
3885     """Get strings describing the arguments for the given object
3886 
3887     Returns a pair of strings representing function parameter lists
3888     including parenthesis.  The first string is suitable for use in
3889     function definition and the second is suitable for use in function
3890     call.  The "self" parameter is not included.
3891     """
3892     defText = callText = ""
3893     # bit of a hack for methods - turn it into a function
3894     # but we drop the "self" param.
3895     # Try and build one for Python defined functions
3896     args, varargs, varkw = inspect.getargs(ob.__code__)
3897     items2 = args[1:]
3898     realArgs = args[1:]
3899     defaults = ob.__defaults__ or []
3900     defaults = ["=%r" % (value,) for value in defaults]
3901     defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3902     items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3903     if varargs is not None:
3904         items1.append("*" + varargs)
3905         items2.append("*" + varargs)
3906     if varkw is not None:
3907         items1.append("**" + varkw)
3908         items2.append("**" + varkw)
3909     defText = ", ".join(items1)
3910     defText = "(%s)" % defText
3911     callText = ", ".join(items2)
3912     callText = "(%s)" % callText
3913     return defText, callText
3914 
3915 def _turtle_docrevise(docstr):
3916     """To reduce docstrings from RawTurtle class for functions
3917     """
3918     import re
3919     if docstr is None:
3920         return None
3921     turtlename = _CFG["exampleturtle"]
3922     newdocstr = docstr.replace("%s." % turtlename,"")
3923     parexp = re.compile(r' \(.+ %s\):' % turtlename)
3924     newdocstr = parexp.sub(":", newdocstr)
3925     return newdocstr
3926 
3927 def _screen_docrevise(docstr):
3928     """To reduce docstrings from TurtleScreen class for functions
3929     """
3930     import re
3931     if docstr is None:
3932         return None
3933     screenname = _CFG["examplescreen"]
3934     newdocstr = docstr.replace("%s." % screenname,"")
3935     parexp = re.compile(r' \(.+ %s\):' % screenname)
3936     newdocstr = parexp.sub(":", newdocstr)
3937     return newdocstr
3938 
3939 ## The following mechanism makes all methods of RawTurtle and Turtle available
3940 ## as functions. So we can enhance, change, add, delete methods to these
3941 ## classes and do not need to change anything here.
3942 
3943 __func_body = """\
3944 def {name}{paramslist}:
3945     if {obj} is None:
3946         if not TurtleScreen._RUNNING:
3947             TurtleScreen._RUNNING = True
3948             raise Terminator
3949         {obj} = {init}
3950     try:
3951         return {obj}.{name}{argslist}
3952     except TK.TclError:
3953         if not TurtleScreen._RUNNING:
3954             TurtleScreen._RUNNING = True
3955             raise Terminator
3956         raise
3957 """
3958 
3959 def _make_global_funcs(functions, cls, obj, init, docrevise):
3960     for methodname in functions:
3961         method = getattr(cls, methodname)
3962         pl1, pl2 = getmethparlist(method)
3963         if pl1 == "":
3964             print(">>>>>>", pl1, pl2)
3965             continue
3966         defstr = __func_body.format(obj=obj, init=init, name=methodname,
3967                                     paramslist=pl1, argslist=pl2)
3968         exec(defstr, globals())
3969         globals()[methodname].__doc__ = docrevise(method.__doc__)
3970 
3971 _make_global_funcs(_tg_screen_functions, _Screen,
3972                    'Turtle._screen', 'Screen()', _screen_docrevise)
3973 _make_global_funcs(_tg_turtle_functions, Turtle,
3974                    'Turtle._pen', 'Turtle()', _turtle_docrevise)
3975 
3976 
3977 done = mainloop
3978 
3979 if __name__ == "__main__":
3980     def switchpen():
3981         if isdown():
3982             pu()
3983         else:
3984             pd()
3985 
3986     def demo1():
3987         """Demo of old turtle.py - module"""
3988         reset()
3989         tracer(True)
3990         up()
3991         backward(100)
3992         down()
3993         # draw 3 squares; the last filled
3994         width(3)
3995         for i in range(3):
3996             if i == 2:
3997                 begin_fill()
3998             for _ in range(4):
3999                 forward(20)
4000                 left(90)
4001             if i == 2:
4002                 color("maroon")
4003                 end_fill()
4004             up()
4005             forward(30)
4006             down()
4007         width(1)
4008         color("black")
4009         # move out of the way
4010         tracer(False)
4011         up()
4012         right(90)
4013         forward(100)
4014         right(90)
4015         forward(100)
4016         right(180)
4017         down()
4018         # some text
4019         write("startstart", 1)
4020         write("start", 1)
4021         color("red")
4022         # staircase
4023         for i in range(5):
4024             forward(20)
4025             left(90)
4026             forward(20)
4027             right(90)
4028         # filled staircase
4029         tracer(True)
4030         begin_fill()
4031         for i in range(5):
4032             forward(20)
4033             left(90)
4034             forward(20)
4035             right(90)
4036         end_fill()
4037         # more text
4038 
4039     def demo2():
4040         """Demo of some new features."""
4041         speed(1)
4042         st()
4043         pensize(3)
4044         setheading(towards(0, 0))
4045         radius = distance(0, 0)/2.0
4046         rt(90)
4047         for _ in range(18):
4048             switchpen()
4049             circle(radius, 10)
4050         write("wait a moment...")
4051         while undobufferentries():
4052             undo()
4053         reset()
4054         lt(90)
4055         colormode(255)
4056         laenge = 10
4057         pencolor("green")
4058         pensize(3)
4059         lt(180)
4060         for i in range(-2, 16):
4061             if i > 0:
4062                 begin_fill()
4063                 fillcolor(255-15*i, 0, 15*i)
4064             for _ in range(3):
4065                 fd(laenge)
4066                 lt(120)
4067             end_fill()
4068             laenge += 10
4069             lt(15)
4070             speed((speed()+1)%12)
4071         #end_fill()
4072 
4073         lt(120)
4074         pu()
4075         fd(70)
4076         rt(30)
4077         pd()
4078         color("red","yellow")
4079         speed(0)
4080         begin_fill()
4081         for _ in range(4):
4082             circle(50, 90)
4083             rt(90)
4084             fd(30)
4085             rt(90)
4086         end_fill()
4087         lt(90)
4088         pu()
4089         fd(30)
4090         pd()
4091         shape("turtle")
4092 
4093         tri = getturtle()
4094         tri.resizemode("auto")
4095         turtle = Turtle()
4096         turtle.resizemode("auto")
4097         turtle.shape("turtle")
4098         turtle.reset()
4099         turtle.left(90)
4100         turtle.speed(0)
4101         turtle.up()
4102         turtle.goto(280, 40)
4103         turtle.lt(30)
4104         turtle.down()
4105         turtle.speed(6)
4106         turtle.color("blue","orange")
4107         turtle.pensize(2)
4108         tri.speed(6)
4109         setheading(towards(turtle))
4110         count = 1
4111         while tri.distance(turtle) > 4:
4112             turtle.fd(3.5)
4113             turtle.lt(0.6)
4114             tri.setheading(tri.towards(turtle))
4115             tri.fd(4)
4116             if count % 20 == 0:
4117                 turtle.stamp()
4118                 tri.stamp()
4119                 switchpen()
4120             count += 1
4121         tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4122         tri.pencolor("black")
4123         tri.pencolor("red")
4124 
4125         def baba(xdummy, ydummy):
4126             clearscreen()
4127             bye()
4128 
4129         time.sleep(2)
4130 
4131         while undobufferentries():
4132             tri.undo()
4133             turtle.undo()
4134         tri.fd(50)
4135         tri.write("  Click me!", font = ("Courier", 12, "bold") )
4136         tri.onclick(baba, 1)
4137 
4138     demo1()
4139     demo2()
4140     exitonclick()
4141