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