blob: a003433273272925f29cef9e3e147c085d2b0784 [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#
Georg Brandl8cdc9bc2010-01-01 13:07:05 +00005# Copyright (C) 2006 - 2010 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
Florent Xiclunac8a730b2010-03-25 20:32:07 +0000786 if canvwidth is canvheight is bg is None:
Martin v. Löwis87184592008-06-04 06:29:55 +0000787 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 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00001002 if mode is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00001003 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 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00001342 if fun is 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
Ezio Melotti24b07bc2011-03-15 18:55:01 +02001388 If picname is a filename, set the corresponding image as background.
Martin v. Löwis87184592008-06-04 06:29:55 +00001389 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
Ezio Melottic2077b02011-03-16 12:34:31 +02001412 bg -- colorstring or color-tuple, new backgroundcolor
Martin v. Löwis87184592008-06-04 06:29:55 +00001413 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 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00001463 if mode is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00001464 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
Alexander Belopolskyab016d22010-11-05 01:56:24 +00001498
1499 Change angle measurement unit to grad (also known as gon,
1500 grade, or gradian and equals 1/100-th of the right angle.)
1501 >>> turtle.degrees(400.0)
Martin v. Löwis87184592008-06-04 06:29:55 +00001502 >>> turtle.heading()
1503 100
1504
1505 """
1506 self._setDegreesPerAU(fullcircle)
1507
1508 def radians(self):
1509 """ Set the angle measurement units to radians.
1510
1511 No arguments.
1512
1513 Example (for a Turtle instance named turtle):
1514 >>> turtle.heading()
1515 90
1516 >>> turtle.radians()
1517 >>> turtle.heading()
1518 1.5707963267948966
1519 """
1520 self._setDegreesPerAU(2*math.pi)
1521
1522 def _go(self, distance):
1523 """move turtle forward by specified distance"""
1524 ende = self._position + self._orient * distance
1525 self._goto(ende)
1526
1527 def _rotate(self, angle):
1528 """Turn turtle counterclockwise by specified angle if angle > 0."""
1529 angle *= self._degreesPerAU
1530 self._orient = self._orient.rotate(angle)
1531
1532 def _goto(self, end):
1533 """move turtle to position end."""
1534 self._position = end
1535
1536 def forward(self, distance):
1537 """Move the turtle forward by the specified distance.
1538
1539 Aliases: forward | fd
1540
1541 Argument:
1542 distance -- a number (integer or float)
1543
1544 Move the turtle forward by the specified distance, in the direction
1545 the turtle is headed.
1546
1547 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001548 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001549 (0.00, 0.00)
1550 >>> turtle.forward(25)
1551 >>> turtle.position()
1552 (25.00,0.00)
1553 >>> turtle.forward(-75)
1554 >>> turtle.position()
1555 (-50.00,0.00)
1556 """
1557 self._go(distance)
1558
1559 def back(self, distance):
1560 """Move the turtle backward by distance.
1561
1562 Aliases: back | backward | bk
1563
1564 Argument:
1565 distance -- a number
1566
1567 Move the turtle backward by distance ,opposite to the direction the
1568 turtle is headed. Do not change the turtle's heading.
1569
1570 Example (for a Turtle instance named turtle):
1571 >>> turtle.position()
1572 (0.00, 0.00)
1573 >>> turtle.backward(30)
1574 >>> turtle.position()
1575 (-30.00, 0.00)
1576 """
1577 self._go(-distance)
1578
1579 def right(self, angle):
1580 """Turn turtle right by angle units.
1581
1582 Aliases: right | rt
1583
1584 Argument:
1585 angle -- a number (integer or float)
1586
1587 Turn turtle right by angle units. (Units are by default degrees,
1588 but can be set via the degrees() and radians() functions.)
1589 Angle orientation depends on mode. (See this.)
1590
1591 Example (for a Turtle instance named turtle):
1592 >>> turtle.heading()
1593 22.0
1594 >>> turtle.right(45)
1595 >>> turtle.heading()
1596 337.0
1597 """
1598 self._rotate(-angle)
1599
1600 def left(self, angle):
1601 """Turn turtle left by angle units.
1602
1603 Aliases: left | lt
1604
1605 Argument:
1606 angle -- a number (integer or float)
1607
1608 Turn turtle left by angle units. (Units are by default degrees,
1609 but can be set via the degrees() and radians() functions.)
1610 Angle orientation depends on mode. (See this.)
1611
1612 Example (for a Turtle instance named turtle):
1613 >>> turtle.heading()
1614 22.0
1615 >>> turtle.left(45)
1616 >>> turtle.heading()
1617 67.0
1618 """
1619 self._rotate(angle)
1620
1621 def pos(self):
1622 """Return the turtle's current location (x,y), as a Vec2D-vector.
1623
1624 Aliases: pos | position
1625
1626 No arguments.
1627
1628 Example (for a Turtle instance named turtle):
1629 >>> turtle.pos()
1630 (0.00, 240.00)
1631 """
1632 return self._position
1633
1634 def xcor(self):
1635 """ Return the turtle's x coordinate.
1636
1637 No arguments.
1638
1639 Example (for a Turtle instance named turtle):
1640 >>> reset()
1641 >>> turtle.left(60)
1642 >>> turtle.forward(100)
1643 >>> print turtle.xcor()
1644 50.0
1645 """
1646 return self._position[0]
1647
1648 def ycor(self):
1649 """ Return the turtle's y coordinate
1650 ---
1651 No arguments.
1652
1653 Example (for a Turtle instance named turtle):
1654 >>> reset()
1655 >>> turtle.left(60)
1656 >>> turtle.forward(100)
1657 >>> print turtle.ycor()
1658 86.6025403784
1659 """
1660 return self._position[1]
1661
1662
1663 def goto(self, x, y=None):
1664 """Move turtle to an absolute position.
1665
1666 Aliases: setpos | setposition | goto:
1667
1668 Arguments:
1669 x -- a number or a pair/vector of numbers
1670 y -- a number None
1671
1672 call: goto(x, y) # two coordinates
1673 --or: goto((x, y)) # a pair (tuple) of coordinates
1674 --or: goto(vec) # e.g. as returned by pos()
1675
1676 Move turtle to an absolute position. If the pen is down,
1677 a line will be drawn. The turtle's orientation does not change.
1678
1679 Example (for a Turtle instance named turtle):
1680 >>> tp = turtle.pos()
1681 >>> tp
1682 (0.00, 0.00)
1683 >>> turtle.setpos(60,30)
1684 >>> turtle.pos()
1685 (60.00,30.00)
1686 >>> turtle.setpos((20,80))
1687 >>> turtle.pos()
1688 (20.00,80.00)
1689 >>> turtle.setpos(tp)
1690 >>> turtle.pos()
1691 (0.00,0.00)
1692 """
1693 if y is None:
1694 self._goto(Vec2D(*x))
1695 else:
1696 self._goto(Vec2D(x, y))
1697
1698 def home(self):
1699 """Move turtle to the origin - coordinates (0,0).
1700
1701 No arguments.
1702
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001703 Move turtle to the origin - coordinates (0,0) and set its
1704 heading to its start-orientation (which depends on mode).
Martin v. Löwis87184592008-06-04 06:29:55 +00001705
1706 Example (for a Turtle instance named turtle):
1707 >>> turtle.home()
1708 """
1709 self.goto(0, 0)
1710 self.setheading(0)
1711
1712 def setx(self, x):
1713 """Set the turtle's first coordinate to x
1714
1715 Argument:
1716 x -- a number (integer or float)
1717
1718 Set the turtle's first coordinate to x, leave second coordinate
1719 unchanged.
1720
1721 Example (for a Turtle instance named turtle):
1722 >>> turtle.position()
1723 (0.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001724 >>> turtle.setx(10)
1725 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001726 (10.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001727 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001728 self._goto(Vec2D(x, self._position[1]))
Georg Brandl33cece02008-05-20 06:58:21 +00001729
Martin v. Löwis87184592008-06-04 06:29:55 +00001730 def sety(self, y):
1731 """Set the turtle's second coordinate to y
Georg Brandl33cece02008-05-20 06:58:21 +00001732
Martin v. Löwis87184592008-06-04 06:29:55 +00001733 Argument:
1734 y -- a number (integer or float)
1735
1736 Set the turtle's first coordinate to x, second coordinate remains
1737 unchanged.
1738
1739 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001740 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001741 (0.00, 40.00)
1742 >>> turtle.sety(-10)
Georg Brandl33cece02008-05-20 06:58:21 +00001743 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001744 (0.00, -10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001745 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001746 self._goto(Vec2D(self._position[0], y))
Georg Brandl33cece02008-05-20 06:58:21 +00001747
Martin v. Löwis87184592008-06-04 06:29:55 +00001748 def distance(self, x, y=None):
1749 """Return the distance from the turtle to (x,y) in turtle step units.
Georg Brandl33cece02008-05-20 06:58:21 +00001750
Martin v. Löwis87184592008-06-04 06:29:55 +00001751 Arguments:
1752 x -- a number or a pair/vector of numbers or a turtle instance
1753 y -- a number None None
Georg Brandl33cece02008-05-20 06:58:21 +00001754
Martin v. Löwis87184592008-06-04 06:29:55 +00001755 call: distance(x, y) # two coordinates
1756 --or: distance((x, y)) # a pair (tuple) of coordinates
1757 --or: distance(vec) # e.g. as returned by pos()
1758 --or: distance(mypen) # where mypen is another turtle
1759
1760 Example (for a Turtle instance named turtle):
1761 >>> turtle.pos()
1762 (0.00, 0.00)
1763 >>> turtle.distance(30,40)
1764 50.0
1765 >>> pen = Turtle()
1766 >>> pen.forward(77)
1767 >>> turtle.distance(pen)
1768 77.0
1769 """
1770 if y is not None:
1771 pos = Vec2D(x, y)
1772 if isinstance(x, Vec2D):
1773 pos = x
1774 elif isinstance(x, tuple):
1775 pos = Vec2D(*x)
1776 elif isinstance(x, TNavigator):
1777 pos = x._position
1778 return abs(pos - self._position)
1779
1780 def towards(self, x, y=None):
1781 """Return the angle of the line from the turtle's position to (x, y).
1782
1783 Arguments:
1784 x -- a number or a pair/vector of numbers or a turtle instance
1785 y -- a number None None
1786
1787 call: distance(x, y) # two coordinates
1788 --or: distance((x, y)) # a pair (tuple) of coordinates
1789 --or: distance(vec) # e.g. as returned by pos()
1790 --or: distance(mypen) # where mypen is another turtle
1791
1792 Return the angle, between the line from turtle-position to position
1793 specified by x, y and the turtle's start orientation. (Depends on
1794 modes - "standard" or "logo")
1795
1796 Example (for a Turtle instance named turtle):
1797 >>> turtle.pos()
1798 (10.00, 10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001799 >>> turtle.towards(0,0)
1800 225.0
1801 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001802 if y is not None:
1803 pos = Vec2D(x, y)
1804 if isinstance(x, Vec2D):
1805 pos = x
1806 elif isinstance(x, tuple):
1807 pos = Vec2D(*x)
1808 elif isinstance(x, TNavigator):
1809 pos = x._position
1810 x, y = pos - self._position
1811 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1812 result /= self._degreesPerAU
1813 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1814
1815 def heading(self):
1816 """ Return the turtle's current heading.
1817
1818 No arguments.
1819
1820 Example (for a Turtle instance named turtle):
1821 >>> turtle.left(67)
1822 >>> turtle.heading()
1823 67.0
1824 """
1825 x, y = self._orient
1826 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1827 result /= self._degreesPerAU
1828 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1829
1830 def setheading(self, to_angle):
1831 """Set the orientation of the turtle to to_angle.
1832
1833 Aliases: setheading | seth
1834
1835 Argument:
1836 to_angle -- a number (integer or float)
1837
1838 Set the orientation of the turtle to to_angle.
1839 Here are some common directions in degrees:
1840
1841 standard - mode: logo-mode:
1842 -------------------|--------------------
1843 0 - east 0 - north
1844 90 - north 90 - east
1845 180 - west 180 - south
1846 270 - south 270 - west
1847
1848 Example (for a Turtle instance named turtle):
1849 >>> turtle.setheading(90)
1850 >>> turtle.heading()
1851 90
1852 """
1853 angle = (to_angle - self.heading())*self._angleOrient
1854 full = self._fullcircle
1855 angle = (angle+full/2.)%full - full/2.
1856 self._rotate(angle)
1857
1858 def circle(self, radius, extent = None, steps = None):
1859 """ Draw a circle with given radius.
1860
1861 Arguments:
1862 radius -- a number
1863 extent (optional) -- a number
1864 steps (optional) -- an integer
1865
1866 Draw a circle with given radius. The center is radius units left
1867 of the turtle; extent - an angle - determines which part of the
1868 circle is drawn. If extent is not given, draw the entire circle.
1869 If extent is not a full circle, one endpoint of the arc is the
1870 current pen position. Draw the arc in counterclockwise direction
1871 if radius is positive, otherwise in clockwise direction. Finally
1872 the direction of the turtle is changed by the amount of extent.
1873
1874 As the circle is approximated by an inscribed regular polygon,
1875 steps determines the number of steps to use. If not given,
1876 it will be calculated automatically. Maybe used to draw regular
1877 polygons.
1878
1879 call: circle(radius) # full circle
1880 --or: circle(radius, extent) # arc
1881 --or: circle(radius, extent, steps)
1882 --or: circle(radius, steps=6) # 6-sided polygon
1883
1884 Example (for a Turtle instance named turtle):
1885 >>> turtle.circle(50)
1886 >>> turtle.circle(120, 180) # semicircle
1887 """
1888 if self.undobuffer:
1889 self.undobuffer.push(["seq"])
1890 self.undobuffer.cumulate = True
1891 speed = self.speed()
1892 if extent is None:
1893 extent = self._fullcircle
1894 if steps is None:
1895 frac = abs(extent)/self._fullcircle
1896 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1897 w = 1.0 * extent / steps
1898 w2 = 0.5 * w
1899 l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1900 if radius < 0:
1901 l, w, w2 = -l, -w, -w2
1902 tr = self.tracer()
1903 dl = self._delay()
1904 if speed == 0:
1905 self.tracer(0, 0)
Georg Brandl33cece02008-05-20 06:58:21 +00001906 else:
Martin v. Löwis87184592008-06-04 06:29:55 +00001907 self.speed(0)
1908 self._rotate(w2)
1909 for i in range(steps):
1910 self.speed(speed)
1911 self._go(l)
1912 self.speed(0)
1913 self._rotate(w)
1914 self._rotate(-w2)
1915 if speed == 0:
1916 self.tracer(tr, dl)
1917 self.speed(speed)
1918 if self.undobuffer:
1919 self.undobuffer.cumulate = False
Georg Brandl33cece02008-05-20 06:58:21 +00001920
Martin v. Löwis87184592008-06-04 06:29:55 +00001921## three dummy methods to be implemented by child class:
Georg Brandl33cece02008-05-20 06:58:21 +00001922
Martin v. Löwis87184592008-06-04 06:29:55 +00001923 def speed(self, s=0):
1924 """dummy method - to be overwritten by child class"""
1925 def tracer(self, a=None, b=None):
1926 """dummy method - to be overwritten by child class"""
1927 def _delay(self, n=None):
1928 """dummy method - to be overwritten by child class"""
Georg Brandl33cece02008-05-20 06:58:21 +00001929
Martin v. Löwis87184592008-06-04 06:29:55 +00001930 fd = forward
1931 bk = back
1932 backward = back
1933 rt = right
1934 lt = left
1935 position = pos
1936 setpos = goto
1937 setposition = goto
1938 seth = setheading
Georg Brandl33cece02008-05-20 06:58:21 +00001939
Georg Brandl33cece02008-05-20 06:58:21 +00001940
Martin v. Löwis87184592008-06-04 06:29:55 +00001941class TPen(object):
1942 """Drawing part of the RawTurtle.
1943 Implements drawing properties.
1944 """
1945 def __init__(self, resizemode=_CFG["resizemode"]):
1946 self._resizemode = resizemode # or "user" or "noresize"
1947 self.undobuffer = None
1948 TPen._reset(self)
Georg Brandl33cece02008-05-20 06:58:21 +00001949
Martin v. Löwis87184592008-06-04 06:29:55 +00001950 def _reset(self, pencolor=_CFG["pencolor"],
1951 fillcolor=_CFG["fillcolor"]):
1952 self._pensize = 1
1953 self._shown = True
1954 self._pencolor = pencolor
1955 self._fillcolor = fillcolor
1956 self._drawing = True
1957 self._speed = 3
1958 self._stretchfactor = (1, 1)
1959 self._tilt = 0
1960 self._outlinewidth = 1
1961 ### self.screen = None # to override by child class
1962
1963 def resizemode(self, rmode=None):
1964 """Set resizemode to one of the values: "auto", "user", "noresize".
1965
1966 (Optional) Argument:
1967 rmode -- one of the strings "auto", "user", "noresize"
1968
1969 Different resizemodes have the following effects:
1970 - "auto" adapts the appearance of the turtle
1971 corresponding to the value of pensize.
1972 - "user" adapts the appearance of the turtle according to the
1973 values of stretchfactor and outlinewidth (outline),
1974 which are set by shapesize()
1975 - "noresize" no adaption of the turtle's appearance takes place.
1976 If no argument is given, return current resizemode.
1977 resizemode("user") is called by a call of shapesize with arguments.
1978
1979
1980 Examples (for a Turtle instance named turtle):
1981 >>> turtle.resizemode("noresize")
1982 >>> turtle.resizemode()
1983 'noresize'
Georg Brandl33cece02008-05-20 06:58:21 +00001984 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001985 if rmode is None:
1986 return self._resizemode
1987 rmode = rmode.lower()
1988 if rmode in ["auto", "user", "noresize"]:
1989 self.pen(resizemode=rmode)
Georg Brandl33cece02008-05-20 06:58:21 +00001990
Martin v. Löwis87184592008-06-04 06:29:55 +00001991 def pensize(self, width=None):
1992 """Set or return the line thickness.
Georg Brandl33cece02008-05-20 06:58:21 +00001993
Martin v. Löwis87184592008-06-04 06:29:55 +00001994 Aliases: pensize | width
Georg Brandl33cece02008-05-20 06:58:21 +00001995
Martin v. Löwis87184592008-06-04 06:29:55 +00001996 Argument:
1997 width -- positive number
Georg Brandl33cece02008-05-20 06:58:21 +00001998
Martin v. Löwis87184592008-06-04 06:29:55 +00001999 Set the line thickness to width or return it. If resizemode is set
2000 to "auto" and turtleshape is a polygon, that polygon is drawn with
2001 the same line thickness. If no argument is given, current pensize
2002 is returned.
Georg Brandl33cece02008-05-20 06:58:21 +00002003
Martin v. Löwis87184592008-06-04 06:29:55 +00002004 Example (for a Turtle instance named turtle):
2005 >>> turtle.pensize()
2006 1
2007 turtle.pensize(10) # from here on lines of width 10 are drawn
Georg Brandl33cece02008-05-20 06:58:21 +00002008 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002009 if width is None:
2010 return self._pensize
2011 self.pen(pensize=width)
Georg Brandl33cece02008-05-20 06:58:21 +00002012
2013
Martin v. Löwis87184592008-06-04 06:29:55 +00002014 def penup(self):
2015 """Pull the pen up -- no drawing when moving.
Georg Brandl33cece02008-05-20 06:58:21 +00002016
Martin v. Löwis87184592008-06-04 06:29:55 +00002017 Aliases: penup | pu | up
Georg Brandl33cece02008-05-20 06:58:21 +00002018
Martin v. Löwis87184592008-06-04 06:29:55 +00002019 No argument
2020
2021 Example (for a Turtle instance named turtle):
2022 >>> turtle.penup()
Georg Brandl33cece02008-05-20 06:58:21 +00002023 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002024 if not self._drawing:
Georg Brandl33cece02008-05-20 06:58:21 +00002025 return
Martin v. Löwis87184592008-06-04 06:29:55 +00002026 self.pen(pendown=False)
Georg Brandl33cece02008-05-20 06:58:21 +00002027
Martin v. Löwis87184592008-06-04 06:29:55 +00002028 def pendown(self):
2029 """Pull the pen down -- drawing when moving.
2030
2031 Aliases: pendown | pd | down
2032
2033 No argument.
2034
2035 Example (for a Turtle instance named turtle):
2036 >>> turtle.pendown()
2037 """
2038 if self._drawing:
2039 return
2040 self.pen(pendown=True)
2041
2042 def isdown(self):
2043 """Return True if pen is down, False if it's up.
2044
2045 No argument.
2046
2047 Example (for a Turtle instance named turtle):
2048 >>> turtle.penup()
2049 >>> turtle.isdown()
2050 False
2051 >>> turtle.pendown()
2052 >>> turtle.isdown()
2053 True
2054 """
2055 return self._drawing
2056
2057 def speed(self, speed=None):
2058 """ Return or set the turtle's speed.
2059
2060 Optional argument:
2061 speed -- an integer in the range 0..10 or a speedstring (see below)
2062
2063 Set the turtle's speed to an integer value in the range 0 .. 10.
2064 If no argument is given: return current speed.
2065
2066 If input is a number greater than 10 or smaller than 0.5,
2067 speed is set to 0.
2068 Speedstrings are mapped to speedvalues in the following way:
2069 'fastest' : 0
2070 'fast' : 10
2071 'normal' : 6
2072 'slow' : 3
2073 'slowest' : 1
2074 speeds from 1 to 10 enforce increasingly faster animation of
2075 line drawing and turtle turning.
2076
2077 Attention:
2078 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2079 and likewise left/right make the turtle turn instantly.
2080
2081 Example (for a Turtle instance named turtle):
2082 >>> turtle.speed(3)
2083 """
2084 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2085 if speed is None:
2086 return self._speed
2087 if speed in speeds:
2088 speed = speeds[speed]
2089 elif 0.5 < speed < 10.5:
2090 speed = int(round(speed))
2091 else:
2092 speed = 0
2093 self.pen(speed=speed)
2094
2095 def color(self, *args):
2096 """Return or set the pencolor and fillcolor.
2097
2098 Arguments:
2099 Several input formats are allowed.
2100 They use 0, 1, 2, or 3 arguments as follows:
2101
2102 color()
2103 Return the current pencolor and the current fillcolor
2104 as a pair of color specification strings as are returned
2105 by pencolor and fillcolor.
2106 color(colorstring), color((r,g,b)), color(r,g,b)
2107 inputs as in pencolor, set both, fillcolor and pencolor,
2108 to the given value.
2109 color(colorstring1, colorstring2),
2110 color((r1,g1,b1), (r2,g2,b2))
2111 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2112 and analogously, if the other input format is used.
2113
2114 If turtleshape is a polygon, outline and interior of that polygon
2115 is drawn with the newly set colors.
2116 For mor info see: pencolor, fillcolor
2117
2118 Example (for a Turtle instance named turtle):
2119 >>> turtle.color('red', 'green')
2120 >>> turtle.color()
2121 ('red', 'green')
2122 >>> colormode(255)
2123 >>> color((40, 80, 120), (160, 200, 240))
2124 >>> color()
2125 ('#285078', '#a0c8f0')
2126 """
2127 if args:
2128 l = len(args)
2129 if l == 1:
2130 pcolor = fcolor = args[0]
2131 elif l == 2:
2132 pcolor, fcolor = args
2133 elif l == 3:
2134 pcolor = fcolor = args
2135 pcolor = self._colorstr(pcolor)
2136 fcolor = self._colorstr(fcolor)
2137 self.pen(pencolor=pcolor, fillcolor=fcolor)
2138 else:
2139 return self._color(self._pencolor), self._color(self._fillcolor)
2140
2141 def pencolor(self, *args):
2142 """ Return or set the pencolor.
2143
2144 Arguments:
2145 Four input formats are allowed:
2146 - pencolor()
2147 Return the current pencolor as color specification string,
2148 possibly in hex-number format (see example).
2149 May be used as input to another color/pencolor/fillcolor call.
2150 - pencolor(colorstring)
2151 s is a Tk color specification string, such as "red" or "yellow"
2152 - pencolor((r, g, b))
2153 *a tuple* of r, g, and b, which represent, an RGB color,
2154 and each of r, g, and b are in the range 0..colormode,
2155 where colormode is either 1.0 or 255
2156 - pencolor(r, g, b)
2157 r, g, and b represent an RGB color, and each of r, g, and b
2158 are in the range 0..colormode
2159
2160 If turtleshape is a polygon, the outline of that polygon is drawn
2161 with the newly set pencolor.
2162
2163 Example (for a Turtle instance named turtle):
2164 >>> turtle.pencolor('brown')
2165 >>> tup = (0.2, 0.8, 0.55)
2166 >>> turtle.pencolor(tup)
2167 >>> turtle.pencolor()
2168 '#33cc8c'
2169 """
2170 if args:
2171 color = self._colorstr(args)
2172 if color == self._pencolor:
2173 return
2174 self.pen(pencolor=color)
2175 else:
2176 return self._color(self._pencolor)
2177
2178 def fillcolor(self, *args):
2179 """ Return or set the fillcolor.
2180
2181 Arguments:
2182 Four input formats are allowed:
2183 - fillcolor()
2184 Return the current fillcolor as color specification string,
2185 possibly in hex-number format (see example).
2186 May be used as input to another color/pencolor/fillcolor call.
2187 - fillcolor(colorstring)
2188 s is a Tk color specification string, such as "red" or "yellow"
2189 - fillcolor((r, g, b))
2190 *a tuple* of r, g, and b, which represent, an RGB color,
2191 and each of r, g, and b are in the range 0..colormode,
2192 where colormode is either 1.0 or 255
2193 - fillcolor(r, g, b)
2194 r, g, and b represent an RGB color, and each of r, g, and b
2195 are in the range 0..colormode
2196
2197 If turtleshape is a polygon, the interior of that polygon is drawn
2198 with the newly set fillcolor.
2199
2200 Example (for a Turtle instance named turtle):
2201 >>> turtle.fillcolor('violet')
2202 >>> col = turtle.pencolor()
2203 >>> turtle.fillcolor(col)
2204 >>> turtle.fillcolor(0, .5, 0)
2205 """
2206 if args:
2207 color = self._colorstr(args)
2208 if color == self._fillcolor:
2209 return
2210 self.pen(fillcolor=color)
2211 else:
2212 return self._color(self._fillcolor)
2213
2214 def showturtle(self):
2215 """Makes the turtle visible.
2216
2217 Aliases: showturtle | st
2218
2219 No argument.
2220
2221 Example (for a Turtle instance named turtle):
2222 >>> turtle.hideturtle()
2223 >>> turtle.showturtle()
2224 """
2225 self.pen(shown=True)
2226
2227 def hideturtle(self):
2228 """Makes the turtle invisible.
2229
2230 Aliases: hideturtle | ht
2231
2232 No argument.
2233
2234 It's a good idea to do this while you're in the
2235 middle of a complicated drawing, because hiding
2236 the turtle speeds up the drawing observably.
2237
2238 Example (for a Turtle instance named turtle):
2239 >>> turtle.hideturtle()
2240 """
2241 self.pen(shown=False)
2242
2243 def isvisible(self):
2244 """Return True if the Turtle is shown, False if it's hidden.
2245
2246 No argument.
2247
2248 Example (for a Turtle instance named turtle):
2249 >>> turtle.hideturtle()
2250 >>> print turtle.isvisible():
2251 False
2252 """
2253 return self._shown
2254
2255 def pen(self, pen=None, **pendict):
2256 """Return or set the pen's attributes.
2257
2258 Arguments:
2259 pen -- a dictionary with some or all of the below listed keys.
2260 **pendict -- one or more keyword-arguments with the below
2261 listed keys as keywords.
2262
2263 Return or set the pen's attributes in a 'pen-dictionary'
2264 with the following key/value pairs:
2265 "shown" : True/False
2266 "pendown" : True/False
2267 "pencolor" : color-string or color-tuple
2268 "fillcolor" : color-string or color-tuple
2269 "pensize" : positive number
2270 "speed" : number in range 0..10
2271 "resizemode" : "auto" or "user" or "noresize"
2272 "stretchfactor": (positive number, positive number)
2273 "outline" : positive number
2274 "tilt" : number
2275
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002276 This dictionary can be used as argument for a subsequent
Martin v. Löwis87184592008-06-04 06:29:55 +00002277 pen()-call to restore the former pen-state. Moreover one
2278 or more of these attributes can be provided as keyword-arguments.
2279 This can be used to set several pen attributes in one statement.
Georg Brandl33cece02008-05-20 06:58:21 +00002280
2281
Martin v. Löwis87184592008-06-04 06:29:55 +00002282 Examples (for a Turtle instance named turtle):
2283 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2284 >>> turtle.pen()
2285 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2286 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
2287 'stretchfactor': (1,1), 'speed': 3}
2288 >>> penstate=turtle.pen()
2289 >>> turtle.color("yellow","")
2290 >>> turtle.penup()
2291 >>> turtle.pen()
2292 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2293 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
2294 'stretchfactor': (1,1), 'speed': 3}
2295 >>> p.pen(penstate, fillcolor="green")
2296 >>> p.pen()
2297 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2298 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
2299 'stretchfactor': (1,1), 'speed': 3}
2300 """
2301 _pd = {"shown" : self._shown,
2302 "pendown" : self._drawing,
2303 "pencolor" : self._pencolor,
2304 "fillcolor" : self._fillcolor,
2305 "pensize" : self._pensize,
2306 "speed" : self._speed,
2307 "resizemode" : self._resizemode,
2308 "stretchfactor" : self._stretchfactor,
2309 "outline" : self._outlinewidth,
2310 "tilt" : self._tilt
2311 }
Georg Brandl33cece02008-05-20 06:58:21 +00002312
Martin v. Löwis87184592008-06-04 06:29:55 +00002313 if not (pen or pendict):
2314 return _pd
2315
2316 if isinstance(pen, dict):
2317 p = pen
2318 else:
2319 p = {}
2320 p.update(pendict)
2321
2322 _p_buf = {}
2323 for key in p:
2324 _p_buf[key] = _pd[key]
2325
2326 if self.undobuffer:
2327 self.undobuffer.push(("pen", _p_buf))
2328
2329 newLine = False
2330 if "pendown" in p:
2331 if self._drawing != p["pendown"]:
2332 newLine = True
2333 if "pencolor" in p:
2334 if isinstance(p["pencolor"], tuple):
2335 p["pencolor"] = self._colorstr((p["pencolor"],))
2336 if self._pencolor != p["pencolor"]:
2337 newLine = True
2338 if "pensize" in p:
2339 if self._pensize != p["pensize"]:
2340 newLine = True
2341 if newLine:
2342 self._newLine()
2343 if "pendown" in p:
2344 self._drawing = p["pendown"]
2345 if "pencolor" in p:
2346 self._pencolor = p["pencolor"]
2347 if "pensize" in p:
2348 self._pensize = p["pensize"]
2349 if "fillcolor" in p:
2350 if isinstance(p["fillcolor"], tuple):
2351 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2352 self._fillcolor = p["fillcolor"]
2353 if "speed" in p:
2354 self._speed = p["speed"]
2355 if "resizemode" in p:
2356 self._resizemode = p["resizemode"]
2357 if "stretchfactor" in p:
2358 sf = p["stretchfactor"]
2359 if isinstance(sf, (int, float)):
2360 sf = (sf, sf)
2361 self._stretchfactor = sf
2362 if "outline" in p:
2363 self._outlinewidth = p["outline"]
2364 if "shown" in p:
2365 self._shown = p["shown"]
2366 if "tilt" in p:
2367 self._tilt = p["tilt"]
2368 self._update()
2369
2370## three dummy methods to be implemented by child class:
2371
2372 def _newLine(self, usePos = True):
2373 """dummy method - to be overwritten by child class"""
2374 def _update(self, count=True, forced=False):
2375 """dummy method - to be overwritten by child class"""
2376 def _color(self, args):
2377 """dummy method - to be overwritten by child class"""
2378 def _colorstr(self, args):
2379 """dummy method - to be overwritten by child class"""
2380
2381 width = pensize
2382 up = penup
2383 pu = penup
2384 pd = pendown
2385 down = pendown
2386 st = showturtle
2387 ht = hideturtle
2388
2389
2390class _TurtleImage(object):
2391 """Helper class: Datatype to store Turtle attributes
2392 """
2393
2394 def __init__(self, screen, shapeIndex):
2395 self.screen = screen
2396 self._type = None
2397 self._setshape(shapeIndex)
2398
2399 def _setshape(self, shapeIndex):
2400 screen = self.screen # RawTurtle.screens[self.screenIndex]
2401 self.shapeIndex = shapeIndex
2402 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2403 return
2404 if self._type == "image" == screen._shapes[shapeIndex]._type:
2405 return
2406 if self._type in ["image", "polygon"]:
2407 screen._delete(self._item)
2408 elif self._type == "compound":
2409 for item in self._item:
2410 screen._delete(item)
2411 self._type = screen._shapes[shapeIndex]._type
2412 if self._type == "polygon":
2413 self._item = screen._createpoly()
2414 elif self._type == "image":
2415 self._item = screen._createimage(screen._shapes["blank"]._data)
2416 elif self._type == "compound":
2417 self._item = [screen._createpoly() for item in
2418 screen._shapes[shapeIndex]._data]
2419
2420
2421class RawTurtle(TPen, TNavigator):
2422 """Animation part of the RawTurtle.
2423 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002424 its animation.
Martin v. Löwis87184592008-06-04 06:29:55 +00002425 """
2426 screens = []
2427
2428 def __init__(self, canvas=None,
2429 shape=_CFG["shape"],
2430 undobuffersize=_CFG["undobuffersize"],
2431 visible=_CFG["visible"]):
Martin v. Löwise563aa42008-09-29 22:09:07 +00002432 if isinstance(canvas, _Screen):
Martin v. Löwis87184592008-06-04 06:29:55 +00002433 self.screen = canvas
2434 elif isinstance(canvas, TurtleScreen):
2435 if canvas not in RawTurtle.screens:
2436 RawTurtle.screens.append(canvas)
2437 self.screen = canvas
2438 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2439 for screen in RawTurtle.screens:
2440 if screen.cv == canvas:
2441 self.screen = screen
2442 break
2443 else:
2444 self.screen = TurtleScreen(canvas)
2445 RawTurtle.screens.append(self.screen)
2446 else:
2447 raise TurtleGraphicsError("bad cavas argument %s" % canvas)
2448
2449 screen = self.screen
2450 TNavigator.__init__(self, screen.mode())
2451 TPen.__init__(self)
2452 screen._turtles.append(self)
2453 self.drawingLineItem = screen._createline()
2454 self.turtle = _TurtleImage(screen, shape)
2455 self._poly = None
2456 self._creatingPoly = False
2457 self._fillitem = self._fillpath = None
2458 self._shown = visible
2459 self._hidden_from_screen = False
2460 self.currentLineItem = screen._createline()
2461 self.currentLine = [self._position]
2462 self.items = [self.currentLineItem]
2463 self.stampItems = []
2464 self._undobuffersize = undobuffersize
2465 self.undobuffer = Tbuffer(undobuffersize)
2466 self._update()
2467
2468 def reset(self):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002469 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis87184592008-06-04 06:29:55 +00002470
2471 No argument.
2472,
2473 Delete the turtle's drawings from the screen, re-center the turtle
2474 and set variables to the default values.
2475
2476 Example (for a Turtle instance named turtle):
2477 >>> turtle.position()
2478 (0.00,-22.00)
2479 >>> turtle.heading()
2480 100.0
2481 >>> turtle.reset()
2482 >>> turtle.position()
2483 (0.00,0.00)
2484 >>> turtle.heading()
2485 0.0
2486 """
2487 TNavigator.reset(self)
2488 TPen._reset(self)
2489 self._clear()
2490 self._drawturtle()
2491 self._update()
2492
2493 def setundobuffer(self, size):
2494 """Set or disable undobuffer.
2495
2496 Argument:
2497 size -- an integer or None
2498
2499 If size is an integer an empty undobuffer of given size is installed.
2500 Size gives the maximum number of turtle-actions that can be undone
2501 by the undo() function.
2502 If size is None, no undobuffer is present.
2503
2504 Example (for a Turtle instance named turtle):
2505 >>> turtle.setundobuffer(42)
2506 """
2507 if size is None:
2508 self.undobuffer = None
2509 else:
2510 self.undobuffer = Tbuffer(size)
2511
2512 def undobufferentries(self):
2513 """Return count of entries in the undobuffer.
2514
2515 No argument.
2516
2517 Example (for a Turtle instance named turtle):
2518 >>> while undobufferentries():
2519 undo()
2520 """
2521 if self.undobuffer is None:
2522 return 0
2523 return self.undobuffer.nr_of_items()
2524
2525 def _clear(self):
2526 """Delete all of pen's drawings"""
2527 self._fillitem = self._fillpath = None
2528 for item in self.items:
2529 self.screen._delete(item)
2530 self.currentLineItem = self.screen._createline()
2531 self.currentLine = []
2532 if self._drawing:
2533 self.currentLine.append(self._position)
2534 self.items = [self.currentLineItem]
2535 self.clearstamps()
2536 self.setundobuffer(self._undobuffersize)
2537
2538
2539 def clear(self):
2540 """Delete the turtle's drawings from the screen. Do not move turtle.
2541
2542 No arguments.
2543
2544 Delete the turtle's drawings from the screen. Do not move turtle.
2545 State and position of the turtle as well as drawings of other
2546 turtles are not affected.
2547
2548 Examples (for a Turtle instance named turtle):
2549 >>> turtle.clear()
2550 """
2551 self._clear()
2552 self._update()
2553
2554 def _update_data(self):
2555 self.screen._incrementudc()
2556 if self.screen._updatecounter != 0:
2557 return
2558 if len(self.currentLine)>1:
2559 self.screen._drawline(self.currentLineItem, self.currentLine,
2560 self._pencolor, self._pensize)
2561
2562 def _update(self):
2563 """Perform a Turtle-data update.
2564 """
2565 screen = self.screen
2566 if screen._tracing == 0:
2567 return
2568 elif screen._tracing == 1:
2569 self._update_data()
2570 self._drawturtle()
2571 screen._update() # TurtleScreenBase
2572 screen._delay(screen._delayvalue) # TurtleScreenBase
2573 else:
2574 self._update_data()
2575 if screen._updatecounter == 0:
2576 for t in screen.turtles():
2577 t._drawturtle()
2578 screen._update()
2579
2580 def tracer(self, flag=None, delay=None):
2581 """Turns turtle animation on/off and set delay for update drawings.
2582
2583 Optional arguments:
2584 n -- nonnegative integer
2585 delay -- nonnegative integer
2586
2587 If n is given, only each n-th regular screen update is really performed.
2588 (Can be used to accelerate the drawing of complex graphics.)
2589 Second arguments sets delay value (see RawTurtle.delay())
2590
2591 Example (for a Turtle instance named turtle):
2592 >>> turtle.tracer(8, 25)
2593 >>> dist = 2
2594 >>> for i in range(200):
2595 turtle.fd(dist)
2596 turtle.rt(90)
2597 dist += 2
2598 """
2599 return self.screen.tracer(flag, delay)
2600
2601 def _color(self, args):
2602 return self.screen._color(args)
2603
2604 def _colorstr(self, args):
2605 return self.screen._colorstr(args)
2606
2607 def _cc(self, args):
2608 """Convert colortriples to hexstrings.
2609 """
2610 if isinstance(args, str):
2611 return args
2612 try:
2613 r, g, b = args
2614 except:
2615 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2616 if self.screen._colormode == 1.0:
2617 r, g, b = [round(255.0*x) for x in (r, g, b)]
2618 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2619 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2620 return "#%02x%02x%02x" % (r, g, b)
2621
2622 def clone(self):
2623 """Create and return a clone of the turtle.
2624
2625 No argument.
2626
2627 Create and return a clone of the turtle with same position, heading
2628 and turtle properties.
2629
2630 Example (for a Turtle instance named mick):
2631 mick = Turtle()
2632 joe = mick.clone()
2633 """
2634 screen = self.screen
2635 self._newLine(self._drawing)
2636
2637 turtle = self.turtle
2638 self.screen = None
2639 self.turtle = None # too make self deepcopy-able
2640
2641 q = deepcopy(self)
2642
2643 self.screen = screen
2644 self.turtle = turtle
2645
2646 q.screen = screen
2647 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2648
2649 screen._turtles.append(q)
2650 ttype = screen._shapes[self.turtle.shapeIndex]._type
2651 if ttype == "polygon":
2652 q.turtle._item = screen._createpoly()
2653 elif ttype == "image":
2654 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2655 elif ttype == "compound":
2656 q.turtle._item = [screen._createpoly() for item in
2657 screen._shapes[self.turtle.shapeIndex]._data]
2658 q.currentLineItem = screen._createline()
2659 q._update()
2660 return q
2661
2662 def shape(self, name=None):
2663 """Set turtle shape to shape with given name / return current shapename.
2664
2665 Optional argument:
2666 name -- a string, which is a valid shapename
2667
2668 Set turtle shape to shape with given name or, if name is not given,
2669 return name of current shape.
2670 Shape with name must exist in the TurtleScreen's shape dictionary.
2671 Initially there are the following polygon shapes:
2672 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2673 To learn about how to deal with shapes see Screen-method register_shape.
2674
2675 Example (for a Turtle instance named turtle):
2676 >>> turtle.shape()
2677 'arrow'
2678 >>> turtle.shape("turtle")
2679 >>> turtle.shape()
2680 'turtle'
2681 """
2682 if name is None:
2683 return self.turtle.shapeIndex
2684 if not name in self.screen.getshapes():
2685 raise TurtleGraphicsError("There is no shape named %s" % name)
2686 self.turtle._setshape(name)
2687 self._update()
2688
2689 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2690 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2691
2692 Optinonal arguments:
2693 stretch_wid : positive number
2694 stretch_len : positive number
2695 outline : positive number
2696
2697 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2698 Set resizemode to "user".
2699 If and only if resizemode is set to "user", the turtle will be displayed
2700 stretched according to its stretchfactors:
2701 stretch_wid is stretchfactor perpendicular to orientation
2702 stretch_len is stretchfactor in direction of turtles orientation.
2703 outline determines the width of the shapes's outline.
2704
2705 Examples (for a Turtle instance named turtle):
2706 >>> turtle.resizemode("user")
2707 >>> turtle.shapesize(5, 5, 12)
2708 >>> turtle.shapesize(outline=8)
2709 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00002710 if stretch_wid is stretch_len is outline is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00002711 stretch_wid, stretch_len = self._stretchfactor
2712 return stretch_wid, stretch_len, self._outlinewidth
2713 if stretch_wid is not None:
2714 if stretch_len is None:
2715 stretchfactor = stretch_wid, stretch_wid
2716 else:
2717 stretchfactor = stretch_wid, stretch_len
2718 elif stretch_len is not None:
2719 stretchfactor = self._stretchfactor[0], stretch_len
2720 else:
2721 stretchfactor = self._stretchfactor
2722 if outline is None:
2723 outline = self._outlinewidth
2724 self.pen(resizemode="user",
2725 stretchfactor=stretchfactor, outline=outline)
2726
2727 def settiltangle(self, angle):
2728 """Rotate the turtleshape to point in the specified direction
2729
2730 Optional argument:
2731 angle -- number
2732
2733 Rotate the turtleshape to point in the direction specified by angle,
2734 regardless of its current tilt-angle. DO NOT change the turtle's
2735 heading (direction of movement).
2736
2737
2738 Examples (for a Turtle instance named turtle):
2739 >>> turtle.shape("circle")
2740 >>> turtle.shapesize(5,2)
2741 >>> turtle.settiltangle(45)
2742 >>> stamp()
2743 >>> turtle.fd(50)
2744 >>> turtle.settiltangle(-45)
2745 >>> stamp()
2746 >>> turtle.fd(50)
2747 """
2748 tilt = -angle * self._degreesPerAU * self._angleOrient
2749 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2750 self.pen(resizemode="user", tilt=tilt)
2751
2752 def tiltangle(self):
2753 """Return the current tilt-angle.
2754
2755 No argument.
2756
2757 Return the current tilt-angle, i. e. the angle between the
2758 orientation of the turtleshape and the heading of the turtle
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002759 (its direction of movement).
Martin v. Löwis87184592008-06-04 06:29:55 +00002760
2761 Examples (for a Turtle instance named turtle):
2762 >>> turtle.shape("circle")
2763 >>> turtle.shapesize(5,2)
2764 >>> turtle.tilt(45)
2765 >>> turtle.tiltangle()
2766 >>>
2767 """
2768 tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2769 return (tilt / self._degreesPerAU) % self._fullcircle
2770
2771 def tilt(self, angle):
2772 """Rotate the turtleshape by angle.
2773
2774 Argument:
2775 angle - a number
2776
2777 Rotate the turtleshape by angle from its current tilt-angle,
2778 but do NOT change the turtle's heading (direction of movement).
2779
2780 Examples (for a Turtle instance named turtle):
2781 >>> turtle.shape("circle")
2782 >>> turtle.shapesize(5,2)
2783 >>> turtle.tilt(30)
2784 >>> turtle.fd(50)
2785 >>> turtle.tilt(30)
2786 >>> turtle.fd(50)
2787 """
2788 self.settiltangle(angle + self.tiltangle())
2789
2790 def _polytrafo(self, poly):
2791 """Computes transformed polygon shapes from a shape
2792 according to current position and heading.
2793 """
2794 screen = self.screen
2795 p0, p1 = self._position
2796 e0, e1 = self._orient
2797 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2798 e0, e1 = (1.0 / abs(e)) * e
2799 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2800 for (x, y) in poly]
2801
2802 def _drawturtle(self):
2803 """Manages the correct rendering of the turtle with respect to
Mark Dickinson3e4caeb2009-02-21 20:27:01 +00002804 its shape, resizemode, stretch and tilt etc."""
Martin v. Löwis87184592008-06-04 06:29:55 +00002805 screen = self.screen
2806 shape = screen._shapes[self.turtle.shapeIndex]
2807 ttype = shape._type
2808 titem = self.turtle._item
2809 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
2810 self._hidden_from_screen = False
2811 tshape = shape._data
2812 if ttype == "polygon":
2813 if self._resizemode == "noresize":
2814 w = 1
2815 shape = tshape
2816 else:
2817 if self._resizemode == "auto":
2818 lx = ly = max(1, self._pensize/5.0)
2819 w = self._pensize
2820 tiltangle = 0
2821 elif self._resizemode == "user":
2822 lx, ly = self._stretchfactor
2823 w = self._outlinewidth
2824 tiltangle = self._tilt
2825 shape = [(lx*x, ly*y) for (x, y) in tshape]
2826 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2827 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2828 shape = self._polytrafo(shape)
2829 fc, oc = self._fillcolor, self._pencolor
2830 screen._drawpoly(titem, shape, fill=fc, outline=oc,
2831 width=w, top=True)
2832 elif ttype == "image":
2833 screen._drawimage(titem, self._position, tshape)
2834 elif ttype == "compound":
2835 lx, ly = self._stretchfactor
2836 w = self._outlinewidth
2837 for item, (poly, fc, oc) in zip(titem, tshape):
2838 poly = [(lx*x, ly*y) for (x, y) in poly]
2839 poly = self._polytrafo(poly)
2840 screen._drawpoly(item, poly, fill=self._cc(fc),
2841 outline=self._cc(oc), width=w, top=True)
2842 else:
2843 if self._hidden_from_screen:
2844 return
2845 if ttype == "polygon":
2846 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
2847 elif ttype == "image":
2848 screen._drawimage(titem, self._position,
2849 screen._shapes["blank"]._data)
2850 elif ttype == "compound":
2851 for item in titem:
2852 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
2853 self._hidden_from_screen = True
2854
2855############################## stamp stuff ###############################
2856
2857 def stamp(self):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002858 """Stamp a copy of the turtleshape onto the canvas and return its id.
Martin v. Löwis87184592008-06-04 06:29:55 +00002859
2860 No argument.
2861
2862 Stamp a copy of the turtle shape onto the canvas at the current
2863 turtle position. Return a stamp_id for that stamp, which can be
2864 used to delete it by calling clearstamp(stamp_id).
2865
2866 Example (for a Turtle instance named turtle):
2867 >>> turtle.color("blue")
2868 >>> turtle.stamp()
2869 13
2870 >>> turtle.fd(50)
2871 """
2872 screen = self.screen
2873 shape = screen._shapes[self.turtle.shapeIndex]
2874 ttype = shape._type
2875 tshape = shape._data
2876 if ttype == "polygon":
2877 stitem = screen._createpoly()
2878 if self._resizemode == "noresize":
2879 w = 1
2880 shape = tshape
2881 else:
2882 if self._resizemode == "auto":
2883 lx = ly = max(1, self._pensize/5.0)
2884 w = self._pensize
2885 tiltangle = 0
2886 elif self._resizemode == "user":
2887 lx, ly = self._stretchfactor
2888 w = self._outlinewidth
2889 tiltangle = self._tilt
2890 shape = [(lx*x, ly*y) for (x, y) in tshape]
2891 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2892 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2893 shape = self._polytrafo(shape)
2894 fc, oc = self._fillcolor, self._pencolor
2895 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
2896 width=w, top=True)
2897 elif ttype == "image":
2898 stitem = screen._createimage("")
2899 screen._drawimage(stitem, self._position, tshape)
2900 elif ttype == "compound":
2901 stitem = []
2902 for element in tshape:
2903 item = screen._createpoly()
2904 stitem.append(item)
2905 stitem = tuple(stitem)
2906 lx, ly = self._stretchfactor
2907 w = self._outlinewidth
2908 for item, (poly, fc, oc) in zip(stitem, tshape):
2909 poly = [(lx*x, ly*y) for (x, y) in poly]
2910 poly = self._polytrafo(poly)
2911 screen._drawpoly(item, poly, fill=self._cc(fc),
2912 outline=self._cc(oc), width=w, top=True)
2913 self.stampItems.append(stitem)
2914 self.undobuffer.push(("stamp", stitem))
2915 return stitem
2916
2917 def _clearstamp(self, stampid):
2918 """does the work for clearstamp() and clearstamps()
2919 """
2920 if stampid in self.stampItems:
2921 if isinstance(stampid, tuple):
2922 for subitem in stampid:
2923 self.screen._delete(subitem)
2924 else:
2925 self.screen._delete(stampid)
2926 self.stampItems.remove(stampid)
2927 # Delete stampitem from undobuffer if necessary
2928 # if clearstamp is called directly.
2929 item = ("stamp", stampid)
2930 buf = self.undobuffer
2931 if item not in buf.buffer:
2932 return
2933 index = buf.buffer.index(item)
2934 buf.buffer.remove(item)
2935 if index <= buf.ptr:
2936 buf.ptr = (buf.ptr - 1) % buf.bufsize
2937 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
2938
2939 def clearstamp(self, stampid):
2940 """Delete stamp with given stampid
2941
2942 Argument:
2943 stampid - an integer, must be return value of previous stamp() call.
2944
2945 Example (for a Turtle instance named turtle):
2946 >>> turtle.color("blue")
2947 >>> astamp = turtle.stamp()
2948 >>> turtle.fd(50)
2949 >>> turtle.clearstamp(astamp)
2950 """
2951 self._clearstamp(stampid)
2952 self._update()
2953
2954 def clearstamps(self, n=None):
2955 """Delete all or first/last n of turtle's stamps.
2956
2957 Optional argument:
2958 n -- an integer
2959
2960 If n is None, delete all of pen's stamps,
2961 else if n > 0 delete first n stamps
2962 else if n < 0 delete last n stamps.
2963
2964 Example (for a Turtle instance named turtle):
2965 >>> for i in range(8):
2966 turtle.stamp(); turtle.fd(30)
2967 ...
2968 >>> turtle.clearstamps(2)
2969 >>> turtle.clearstamps(-2)
2970 >>> turtle.clearstamps()
2971 """
2972 if n is None:
2973 toDelete = self.stampItems[:]
2974 elif n >= 0:
2975 toDelete = self.stampItems[:n]
2976 else:
2977 toDelete = self.stampItems[n:]
2978 for item in toDelete:
2979 self._clearstamp(item)
2980 self._update()
2981
2982 def _goto(self, end):
2983 """Move the pen to the point end, thereby drawing a line
2984 if pen is down. All other methodes for turtle movement depend
2985 on this one.
2986 """
2987 ## Version mit undo-stuff
2988 go_modes = ( self._drawing,
2989 self._pencolor,
2990 self._pensize,
2991 isinstance(self._fillpath, list))
2992 screen = self.screen
2993 undo_entry = ("go", self._position, end, go_modes,
2994 (self.currentLineItem,
2995 self.currentLine[:],
2996 screen._pointlist(self.currentLineItem),
2997 self.items[:])
2998 )
2999 if self.undobuffer:
3000 self.undobuffer.push(undo_entry)
3001 start = self._position
3002 if self._speed and screen._tracing == 1:
3003 diff = (end-start)
3004 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3005 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3006 delta = diff * (1.0/nhops)
3007 for n in range(1, nhops):
3008 if n == 1:
3009 top = True
3010 else:
3011 top = False
3012 self._position = start + delta * n
3013 if self._drawing:
3014 screen._drawline(self.drawingLineItem,
3015 (start, self._position),
3016 self._pencolor, self._pensize, top)
3017 self._update()
3018 if self._drawing:
3019 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3020 fill="", width=self._pensize)
3021 # Turtle now at end,
3022 if self._drawing: # now update currentLine
3023 self.currentLine.append(end)
3024 if isinstance(self._fillpath, list):
3025 self._fillpath.append(end)
3026 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3027 self._position = end
3028 if self._creatingPoly:
3029 self._poly.append(end)
3030 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3031 # of life, the universe and everything
3032 self._newLine()
3033 self._update() #count=True)
3034
3035 def _undogoto(self, entry):
3036 """Reverse a _goto. Used for undo()
3037 """
3038 old, new, go_modes, coodata = entry
3039 drawing, pc, ps, filling = go_modes
3040 cLI, cL, pl, items = coodata
3041 screen = self.screen
3042 if abs(self._position - new) > 0.5:
3043 print "undogoto: HALLO-DA-STIMMT-WAS-NICHT!"
3044 # restore former situation
3045 self.currentLineItem = cLI
3046 self.currentLine = cL
3047
3048 if pl == [(0, 0), (0, 0)]:
3049 usepc = ""
3050 else:
3051 usepc = pc
3052 screen._drawline(cLI, pl, fill=usepc, width=ps)
3053
3054 todelete = [i for i in self.items if (i not in items) and
3055 (screen._type(i) == "line")]
3056 for i in todelete:
3057 screen._delete(i)
3058 self.items.remove(i)
3059
3060 start = old
3061 if self._speed and screen._tracing == 1:
3062 diff = old - new
3063 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3064 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3065 delta = diff * (1.0/nhops)
3066 for n in range(1, nhops):
3067 if n == 1:
3068 top = True
3069 else:
3070 top = False
3071 self._position = new + delta * n
3072 if drawing:
3073 screen._drawline(self.drawingLineItem,
3074 (start, self._position),
3075 pc, ps, top)
3076 self._update()
3077 if drawing:
3078 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3079 fill="", width=ps)
3080 # Turtle now at position old,
3081 self._position = old
Ezio Melottic2077b02011-03-16 12:34:31 +02003082 ## if undo is done during creating a polygon, the last vertex
3083 ## will be deleted. if the polygon is entirely deleted,
3084 ## creatingPoly will be set to False.
Martin v. Löwis87184592008-06-04 06:29:55 +00003085 ## Polygons created before the last one will not be affected by undo()
3086 if self._creatingPoly:
3087 if len(self._poly) > 0:
3088 self._poly.pop()
3089 if self._poly == []:
3090 self._creatingPoly = False
3091 self._poly = None
3092 if filling:
3093 if self._fillpath == []:
3094 self._fillpath = None
3095 print "Unwahrscheinlich in _undogoto!"
3096 elif self._fillpath is not None:
3097 self._fillpath.pop()
3098 self._update() #count=True)
3099
3100 def _rotate(self, angle):
3101 """Turns pen clockwise by angle.
3102 """
3103 if self.undobuffer:
3104 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3105 angle *= self._degreesPerAU
3106 neworient = self._orient.rotate(angle)
3107 tracing = self.screen._tracing
3108 if tracing == 1 and self._speed > 0:
3109 anglevel = 3.0 * self._speed
3110 steps = 1 + int(abs(angle)/anglevel)
3111 delta = 1.0*angle/steps
3112 for _ in range(steps):
3113 self._orient = self._orient.rotate(delta)
3114 self._update()
3115 self._orient = neworient
3116 self._update()
3117
3118 def _newLine(self, usePos=True):
3119 """Closes current line item and starts a new one.
3120 Remark: if current line became too long, animation
3121 performance (via _drawline) slowed down considerably.
3122 """
3123 if len(self.currentLine) > 1:
3124 self.screen._drawline(self.currentLineItem, self.currentLine,
3125 self._pencolor, self._pensize)
3126 self.currentLineItem = self.screen._createline()
3127 self.items.append(self.currentLineItem)
3128 else:
3129 self.screen._drawline(self.currentLineItem, top=True)
3130 self.currentLine = []
3131 if usePos:
3132 self.currentLine = [self._position]
3133
3134 def fill(self, flag=None):
3135 """Call fill(True) before drawing a shape to fill, fill(False) when done.
3136
3137 Optional argument:
3138 flag -- True/False (or 1/0 respectively)
3139
3140 Call fill(True) before drawing the shape you want to fill,
3141 and fill(False) when done.
3142 When used without argument: return fillstate (True if filling,
3143 False else)
3144
3145 Example (for a Turtle instance named turtle):
3146 >>> turtle.fill(True)
3147 >>> turtle.forward(100)
3148 >>> turtle.left(90)
3149 >>> turtle.forward(100)
3150 >>> turtle.left(90)
3151 >>> turtle.forward(100)
3152 >>> turtle.left(90)
3153 >>> turtle.forward(100)
3154 >>> turtle.fill(False)
3155 """
3156 filling = isinstance(self._fillpath, list)
3157 if flag is None:
3158 return filling
3159 screen = self.screen
3160 entry1 = entry2 = ()
3161 if filling:
3162 if len(self._fillpath) > 2:
3163 self.screen._drawpoly(self._fillitem, self._fillpath,
3164 fill=self._fillcolor)
3165 entry1 = ("dofill", self._fillitem)
3166 if flag:
3167 self._fillitem = self.screen._createpoly()
3168 self.items.append(self._fillitem)
3169 self._fillpath = [self._position]
3170 entry2 = ("beginfill", self._fillitem) # , self._fillpath)
3171 self._newLine()
3172 else:
3173 self._fillitem = self._fillpath = None
3174 if self.undobuffer:
3175 if entry1 == ():
3176 if entry2 != ():
3177 self.undobuffer.push(entry2)
3178 else:
3179 if entry2 == ():
3180 self.undobuffer.push(entry1)
3181 else:
3182 self.undobuffer.push(["seq", entry1, entry2])
3183 self._update()
3184
3185 def begin_fill(self):
3186 """Called just before drawing a shape to be filled.
3187
3188 No argument.
3189
3190 Example (for a Turtle instance named turtle):
3191 >>> turtle.begin_fill()
3192 >>> turtle.forward(100)
3193 >>> turtle.left(90)
3194 >>> turtle.forward(100)
3195 >>> turtle.left(90)
3196 >>> turtle.forward(100)
3197 >>> turtle.left(90)
3198 >>> turtle.forward(100)
3199 >>> turtle.end_fill()
3200 """
3201 self.fill(True)
3202
3203 def end_fill(self):
3204 """Fill the shape drawn after the call begin_fill().
3205
3206 No argument.
3207
3208 Example (for a Turtle instance named turtle):
3209 >>> turtle.begin_fill()
3210 >>> turtle.forward(100)
3211 >>> turtle.left(90)
3212 >>> turtle.forward(100)
3213 >>> turtle.left(90)
3214 >>> turtle.forward(100)
3215 >>> turtle.left(90)
3216 >>> turtle.forward(100)
3217 >>> turtle.end_fill()
3218 """
3219 self.fill(False)
3220
3221 def dot(self, size=None, *color):
3222 """Draw a dot with diameter size, using color.
3223
Ezio Melotti24b07bc2011-03-15 18:55:01 +02003224 Optional arguments:
Martin v. Löwis87184592008-06-04 06:29:55 +00003225 size -- an integer >= 1 (if given)
3226 color -- a colorstring or a numeric color tuple
3227
3228 Draw a circular dot with diameter size, using color.
3229 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3230
3231 Example (for a Turtle instance named turtle):
3232 >>> turtle.dot()
3233 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3234 """
3235 #print "dot-1:", size, color
3236 if not color:
3237 if isinstance(size, (str, tuple)):
3238 color = self._colorstr(size)
3239 size = self._pensize + max(self._pensize, 4)
3240 else:
3241 color = self._pencolor
3242 if not size:
3243 size = self._pensize + max(self._pensize, 4)
3244 else:
3245 if size is None:
3246 size = self._pensize + max(self._pensize, 4)
3247 color = self._colorstr(color)
3248 #print "dot-2:", size, color
3249 if hasattr(self.screen, "_dot"):
3250 item = self.screen._dot(self._position, size, color)
3251 #print "dot:", size, color, "item:", item
3252 self.items.append(item)
3253 if self.undobuffer:
3254 self.undobuffer.push(("dot", item))
3255 else:
3256 pen = self.pen()
3257 if self.undobuffer:
3258 self.undobuffer.push(["seq"])
3259 self.undobuffer.cumulate = True
3260 try:
3261 if self.resizemode() == 'auto':
3262 self.ht()
3263 self.pendown()
3264 self.pensize(size)
3265 self.pencolor(color)
3266 self.forward(0)
3267 finally:
3268 self.pen(pen)
3269 if self.undobuffer:
3270 self.undobuffer.cumulate = False
3271
3272 def _write(self, txt, align, font):
3273 """Performs the writing for write()
3274 """
3275 item, end = self.screen._write(self._position, txt, align, font,
3276 self._pencolor)
3277 self.items.append(item)
3278 if self.undobuffer:
3279 self.undobuffer.push(("wri", item))
3280 return end
3281
3282 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3283 """Write text at the current turtle position.
3284
3285 Arguments:
3286 arg -- info, which is to be written to the TurtleScreen
3287 move (optional) -- True/False
3288 align (optional) -- one of the strings "left", "center" or right"
3289 font (optional) -- a triple (fontname, fontsize, fonttype)
3290
3291 Write text - the string representation of arg - at the current
3292 turtle position according to align ("left", "center" or right")
3293 and with the given font.
3294 If move is True, the pen is moved to the bottom-right corner
3295 of the text. By default, move is False.
3296
3297 Example (for a Turtle instance named turtle):
3298 >>> turtle.write('Home = ', True, align="center")
3299 >>> turtle.write((0,0), True)
3300 """
3301 if self.undobuffer:
3302 self.undobuffer.push(["seq"])
3303 self.undobuffer.cumulate = True
3304 end = self._write(str(arg), align.lower(), font)
3305 if move:
3306 x, y = self.pos()
3307 self.setpos(end, y)
3308 if self.undobuffer:
3309 self.undobuffer.cumulate = False
3310
3311 def begin_poly(self):
3312 """Start recording the vertices of a polygon.
3313
3314 No argument.
3315
3316 Start recording the vertices of a polygon. Current turtle position
3317 is first point of polygon.
3318
3319 Example (for a Turtle instance named turtle):
3320 >>> turtle.begin_poly()
3321 """
3322 self._poly = [self._position]
3323 self._creatingPoly = True
3324
3325 def end_poly(self):
3326 """Stop recording the vertices of a polygon.
3327
3328 No argument.
3329
3330 Stop recording the vertices of a polygon. Current turtle position is
3331 last point of polygon. This will be connected with the first point.
3332
3333 Example (for a Turtle instance named turtle):
3334 >>> turtle.end_poly()
3335 """
3336 self._creatingPoly = False
3337
3338 def get_poly(self):
3339 """Return the lastly recorded polygon.
3340
3341 No argument.
3342
3343 Example (for a Turtle instance named turtle):
3344 >>> p = turtle.get_poly()
3345 >>> turtle.register_shape("myFavouriteShape", p)
3346 """
3347 ## check if there is any poly? -- 1st solution:
3348 if self._poly is not None:
3349 return tuple(self._poly)
3350
3351 def getscreen(self):
3352 """Return the TurtleScreen object, the turtle is drawing on.
3353
3354 No argument.
3355
3356 Return the TurtleScreen object, the turtle is drawing on.
3357 So TurtleScreen-methods can be called for that object.
3358
3359 Example (for a Turtle instance named turtle):
3360 >>> ts = turtle.getscreen()
3361 >>> ts
3362 <turtle.TurtleScreen object at 0x0106B770>
3363 >>> ts.bgcolor("pink")
3364 """
3365 return self.screen
3366
3367 def getturtle(self):
3368 """Return the Turtleobject itself.
3369
3370 No argument.
3371
3372 Only reasonable use: as a function to return the 'anonymous turtle':
3373
3374 Example:
3375 >>> pet = getturtle()
3376 >>> pet.fd(50)
3377 >>> pet
3378 <turtle.Turtle object at 0x0187D810>
3379 >>> turtles()
3380 [<turtle.Turtle object at 0x0187D810>]
3381 """
3382 return self
3383
3384 getpen = getturtle
3385
3386
3387 ################################################################
3388 ### screen oriented methods recurring to methods of TurtleScreen
3389 ################################################################
3390
3391 def window_width(self):
3392 """ Returns the width of the turtle window.
3393
3394 No argument.
3395
3396 Example (for a TurtleScreen instance named screen):
3397 >>> screen.window_width()
3398 640
3399 """
3400 return self.screen._window_size()[0]
3401
3402 def window_height(self):
3403 """ Return the height of the turtle window.
3404
3405 No argument.
3406
3407 Example (for a TurtleScreen instance named screen):
3408 >>> screen.window_height()
3409 480
3410 """
3411 return self.screen._window_size()[1]
3412
3413 def _delay(self, delay=None):
3414 """Set delay value which determines speed of turtle animation.
3415 """
3416 return self.screen.delay(delay)
3417
3418 ##### event binding methods #####
3419
3420 def onclick(self, fun, btn=1, add=None):
3421 """Bind fun to mouse-click event on this turtle on canvas.
3422
3423 Arguments:
3424 fun -- a function with two arguments, to which will be assigned
3425 the coordinates of the clicked point on the canvas.
3426 num -- number of the mouse-button defaults to 1 (left mouse button).
3427 add -- True or False. If True, new binding will be added, otherwise
3428 it will replace a former binding.
3429
3430 Example for the anonymous turtle, i. e. the procedural way:
3431
3432 >>> def turn(x, y):
3433 left(360)
3434
3435 >>> onclick(turn) # Now clicking into the turtle will turn it.
3436 >>> onclick(None) # event-binding will be removed
3437 """
3438 self.screen._onclick(self.turtle._item, fun, btn, add)
3439 self._update()
3440
3441 def onrelease(self, fun, btn=1, add=None):
3442 """Bind fun to mouse-button-release event on this turtle on canvas.
3443
3444 Arguments:
3445 fun -- a function with two arguments, to which will be assigned
3446 the coordinates of the clicked point on the canvas.
3447 num -- number of the mouse-button defaults to 1 (left mouse button).
3448
3449 Example (for a MyTurtle instance named joe):
3450 >>> class MyTurtle(Turtle):
3451 def glow(self,x,y):
3452 self.fillcolor("red")
3453 def unglow(self,x,y):
3454 self.fillcolor("")
3455
3456 >>> joe = MyTurtle()
3457 >>> joe.onclick(joe.glow)
3458 >>> joe.onrelease(joe.unglow)
3459 ### clicking on joe turns fillcolor red,
3460 ### unclicking turns it to transparent.
3461 """
3462 self.screen._onrelease(self.turtle._item, fun, btn, add)
3463 self._update()
3464
3465 def ondrag(self, fun, btn=1, add=None):
3466 """Bind fun to mouse-move event on this turtle on canvas.
3467
3468 Arguments:
3469 fun -- a function with two arguments, to which will be assigned
3470 the coordinates of the clicked point on the canvas.
3471 num -- number of the mouse-button defaults to 1 (left mouse button).
3472
3473 Every sequence of mouse-move-events on a turtle is preceded by a
3474 mouse-click event on that turtle.
3475
3476 Example (for a Turtle instance named turtle):
3477 >>> turtle.ondrag(turtle.goto)
3478
3479 ### Subsequently clicking and dragging a Turtle will
3480 ### move it across the screen thereby producing handdrawings
3481 ### (if pen is down).
3482 """
3483 self.screen._ondrag(self.turtle._item, fun, btn, add)
3484
3485
3486 def _undo(self, action, data):
3487 """Does the main part of the work for undo()
3488 """
3489 if self.undobuffer is None:
3490 return
3491 if action == "rot":
3492 angle, degPAU = data
3493 self._rotate(-angle*degPAU/self._degreesPerAU)
3494 dummy = self.undobuffer.pop()
3495 elif action == "stamp":
3496 stitem = data[0]
3497 self.clearstamp(stitem)
3498 elif action == "go":
3499 self._undogoto(data)
3500 elif action in ["wri", "dot"]:
3501 item = data[0]
3502 self.screen._delete(item)
3503 self.items.remove(item)
3504 elif action == "dofill":
3505 item = data[0]
3506 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3507 fill="", outline="")
3508 elif action == "beginfill":
3509 item = data[0]
3510 self._fillitem = self._fillpath = None
3511 self.screen._delete(item)
3512 self.items.remove(item)
3513 elif action == "pen":
3514 TPen.pen(self, data[0])
3515 self.undobuffer.pop()
3516
3517 def undo(self):
3518 """undo (repeatedly) the last turtle action.
3519
3520 No argument.
3521
3522 undo (repeatedly) the last turtle action.
3523 Number of available undo actions is determined by the size of
3524 the undobuffer.
3525
3526 Example (for a Turtle instance named turtle):
3527 >>> for i in range(4):
3528 turtle.fd(50); turtle.lt(80)
3529
3530 >>> for i in range(8):
3531 turtle.undo()
3532 """
3533 if self.undobuffer is None:
3534 return
3535 item = self.undobuffer.pop()
3536 action = item[0]
3537 data = item[1:]
3538 if action == "seq":
3539 while data:
3540 item = data.pop()
3541 self._undo(item[0], item[1:])
3542 else:
3543 self._undo(action, data)
3544
3545 turtlesize = shapesize
3546
3547RawPen = RawTurtle
3548
Martin v. Löwise563aa42008-09-29 22:09:07 +00003549### Screen - Singleton ########################
Martin v. Löwis87184592008-06-04 06:29:55 +00003550
Martin v. Löwise563aa42008-09-29 22:09:07 +00003551def Screen():
3552 """Return the singleton screen object.
3553 If none exists at the moment, create a new one and return it,
3554 else return the existing one."""
3555 if Turtle._screen is None:
3556 Turtle._screen = _Screen()
3557 return Turtle._screen
3558
3559class _Screen(TurtleScreen):
Martin v. Löwis87184592008-06-04 06:29:55 +00003560
3561 _root = None
3562 _canvas = None
3563 _title = _CFG["title"]
3564
Georg Brandl33cece02008-05-20 06:58:21 +00003565 def __init__(self):
Martin v. Löwise563aa42008-09-29 22:09:07 +00003566 # XXX there is no need for this code to be conditional,
3567 # as there will be only a single _Screen instance, anyway
3568 # XXX actually, the turtle demo is injecting root window,
3569 # so perhaps the conditional creation of a root should be
3570 # preserved (perhaps by passing it as an optional parameter)
3571 if _Screen._root is None:
3572 _Screen._root = self._root = _Root()
3573 self._root.title(_Screen._title)
Martin v. Löwis87184592008-06-04 06:29:55 +00003574 self._root.ondestroy(self._destroy)
Martin v. Löwise563aa42008-09-29 22:09:07 +00003575 if _Screen._canvas is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00003576 width = _CFG["width"]
3577 height = _CFG["height"]
3578 canvwidth = _CFG["canvwidth"]
3579 canvheight = _CFG["canvheight"]
3580 leftright = _CFG["leftright"]
3581 topbottom = _CFG["topbottom"]
3582 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwise563aa42008-09-29 22:09:07 +00003583 _Screen._canvas = self._root._getcanvas()
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003584 TurtleScreen.__init__(self, _Screen._canvas)
Martin v. Löwis87184592008-06-04 06:29:55 +00003585 self.setup(width, height, leftright, topbottom)
Georg Brandl33cece02008-05-20 06:58:21 +00003586
Martin v. Löwis87184592008-06-04 06:29:55 +00003587 def setup(self, width=_CFG["width"], height=_CFG["height"],
3588 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3589 """ Set the size and position of the main window.
Georg Brandl33cece02008-05-20 06:58:21 +00003590
Martin v. Löwis87184592008-06-04 06:29:55 +00003591 Arguments:
3592 width: as integer a size in pixels, as float a fraction of the screen.
3593 Default is 50% of screen.
3594 height: as integer the height in pixels, as float a fraction of the
3595 screen. Default is 75% of screen.
3596 startx: if positive, starting position in pixels from the left
3597 edge of the screen, if negative from the right edge
3598 Default, startx=None is to center window horizontally.
3599 starty: if positive, starting position in pixels from the top
3600 edge of the screen, if negative from the bottom edge
3601 Default, starty=None is to center window vertically.
Georg Brandl33cece02008-05-20 06:58:21 +00003602
Martin v. Löwis87184592008-06-04 06:29:55 +00003603 Examples (for a Screen instance named screen):
3604 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3605
3606 sets window to 200x200 pixels, in upper left of screen
3607
3608 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3609
3610 sets window to 75% of screen by 50% of screen and centers
3611 """
3612 if not hasattr(self._root, "set_geometry"):
3613 return
3614 sw = self._root.win_width()
3615 sh = self._root.win_height()
3616 if isinstance(width, float) and 0 <= width <= 1:
3617 width = sw*width
3618 if startx is None:
3619 startx = (sw - width) / 2
3620 if isinstance(height, float) and 0 <= height <= 1:
3621 height = sh*height
3622 if starty is None:
3623 starty = (sh - height) / 2
3624 self._root.set_geometry(width, height, startx, starty)
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003625 self.update()
Martin v. Löwis87184592008-06-04 06:29:55 +00003626
3627 def title(self, titlestring):
3628 """Set title of turtle-window
3629
3630 Argument:
3631 titlestring -- a string, to appear in the titlebar of the
3632 turtle graphics window.
3633
3634 This is a method of Screen-class. Not available for TurtleScreen-
3635 objects.
3636
3637 Example (for a Screen instance named screen):
3638 >>> screen.title("Welcome to the turtle-zoo!")
3639 """
Martin v. Löwise563aa42008-09-29 22:09:07 +00003640 if _Screen._root is not None:
3641 _Screen._root.title(titlestring)
3642 _Screen._title = titlestring
Georg Brandl33cece02008-05-20 06:58:21 +00003643
3644 def _destroy(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00003645 root = self._root
Martin v. Löwise563aa42008-09-29 22:09:07 +00003646 if root is _Screen._root:
Martin v. Löwis87184592008-06-04 06:29:55 +00003647 Turtle._pen = None
3648 Turtle._screen = None
Martin v. Löwise563aa42008-09-29 22:09:07 +00003649 _Screen._root = None
3650 _Screen._canvas = None
Martin v. Löwis87184592008-06-04 06:29:55 +00003651 TurtleScreen._RUNNING = True
Georg Brandl33cece02008-05-20 06:58:21 +00003652 root.destroy()
3653
Martin v. Löwis87184592008-06-04 06:29:55 +00003654 def bye(self):
3655 """Shut the turtlegraphics window.
3656
3657 Example (for a TurtleScreen instance named screen):
3658 >>> screen.bye()
3659 """
3660 self._destroy()
3661
3662 def exitonclick(self):
3663 """Go into mainloop until the mouse is clicked.
3664
3665 No arguments.
3666
3667 Bind bye() method to mouseclick on TurtleScreen.
3668 If "using_IDLE" - value in configuration dictionary is False
3669 (default value), enter mainloop.
3670 If IDLE with -n switch (no subprocess) is used, this value should be
3671 set to True in turtle.cfg. In this case IDLE's mainloop
3672 is active also for the client script.
3673
3674 This is a method of the Screen-class and not available for
3675 TurtleScreen instances.
3676
3677 Example (for a Screen instance named screen):
3678 >>> screen.exitonclick()
3679
3680 """
3681 def exitGracefully(x, y):
3682 """Screen.bye() with two dummy-parameters"""
3683 self.bye()
3684 self.onclick(exitGracefully)
3685 if _CFG["using_IDLE"]:
3686 return
3687 try:
3688 mainloop()
3689 except AttributeError:
3690 exit(0)
3691
3692
3693class Turtle(RawTurtle):
Ezio Melottic2077b02011-03-16 12:34:31 +02003694 """RawTurtle auto-creating (scrolled) canvas.
Martin v. Löwis87184592008-06-04 06:29:55 +00003695
3696 When a Turtle object is created or a function derived from some
3697 Turtle method is called a TurtleScreen object is automatically created.
3698 """
3699 _pen = None
3700 _screen = None
3701
3702 def __init__(self,
3703 shape=_CFG["shape"],
3704 undobuffersize=_CFG["undobuffersize"],
3705 visible=_CFG["visible"]):
3706 if Turtle._screen is None:
3707 Turtle._screen = Screen()
3708 RawTurtle.__init__(self, Turtle._screen,
3709 shape=shape,
3710 undobuffersize=undobuffersize,
3711 visible=visible)
3712
3713Pen = Turtle
3714
Georg Brandl33cece02008-05-20 06:58:21 +00003715def _getpen():
Martin v. Löwis87184592008-06-04 06:29:55 +00003716 """Create the 'anonymous' turtle if not already present."""
3717 if Turtle._pen is None:
3718 Turtle._pen = Turtle()
3719 return Turtle._pen
Georg Brandl33cece02008-05-20 06:58:21 +00003720
Martin v. Löwis87184592008-06-04 06:29:55 +00003721def _getscreen():
3722 """Create a TurtleScreen if not already present."""
3723 if Turtle._screen is None:
3724 Turtle._screen = Screen()
3725 return Turtle._screen
Georg Brandl33cece02008-05-20 06:58:21 +00003726
Martin v. Löwis87184592008-06-04 06:29:55 +00003727def write_docstringdict(filename="turtle_docstringdict"):
3728 """Create and write docstring-dictionary to file.
Georg Brandl33cece02008-05-20 06:58:21 +00003729
Martin v. Löwis87184592008-06-04 06:29:55 +00003730 Optional argument:
3731 filename -- a string, used as filename
3732 default value is turtle_docstringdict
Georg Brandl33cece02008-05-20 06:58:21 +00003733
Ezio Melottic2077b02011-03-16 12:34:31 +02003734 Has to be called explicitly, (not used by the turtle-graphics classes)
Martin v. Löwis87184592008-06-04 06:29:55 +00003735 The docstring dictionary will be written to the Python script <filname>.py
3736 It is intended to serve as a template for translation of the docstrings
3737 into different languages.
Georg Brandl33cece02008-05-20 06:58:21 +00003738 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003739 docsdict = {}
Georg Brandl33cece02008-05-20 06:58:21 +00003740
Martin v. Löwis87184592008-06-04 06:29:55 +00003741 for methodname in _tg_screen_functions:
Martin v. Löwise563aa42008-09-29 22:09:07 +00003742 key = "_Screen."+methodname
Martin v. Löwis87184592008-06-04 06:29:55 +00003743 docsdict[key] = eval(key).__doc__
3744 for methodname in _tg_turtle_functions:
3745 key = "Turtle."+methodname
3746 docsdict[key] = eval(key).__doc__
Georg Brandl33cece02008-05-20 06:58:21 +00003747
Martin v. Löwis87184592008-06-04 06:29:55 +00003748 f = open("%s.py" % filename,"w")
3749 keys = sorted([x for x in docsdict.keys()
3750 if x.split('.')[1] not in _alias_list])
3751 f.write('docsdict = {\n\n')
3752 for key in keys[:-1]:
3753 f.write('%s :\n' % repr(key))
3754 f.write(' """%s\n""",\n\n' % docsdict[key])
3755 key = keys[-1]
3756 f.write('%s :\n' % repr(key))
3757 f.write(' """%s\n"""\n\n' % docsdict[key])
3758 f.write("}\n")
3759 f.close()
Georg Brandl33cece02008-05-20 06:58:21 +00003760
Martin v. Löwis87184592008-06-04 06:29:55 +00003761def read_docstrings(lang):
3762 """Read in docstrings from lang-specific docstring dictionary.
Georg Brandl33cece02008-05-20 06:58:21 +00003763
Martin v. Löwis87184592008-06-04 06:29:55 +00003764 Transfer docstrings, translated to lang, from a dictionary-file
3765 to the methods of classes Screen and Turtle and - in revised form -
3766 to the corresponding functions.
Georg Brandl33cece02008-05-20 06:58:21 +00003767 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003768 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3769 module = __import__(modname)
3770 docsdict = module.docsdict
3771 for key in docsdict:
3772 #print key
3773 try:
3774 eval(key).im_func.__doc__ = docsdict[key]
3775 except:
3776 print "Bad docstring-entry: %s" % key
Georg Brandl33cece02008-05-20 06:58:21 +00003777
Martin v. Löwis87184592008-06-04 06:29:55 +00003778_LANGUAGE = _CFG["language"]
Georg Brandl33cece02008-05-20 06:58:21 +00003779
Martin v. Löwis87184592008-06-04 06:29:55 +00003780try:
3781 if _LANGUAGE != "english":
3782 read_docstrings(_LANGUAGE)
3783except ImportError:
3784 print "Cannot find docsdict for", _LANGUAGE
3785except:
3786 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3787 _LANGUAGE)
3788
3789
3790def getmethparlist(ob):
3791 "Get strings describing the arguments for the given object"
3792 argText1 = argText2 = ""
3793 # bit of a hack for methods - turn it into a function
3794 # but we drop the "self" param.
3795 if type(ob)==types.MethodType:
3796 fob = ob.im_func
3797 argOffset = 1
3798 else:
3799 fob = ob
3800 argOffset = 0
3801 # Try and build one for Python defined functions
3802 if type(fob) in [types.FunctionType, types.LambdaType]:
3803 try:
3804 counter = fob.func_code.co_argcount
3805 items2 = list(fob.func_code.co_varnames[argOffset:counter])
3806 realArgs = fob.func_code.co_varnames[argOffset:counter]
3807 defaults = fob.func_defaults or []
3808 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
3809 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3810 items1 = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
3811 if fob.func_code.co_flags & 0x4:
3812 items1.append("*"+fob.func_code.co_varnames[counter])
3813 items2.append("*"+fob.func_code.co_varnames[counter])
3814 counter += 1
3815 if fob.func_code.co_flags & 0x8:
3816 items1.append("**"+fob.func_code.co_varnames[counter])
3817 items2.append("**"+fob.func_code.co_varnames[counter])
3818 argText1 = ", ".join(items1)
3819 argText1 = "(%s)" % argText1
3820 argText2 = ", ".join(items2)
3821 argText2 = "(%s)" % argText2
3822 except:
3823 pass
3824 return argText1, argText2
3825
3826def _turtle_docrevise(docstr):
3827 """To reduce docstrings from RawTurtle class for functions
3828 """
3829 import re
3830 if docstr is None:
3831 return None
3832 turtlename = _CFG["exampleturtle"]
3833 newdocstr = docstr.replace("%s." % turtlename,"")
3834 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3835 newdocstr = parexp.sub(":", newdocstr)
3836 return newdocstr
3837
3838def _screen_docrevise(docstr):
3839 """To reduce docstrings from TurtleScreen class for functions
3840 """
3841 import re
3842 if docstr is None:
3843 return None
3844 screenname = _CFG["examplescreen"]
3845 newdocstr = docstr.replace("%s." % screenname,"")
3846 parexp = re.compile(r' \(.+ %s\):' % screenname)
3847 newdocstr = parexp.sub(":", newdocstr)
3848 return newdocstr
3849
3850## The following mechanism makes all methods of RawTurtle and Turtle available
3851## as functions. So we can enhance, change, add, delete methods to these
3852## classes and do not need to change anything here.
3853
3854
3855for methodname in _tg_screen_functions:
Martin v. Löwise563aa42008-09-29 22:09:07 +00003856 pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
Martin v. Löwis87184592008-06-04 06:29:55 +00003857 if pl1 == "":
3858 print ">>>>>>", pl1, pl2
3859 continue
3860 defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
3861 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3862 exec defstr
Martin v. Löwise563aa42008-09-29 22:09:07 +00003863 eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
Martin v. Löwis87184592008-06-04 06:29:55 +00003864
3865for methodname in _tg_turtle_functions:
3866 pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
3867 if pl1 == "":
3868 print ">>>>>>", pl1, pl2
3869 continue
3870 defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
3871 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3872 exec defstr
3873 eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
3874
3875
3876done = mainloop = TK.mainloop
3877del pl1, pl2, defstr
3878
3879if __name__ == "__main__":
3880 def switchpen():
3881 if isdown():
3882 pu()
3883 else:
3884 pd()
3885
3886 def demo1():
3887 """Demo of old turtle.py - module"""
3888 reset()
3889 tracer(True)
3890 up()
3891 backward(100)
3892 down()
3893 # draw 3 squares; the last filled
3894 width(3)
3895 for i in range(3):
3896 if i == 2:
3897 fill(1)
3898 for _ in range(4):
3899 forward(20)
3900 left(90)
3901 if i == 2:
3902 color("maroon")
3903 fill(0)
3904 up()
3905 forward(30)
3906 down()
3907 width(1)
3908 color("black")
3909 # move out of the way
3910 tracer(False)
3911 up()
3912 right(90)
3913 forward(100)
3914 right(90)
3915 forward(100)
3916 right(180)
3917 down()
3918 # some text
3919 write("startstart", 1)
3920 write("start", 1)
3921 color("red")
3922 # staircase
3923 for i in range(5):
Georg Brandl33cece02008-05-20 06:58:21 +00003924 forward(20)
3925 left(90)
Martin v. Löwis87184592008-06-04 06:29:55 +00003926 forward(20)
3927 right(90)
3928 # filled staircase
3929 tracer(True)
3930 fill(1)
3931 for i in range(5):
3932 forward(20)
3933 left(90)
3934 forward(20)
3935 right(90)
3936 fill(0)
3937 # more text
Georg Brandl33cece02008-05-20 06:58:21 +00003938
Martin v. Löwis87184592008-06-04 06:29:55 +00003939 def demo2():
3940 """Demo of some new features."""
3941 speed(1)
3942 st()
3943 pensize(3)
3944 setheading(towards(0, 0))
3945 radius = distance(0, 0)/2.0
3946 rt(90)
3947 for _ in range(18):
3948 switchpen()
3949 circle(radius, 10)
3950 write("wait a moment...")
3951 while undobufferentries():
3952 undo()
3953 reset()
3954 lt(90)
3955 colormode(255)
3956 laenge = 10
3957 pencolor("green")
3958 pensize(3)
3959 lt(180)
3960 for i in range(-2, 16):
3961 if i > 0:
3962 begin_fill()
3963 fillcolor(255-15*i, 0, 15*i)
3964 for _ in range(3):
3965 fd(laenge)
3966 lt(120)
3967 laenge += 10
3968 lt(15)
3969 speed((speed()+1)%12)
3970 end_fill()
Georg Brandl33cece02008-05-20 06:58:21 +00003971
Martin v. Löwis87184592008-06-04 06:29:55 +00003972 lt(120)
3973 pu()
3974 fd(70)
3975 rt(30)
3976 pd()
3977 color("red","yellow")
3978 speed(0)
3979 fill(1)
3980 for _ in range(4):
3981 circle(50, 90)
3982 rt(90)
3983 fd(30)
3984 rt(90)
3985 fill(0)
3986 lt(90)
3987 pu()
3988 fd(30)
3989 pd()
3990 shape("turtle")
Georg Brandl33cece02008-05-20 06:58:21 +00003991
Martin v. Löwis87184592008-06-04 06:29:55 +00003992 tri = getturtle()
3993 tri.resizemode("auto")
3994 turtle = Turtle()
3995 turtle.resizemode("auto")
3996 turtle.shape("turtle")
3997 turtle.reset()
3998 turtle.left(90)
3999 turtle.speed(0)
4000 turtle.up()
4001 turtle.goto(280, 40)
4002 turtle.lt(30)
4003 turtle.down()
4004 turtle.speed(6)
4005 turtle.color("blue","orange")
4006 turtle.pensize(2)
4007 tri.speed(6)
Georg Brandl33cece02008-05-20 06:58:21 +00004008 setheading(towards(turtle))
Martin v. Löwis87184592008-06-04 06:29:55 +00004009 count = 1
4010 while tri.distance(turtle) > 4:
4011 turtle.fd(3.5)
4012 turtle.lt(0.6)
4013 tri.setheading(tri.towards(turtle))
4014 tri.fd(4)
4015 if count % 20 == 0:
4016 turtle.stamp()
4017 tri.stamp()
4018 switchpen()
4019 count += 1
4020 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4021 tri.pencolor("black")
4022 tri.pencolor("red")
Georg Brandl33cece02008-05-20 06:58:21 +00004023
Martin v. Löwis87184592008-06-04 06:29:55 +00004024 def baba(xdummy, ydummy):
4025 clearscreen()
4026 bye()
Georg Brandl33cece02008-05-20 06:58:21 +00004027
Martin v. Löwis87184592008-06-04 06:29:55 +00004028 time.sleep(2)
Georg Brandl33cece02008-05-20 06:58:21 +00004029
Martin v. Löwis87184592008-06-04 06:29:55 +00004030 while undobufferentries():
4031 tri.undo()
4032 turtle.undo()
4033 tri.fd(50)
4034 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4035 tri.onclick(baba, 1)
4036
4037 demo1()
Georg Brandl33cece02008-05-20 06:58:21 +00004038 demo2()
Martin v. Löwis87184592008-06-04 06:29:55 +00004039 exitonclick()