blob: afe5658cd9f2a28b8acc7a70c6008900e15b32c7 [file] [log] [blame]
Martin v. Löwis87184592008-06-04 06:29:55 +00001#
2# turtle.py: a Tkinter based turtle graphics module for Python
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003# Version 1.0.1 - 24. 9. 2009
Martin v. Löwis87184592008-06-04 06:29:55 +00004#
R. David Murrayddfb6cd2009-09-28 18:29:28 +00005# Copyright (C) 2006 - 2009 Gregor Lingl
Martin v. Löwis87184592008-06-04 06:29:55 +00006# email: glingl@aon.at
7#
8# This software is provided 'as-is', without any express or implied
9# warranty. In no event will the authors be held liable for any damages
10# arising from the use of this software.
11#
12# Permission is granted to anyone to use this software for any purpose,
13# including commercial applications, and to alter it and redistribute it
14# freely, subject to the following restrictions:
15#
16# 1. The origin of this software must not be misrepresented; you must not
17# claim that you wrote the original software. If you use this software
18# in a product, an acknowledgment in the product documentation would be
19# appreciated but is not required.
20# 2. Altered source versions must be plainly marked as such, and must not be
21# misrepresented as being the original software.
22# 3. This notice may not be removed or altered from any source distribution.
23
Georg Brandl33cece02008-05-20 06:58:21 +000024
25"""
26Turtle graphics is a popular way for introducing programming to
27kids. It was part of the original Logo programming language developed
Martin v. Löwis87184592008-06-04 06:29:55 +000028by Wally Feurzig and Seymour Papert in 1966.
Georg Brandl33cece02008-05-20 06:58:21 +000029
30Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it
31the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
32the direction it is facing, drawing a line as it moves. Give it the
33command turtle.left(25), and it rotates in-place 25 degrees clockwise.
34
35By combining together these and similar commands, intricate shapes and
36pictures can easily be drawn.
Martin v. Löwis87184592008-06-04 06:29:55 +000037
38----- turtle.py
39
40This module is an extended reimplementation of turtle.py from the
Mark Dickinson2752e9b2009-02-20 20:42:53 +000041Python standard distribution up to Python 2.5. (See: http://www.python.org)
Martin v. Löwis87184592008-06-04 06:29:55 +000042
43It tries to keep the merits of turtle.py and to be (nearly) 100%
44compatible with it. This means in the first place to enable the
45learning programmer to use all the commands, classes and methods
46interactively when using the module from within IDLE run with
47the -n switch.
48
49Roughly it has the following features added:
50
51- Better animation of the turtle movements, especially of turning the
52 turtle. So the turtles can more easily be used as a visual feedback
53 instrument by the (beginning) programmer.
54
55- Different turtle shapes, gif-images as turtle shapes, user defined
56 and user controllable turtle shapes, among them compound
Mark Dickinson2752e9b2009-02-20 20:42:53 +000057 (multicolored) shapes. Turtle shapes can be stretched and tilted, which
58 makes turtles very versatile geometrical objects.
Martin v. Löwis87184592008-06-04 06:29:55 +000059
60- Fine control over turtle movement and screen updates via delay(),
61 and enhanced tracer() and speed() methods.
62
63- Aliases for the most commonly used commands, like fd for forward etc.,
64 following the early Logo traditions. This reduces the boring work of
65 typing long sequences of commands, which often occur in a natural way
66 when kids try to program fancy pictures on their first encounter with
Mark Dickinson2752e9b2009-02-20 20:42:53 +000067 turtle graphics.
Martin v. Löwis87184592008-06-04 06:29:55 +000068
69- Turtles now have an undo()-method with configurable undo-buffer.
70
71- Some simple commands/methods for creating event driven programs
72 (mouse-, key-, timer-events). Especially useful for programming games.
73
74- A scrollable Canvas class. The default scrollable Canvas can be
75 extended interactively as needed while playing around with the turtle(s).
76
77- A TurtleScreen class with methods controlling background color or
78 background image, window and canvas size and other properties of the
79 TurtleScreen.
80
81- There is a method, setworldcoordinates(), to install a user defined
82 coordinate-system for the TurtleScreen.
83
84- The implementation uses a 2-vector class named Vec2D, derived from tuple.
85 This class is public, so it can be imported by the application programmer,
86 which makes certain types of computations very natural and compact.
87
88- Appearance of the TurtleScreen and the Turtles at startup/import can be
89 configured by means of a turtle.cfg configuration file.
90 The default configuration mimics the appearance of the old turtle module.
91
92- If configured appropriately the module reads in docstrings from a docstring
93 dictionary in some different language, supplied separately and replaces
Mark Dickinson2752e9b2009-02-20 20:42:53 +000094 the English ones by those read in. There is a utility function
95 write_docstringdict() to write a dictionary with the original (English)
Martin v. Löwis87184592008-06-04 06:29:55 +000096 docstrings to disc, so it can serve as a template for translations.
97
98Behind the scenes there are some features included with possible
Mark Dickinson2752e9b2009-02-20 20:42:53 +000099extensions in in mind. These will be commented and documented elsewhere.
Martin v. Löwis87184592008-06-04 06:29:55 +0000100
Georg Brandl33cece02008-05-20 06:58:21 +0000101"""
102
Martin v. Löwis87184592008-06-04 06:29:55 +0000103_ver = "turtle 1.0b1 - for Python 2.6 - 30. 5. 2008, 18:08"
Georg Brandl33cece02008-05-20 06:58:21 +0000104
Martin v. Löwis87184592008-06-04 06:29:55 +0000105#print _ver
Georg Brandl33cece02008-05-20 06:58:21 +0000106
Martin v. Löwis87184592008-06-04 06:29:55 +0000107import Tkinter as TK
108import types
109import math
110import time
111import os
Georg Brandl33cece02008-05-20 06:58:21 +0000112
Martin v. Löwis87184592008-06-04 06:29:55 +0000113from os.path import isfile, split, join
114from copy import deepcopy
Georg Brandl33cece02008-05-20 06:58:21 +0000115
Martin v. Löwis87184592008-06-04 06:29:55 +0000116from math import * ## for compatibility with old turtle module
Georg Brandl33cece02008-05-20 06:58:21 +0000117
Martin v. Löwis87184592008-06-04 06:29:55 +0000118_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']
Georg Brandl33cece02008-05-20 06:58:21 +0000143
Martin v. Löwis87184592008-06-04 06:29:55 +0000144__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
145 _tg_utilities + _math_functions)
Georg Brandl33cece02008-05-20 06:58:21 +0000146
Martin v. Löwis87184592008-06-04 06:29:55 +0000147_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
148 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
149 'turtlesize', 'up', 'width']
Georg Brandl33cece02008-05-20 06:58:21 +0000150
Martin v. Löwis87184592008-06-04 06:29:55 +0000151_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
171 }
Georg Brandl33cece02008-05-20 06:58:21 +0000172
Martin v. Löwis87184592008-06-04 06:29:55 +0000173##print "cwd:", os.getcwd()
174##print "__file__:", __file__
175##
176##def show(dictionary):
177## print "=========================="
178## for key in sorted(dictionary.keys()):
179## print key, ":", dictionary[key]
180## print "=========================="
181## print
Georg Brandl33cece02008-05-20 06:58:21 +0000182
Martin v. Löwis87184592008-06-04 06:29:55 +0000183def 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)
Georg Brandl33cece02008-05-20 06:58:21 +0000202 else:
203 try:
Martin v. Löwis87184592008-06-04 06:29:55 +0000204 if "." in value:
205 value = float(value)
206 else:
207 value = int(value)
Georg Brandl33cece02008-05-20 06:58:21 +0000208 except:
Martin v. Löwis87184592008-06-04 06:29:55 +0000209 pass # value need not be converted
210 cfgdict[key] = value
211 return cfgdict
Georg Brandl33cece02008-05-20 06:58:21 +0000212
Martin v. Löwis87184592008-06-04 06:29:55 +0000213def readconfig(cfgdict):
214 """Read config-files, change configuration-dict accordingly.
Georg Brandl33cece02008-05-20 06:58:21 +0000215
Martin v. Löwis87184592008-06-04 06:29:55 +0000216 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.
225 """
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)
Georg Brandl33cece02008-05-20 06:58:21 +0000249
Martin v. Löwis87184592008-06-04 06:29:55 +0000250try:
251 readconfig(_CFG)
252except:
253 print "No configfile read, reason unknown"
254
255
256class 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!
261
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
269 """
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
289 """
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
298
299
300##############################################################################
301### From here up to line : Tkinter - Interface for turtle.py ###
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000302### May be replaced by an interface to some different graphics toolkit ###
Martin v. Löwis87184592008-06-04 06:29:55 +0000303##############################################################################
304
305## helper functions for Scrolled Canvas, to forward Canvas-methods
306## to ScrolledCanvas class
307
308def __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
317
318def __methods(cls):
319 """helper function for Scrolled Canvas"""
320 _dict = {}
321 __methodDict(cls, _dict)
322 return _dict.keys()
323
324__stringBody = (
325 'def %(method)s(self, *args, **kw): return ' +
326 'self.%(attribute)s.%(method)s(*args, **kw)')
327
328def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
329 """Helper functions for Scrolled Canvas, used to forward
330 ScrolledCanvas-methods to Tkinter.Canvas class.
331 """
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:
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000338 if ex in _dict:
Martin v. Löwis87184592008-06-04 06:29:55 +0000339 del _dict[ex]
340 for ex in __methods(fromClass):
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000341 if ex in _dict:
Martin v. Löwis87184592008-06-04 06:29:55 +0000342 del _dict[ex]
343
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]
351
352
353class ScrolledCanvas(TK.Frame):
354 """Modeled after the scrolled canvas class from Grayons's Tkinter book.
355
356 Used as the default canvas, which pops up automatically when
357 using turtle graphics functions or the Turtle class.
358 """
359 def __init__(self, master, width=500, height=350,
360 canvwidth=600, canvheight=500):
361 TK.Frame.__init__(self, master, width=width, height=height)
Martin v. Löwisbb9b1f12008-11-19 09:09:41 +0000362 self._rootwindow = self.winfo_toplevel()
Martin v. Löwis87184592008-06-04 06:29:55 +0000363 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()
Martin v. Löwisbb9b1f12008-11-19 09:09:41 +0000382 self._rootwindow.bind('<Configure>', self.onResize)
Martin v. Löwis87184592008-06-04 06:29:55 +0000383
384 def reset(self, canvwidth=None, canvheight=None, bg = None):
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000385 """Adjust canvas and scrollbars according to given canvas size."""
Martin v. Löwis87184592008-06-04 06:29:55 +0000386 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()
400
401
402 def adjustScrolls(self):
403 """ Adjust scrollbars according to window- and canvas-size.
404 """
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()
417
418 def onResize(self, event):
419 """self-explanatory"""
420 self.adjustScrolls()
421
422 def bbox(self, *args):
423 """ 'forward' method, which canvas itself has inherited...
424 """
425 return self._canvas.bbox(*args)
426
427 def cget(self, *args, **kwargs):
428 """ 'forward' method, which canvas itself has inherited...
429 """
430 return self._canvas.cget(*args, **kwargs)
431
432 def config(self, *args, **kwargs):
433 """ 'forward' method, which canvas itself has inherited...
434 """
435 self._canvas.config(*args, **kwargs)
436
437 def bind(self, *args, **kwargs):
438 """ 'forward' method, which canvas itself has inherited...
439 """
440 self._canvas.bind(*args, **kwargs)
441
442 def unbind(self, *args, **kwargs):
443 """ 'forward' method, which canvas itself has inherited...
444 """
445 self._canvas.unbind(*args, **kwargs)
446
447 def focus_force(self):
448 """ 'forward' method, which canvas itself has inherited...
449 """
450 self._canvas.focus_force()
451
452__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
453
454
455class _Root(TK.Tk):
456 """Root class for Screen based on Tkinter."""
457 def __init__(self):
458 TK.Tk.__init__(self)
459
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")
463
464 def _getcanvas(self):
465 return self._canvas
466
467 def set_geometry(self, width, height, startx, starty):
468 self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
469
470 def ondestroy(self, destroy):
471 self.wm_protocol("WM_DELETE_WINDOW", destroy)
472
473 def win_width(self):
474 return self.winfo_screenwidth()
475
476 def win_height(self):
477 return self.winfo_screenheight()
478
479Canvas = TK.Canvas
480
481
482class TurtleScreenBase(object):
483 """Provide the basic graphics functionality.
484 Interface between Tkinter and turtle.py.
485
486 To port turtle.py to some different graphics toolkit
487 a corresponding TurtleScreenBase class has to be implemented.
488 """
489
490 @staticmethod
491 def _blankimage():
492 """return a blank image object
493 """
494 img = TK.PhotoImage(width=1, height=1)
495 img.blank()
496 return img
497
498 @staticmethod
499 def _image(filename):
500 """return an image object containing the
501 imagedata from a gif-file named filename.
502 """
503 return TK.PhotoImage(file=filename)
504
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
517
518 def _createpoly(self):
519 """Create an invisible polygon item on canvas self.cv)
520 """
521 return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
522
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.
533 """
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)
547
548 def _createline(self):
549 """Create an invisible line item on canvas self.cv)
550 """
551 return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
552 capstyle = TK.ROUND)
553
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.
563 """
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)
576
577 def _delete(self, item):
578 """Delete graphics item from canvas.
579 If item is"all" delete all graphics items.
580 """
581 self.cv.delete(item)
582
583 def _update(self):
584 """Redraw graphics items on canvas
585 """
586 self.cv.update()
587
588 def _delay(self, delay):
589 """Delay subsequent canvas actions for delay ms."""
590 self.cv.after(delay)
591
592 def _iscolorstring(self, color):
593 """Check if the string color is a legal Tkinter color string.
594 """
595 try:
596 rgb = self.cv.winfo_rgb(color)
597 ok = True
598 except TK.TclError:
599 ok = False
600 return ok
601
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")
610
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
625
626## def _dot(self, pos, size, color):
627## """may be implemented for some other graphics toolkit"""
628
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
634 """
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)
643
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
649
650 If a turtle is clicked, first _onclick-event will be performed,
651 then _onscreensclick-event.
652 """
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)
662
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
668
669 Every sequence of mouse-move-events on a turtle is preceded by a
670 mouse-click event on that turtle.
671 """
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)
683
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
689
690 If a turtle is clicked, first _onclick-event will be performed,
691 then _onscreensclick-event.
692 """
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)
701
702 def _onkey(self, fun, key):
703 """Bind fun to key-release event of key.
704 Canvas must have focus. See method listen
705 """
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)
712
713 def _listen(self):
714 """Set focus on canvas (in order to collect key-events)
715 """
716 self.cv.focus_force()
717
718 def _ontimer(self, fun, t):
719 """Install a timer, which calls fun after t milliseconds.
720 """
721 if t == 0:
722 self.cv.after_idle(fun)
723 else:
724 self.cv.after(t, fun)
725
726 def _createimage(self, image):
727 """Create and return image item on canvas.
728 """
729 return self.cv.create_image(0, 0, image=image)
730
731 def _drawimage(self, item, (x, y), image):
732 """Configure image item as to draw image object
733 at position (x,y) on canvas)
734 """
Benjamin Peterson52d52f12008-09-24 22:11:59 +0000735 self.cv.coords(item, (x * self.xscale, -y * self.yscale))
Martin v. Löwis87184592008-06-04 06:29:55 +0000736 self.cv.itemconfig(item, image=image)
737
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)
745
746 def _type(self, item):
747 """Return 'line' or 'polygon' or 'image' depending on
748 type of item.
749 """
750 return self.cv.type(item)
751
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
763
764 def _setscrollregion(self, srx1, sry1, srx2, sry2):
765 self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
766
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)
778
779 def _resize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000780 """Resize the canvas the turtles are drawing on. Does
Martin v. Löwis87184592008-06-04 06:29:55 +0000781 not alter the drawing window.
782 """
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)
793
794 def _window_size(self):
795 """ Return the width and height of the turtle window.
796 """
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
804
805
806##############################################################################
807### End of Tkinter - interface ###
808##############################################################################
809
810
811class Terminator (Exception):
812 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
813
814 Thus stops execution of turtle graphics script. Main purpose: use in
815 in the Demo-Viewer turtle.Demo.py.
816 """
817 pass
818
819
820class TurtleGraphicsError(Exception):
821 """Some TurtleGraphics Error
822 """
823
824
825class Shape(object):
826 """Data structure modeling shapes.
827
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.
831 """
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
847
848 def addcomponent(self, poly, fill, outline=None):
849 """Add component to a shape of type compound.
850
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.
854
855 call (for a Shapeobject namend s):
856 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Georg Brandl33cece02008-05-20 06:58:21 +0000857
858 Example:
Martin v. Löwis87184592008-06-04 06:29:55 +0000859 >>> 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()
Georg Brandl33cece02008-05-20 06:58:21 +0000863 """
Martin v. Löwis87184592008-06-04 06:29:55 +0000864 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])
Georg Brandl33cece02008-05-20 06:58:21 +0000870
Georg Brandl33cece02008-05-20 06:58:21 +0000871
Martin v. Löwis87184592008-06-04 06:29:55 +0000872class 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)
907
908
909
910class TurtleScreen(TurtleScreenBase):
911 """Provides screen oriented methods like setbg etc.
912
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.
916 """
917# _STANDARD_DELAY = 5
918 _RUNNING = True
919
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())
941 }
942
943 self._bgpics = {"nopic" : ""}
944
945 TurtleScreenBase.__init__(self, cv)
946 self._mode = mode
947 self._delayvalue = delay
948 self._colormode = _CFG["colormode"]
949 self._keys = []
950 self.clear()
951
952 def clear(self):
953 """Delete all drawings and all turtles from the TurtleScreen.
954
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000955 Reset empty TurtleScreen to its initial state: white background,
Martin v. Löwis87184592008-06-04 06:29:55 +0000956 no backgroundimage, no eventbindings and tracing on.
957
958 No argument.
959
960 Example (for a TurtleScreen instance named screen):
961 screen.clear()
962
963 Note: this method is not available as function.
Georg Brandl33cece02008-05-20 06:58:21 +0000964 """
Martin v. Löwis87184592008-06-04 06:29:55 +0000965 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
Georg Brandl33cece02008-05-20 06:58:21 +0000979
Martin v. Löwis87184592008-06-04 06:29:55 +0000980 def mode(self, mode=None):
981 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Georg Brandl33cece02008-05-20 06:58:21 +0000982
Martin v. Löwis87184592008-06-04 06:29:55 +0000983 Optional argument:
984 mode -- on of the strings 'standard', 'logo' or 'world'
985
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.
991
992 Mode Initial turtle heading positive angles
993 ------------|-------------------------|-------------------
994 'standard' to the right (east) counterclockwise
995 'logo' upward (north) clockwise
996
997 Examples:
998 >>> mode('logo') # resets turtle heading to north
999 >>> mode()
1000 'logo'
Georg Brandl33cece02008-05-20 06:58:21 +00001001 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001002 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()
Georg Brandl33cece02008-05-20 06:58:21 +00001013
Martin v. Löwis87184592008-06-04 06:29:55 +00001014 def setworldcoordinates(self, llx, lly, urx, ury):
1015 """Set up a user defined coordinate-system.
Georg Brandl33cece02008-05-20 06:58:21 +00001016
Martin v. Löwis87184592008-06-04 06:29:55 +00001017 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
1022
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.
1026
1027 But ATTENTION: in user-defined coordinatesystems angles may appear
1028 distorted. (see Screen.mode())
1029
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)
Georg Brandl33cece02008-05-20 06:58:21 +00001035 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001036 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()
Georg Brandl33cece02008-05-20 06:58:21 +00001052
Martin v. Löwis87184592008-06-04 06:29:55 +00001053 def register_shape(self, name, shape=None):
1054 """Adds a turtle shape to TurtleScreen's shapelist.
Georg Brandl33cece02008-05-20 06:58:21 +00001055
Martin v. Löwis87184592008-06-04 06:29:55 +00001056 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).
Georg Brandl33cece02008-05-20 06:58:21 +00001068
Martin v. Löwis87184592008-06-04 06:29:55 +00001069 call: register_shape("turtle.gif")
1070 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1071
1072 Example (for a TurtleScreen instance named screen):
1073 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1074
Georg Brandl33cece02008-05-20 06:58:21 +00001075 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001076 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
Georg Brandl33cece02008-05-20 06:58:21 +00001088
Martin v. Löwis87184592008-06-04 06:29:55 +00001089 def _colorstr(self, color):
1090 """Return color string corresponding to args.
Georg Brandl33cece02008-05-20 06:58:21 +00001091
Martin v. Löwis87184592008-06-04 06:29:55 +00001092 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.
1095
1096 If the argument doesn't represent a color,
1097 an error is raised.
Georg Brandl33cece02008-05-20 06:58:21 +00001098 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001099 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)
Georg Brandl33cece02008-05-20 06:58:21 +00001115
Martin v. Löwis87184592008-06-04 06:29:55 +00001116 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])
Georg Brandl33cece02008-05-20 06:58:21 +00001126
Martin v. Löwis87184592008-06-04 06:29:55 +00001127 def colormode(self, cmode=None):
1128 """Return the colormode or set it to 1.0 or 255.
Georg Brandl33cece02008-05-20 06:58:21 +00001129
Martin v. Löwis87184592008-06-04 06:29:55 +00001130 Optional argument:
1131 cmode -- one of the values 1.0 or 255
Georg Brandl33cece02008-05-20 06:58:21 +00001132
Martin v. Löwis87184592008-06-04 06:29:55 +00001133 r, g, b values of colortriples have to be in range 0..cmode.
1134
1135 Example (for a TurtleScreen instance named screen):
1136 >>> screen.colormode()
1137 1.0
1138 >>> screen.colormode(255)
1139 >>> turtle.pencolor(240,160,80)
Georg Brandl33cece02008-05-20 06:58:21 +00001140 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001141 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)
1147
1148 def reset(self):
1149 """Reset all Turtles on the Screen to their initial state.
1150
1151 No argument.
1152
1153 Example (for a TurtleScreen instance named screen):
1154 >>> screen.reset()
1155 """
1156 for turtle in self._turtles:
1157 turtle._setmode(self._mode)
1158 turtle.reset()
1159
1160 def turtles(self):
1161 """Return the list of turtles on the screen.
1162
1163 Example (for a TurtleScreen instance named screen):
1164 >>> screen.turtles()
1165 [<turtle.Turtle object at 0x00E11FB0>]
1166 """
1167 return self._turtles
1168
1169 def bgcolor(self, *args):
1170 """Set or return backgroundcolor of the TurtleScreen.
1171
1172 Arguments (if given): a color string or three numbers
1173 in the range 0..colormode or a 3-tuple of such numbers.
1174
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'
1182 """
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
1191
1192 def tracer(self, n=None, delay=None):
1193 """Turns turtle animation on/off and set delay for update drawings.
1194
1195 Optional arguments:
1196 n -- nonnegative integer
1197 delay -- nonnegative integer
1198
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())
1202
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
1210 """
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()
1219
1220 def delay(self, delay=None):
1221 """ Return or set the drawing delay in milliseconds.
1222
1223 Optional argument:
1224 delay -- positive integer
1225
1226 Example (for a TurtleScreen instance named screen):
1227 >>> screen.delay(15)
1228 >>> screen.delay()
1229 15
1230 """
1231 if delay is None:
1232 return self._delayvalue
1233 self._delayvalue = int(delay)
1234
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
1243
1244 def update(self):
1245 """Perform a TurtleScreen update.
1246 """
R. David Murrayddfb6cd2009-09-28 18:29:28 +00001247 tracing = self._tracing
1248 self._tracing = True
Martin v. Löwis87184592008-06-04 06:29:55 +00001249 for t in self.turtles():
1250 t._update_data()
1251 t._drawturtle()
R. David Murrayddfb6cd2009-09-28 18:29:28 +00001252 self._tracing = tracing
Martin v. Löwis87184592008-06-04 06:29:55 +00001253 self._update()
Georg Brandl33cece02008-05-20 06:58:21 +00001254
1255 def window_width(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00001256 """ Return the width of the turtle window.
Georg Brandl33cece02008-05-20 06:58:21 +00001257
Martin v. Löwis87184592008-06-04 06:29:55 +00001258 Example (for a TurtleScreen instance named screen):
1259 >>> screen.window_width()
Georg Brandl33cece02008-05-20 06:58:21 +00001260 640
1261 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001262 return self._window_size()[0]
Georg Brandl33cece02008-05-20 06:58:21 +00001263
1264 def window_height(self):
1265 """ Return the height of the turtle window.
1266
Martin v. Löwis87184592008-06-04 06:29:55 +00001267 Example (for a TurtleScreen instance named screen):
1268 >>> screen.window_height()
1269 480
Georg Brandl33cece02008-05-20 06:58:21 +00001270 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001271 return self._window_size()[1]
Georg Brandl33cece02008-05-20 06:58:21 +00001272
Martin v. Löwis87184592008-06-04 06:29:55 +00001273 def getcanvas(self):
1274 """Return the Canvas of this TurtleScreen.
Georg Brandl33cece02008-05-20 06:58:21 +00001275
Martin v. Löwis87184592008-06-04 06:29:55 +00001276 No argument.
1277
1278 Example (for a Screen instance named screen):
1279 >>> cv = screen.getcanvas()
1280 >>> cv
1281 <turtle.ScrolledCanvas instance at 0x010742D8>
Georg Brandl33cece02008-05-20 06:58:21 +00001282 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001283 return self.cv
Georg Brandl33cece02008-05-20 06:58:21 +00001284
Martin v. Löwis87184592008-06-04 06:29:55 +00001285 def getshapes(self):
1286 """Return a list of names of all currently available turtle shapes.
Georg Brandl33cece02008-05-20 06:58:21 +00001287
Martin v. Löwis87184592008-06-04 06:29:55 +00001288 No argument.
1289
1290 Example (for a TurtleScreen instance named screen):
1291 >>> screen.getshapes()
1292 ['arrow', 'blank', 'circle', ... , 'turtle']
1293 """
1294 return sorted(self._shapes.keys())
1295
1296 def onclick(self, fun, btn=1, add=None):
1297 """Bind fun to mouse-click event on canvas.
1298
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
1303
1304 Example (for a TurtleScreen instance named screen
1305 and a Turtle instance named turtle):
1306
1307 >>> screen.onclick(turtle.goto)
1308
1309 ### Subsequently clicking into the TurtleScreen will
1310 ### make the turtle move to the clicked point.
1311 >>> screen.onclick(None)
1312
1313 ### event-binding will be removed
1314 """
1315 self._onscreenclick(fun, btn, add)
1316
1317 def onkey(self, fun, key):
1318 """Bind fun to key-release event of key.
1319
1320 Arguments:
1321 fun -- a function with no arguments
1322 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1323
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001324 In order to be able to register key-events, TurtleScreen
Martin v. Löwis87184592008-06-04 06:29:55 +00001325 must have focus. (See method listen.)
1326
1327 Example (for a TurtleScreen instance named screen
1328 and a Turtle instance named turtle):
1329
1330 >>> def f():
1331 fd(50)
1332 lt(60)
1333
1334
1335 >>> screen.onkey(f, "Up")
1336 >>> screen.listen()
1337
1338 ### Subsequently the turtle can be moved by
1339 ### repeatedly pressing the up-arrow key,
1340 ### consequently drawing a hexagon
1341 """
1342 if fun == None:
R. David Murrayddfb6cd2009-09-28 18:29:28 +00001343 if key in self._keys:
1344 self._keys.remove(key)
Martin v. Löwis87184592008-06-04 06:29:55 +00001345 elif key not in self._keys:
1346 self._keys.append(key)
1347 self._onkey(fun, key)
1348
1349 def listen(self, xdummy=None, ydummy=None):
1350 """Set focus on TurtleScreen (in order to collect key-events)
1351
1352 No arguments.
1353 Dummy arguments are provided in order
1354 to be able to pass listen to the onclick method.
1355
1356 Example (for a TurtleScreen instance named screen):
1357 >>> screen.listen()
1358 """
1359 self._listen()
1360
1361 def ontimer(self, fun, t=0):
1362 """Install a timer, which calls fun after t milliseconds.
1363
1364 Arguments:
1365 fun -- a function with no arguments.
1366 t -- a number >= 0
1367
1368 Example (for a TurtleScreen instance named screen):
1369
1370 >>> running = True
1371 >>> def f():
1372 if running:
1373 fd(50)
1374 lt(60)
1375 screen.ontimer(f, 250)
1376
1377 >>> f() ### makes the turtle marching around
1378 >>> running = False
1379 """
1380 self._ontimer(fun, t)
1381
1382 def bgpic(self, picname=None):
1383 """Set background image or return name of current backgroundimage.
1384
1385 Optional argument:
1386 picname -- a string, name of a gif-file or "nopic".
1387
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.
1391
1392 Example (for a TurtleScreen instance named screen):
1393 >>> screen.bgpic()
1394 'nopic'
1395 >>> screen.bgpic("landscape.gif")
1396 >>> screen.bgpic()
1397 'landscape.gif'
1398 """
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
1405
1406 def screensize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001407 """Resize the canvas the turtles are drawing on.
Martin v. Löwis87184592008-06-04 06:29:55 +00001408
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)
1414
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!)
1418
1419 Example (for a Turtle instance named turtle):
1420 >>> turtle.screensize(2000,1500)
1421 ### e. g. to search for an erroneously escaped turtle ;-)
1422 """
1423 return self._resize(canvwidth, canvheight, bg)
1424
1425 onscreenclick = onclick
1426 resetscreen = reset
1427 clearscreen = clear
1428 addshape = register_shape
1429
1430class TNavigator(object):
1431 """Navigation part of the RawTurtle.
1432 Implements methods for turtle movement.
1433 """
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
1441
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)
1451
1452 def reset(self):
1453 """reset turtle to its initial values
1454
1455 Will be overwritten by parent class
1456 """
1457 self._position = Vec2D(0.0, 0.0)
1458 self._orient = TNavigator.START_ORIENTATION[self._mode]
1459
1460 def _setmode(self, mode=None):
1461 """Set turtle-mode to 'standard', 'world' or 'logo'.
1462 """
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
1474
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.
1483
1484 def degrees(self, fullcircle=360.0):
1485 """ Set angle measurement units to degrees.
1486
1487 Optional argument:
1488 fullcircle - a number
1489
1490 Set angle measurement units, i. e. set number
1491 of 'degrees' for a full circle. Dafault value is
1492 360 degrees.
1493
1494 Example (for a Turtle instance named turtle):
1495 >>> turtle.left(90)
1496 >>> turtle.heading()
1497 90
1498 >>> turtle.degrees(400.0) # angle measurement in gon
1499 >>> turtle.heading()
1500 100
1501
1502 """
1503 self._setDegreesPerAU(fullcircle)
1504
1505 def radians(self):
1506 """ Set the angle measurement units to radians.
1507
1508 No arguments.
1509
1510 Example (for a Turtle instance named turtle):
1511 >>> turtle.heading()
1512 90
1513 >>> turtle.radians()
1514 >>> turtle.heading()
1515 1.5707963267948966
1516 """
1517 self._setDegreesPerAU(2*math.pi)
1518
1519 def _go(self, distance):
1520 """move turtle forward by specified distance"""
1521 ende = self._position + self._orient * distance
1522 self._goto(ende)
1523
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)
1528
1529 def _goto(self, end):
1530 """move turtle to position end."""
1531 self._position = end
1532
1533 def forward(self, distance):
1534 """Move the turtle forward by the specified distance.
1535
1536 Aliases: forward | fd
1537
1538 Argument:
1539 distance -- a number (integer or float)
1540
1541 Move the turtle forward by the specified distance, in the direction
1542 the turtle is headed.
1543
1544 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001545 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001546 (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)
1553 """
1554 self._go(distance)
1555
1556 def back(self, distance):
1557 """Move the turtle backward by distance.
1558
1559 Aliases: back | backward | bk
1560
1561 Argument:
1562 distance -- a number
1563
1564 Move the turtle backward by distance ,opposite to the direction the
1565 turtle is headed. Do not change the turtle's heading.
1566
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)
1573 """
1574 self._go(-distance)
1575
1576 def right(self, angle):
1577 """Turn turtle right by angle units.
1578
1579 Aliases: right | rt
1580
1581 Argument:
1582 angle -- a number (integer or float)
1583
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.)
1587
1588 Example (for a Turtle instance named turtle):
1589 >>> turtle.heading()
1590 22.0
1591 >>> turtle.right(45)
1592 >>> turtle.heading()
1593 337.0
1594 """
1595 self._rotate(-angle)
1596
1597 def left(self, angle):
1598 """Turn turtle left by angle units.
1599
1600 Aliases: left | lt
1601
1602 Argument:
1603 angle -- a number (integer or float)
1604
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.)
1608
1609 Example (for a Turtle instance named turtle):
1610 >>> turtle.heading()
1611 22.0
1612 >>> turtle.left(45)
1613 >>> turtle.heading()
1614 67.0
1615 """
1616 self._rotate(angle)
1617
1618 def pos(self):
1619 """Return the turtle's current location (x,y), as a Vec2D-vector.
1620
1621 Aliases: pos | position
1622
1623 No arguments.
1624
1625 Example (for a Turtle instance named turtle):
1626 >>> turtle.pos()
1627 (0.00, 240.00)
1628 """
1629 return self._position
1630
1631 def xcor(self):
1632 """ Return the turtle's x coordinate.
1633
1634 No arguments.
1635
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
1642 """
1643 return self._position[0]
1644
1645 def ycor(self):
1646 """ Return the turtle's y coordinate
1647 ---
1648 No arguments.
1649
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
1656 """
1657 return self._position[1]
1658
1659
1660 def goto(self, x, y=None):
1661 """Move turtle to an absolute position.
1662
1663 Aliases: setpos | setposition | goto:
1664
1665 Arguments:
1666 x -- a number or a pair/vector of numbers
1667 y -- a number None
1668
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()
1672
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.
1675
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)
1689 """
1690 if y is None:
1691 self._goto(Vec2D(*x))
1692 else:
1693 self._goto(Vec2D(x, y))
1694
1695 def home(self):
1696 """Move turtle to the origin - coordinates (0,0).
1697
1698 No arguments.
1699
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001700 Move turtle to the origin - coordinates (0,0) and set its
1701 heading to its start-orientation (which depends on mode).
Martin v. Löwis87184592008-06-04 06:29:55 +00001702
1703 Example (for a Turtle instance named turtle):
1704 >>> turtle.home()
1705 """
1706 self.goto(0, 0)
1707 self.setheading(0)
1708
1709 def setx(self, x):
1710 """Set the turtle's first coordinate to x
1711
1712 Argument:
1713 x -- a number (integer or float)
1714
1715 Set the turtle's first coordinate to x, leave second coordinate
1716 unchanged.
1717
1718 Example (for a Turtle instance named turtle):
1719 >>> turtle.position()
1720 (0.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001721 >>> turtle.setx(10)
1722 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001723 (10.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001724 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001725 self._goto(Vec2D(x, self._position[1]))
Georg Brandl33cece02008-05-20 06:58:21 +00001726
Martin v. Löwis87184592008-06-04 06:29:55 +00001727 def sety(self, y):
1728 """Set the turtle's second coordinate to y
Georg Brandl33cece02008-05-20 06:58:21 +00001729
Martin v. Löwis87184592008-06-04 06:29:55 +00001730 Argument:
1731 y -- a number (integer or float)
1732
1733 Set the turtle's first coordinate to x, second coordinate remains
1734 unchanged.
1735
1736 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001737 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001738 (0.00, 40.00)
1739 >>> turtle.sety(-10)
Georg Brandl33cece02008-05-20 06:58:21 +00001740 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001741 (0.00, -10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001742 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001743 self._goto(Vec2D(self._position[0], y))
Georg Brandl33cece02008-05-20 06:58:21 +00001744
Martin v. Löwis87184592008-06-04 06:29:55 +00001745 def distance(self, x, y=None):
1746 """Return the distance from the turtle to (x,y) in turtle step units.
Georg Brandl33cece02008-05-20 06:58:21 +00001747
Martin v. Löwis87184592008-06-04 06:29:55 +00001748 Arguments:
1749 x -- a number or a pair/vector of numbers or a turtle instance
1750 y -- a number None None
Georg Brandl33cece02008-05-20 06:58:21 +00001751
Martin v. Löwis87184592008-06-04 06:29:55 +00001752 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
1756
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
1766 """
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)
1776
1777 def towards(self, x, y=None):
1778 """Return the angle of the line from the turtle's position to (x, y).
1779
1780 Arguments:
1781 x -- a number or a pair/vector of numbers or a turtle instance
1782 y -- a number None None
1783
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
1788
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")
1792
1793 Example (for a Turtle instance named turtle):
1794 >>> turtle.pos()
1795 (10.00, 10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001796 >>> turtle.towards(0,0)
1797 225.0
1798 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001799 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
1811
1812 def heading(self):
1813 """ Return the turtle's current heading.
1814
1815 No arguments.
1816
1817 Example (for a Turtle instance named turtle):
1818 >>> turtle.left(67)
1819 >>> turtle.heading()
1820 67.0
1821 """
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
1826
1827 def setheading(self, to_angle):
1828 """Set the orientation of the turtle to to_angle.
1829
1830 Aliases: setheading | seth
1831
1832 Argument:
1833 to_angle -- a number (integer or float)
1834
1835 Set the orientation of the turtle to to_angle.
1836 Here are some common directions in degrees:
1837
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
1844
1845 Example (for a Turtle instance named turtle):
1846 >>> turtle.setheading(90)
1847 >>> turtle.heading()
1848 90
1849 """
1850 angle = (to_angle - self.heading())*self._angleOrient
1851 full = self._fullcircle
1852 angle = (angle+full/2.)%full - full/2.
1853 self._rotate(angle)
1854
1855 def circle(self, radius, extent = None, steps = None):
1856 """ Draw a circle with given radius.
1857
1858 Arguments:
1859 radius -- a number
1860 extent (optional) -- a number
1861 steps (optional) -- an integer
1862
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.
1870
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.
1875
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
1880
1881 Example (for a Turtle instance named turtle):
1882 >>> turtle.circle(50)
1883 >>> turtle.circle(120, 180) # semicircle
1884 """
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)
Georg Brandl33cece02008-05-20 06:58:21 +00001903 else:
Martin v. Löwis87184592008-06-04 06:29:55 +00001904 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
Georg Brandl33cece02008-05-20 06:58:21 +00001917
Martin v. Löwis87184592008-06-04 06:29:55 +00001918## three dummy methods to be implemented by child class:
Georg Brandl33cece02008-05-20 06:58:21 +00001919
Martin v. Löwis87184592008-06-04 06:29:55 +00001920 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"""
Georg Brandl33cece02008-05-20 06:58:21 +00001926
Martin v. Löwis87184592008-06-04 06:29:55 +00001927 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
Georg Brandl33cece02008-05-20 06:58:21 +00001936
Georg Brandl33cece02008-05-20 06:58:21 +00001937
Martin v. Löwis87184592008-06-04 06:29:55 +00001938class TPen(object):
1939 """Drawing part of the RawTurtle.
1940 Implements drawing properties.
1941 """
1942 def __init__(self, resizemode=_CFG["resizemode"]):
1943 self._resizemode = resizemode # or "user" or "noresize"
1944 self.undobuffer = None
1945 TPen._reset(self)
Georg Brandl33cece02008-05-20 06:58:21 +00001946
Martin v. Löwis87184592008-06-04 06:29:55 +00001947 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
1959
1960 def resizemode(self, rmode=None):
1961 """Set resizemode to one of the values: "auto", "user", "noresize".
1962
1963 (Optional) Argument:
1964 rmode -- one of the strings "auto", "user", "noresize"
1965
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.
1975
1976
1977 Examples (for a Turtle instance named turtle):
1978 >>> turtle.resizemode("noresize")
1979 >>> turtle.resizemode()
1980 'noresize'
Georg Brandl33cece02008-05-20 06:58:21 +00001981 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001982 if rmode is None:
1983 return self._resizemode
1984 rmode = rmode.lower()
1985 if rmode in ["auto", "user", "noresize"]:
1986 self.pen(resizemode=rmode)
Georg Brandl33cece02008-05-20 06:58:21 +00001987
Martin v. Löwis87184592008-06-04 06:29:55 +00001988 def pensize(self, width=None):
1989 """Set or return the line thickness.
Georg Brandl33cece02008-05-20 06:58:21 +00001990
Martin v. Löwis87184592008-06-04 06:29:55 +00001991 Aliases: pensize | width
Georg Brandl33cece02008-05-20 06:58:21 +00001992
Martin v. Löwis87184592008-06-04 06:29:55 +00001993 Argument:
1994 width -- positive number
Georg Brandl33cece02008-05-20 06:58:21 +00001995
Martin v. Löwis87184592008-06-04 06:29:55 +00001996 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.
Georg Brandl33cece02008-05-20 06:58:21 +00002000
Martin v. Löwis87184592008-06-04 06:29:55 +00002001 Example (for a Turtle instance named turtle):
2002 >>> turtle.pensize()
2003 1
2004 turtle.pensize(10) # from here on lines of width 10 are drawn
Georg Brandl33cece02008-05-20 06:58:21 +00002005 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002006 if width is None:
2007 return self._pensize
2008 self.pen(pensize=width)
Georg Brandl33cece02008-05-20 06:58:21 +00002009
2010
Martin v. Löwis87184592008-06-04 06:29:55 +00002011 def penup(self):
2012 """Pull the pen up -- no drawing when moving.
Georg Brandl33cece02008-05-20 06:58:21 +00002013
Martin v. Löwis87184592008-06-04 06:29:55 +00002014 Aliases: penup | pu | up
Georg Brandl33cece02008-05-20 06:58:21 +00002015
Martin v. Löwis87184592008-06-04 06:29:55 +00002016 No argument
2017
2018 Example (for a Turtle instance named turtle):
2019 >>> turtle.penup()
Georg Brandl33cece02008-05-20 06:58:21 +00002020 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002021 if not self._drawing:
Georg Brandl33cece02008-05-20 06:58:21 +00002022 return
Martin v. Löwis87184592008-06-04 06:29:55 +00002023 self.pen(pendown=False)
Georg Brandl33cece02008-05-20 06:58:21 +00002024
Martin v. Löwis87184592008-06-04 06:29:55 +00002025 def pendown(self):
2026 """Pull the pen down -- drawing when moving.
2027
2028 Aliases: pendown | pd | down
2029
2030 No argument.
2031
2032 Example (for a Turtle instance named turtle):
2033 >>> turtle.pendown()
2034 """
2035 if self._drawing:
2036 return
2037 self.pen(pendown=True)
2038
2039 def isdown(self):
2040 """Return True if pen is down, False if it's up.
2041
2042 No argument.
2043
2044 Example (for a Turtle instance named turtle):
2045 >>> turtle.penup()
2046 >>> turtle.isdown()
2047 False
2048 >>> turtle.pendown()
2049 >>> turtle.isdown()
2050 True
2051 """
2052 return self._drawing
2053
2054 def speed(self, speed=None):
2055 """ Return or set the turtle's speed.
2056
2057 Optional argument:
2058 speed -- an integer in the range 0..10 or a speedstring (see below)
2059
2060 Set the turtle's speed to an integer value in the range 0 .. 10.
2061 If no argument is given: return current speed.
2062
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.
2073
2074 Attention:
2075 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2076 and likewise left/right make the turtle turn instantly.
2077
2078 Example (for a Turtle instance named turtle):
2079 >>> turtle.speed(3)
2080 """
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)
2091
2092 def color(self, *args):
2093 """Return or set the pencolor and fillcolor.
2094
2095 Arguments:
2096 Several input formats are allowed.
2097 They use 0, 1, 2, or 3 arguments as follows:
2098
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.
2110
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
2114
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')
2123 """
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)
2137
2138 def pencolor(self, *args):
2139 """ Return or set the pencolor.
2140
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
2156
2157 If turtleshape is a polygon, the outline of that polygon is drawn
2158 with the newly set pencolor.
2159
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'
2166 """
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)
2174
2175 def fillcolor(self, *args):
2176 """ Return or set the fillcolor.
2177
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
2193
2194 If turtleshape is a polygon, the interior of that polygon is drawn
2195 with the newly set fillcolor.
2196
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)
2202 """
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)
2210
2211 def showturtle(self):
2212 """Makes the turtle visible.
2213
2214 Aliases: showturtle | st
2215
2216 No argument.
2217
2218 Example (for a Turtle instance named turtle):
2219 >>> turtle.hideturtle()
2220 >>> turtle.showturtle()
2221 """
2222 self.pen(shown=True)
2223
2224 def hideturtle(self):
2225 """Makes the turtle invisible.
2226
2227 Aliases: hideturtle | ht
2228
2229 No argument.
2230
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.
2234
2235 Example (for a Turtle instance named turtle):
2236 >>> turtle.hideturtle()
2237 """
2238 self.pen(shown=False)
2239
2240 def isvisible(self):
2241 """Return True if the Turtle is shown, False if it's hidden.
2242
2243 No argument.
2244
2245 Example (for a Turtle instance named turtle):
2246 >>> turtle.hideturtle()
2247 >>> print turtle.isvisible():
2248 False
2249 """
2250 return self._shown
2251
2252 def pen(self, pen=None, **pendict):
2253 """Return or set the pen's attributes.
2254
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.
2259
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
2272
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002273 This dictionary can be used as argument for a subsequent
Martin v. Löwis87184592008-06-04 06:29:55 +00002274 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.
Georg Brandl33cece02008-05-20 06:58:21 +00002277
2278
Martin v. Löwis87184592008-06-04 06:29:55 +00002279 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}
2297 """
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
2308 }
Georg Brandl33cece02008-05-20 06:58:21 +00002309
Martin v. Löwis87184592008-06-04 06:29:55 +00002310 if not (pen or pendict):
2311 return _pd
2312
2313 if isinstance(pen, dict):
2314 p = pen
2315 else:
2316 p = {}
2317 p.update(pendict)
2318
2319 _p_buf = {}
2320 for key in p:
2321 _p_buf[key] = _pd[key]
2322
2323 if self.undobuffer:
2324 self.undobuffer.push(("pen", _p_buf))
2325
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()
2366
2367## three dummy methods to be implemented by child class:
2368
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"""
2377
2378 width = pensize
2379 up = penup
2380 pu = penup
2381 pd = pendown
2382 down = pendown
2383 st = showturtle
2384 ht = hideturtle
2385
2386
2387class _TurtleImage(object):
2388 """Helper class: Datatype to store Turtle attributes
2389 """
2390
2391 def __init__(self, screen, shapeIndex):
2392 self.screen = screen
2393 self._type = None
2394 self._setshape(shapeIndex)
2395
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]
2416
2417
2418class RawTurtle(TPen, TNavigator):
2419 """Animation part of the RawTurtle.
2420 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002421 its animation.
Martin v. Löwis87184592008-06-04 06:29:55 +00002422 """
2423 screens = []
2424
2425 def __init__(self, canvas=None,
2426 shape=_CFG["shape"],
2427 undobuffersize=_CFG["undobuffersize"],
2428 visible=_CFG["visible"]):
Martin v. Löwise563aa42008-09-29 22:09:07 +00002429 if isinstance(canvas, _Screen):
Martin v. Löwis87184592008-06-04 06:29:55 +00002430 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)
2445
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()
2464
2465 def reset(self):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002466 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis87184592008-06-04 06:29:55 +00002467
2468 No argument.
2469,
2470 Delete the turtle's drawings from the screen, re-center the turtle
2471 and set variables to the default values.
2472
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()
2482 0.0
2483 """
2484 TNavigator.reset(self)
2485 TPen._reset(self)
2486 self._clear()
2487 self._drawturtle()
2488 self._update()
2489
2490 def setundobuffer(self, size):
2491 """Set or disable undobuffer.
2492
2493 Argument:
2494 size -- an integer or None
2495
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.
2500
2501 Example (for a Turtle instance named turtle):
2502 >>> turtle.setundobuffer(42)
2503 """
2504 if size is None:
2505 self.undobuffer = None
2506 else:
2507 self.undobuffer = Tbuffer(size)
2508
2509 def undobufferentries(self):
2510 """Return count of entries in the undobuffer.
2511
2512 No argument.
2513
2514 Example (for a Turtle instance named turtle):
2515 >>> while undobufferentries():
2516 undo()
2517 """
2518 if self.undobuffer is None:
2519 return 0
2520 return self.undobuffer.nr_of_items()
2521
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)
2534
2535
2536 def clear(self):
2537 """Delete the turtle's drawings from the screen. Do not move turtle.
2538
2539 No arguments.
2540
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.
2544
2545 Examples (for a Turtle instance named turtle):
2546 >>> turtle.clear()
2547 """
2548 self._clear()
2549 self._update()
2550
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)
2558
2559 def _update(self):
2560 """Perform a Turtle-data update.
2561 """
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()
2576
2577 def tracer(self, flag=None, delay=None):
2578 """Turns turtle animation on/off and set delay for update drawings.
2579
2580 Optional arguments:
2581 n -- nonnegative integer
2582 delay -- nonnegative integer
2583
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())
2587
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
2595 """
2596 return self.screen.tracer(flag, delay)
2597
2598 def _color(self, args):
2599 return self.screen._color(args)
2600
2601 def _colorstr(self, args):
2602 return self.screen._colorstr(args)
2603
2604 def _cc(self, args):
2605 """Convert colortriples to hexstrings.
2606 """
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)
2618
2619 def clone(self):
2620 """Create and return a clone of the turtle.
2621
2622 No argument.
2623
2624 Create and return a clone of the turtle with same position, heading
2625 and turtle properties.
2626
2627 Example (for a Turtle instance named mick):
2628 mick = Turtle()
2629 joe = mick.clone()
2630 """
2631 screen = self.screen
2632 self._newLine(self._drawing)
2633
2634 turtle = self.turtle
2635 self.screen = None
2636 self.turtle = None # too make self deepcopy-able
2637
2638 q = deepcopy(self)
2639
2640 self.screen = screen
2641 self.turtle = turtle
2642
2643 q.screen = screen
2644 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2645
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
2658
2659 def shape(self, name=None):
2660 """Set turtle shape to shape with given name / return current shapename.
2661
2662 Optional argument:
2663 name -- a string, which is a valid shapename
2664
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.
2671
2672 Example (for a Turtle instance named turtle):
2673 >>> turtle.shape()
2674 'arrow'
2675 >>> turtle.shape("turtle")
2676 >>> turtle.shape()
2677 'turtle'
2678 """
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()
2685
2686 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2687 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2688
2689 Optinonal arguments:
2690 stretch_wid : positive number
2691 stretch_len : positive number
2692 outline : positive number
2693
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.
2701
2702 Examples (for a Turtle instance named turtle):
2703 >>> turtle.resizemode("user")
2704 >>> turtle.shapesize(5, 5, 12)
2705 >>> turtle.shapesize(outline=8)
2706 """
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)
2723
2724 def settiltangle(self, angle):
2725 """Rotate the turtleshape to point in the specified direction
2726
2727 Optional argument:
2728 angle -- number
2729
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).
2733
2734
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)
2744 """
2745 tilt = -angle * self._degreesPerAU * self._angleOrient
2746 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2747 self.pen(resizemode="user", tilt=tilt)
2748
2749 def tiltangle(self):
2750 """Return the current tilt-angle.
2751
2752 No argument.
2753
2754 Return the current tilt-angle, i. e. the angle between the
2755 orientation of the turtleshape and the heading of the turtle
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002756 (its direction of movement).
Martin v. Löwis87184592008-06-04 06:29:55 +00002757
2758 Examples (for a Turtle instance named turtle):
2759 >>> turtle.shape("circle")
2760 >>> turtle.shapesize(5,2)
2761 >>> turtle.tilt(45)
2762 >>> turtle.tiltangle()
2763 >>>
2764 """
2765 tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2766 return (tilt / self._degreesPerAU) % self._fullcircle
2767
2768 def tilt(self, angle):
2769 """Rotate the turtleshape by angle.
2770
2771 Argument:
2772 angle - a number
2773
2774 Rotate the turtleshape by angle from its current tilt-angle,
2775 but do NOT change the turtle's heading (direction of movement).
2776
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)
2784 """
2785 self.settiltangle(angle + self.tiltangle())
2786
2787 def _polytrafo(self, poly):
2788 """Computes transformed polygon shapes from a shape
2789 according to current position and heading.
2790 """
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]
2798
2799 def _drawturtle(self):
2800 """Manages the correct rendering of the turtle with respect to
Mark Dickinson3e4caeb2009-02-21 20:27:01 +00002801 its shape, resizemode, stretch and tilt etc."""
Martin v. Löwis87184592008-06-04 06:29:55 +00002802 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
2851
2852############################## stamp stuff ###############################
2853
2854 def stamp(self):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002855 """Stamp a copy of the turtleshape onto the canvas and return its id.
Martin v. Löwis87184592008-06-04 06:29:55 +00002856
2857 No argument.
2858
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).
2862
2863 Example (for a Turtle instance named turtle):
2864 >>> turtle.color("blue")
2865 >>> turtle.stamp()
2866 13
2867 >>> turtle.fd(50)
2868 """
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
2913
2914 def _clearstamp(self, stampid):
2915 """does the work for clearstamp() and clearstamps()
2916 """
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])
2935
2936 def clearstamp(self, stampid):
2937 """Delete stamp with given stampid
2938
2939 Argument:
2940 stampid - an integer, must be return value of previous stamp() call.
2941
2942 Example (for a Turtle instance named turtle):
2943 >>> turtle.color("blue")
2944 >>> astamp = turtle.stamp()
2945 >>> turtle.fd(50)
2946 >>> turtle.clearstamp(astamp)
2947 """
2948 self._clearstamp(stampid)
2949 self._update()
2950
2951 def clearstamps(self, n=None):
2952 """Delete all or first/last n of turtle's stamps.
2953
2954 Optional argument:
2955 n -- an integer
2956
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.
2960
2961 Example (for a Turtle instance named turtle):
2962 >>> for i in range(8):
2963 turtle.stamp(); turtle.fd(30)
2964 ...
2965 >>> turtle.clearstamps(2)
2966 >>> turtle.clearstamps(-2)
2967 >>> turtle.clearstamps()
2968 """
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()
2978
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.
2983 """
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[:])
2995 )
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)
3031
3032 def _undogoto(self, entry):
3033 """Reverse a _goto. Used for undo()
3034 """
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
3044
3045 if pl == [(0, 0), (0, 0)]:
3046 usepc = ""
3047 else:
3048 usepc = pc
3049 screen._drawline(cLI, pl, fill=usepc, width=ps)
3050
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)
3056
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)
3096
3097 def _rotate(self, angle):
3098 """Turns pen clockwise by angle.
3099 """
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()
3114
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.
3119 """
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]
3130
3131 def fill(self, flag=None):
3132 """Call fill(True) before drawing a shape to fill, fill(False) when done.
3133
3134 Optional argument:
3135 flag -- True/False (or 1/0 respectively)
3136
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)
3141
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)
3152 """
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()
3181
3182 def begin_fill(self):
3183 """Called just before drawing a shape to be filled.
3184
3185 No argument.
3186
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()
3197 """
3198 self.fill(True)
3199
3200 def end_fill(self):
3201 """Fill the shape drawn after the call begin_fill().
3202
3203 No argument.
3204
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()
3215 """
3216 self.fill(False)
3217
3218 def dot(self, size=None, *color):
3219 """Draw a dot with diameter size, using color.
3220
3221 Optional argumentS:
3222 size -- an integer >= 1 (if given)
3223 color -- a colorstring or a numeric color tuple
3224
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.
3227
3228 Example (for a Turtle instance named turtle):
3229 >>> turtle.dot()
3230 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3231 """
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
3268
3269 def _write(self, txt, align, font):
3270 """Performs the writing for write()
3271 """
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
3278
3279 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3280 """Write text at the current turtle position.
3281
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)
3287
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.
3293
3294 Example (for a Turtle instance named turtle):
3295 >>> turtle.write('Home = ', True, align="center")
3296 >>> turtle.write((0,0), True)
3297 """
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
3307
3308 def begin_poly(self):
3309 """Start recording the vertices of a polygon.
3310
3311 No argument.
3312
3313 Start recording the vertices of a polygon. Current turtle position
3314 is first point of polygon.
3315
3316 Example (for a Turtle instance named turtle):
3317 >>> turtle.begin_poly()
3318 """
3319 self._poly = [self._position]
3320 self._creatingPoly = True
3321
3322 def end_poly(self):
3323 """Stop recording the vertices of a polygon.
3324
3325 No argument.
3326
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.
3329
3330 Example (for a Turtle instance named turtle):
3331 >>> turtle.end_poly()
3332 """
3333 self._creatingPoly = False
3334
3335 def get_poly(self):
3336 """Return the lastly recorded polygon.
3337
3338 No argument.
3339
3340 Example (for a Turtle instance named turtle):
3341 >>> p = turtle.get_poly()
3342 >>> turtle.register_shape("myFavouriteShape", p)
3343 """
3344 ## check if there is any poly? -- 1st solution:
3345 if self._poly is not None:
3346 return tuple(self._poly)
3347
3348 def getscreen(self):
3349 """Return the TurtleScreen object, the turtle is drawing on.
3350
3351 No argument.
3352
3353 Return the TurtleScreen object, the turtle is drawing on.
3354 So TurtleScreen-methods can be called for that object.
3355
3356 Example (for a Turtle instance named turtle):
3357 >>> ts = turtle.getscreen()
3358 >>> ts
3359 <turtle.TurtleScreen object at 0x0106B770>
3360 >>> ts.bgcolor("pink")
3361 """
3362 return self.screen
3363
3364 def getturtle(self):
3365 """Return the Turtleobject itself.
3366
3367 No argument.
3368
3369 Only reasonable use: as a function to return the 'anonymous turtle':
3370
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>]
3378 """
3379 return self
3380
3381 getpen = getturtle
3382
3383
3384 ################################################################
3385 ### screen oriented methods recurring to methods of TurtleScreen
3386 ################################################################
3387
3388 def window_width(self):
3389 """ Returns the width of the turtle window.
3390
3391 No argument.
3392
3393 Example (for a TurtleScreen instance named screen):
3394 >>> screen.window_width()
3395 640
3396 """
3397 return self.screen._window_size()[0]
3398
3399 def window_height(self):
3400 """ Return the height of the turtle window.
3401
3402 No argument.
3403
3404 Example (for a TurtleScreen instance named screen):
3405 >>> screen.window_height()
3406 480
3407 """
3408 return self.screen._window_size()[1]
3409
3410 def _delay(self, delay=None):
3411 """Set delay value which determines speed of turtle animation.
3412 """
3413 return self.screen.delay(delay)
3414
3415 ##### event binding methods #####
3416
3417 def onclick(self, fun, btn=1, add=None):
3418 """Bind fun to mouse-click event on this turtle on canvas.
3419
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.
3426
3427 Example for the anonymous turtle, i. e. the procedural way:
3428
3429 >>> def turn(x, y):
3430 left(360)
3431
3432 >>> onclick(turn) # Now clicking into the turtle will turn it.
3433 >>> onclick(None) # event-binding will be removed
3434 """
3435 self.screen._onclick(self.turtle._item, fun, btn, add)
3436 self._update()
3437
3438 def onrelease(self, fun, btn=1, add=None):
3439 """Bind fun to mouse-button-release event on this turtle on canvas.
3440
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).
3445
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("")
3452
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.
3458 """
3459 self.screen._onrelease(self.turtle._item, fun, btn, add)
3460 self._update()
3461
3462 def ondrag(self, fun, btn=1, add=None):
3463 """Bind fun to mouse-move event on this turtle on canvas.
3464
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).
3469
3470 Every sequence of mouse-move-events on a turtle is preceded by a
3471 mouse-click event on that turtle.
3472
3473 Example (for a Turtle instance named turtle):
3474 >>> turtle.ondrag(turtle.goto)
3475
3476 ### Subsequently clicking and dragging a Turtle will
3477 ### move it across the screen thereby producing handdrawings
3478 ### (if pen is down).
3479 """
3480 self.screen._ondrag(self.turtle._item, fun, btn, add)
3481
3482
3483 def _undo(self, action, data):
3484 """Does the main part of the work for undo()
3485 """
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()
3513
3514 def undo(self):
3515 """undo (repeatedly) the last turtle action.
3516
3517 No argument.
3518
3519 undo (repeatedly) the last turtle action.
3520 Number of available undo actions is determined by the size of
3521 the undobuffer.
3522
3523 Example (for a Turtle instance named turtle):
3524 >>> for i in range(4):
3525 turtle.fd(50); turtle.lt(80)
3526
3527 >>> for i in range(8):
3528 turtle.undo()
3529 """
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)
3541
3542 turtlesize = shapesize
3543
3544RawPen = RawTurtle
3545
Martin v. Löwise563aa42008-09-29 22:09:07 +00003546### Screen - Singleton ########################
Martin v. Löwis87184592008-06-04 06:29:55 +00003547
Martin v. Löwise563aa42008-09-29 22:09:07 +00003548def 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
3555
3556class _Screen(TurtleScreen):
Martin v. Löwis87184592008-06-04 06:29:55 +00003557
3558 _root = None
3559 _canvas = None
3560 _title = _CFG["title"]
3561
Georg Brandl33cece02008-05-20 06:58:21 +00003562 def __init__(self):
Martin v. Löwise563aa42008-09-29 22:09:07 +00003563 # 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)
Martin v. Löwis87184592008-06-04 06:29:55 +00003571 self._root.ondestroy(self._destroy)
Martin v. Löwise563aa42008-09-29 22:09:07 +00003572 if _Screen._canvas is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00003573 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)
Martin v. Löwise563aa42008-09-29 22:09:07 +00003580 _Screen._canvas = self._root._getcanvas()
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003581 TurtleScreen.__init__(self, _Screen._canvas)
Martin v. Löwis87184592008-06-04 06:29:55 +00003582 self.setup(width, height, leftright, topbottom)
Georg Brandl33cece02008-05-20 06:58:21 +00003583
Martin v. Löwis87184592008-06-04 06:29:55 +00003584 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.
Georg Brandl33cece02008-05-20 06:58:21 +00003587
Martin v. Löwis87184592008-06-04 06:29:55 +00003588 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.
Georg Brandl33cece02008-05-20 06:58:21 +00003599
Martin v. Löwis87184592008-06-04 06:29:55 +00003600 Examples (for a Screen instance named screen):
3601 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3602
3603 sets window to 200x200 pixels, in upper left of screen
3604
3605 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3606
3607 sets window to 75% of screen by 50% of screen and centers
3608 """
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)
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003622 self.update()
Martin v. Löwis87184592008-06-04 06:29:55 +00003623
3624 def title(self, titlestring):
3625 """Set title of turtle-window
3626
3627 Argument:
3628 titlestring -- a string, to appear in the titlebar of the
3629 turtle graphics window.
3630
3631 This is a method of Screen-class. Not available for TurtleScreen-
3632 objects.
3633
3634 Example (for a Screen instance named screen):
3635 >>> screen.title("Welcome to the turtle-zoo!")
3636 """
Martin v. Löwise563aa42008-09-29 22:09:07 +00003637 if _Screen._root is not None:
3638 _Screen._root.title(titlestring)
3639 _Screen._title = titlestring
Georg Brandl33cece02008-05-20 06:58:21 +00003640
3641 def _destroy(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00003642 root = self._root
Martin v. Löwise563aa42008-09-29 22:09:07 +00003643 if root is _Screen._root:
Martin v. Löwis87184592008-06-04 06:29:55 +00003644 Turtle._pen = None
3645 Turtle._screen = None
Martin v. Löwise563aa42008-09-29 22:09:07 +00003646 _Screen._root = None
3647 _Screen._canvas = None
Martin v. Löwis87184592008-06-04 06:29:55 +00003648 TurtleScreen._RUNNING = True
Georg Brandl33cece02008-05-20 06:58:21 +00003649 root.destroy()
3650
Martin v. Löwis87184592008-06-04 06:29:55 +00003651 def bye(self):
3652 """Shut the turtlegraphics window.
3653
3654 Example (for a TurtleScreen instance named screen):
3655 >>> screen.bye()
3656 """
3657 self._destroy()
3658
3659 def exitonclick(self):
3660 """Go into mainloop until the mouse is clicked.
3661
3662 No arguments.
3663
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.
3670
3671 This is a method of the Screen-class and not available for
3672 TurtleScreen instances.
3673
3674 Example (for a Screen instance named screen):
3675 >>> screen.exitonclick()
3676
3677 """
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)
3688
3689
3690class Turtle(RawTurtle):
3691 """RawTurtle auto-crating (scrolled) canvas.
3692
3693 When a Turtle object is created or a function derived from some
3694 Turtle method is called a TurtleScreen object is automatically created.
3695 """
3696 _pen = None
3697 _screen = None
3698
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)
3709
3710Pen = Turtle
3711
Georg Brandl33cece02008-05-20 06:58:21 +00003712def _getpen():
Martin v. Löwis87184592008-06-04 06:29:55 +00003713 """Create the 'anonymous' turtle if not already present."""
3714 if Turtle._pen is None:
3715 Turtle._pen = Turtle()
3716 return Turtle._pen
Georg Brandl33cece02008-05-20 06:58:21 +00003717
Martin v. Löwis87184592008-06-04 06:29:55 +00003718def _getscreen():
3719 """Create a TurtleScreen if not already present."""
3720 if Turtle._screen is None:
3721 Turtle._screen = Screen()
3722 return Turtle._screen
Georg Brandl33cece02008-05-20 06:58:21 +00003723
Martin v. Löwis87184592008-06-04 06:29:55 +00003724def write_docstringdict(filename="turtle_docstringdict"):
3725 """Create and write docstring-dictionary to file.
Georg Brandl33cece02008-05-20 06:58:21 +00003726
Martin v. Löwis87184592008-06-04 06:29:55 +00003727 Optional argument:
3728 filename -- a string, used as filename
3729 default value is turtle_docstringdict
Georg Brandl33cece02008-05-20 06:58:21 +00003730
Martin v. Löwis87184592008-06-04 06:29:55 +00003731 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.
Georg Brandl33cece02008-05-20 06:58:21 +00003735 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003736 docsdict = {}
Georg Brandl33cece02008-05-20 06:58:21 +00003737
Martin v. Löwis87184592008-06-04 06:29:55 +00003738 for methodname in _tg_screen_functions:
Martin v. Löwise563aa42008-09-29 22:09:07 +00003739 key = "_Screen."+methodname
Martin v. Löwis87184592008-06-04 06:29:55 +00003740 docsdict[key] = eval(key).__doc__
3741 for methodname in _tg_turtle_functions:
3742 key = "Turtle."+methodname
3743 docsdict[key] = eval(key).__doc__
Georg Brandl33cece02008-05-20 06:58:21 +00003744
Martin v. Löwis87184592008-06-04 06:29:55 +00003745 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()
Georg Brandl33cece02008-05-20 06:58:21 +00003757
Martin v. Löwis87184592008-06-04 06:29:55 +00003758def read_docstrings(lang):
3759 """Read in docstrings from lang-specific docstring dictionary.
Georg Brandl33cece02008-05-20 06:58:21 +00003760
Martin v. Löwis87184592008-06-04 06:29:55 +00003761 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.
Georg Brandl33cece02008-05-20 06:58:21 +00003764 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003765 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
Georg Brandl33cece02008-05-20 06:58:21 +00003774
Martin v. Löwis87184592008-06-04 06:29:55 +00003775_LANGUAGE = _CFG["language"]
Georg Brandl33cece02008-05-20 06:58:21 +00003776
Martin v. Löwis87184592008-06-04 06:29:55 +00003777try:
3778 if _LANGUAGE != "english":
3779 read_docstrings(_LANGUAGE)
3780except ImportError:
3781 print "Cannot find docsdict for", _LANGUAGE
3782except:
3783 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3784 _LANGUAGE)
3785
3786
3787def 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
3822
3823def _turtle_docrevise(docstr):
3824 """To reduce docstrings from RawTurtle class for functions
3825 """
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
3834
3835def _screen_docrevise(docstr):
3836 """To reduce docstrings from TurtleScreen class for functions
3837 """
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
3846
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.
3850
3851
3852for methodname in _tg_screen_functions:
Martin v. Löwise563aa42008-09-29 22:09:07 +00003853 pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
Martin v. Löwis87184592008-06-04 06:29:55 +00003854 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
Martin v. Löwise563aa42008-09-29 22:09:07 +00003860 eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
Martin v. Löwis87184592008-06-04 06:29:55 +00003861
3862for 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__)
3871
3872
3873done = mainloop = TK.mainloop
3874del pl1, pl2, defstr
3875
3876if __name__ == "__main__":
3877 def switchpen():
3878 if isdown():
3879 pu()
3880 else:
3881 pd()
3882
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):
Georg Brandl33cece02008-05-20 06:58:21 +00003921 forward(20)
3922 left(90)
Martin v. Löwis87184592008-06-04 06:29:55 +00003923 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
Georg Brandl33cece02008-05-20 06:58:21 +00003935
Martin v. Löwis87184592008-06-04 06:29:55 +00003936 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()
Georg Brandl33cece02008-05-20 06:58:21 +00003968
Martin v. Löwis87184592008-06-04 06:29:55 +00003969 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")
Georg Brandl33cece02008-05-20 06:58:21 +00003988
Martin v. Löwis87184592008-06-04 06:29:55 +00003989 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)
Georg Brandl33cece02008-05-20 06:58:21 +00004005 setheading(towards(turtle))
Martin v. Löwis87184592008-06-04 06:29:55 +00004006 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")
Georg Brandl33cece02008-05-20 06:58:21 +00004020
Martin v. Löwis87184592008-06-04 06:29:55 +00004021 def baba(xdummy, ydummy):
4022 clearscreen()
4023 bye()
Georg Brandl33cece02008-05-20 06:58:21 +00004024
Martin v. Löwis87184592008-06-04 06:29:55 +00004025 time.sleep(2)
Georg Brandl33cece02008-05-20 06:58:21 +00004026
Martin v. Löwis87184592008-06-04 06:29:55 +00004027 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)
4033
4034 demo1()
Georg Brandl33cece02008-05-20 06:58:21 +00004035 demo2()
Martin v. Löwis87184592008-06-04 06:29:55 +00004036 exitonclick()