blob: 6f733745217fea793cd00a47d271e355a5a1a28b [file] [log] [blame]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001#
2# turtle.py: a Tkinter based turtle graphics module for Python
3# Version 1.0b1 - 31. 5. 2008
4#
5# Copyright (C) 2006 - 2008 Gregor Lingl
6# email: glingl@aon.at
7#
8# This software is provided 'as-is', without any express or implied
9# warranty. In no event will the authors be held liable for any damages
10# arising from the use of this software.
11#
12# Permission is granted to anyone to use this software for any purpose,
13# including commercial applications, and to alter it and redistribute it
14# freely, subject to the following restrictions:
15#
16# 1. The origin of this software must not be misrepresented; you must not
17# claim that you wrote the original software. If you use this software
18# in a product, an acknowledgment in the product documentation would be
19# appreciated but is not required.
20# 2. Altered source versions must be plainly marked as such, and must not be
21# misrepresented as being the original software.
22# 3. This notice may not be removed or altered from any source distribution.
23
Guido van Rossumb241b671998-12-04 16:42:46 +000024
Thomas Wouters477c8d52006-05-27 19:21:47 +000025"""
26Turtle graphics is a popular way for introducing programming to
27kids. It was part of the original Logo programming language developed
Martin v. Löwis97cf99f2008-06-10 04:44:07 +000028by Wally Feurzig and Seymour Papert in 1966.
Thomas Wouters477c8d52006-05-27 19:21:47 +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öwis97cf99f2008-06-10 04:44:07 +000037
38----- turtle.py
39
40This module is an extended reimplementation of turtle.py from the
41Python standard distribution up to Python 2.5. (See: http:\\www.python.org)
42
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
57 (multicolored) shapes. Turtle shapes can be stgretched and tilted, which
58 makes turtles zu very versatile geometrical objects.
59
60- Fine control over turtle movement and screen updates via delay(),
61 and enhanced tracer() and speed() methods.
62
63- Aliases for the most commonly used commands, like fd for forward etc.,
64 following the early Logo traditions. This reduces the boring work of
65 typing long sequences of commands, which often occur in a natural way
66 when kids try to program fancy pictures on their first encounter with
67 turtle graphcis.
68
69- Turtles now have an undo()-method with configurable undo-buffer.
70
71- Some simple commands/methods for creating event driven programs
72 (mouse-, key-, timer-events). Especially useful for programming games.
73
74- A scrollable Canvas class. The default scrollable Canvas can be
75 extended interactively as needed while playing around with the turtle(s).
76
77- A TurtleScreen class with methods controlling background color or
78 background image, window and canvas size and other properties of the
79 TurtleScreen.
80
81- There is a method, setworldcoordinates(), to install a user defined
82 coordinate-system for the TurtleScreen.
83
84- The implementation uses a 2-vector class named Vec2D, derived from tuple.
85 This class is public, so it can be imported by the application programmer,
86 which makes certain types of computations very natural and compact.
87
88- Appearance of the TurtleScreen and the Turtles at startup/import can be
89 configured by means of a turtle.cfg configuration file.
90 The default configuration mimics the appearance of the old turtle module.
91
92- If configured appropriately the module reads in docstrings from a docstring
93 dictionary in some different language, supplied separately and replaces
94 the english ones by those read in. There is a utility function
95 write_docstringdict() to write a dictionary with the original (english)
96 docstrings to disc, so it can serve as a template for translations.
97
98Behind the scenes there are some features included with possible
99extensionsin in mind. These will be commented and documented elsewhere.
100
Thomas Wouters477c8d52006-05-27 19:21:47 +0000101"""
102
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000103_ver = "turtle 1.0b1- - for Python 3.0 - 9. 6. 2008, 01:15"
Guido van Rossumfd2ede22002-09-23 16:55:05 +0000104
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000105# print(_ver)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000106
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000107import tkinter as TK
108import types
109import math
110import time
111import os
Guido van Rossumb241b671998-12-04 16:42:46 +0000112
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000113from os.path import isfile, split, join
114from copy import deepcopy
Guido van Rossumb241b671998-12-04 16:42:46 +0000115
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000116#from math import * ## for compatibility with old turtle module
Guido van Rossumb241b671998-12-04 16:42:46 +0000117
Martin v. Löwis97cf99f2008-06-10 04:44:07 +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',
130 'fillcolor', 'forward', 'get_poly', 'getpen', 'getscreen',
131 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
132 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
133 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
134 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
135 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
136 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'showturtle',
137 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', #'tracer',
138 'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
139 #'window_height', 'window_width',
140 'write', 'xcor', 'ycor']
141_tg_utilities = ['write_docstringdict', 'done', 'mainloop']
142##_math_functions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
143## 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
144## 'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
Thomas Wouters477c8d52006-05-27 19:21:47 +0000145
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000146__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
147 _tg_utilities) # + _math_functions)
Guido van Rossumb241b671998-12-04 16:42:46 +0000148
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000149_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
150 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
151 'turtlesize', 'up', 'width']
Thomas Wouters477c8d52006-05-27 19:21:47 +0000152
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000153_CFG = {"width" : 0.5, # Screen
154 "height" : 0.75,
155 "canvwidth" : 400,
156 "canvheight": 300,
157 "leftright": None,
158 "topbottom": None,
159 "mode": "standard", # TurtleScreen
160 "colormode": 1.0,
161 "delay": 10,
162 "undobuffersize": 1000, # RawTurtle
163 "shape": "classic",
164 "pencolor" : "black",
165 "fillcolor" : "black",
166 "resizemode" : "noresize",
167 "visible" : True,
168 "language": "english", # docstrings
169 "exampleturtle": "turtle",
170 "examplescreen": "screen",
171 "title": "Python Turtle Graphics",
172 "using_IDLE": False
173 }
Guido van Rossumb241b671998-12-04 16:42:46 +0000174
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000175##print "cwd:", os.getcwd()
176##print "__file__:", __file__
177##
178##def show(dictionary):
179## print "=========================="
180## for key in sorted(dictionary.keys()):
181## print key, ":", dictionary[key]
182## print "=========================="
183## print
Thomas Wouters477c8d52006-05-27 19:21:47 +0000184
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000185def config_dict(filename):
186 """Convert content of config-file into dictionary."""
187 f = open(filename, "r")
188 cfglines = f.readlines()
189 f.close()
190 cfgdict = {}
191 for line in cfglines:
192 line = line.strip()
193 if not line or line.startswith("#"):
194 continue
195 try:
196 key, value = line.split("=")
197 except:
198 print("Bad line in config-file %s:\n%s" % (filename,line))
199 continue
200 key = key.strip()
201 value = value.strip()
202 if value in ["True", "False", "None", "''", '""']:
203 value = eval(value)
Guido van Rossumb241b671998-12-04 16:42:46 +0000204 else:
205 try:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000206 if "." in value:
207 value = float(value)
208 else:
209 value = int(value)
Guido van Rossumb241b671998-12-04 16:42:46 +0000210 except:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000211 pass # value need not be converted
212 cfgdict[key] = value
213 return cfgdict
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000214
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000215def readconfig(cfgdict):
216 """Read config-files, change configuration-dict accordingly.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000217
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000218 If there is a turtle.cfg file in the current working directory,
219 read it from there. If this contains an importconfig-value,
220 say 'myway', construct filename turtle_mayway.cfg else use
221 turtle.cfg and read it from the import-directory, where
222 turtle.py is located.
223 Update configuration dictionary first according to config-file,
224 in the import directory, then according to config-file in the
225 current working directory.
226 If no config-file is found, the default configuration is used.
227 """
228 default_cfg = "turtle.cfg"
229 cfgdict1 = {}
230 cfgdict2 = {}
231 if isfile(default_cfg):
232 cfgdict1 = config_dict(default_cfg)
233 #print "1. Loading config-file %s from: %s" % (default_cfg, os.getcwd())
234 if "importconfig" in cfgdict1:
235 default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
236 try:
237 head, tail = split(__file__)
238 cfg_file2 = join(head, default_cfg)
239 except:
240 cfg_file2 = ""
241 if isfile(cfg_file2):
242 #print "2. Loading config-file %s:" % cfg_file2
243 cfgdict2 = config_dict(cfg_file2)
244## show(_CFG)
245## show(cfgdict2)
246 _CFG.update(cfgdict2)
247## show(_CFG)
248## show(cfgdict1)
249 _CFG.update(cfgdict1)
250## show(_CFG)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000251
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000252try:
253 readconfig(_CFG)
254except:
255 print ("No configfile read, reason unknown")
256
257
258class Vec2D(tuple):
259 """A 2 dimensional vector class, used as a helper class
260 for implementing turtle graphics.
261 May be useful for turtle graphics programs also.
262 Derived from tuple, so a vector is a tuple!
263
264 Provides (for a, b vectors, k number):
265 a+b vector addition
266 a-b vector subtraction
267 a*b inner product
268 k*a and a*k multiplication with scalar
269 |a| absolute value of a
270 a.rotate(angle) rotation
271 """
272 def __new__(cls, x, y):
273 return tuple.__new__(cls, (x, y))
274 def __add__(self, other):
275 return Vec2D(self[0]+other[0], self[1]+other[1])
276 def __mul__(self, other):
277 if isinstance(other, Vec2D):
278 return self[0]*other[0]+self[1]*other[1]
279 return Vec2D(self[0]*other, self[1]*other)
280 def __rmul__(self, other):
281 if isinstance(other, int) or isinstance(other, float):
282 return Vec2D(self[0]*other, self[1]*other)
283 def __sub__(self, other):
284 return Vec2D(self[0]-other[0], self[1]-other[1])
285 def __neg__(self):
286 return Vec2D(-self[0], -self[1])
287 def __abs__(self):
288 return (self[0]**2 + self[1]**2)**0.5
289 def rotate(self, angle):
290 """rotate self counterclockwise by angle
291 """
292 perp = Vec2D(-self[1], self[0])
293 angle = angle * math.pi / 180.0
294 c, s = math.cos(angle), math.sin(angle)
295 return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
296 def __getnewargs__(self):
297 return (self[0], self[1])
298 def __repr__(self):
299 return "(%.2f,%.2f)" % self
300
301
302##############################################################################
303### From here up to line : Tkinter - Interface for turtle.py ###
304### May be replaced by an interface to some different graphcis-toolkit ###
305##############################################################################
306
307## helper functions for Scrolled Canvas, to forward Canvas-methods
308## to ScrolledCanvas class
309
310def __methodDict(cls, _dict):
311 """helper function for Scrolled Canvas"""
312 baseList = list(cls.__bases__)
313 baseList.reverse()
314 for _super in baseList:
315 __methodDict(_super, _dict)
316 for key, value in cls.__dict__.items():
317 if type(value) == types.FunctionType:
318 _dict[key] = value
319
320def __methods(cls):
321 """helper function for Scrolled Canvas"""
322 _dict = {}
323 __methodDict(cls, _dict)
324 return _dict.keys()
325
326__stringBody = (
327 'def %(method)s(self, *args, **kw): return ' +
328 'self.%(attribute)s.%(method)s(*args, **kw)')
329
330def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
331 ### MANY CHANGES ###
332 _dict_1 = {}
333 __methodDict(toClass, _dict_1)
334 _dict = {}
335 mfc = __methods(fromClass)
336 for ex in _dict_1.keys():
337 if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
338 pass
339 else:
340 _dict[ex] = _dict_1[ex]
341
342 for method, func in _dict.items():
343 d = {'method': method, 'func': func}
344 if isinstance(toPart, str):
345 execString = \
346 __stringBody % {'method' : method, 'attribute' : toPart}
347 exec(execString, d)
348 setattr(fromClass, method, d[method]) ### NEWU!
349
350
351class ScrolledCanvas(TK.Frame):
352 """Modeled after the scrolled canvas class from Grayons's Tkinter book.
353
354 Used as the default canvas, which pops up automatically when
355 using turtle graphics functions or the Turtle class.
356 """
357 def __init__(self, master, width=500, height=350,
358 canvwidth=600, canvheight=500):
359 TK.Frame.__init__(self, master, width=width, height=height)
Martin v. Löwis22d297b2008-11-19 09:14:30 +0000360 self._rootwindow = self.winfo_toplevel()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000361 self.width, self.height = width, height
362 self.canvwidth, self.canvheight = canvwidth, canvheight
363 self.bg = "white"
364 self._canvas = TK.Canvas(master, width=width, height=height,
365 bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
366 self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
367 orient=TK.HORIZONTAL)
368 self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
369 self._canvas.configure(xscrollcommand=self.hscroll.set,
370 yscrollcommand=self.vscroll.set)
371 self.rowconfigure(0, weight=1, minsize=0)
372 self.columnconfigure(0, weight=1, minsize=0)
373 self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
374 column=0, rowspan=1, columnspan=1, sticky='news')
375 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
376 column=1, rowspan=1, columnspan=1, sticky='news')
377 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
378 column=0, rowspan=1, columnspan=1, sticky='news')
379 self.reset()
Martin v. Löwis22d297b2008-11-19 09:14:30 +0000380 self._rootwindow.bind('<Configure>', self.onResize)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000381
382 def reset(self, canvwidth=None, canvheight=None, bg = None):
383 """Ajust canvas and scrollbars according to given canvas size."""
384 if canvwidth:
385 self.canvwidth = canvwidth
386 if canvheight:
387 self.canvheight = canvheight
388 if bg:
389 self.bg = bg
390 self._canvas.config(bg=bg,
391 scrollregion=(-self.canvwidth//2, -self.canvheight//2,
392 self.canvwidth//2, self.canvheight//2))
393 self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
394 self.canvwidth)
395 self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
396 self.canvheight)
397 self.adjustScrolls()
398
399
400 def adjustScrolls(self):
401 """ Adjust scrollbars according to window- and canvas-size.
402 """
403 cwidth = self._canvas.winfo_width()
404 cheight = self._canvas.winfo_height()
405 self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
406 self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
407 if cwidth < self.canvwidth or cheight < self.canvheight:
408 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
409 column=0, rowspan=1, columnspan=1, sticky='news')
410 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
411 column=1, rowspan=1, columnspan=1, sticky='news')
412 else:
413 self.hscroll.grid_forget()
414 self.vscroll.grid_forget()
415
416 def onResize(self, event):
417 """self-explanatory"""
418 self.adjustScrolls()
419
420 def bbox(self, *args):
421 """ 'forward' method, which canvas itself has inherited...
422 """
423 return self._canvas.bbox(*args)
424
425 def cget(self, *args, **kwargs):
426 """ 'forward' method, which canvas itself has inherited...
427 """
428 return self._canvas.cget(*args, **kwargs)
429
430 def config(self, *args, **kwargs):
431 """ 'forward' method, which canvas itself has inherited...
432 """
433 self._canvas.config(*args, **kwargs)
434
435 def bind(self, *args, **kwargs):
436 """ 'forward' method, which canvas itself has inherited...
437 """
438 self._canvas.bind(*args, **kwargs)
439
440 def unbind(self, *args, **kwargs):
441 """ 'forward' method, which canvas itself has inherited...
442 """
443 self._canvas.unbind(*args, **kwargs)
444
445 def focus_force(self):
446 """ 'forward' method, which canvas itself has inherited...
447 """
448 self._canvas.focus_force()
449
450__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
451
452
453class _Root(TK.Tk):
454 """Root class for Screen based on Tkinter."""
455 def __init__(self):
456 TK.Tk.__init__(self)
457
458 def setupcanvas(self, width, height, cwidth, cheight):
459 self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
460 self._canvas.pack(expand=1, fill="both")
461
462 def _getcanvas(self):
463 return self._canvas
464
465 def set_geometry(self, width, height, startx, starty):
466 self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
467
468 def ondestroy(self, destroy):
469 self.wm_protocol("WM_DELETE_WINDOW", destroy)
470
471 def win_width(self):
472 return self.winfo_screenwidth()
473
474 def win_height(self):
475 return self.winfo_screenheight()
476
477Canvas = TK.Canvas
478
479
480class TurtleScreenBase(object):
481 """Provide the basic graphics functionality.
482 Interface between Tkinter and turtle.py.
483
484 To port turtle.py to some different graphics toolkit
485 a corresponding TurtleScreenBase class has to be implemented.
486 """
487
488 @staticmethod
489 def _blankimage():
490 """return a blank image object
491 """
492 img = TK.PhotoImage(width=1, height=1)
493 img.blank()
494 return img
495
496 @staticmethod
497 def _image(filename):
498 """return an image object containing the
499 imagedata from a gif-file named filename.
500 """
501 return TK.PhotoImage(file=filename)
502
503 def __init__(self, cv):
504 self.cv = cv
505 if isinstance(cv, ScrolledCanvas):
506 w = self.cv.canvwidth
507 h = self.cv.canvheight
508 else: # expected: ordinary TK.Canvas
509 w = int(self.cv.cget("width"))
510 h = int(self.cv.cget("height"))
511 self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
512 self.canvwidth = w
513 self.canvheight = h
514 self.xscale = self.yscale = 1.0
515
516 def _createpoly(self):
517 """Create an invisible polygon item on canvas self.cv)
518 """
519 return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
520
521 def _drawpoly(self, polyitem, coordlist, fill=None,
522 outline=None, width=None, top=False):
523 """Configure polygonitem polyitem according to provided
524 arguments:
525 coordlist is sequence of coordinates
526 fill is filling color
527 outline is outline color
528 top is a boolean value, which specifies if polyitem
529 will be put on top of the canvas' displaylist so it
530 will not be covered by other items.
531 """
532 cl = []
533 for x, y in coordlist:
534 cl.append(x * self.xscale)
535 cl.append(-y * self.yscale)
536 self.cv.coords(polyitem, *cl)
537 if fill is not None:
538 self.cv.itemconfigure(polyitem, fill=fill)
539 if outline is not None:
540 self.cv.itemconfigure(polyitem, outline=outline)
541 if width is not None:
542 self.cv.itemconfigure(polyitem, width=width)
543 if top:
544 self.cv.tag_raise(polyitem)
545
546 def _createline(self):
547 """Create an invisible line item on canvas self.cv)
548 """
549 return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
550 capstyle = TK.ROUND)
551
552 def _drawline(self, lineitem, coordlist=None,
553 fill=None, width=None, top=False):
554 """Configure lineitem according to provided arguments:
555 coordlist is sequence of coordinates
556 fill is drawing color
557 width is width of drawn line.
558 top is a boolean value, which specifies if polyitem
559 will be put on top of the canvas' displaylist so it
560 will not be covered by other items.
561 """
562 if coordlist is not None:
563 cl = []
564 for x, y in coordlist:
565 cl.append(x * self.xscale)
566 cl.append(-y * self.yscale)
567 self.cv.coords(lineitem, *cl)
568 if fill is not None:
569 self.cv.itemconfigure(lineitem, fill=fill)
570 if width is not None:
571 self.cv.itemconfigure(lineitem, width=width)
572 if top:
573 self.cv.tag_raise(lineitem)
574
575 def _delete(self, item):
576 """Delete graphics item from canvas.
577 If item is"all" delete all graphics items.
578 """
579 self.cv.delete(item)
580
581 def _update(self):
582 """Redraw graphics items on canvas
583 """
584 self.cv.update()
585
586 def _delay(self, delay):
587 """Delay subsequent canvas actions for delay ms."""
588 self.cv.after(delay)
589
590 def _iscolorstring(self, color):
591 """Check if the string color is a legal Tkinter color string.
592 """
593 try:
594 rgb = self.cv.winfo_rgb(color)
595 ok = True
596 except TK.TclError:
597 ok = False
598 return ok
599
600 def _bgcolor(self, color=None):
601 """Set canvas' backgroundcolor if color is not None,
602 else return backgroundcolor."""
603 if color is not None:
604 self.cv.config(bg = color)
605 self._update()
606 else:
607 return self.cv.cget("bg")
608
609 def _write(self, pos, txt, align, font, pencolor):
610 """Write txt at pos in canvas with specified font
611 and color.
612 Return text item and x-coord of right bottom corner
613 of text's bounding box."""
614 x, y = pos
615 x = x * self.xscale
616 y = y * self.yscale
617 anchor = {"left":"sw", "center":"s", "right":"se" }
618 item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
619 fill = pencolor, font = font)
620 x0, y0, x1, y1 = self.cv.bbox(item)
621 self.cv.update()
622 return item, x1-1
623
624## def _dot(self, pos, size, color):
625## """may be implemented for some other graphics toolkit"""
626
627 def _onclick(self, item, fun, num=1, add=None):
628 """Bind fun to mouse-click event on turtle.
629 fun must be a function with two arguments, the coordinates
630 of the clicked point on the canvas.
631 num, the number of the mouse-button defaults to 1
632 """
633 if fun is None:
634 self.cv.tag_unbind(item, "<Button-%s>" % num)
635 else:
636 def eventfun(event):
637 x, y = (self.cv.canvasx(event.x)/self.xscale,
638 -self.cv.canvasy(event.y)/self.yscale)
639 fun(x, y)
640 self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
641
642 def _onrelease(self, item, fun, num=1, add=None):
643 """Bind fun to mouse-button-release event on turtle.
644 fun must be a function with two arguments, the coordinates
645 of the point on the canvas where mouse button is released.
646 num, the number of the mouse-button defaults to 1
647
648 If a turtle is clicked, first _onclick-event will be performed,
649 then _onscreensclick-event.
650 """
651 if fun is None:
652 self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
653 else:
654 def eventfun(event):
655 x, y = (self.cv.canvasx(event.x)/self.xscale,
656 -self.cv.canvasy(event.y)/self.yscale)
657 fun(x, y)
658 self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
659 eventfun, add)
660
661 def _ondrag(self, item, fun, num=1, add=None):
662 """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
663 fun must be a function with two arguments, the coordinates of the
664 actual mouse position on the canvas.
665 num, the number of the mouse-button defaults to 1
666
667 Every sequence of mouse-move-events on a turtle is preceded by a
668 mouse-click event on that turtle.
669 """
670 if fun is None:
671 self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
672 else:
673 def eventfun(event):
674 try:
675 x, y = (self.cv.canvasx(event.x)/self.xscale,
676 -self.cv.canvasy(event.y)/self.yscale)
677 fun(x, y)
678 except:
679 pass
680 self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
681
682 def _onscreenclick(self, fun, num=1, add=None):
683 """Bind fun to mouse-click event on canvas.
684 fun must be a function with two arguments, the coordinates
685 of the clicked point on the canvas.
686 num, the number of the mouse-button defaults to 1
687
688 If a turtle is clicked, first _onclick-event will be performed,
689 then _onscreensclick-event.
690 """
691 if fun is None:
692 self.cv.unbind("<Button-%s>" % num)
693 else:
694 def eventfun(event):
695 x, y = (self.cv.canvasx(event.x)/self.xscale,
696 -self.cv.canvasy(event.y)/self.yscale)
697 fun(x, y)
698 self.cv.bind("<Button-%s>" % num, eventfun, add)
699
700 def _onkey(self, fun, key):
701 """Bind fun to key-release event of key.
702 Canvas must have focus. See method listen
703 """
704 if fun is None:
705 self.cv.unbind("<KeyRelease-%s>" % key, None)
706 else:
707 def eventfun(event):
708 fun()
709 self.cv.bind("<KeyRelease-%s>" % key, eventfun)
710
711 def _listen(self):
712 """Set focus on canvas (in order to collect key-events)
713 """
714 self.cv.focus_force()
715
716 def _ontimer(self, fun, t):
717 """Install a timer, which calls fun after t milliseconds.
718 """
719 if t == 0:
720 self.cv.after_idle(fun)
721 else:
722 self.cv.after(t, fun)
723
724 def _createimage(self, image):
725 """Create and return image item on canvas.
726 """
727 return self.cv.create_image(0, 0, image=image)
728
729 def _drawimage(self, item, pos, image):
730 """Configure image item as to draw image object
731 at position (x,y) on canvas)
732 """
733 x, y = pos
734 self.cv.coords(item, (x * self.xscale, -y * self.yscale))
735 self.cv.itemconfig(item, image=image)
736
737 def _setbgpic(self, item, image):
738 """Configure image item as to draw image object
739 at center of canvas. Set item to the first item
740 in the displaylist, so it will be drawn below
741 any other item ."""
742 self.cv.itemconfig(item, image=image)
743 self.cv.tag_lower(item)
744
745 def _type(self, item):
746 """Return 'line' or 'polygon' or 'image' depending on
747 type of item.
748 """
749 return self.cv.type(item)
750
751 def _pointlist(self, item):
752 """returns list of coordinate-pairs of points of item
753 Example (for insiders):
754 >>> from turtle import *
755 >>> getscreen()._pointlist(getturtle().turtle._item)
756 [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
757 (9.9999999999999982, 0.0)]
758 >>> """
759 cl = list(self.cv.coords(item))
760 pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
761 return pl
762
763 def _setscrollregion(self, srx1, sry1, srx2, sry2):
764 self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
765
766 def _rescale(self, xscalefactor, yscalefactor):
767 items = self.cv.find_all()
768 for item in items:
769 coordinates = list(self.cv.coords(item))
770 newcoordlist = []
771 while coordinates:
772 x, y = coordinates[:2]
773 newcoordlist.append(x * xscalefactor)
774 newcoordlist.append(y * yscalefactor)
775 coordinates = coordinates[2:]
776 self.cv.coords(item, *newcoordlist)
777
778 def _resize(self, canvwidth=None, canvheight=None, bg=None):
779 """Resize the canvas, the turtles are drawing on. Does
780 not alter the drawing window.
781 """
782 # needs amendment
783 if not isinstance(self.cv, ScrolledCanvas):
784 return self.canvwidth, self.canvheight
785 if canvwidth is None and canvheight is None and bg is None:
786 return self.cv.canvwidth, self.cv.canvheight
787 if canvwidth is not None:
788 self.canvwidth = canvwidth
789 if canvheight is not None:
790 self.canvheight = canvheight
791 self.cv.reset(canvwidth, canvheight, bg)
792
793 def _window_size(self):
794 """ Return the width and height of the turtle window.
795 """
796 width = self.cv.winfo_width()
797 if width <= 1: # the window isn't managed by a geometry manager
798 width = self.cv['width']
799 height = self.cv.winfo_height()
800 if height <= 1: # the window isn't managed by a geometry manager
801 height = self.cv['height']
802 return width, height
803
804
805##############################################################################
806### End of Tkinter - interface ###
807##############################################################################
808
809
810class Terminator (Exception):
811 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
812
813 Thus stops execution of turtle graphics script. Main purpose: use in
814 in the Demo-Viewer turtle.Demo.py.
815 """
816 pass
817
818
819class TurtleGraphicsError(Exception):
820 """Some TurtleGraphics Error
821 """
822
823
824class Shape(object):
825 """Data structure modeling shapes.
826
827 attribute _type is one of "polygon", "image", "compound"
828 attribute _data is - depending on _type a poygon-tuple,
829 an image or a list constructed using the addcomponent method.
830 """
831 def __init__(self, type_, data=None):
832 self._type = type_
833 if type_ == "polygon":
834 if isinstance(data, list):
835 data = tuple(data)
836 elif type_ == "image":
837 if isinstance(data, str):
838 if data.lower().endswith(".gif") and isfile(data):
839 data = TurtleScreen._image(data)
840 # else data assumed to be Photoimage
841 elif type_ == "compound":
842 data = []
843 else:
844 raise TurtleGraphicsError("There is no shape type %s" % type_)
845 self._data = data
846
847 def addcomponent(self, poly, fill, outline=None):
848 """Add component to a shape of type compound.
849
850 Arguments: poly is a polygon, i. e. a tuple of number pairs.
851 fill is the fillcolor of the component,
852 outline is the outline color of the component.
853
854 call (for a Shapeobject namend s):
855 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000856
857 Example:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000858 >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
859 >>> s = Shape("compound")
860 >>> s.addcomponent(poly, "red", "blue")
861 ### .. add more components and then use register_shape()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000862 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000863 if self._type != "compound":
864 raise TurtleGraphicsError("Cannot add component to %s Shape"
865 % self._type)
866 if outline is None:
867 outline = fill
868 self._data.append([poly, fill, outline])
Guido van Rossumb241b671998-12-04 16:42:46 +0000869
Thomas Wouters477c8d52006-05-27 19:21:47 +0000870
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000871class Tbuffer(object):
872 """Ring buffer used as undobuffer for RawTurtle objects."""
873 def __init__(self, bufsize=10):
874 self.bufsize = bufsize
875 self.buffer = [[None]] * bufsize
876 self.ptr = -1
877 self.cumulate = False
878 def reset(self, bufsize=None):
879 if bufsize is None:
880 for i in range(self.bufsize):
881 self.buffer[i] = [None]
882 else:
883 self.bufsize = bufsize
884 self.buffer = [[None]] * bufsize
885 self.ptr = -1
886 def push(self, item):
887 if self.bufsize > 0:
888 if not self.cumulate:
889 self.ptr = (self.ptr + 1) % self.bufsize
890 self.buffer[self.ptr] = item
891 else:
892 self.buffer[self.ptr].append(item)
893 def pop(self):
894 if self.bufsize > 0:
895 item = self.buffer[self.ptr]
896 if item is None:
897 return None
898 else:
899 self.buffer[self.ptr] = [None]
900 self.ptr = (self.ptr - 1) % self.bufsize
901 return (item)
902 def nr_of_items(self):
903 return self.bufsize - self.buffer.count([None])
904 def __repr__(self):
905 return str(self.buffer) + " " + str(self.ptr)
906
907
908
909class TurtleScreen(TurtleScreenBase):
910 """Provides screen oriented methods like setbg etc.
911
912 Only relies upon the methods of TurtleScreenBase and NOT
913 upon components of the underlying graphics toolkit -
914 which is Tkinter in this case.
915 """
916# _STANDARD_DELAY = 5
917 _RUNNING = True
918
919 def __init__(self, cv, mode=_CFG["mode"],
920 colormode=_CFG["colormode"], delay=_CFG["delay"]):
921 self._shapes = {
922 "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
923 "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
924 (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
925 (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
926 (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
927 (2,14))),
928 "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
929 (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
930 (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
931 (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
932 (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
933 (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
934 "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
935 (-10,-10))),
936 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
937 (-10,-5.77))),
938 "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
939 "blank" : Shape("image", self._blankimage())
940 }
941
942 self._bgpics = {"nopic" : ""}
943
944 TurtleScreenBase.__init__(self, cv)
945 self._mode = mode
946 self._delayvalue = delay
947 self._colormode = _CFG["colormode"]
948 self._keys = []
949 self.clear()
950
951 def clear(self):
952 """Delete all drawings and all turtles from the TurtleScreen.
953
954 Reset empty TurtleScreen to it's initial state: white background,
955 no backgroundimage, no eventbindings and tracing on.
956
957 No argument.
958
959 Example (for a TurtleScreen instance named screen):
960 screen.clear()
961
962 Note: this method is not available as function.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000963 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000964 self._delayvalue = _CFG["delay"]
965 self._colormode = _CFG["colormode"]
966 self._delete("all")
967 self._bgpic = self._createimage("")
968 self._bgpicname = "nopic"
969 self._tracing = 1
970 self._updatecounter = 0
971 self._turtles = []
972 self.bgcolor("white")
973 for btn in 1, 2, 3:
974 self.onclick(None, btn)
975 for key in self._keys[:]:
976 self.onkey(None, key)
977 Turtle._pen = None
Guido van Rossumb241b671998-12-04 16:42:46 +0000978
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000979 def mode(self, mode=None):
980 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000981
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000982 Optional argument:
983 mode -- on of the strings 'standard', 'logo' or 'world'
984
985 Mode 'standard' is compatible with turtle.py.
986 Mode 'logo' is compatible with most Logo-Turtle-Graphics.
987 Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
988 this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
989 If mode is not given, return the current mode.
990
991 Mode Initial turtle heading positive angles
992 ------------|-------------------------|-------------------
993 'standard' to the right (east) counterclockwise
994 'logo' upward (north) clockwise
995
996 Examples:
997 >>> mode('logo') # resets turtle heading to north
998 >>> mode()
999 'logo'
Thomas Wouters477c8d52006-05-27 19:21:47 +00001000 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001001 if mode == None:
1002 return self._mode
1003 mode = mode.lower()
1004 if mode not in ["standard", "logo", "world"]:
1005 raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1006 self._mode = mode
1007 if mode in ["standard", "logo"]:
1008 self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1009 self.canvwidth//2, self.canvheight//2)
1010 self.xscale = self.yscale = 1.0
1011 self.reset()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001012
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001013 def setworldcoordinates(self, llx, lly, urx, ury):
1014 """Set up a user defined coordinate-system.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001015
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001016 Arguments:
1017 llx -- a number, x-coordinate of lower left corner of canvas
1018 lly -- a number, y-coordinate of lower left corner of canvas
1019 urx -- a number, x-coordinate of upper right corner of canvas
1020 ury -- a number, y-coordinate of upper right corner of canvas
1021
1022 Set up user coodinat-system and switch to mode 'world' if necessary.
1023 This performs a screen.reset. If mode 'world' is already active,
1024 all drawings are redrawn according to the new coordinates.
1025
1026 But ATTENTION: in user-defined coordinatesystems angles may appear
1027 distorted. (see Screen.mode())
1028
1029 Example (for a TurtleScreen instance named screen):
1030 >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1031 >>> for _ in range(36):
1032 left(10)
1033 forward(0.5)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001034 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001035 if self.mode() != "world":
1036 self.mode("world")
1037 xspan = float(urx - llx)
1038 yspan = float(ury - lly)
1039 wx, wy = self._window_size()
1040 self.screensize(wx-20, wy-20)
1041 oldxscale, oldyscale = self.xscale, self.yscale
1042 self.xscale = self.canvwidth / xspan
1043 self.yscale = self.canvheight / yspan
1044 srx1 = llx * self.xscale
1045 sry1 = -ury * self.yscale
1046 srx2 = self.canvwidth + srx1
1047 sry2 = self.canvheight + sry1
1048 self._setscrollregion(srx1, sry1, srx2, sry2)
1049 self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1050 self.update()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001051
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001052 def register_shape(self, name, shape=None):
1053 """Adds a turtle shape to TurtleScreen's shapelist.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001054
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001055 Arguments:
1056 (1) name is the name of a gif-file and shape is None.
1057 Installs the corresponding image shape.
1058 !! Image-shapes DO NOT rotate when turning the turtle,
1059 !! so they do not display the heading of the turtle!
1060 (2) name is an arbitrary string and shape is a tuple
1061 of pairs of coordinates. Installs the corresponding
1062 polygon shape
1063 (3) name is an arbitrary string and shape is a
1064 (compound) Shape object. Installs the corresponding
1065 compound shape.
1066 To use a shape, you have to issue the command shape(shapename).
Thomas Wouters477c8d52006-05-27 19:21:47 +00001067
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001068 call: register_shape("turtle.gif")
1069 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1070
1071 Example (for a TurtleScreen instance named screen):
1072 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1073
Thomas Wouters477c8d52006-05-27 19:21:47 +00001074 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001075 if shape is None:
1076 # image
1077 if name.lower().endswith(".gif"):
1078 shape = Shape("image", self._image(name))
1079 else:
1080 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1081 + "Use help(register_shape)" )
1082 elif isinstance(shape, tuple):
1083 shape = Shape("polygon", shape)
1084 ## else shape assumed to be Shape-instance
1085 self._shapes[name] = shape
1086 # print "shape added:" , self._shapes
Guido van Rossumbffa52f2002-09-29 00:25:51 +00001087
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001088 def _colorstr(self, color):
1089 """Return color string corresponding to args.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001090
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001091 Argument may be a string or a tuple of three
1092 numbers corresponding to actual colormode,
1093 i.e. in the range 0<=n<=colormode.
1094
1095 If the argument doesn't represent a color,
1096 an error is raised.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001097 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001098 if len(color) == 1:
1099 color = color[0]
1100 if isinstance(color, str):
1101 if self._iscolorstring(color) or color == "":
1102 return color
1103 else:
1104 raise TurtleGraphicsError("bad color string: %s" % str(color))
1105 try:
1106 r, g, b = color
1107 except:
1108 raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1109 if self._colormode == 1.0:
1110 r, g, b = [round(255.0*x) for x in (r, g, b)]
1111 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1112 raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1113 return "#%02x%02x%02x" % (r, g, b)
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001114
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001115 def _color(self, cstr):
1116 if not cstr.startswith("#"):
1117 return cstr
1118 if len(cstr) == 7:
1119 cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1120 elif len(cstr) == 4:
1121 cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1122 else:
1123 raise TurtleGraphicsError("bad colorstring: %s" % cstr)
1124 return tuple([c * self._colormode/255 for c in cl])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001125
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001126 def colormode(self, cmode=None):
1127 """Return the colormode or set it to 1.0 or 255.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001128
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001129 Optional argument:
1130 cmode -- one of the values 1.0 or 255
Thomas Wouters477c8d52006-05-27 19:21:47 +00001131
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001132 r, g, b values of colortriples have to be in range 0..cmode.
1133
1134 Example (for a TurtleScreen instance named screen):
1135 >>> screen.colormode()
1136 1.0
1137 >>> screen.colormode(255)
1138 >>> turtle.pencolor(240,160,80)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001139 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001140 if cmode is None:
1141 return self._colormode
1142 if cmode == 1.0:
1143 self._colormode = float(cmode)
1144 elif cmode == 255:
1145 self._colormode = int(cmode)
1146
1147 def reset(self):
1148 """Reset all Turtles on the Screen to their initial state.
1149
1150 No argument.
1151
1152 Example (for a TurtleScreen instance named screen):
1153 >>> screen.reset()
1154 """
1155 for turtle in self._turtles:
1156 turtle._setmode(self._mode)
1157 turtle.reset()
1158
1159 def turtles(self):
1160 """Return the list of turtles on the screen.
1161
1162 Example (for a TurtleScreen instance named screen):
1163 >>> screen.turtles()
1164 [<turtle.Turtle object at 0x00E11FB0>]
1165 """
1166 return self._turtles
1167
1168 def bgcolor(self, *args):
1169 """Set or return backgroundcolor of the TurtleScreen.
1170
1171 Arguments (if given): a color string or three numbers
1172 in the range 0..colormode or a 3-tuple of such numbers.
1173
1174 Example (for a TurtleScreen instance named screen):
1175 >>> screen.bgcolor("orange")
1176 >>> screen.bgcolor()
1177 'orange'
1178 >>> screen.bgcolor(0.5,0,0.5)
1179 >>> screen.bgcolor()
1180 '#800080'
1181 """
1182 if args:
1183 color = self._colorstr(args)
1184 else:
1185 color = None
1186 color = self._bgcolor(color)
1187 if color is not None:
1188 color = self._color(color)
1189 return color
1190
1191 def tracer(self, n=None, delay=None):
1192 """Turns turtle animation on/off and set delay for update drawings.
1193
1194 Optional arguments:
1195 n -- nonnegative integer
1196 delay -- nonnegative integer
1197
1198 If n is given, only each n-th regular screen update is really performed.
1199 (Can be used to accelerate the drawing of complex graphics.)
1200 Second arguments sets delay value (see RawTurtle.delay())
1201
1202 Example (for a TurtleScreen instance named screen):
1203 >>> screen.tracer(8, 25)
1204 >>> dist = 2
1205 >>> for i in range(200):
1206 fd(dist)
1207 rt(90)
1208 dist += 2
1209 """
1210 if n is None:
1211 return self._tracing
1212 self._tracing = int(n)
1213 self._updatecounter = 0
1214 if delay is not None:
1215 self._delayvalue = int(delay)
1216 if self._tracing:
1217 self.update()
1218
1219 def delay(self, delay=None):
1220 """ Return or set the drawing delay in milliseconds.
1221
1222 Optional argument:
1223 delay -- positive integer
1224
1225 Example (for a TurtleScreen instance named screen):
1226 >>> screen.delay(15)
1227 >>> screen.delay()
1228 15
1229 """
1230 if delay is None:
1231 return self._delayvalue
1232 self._delayvalue = int(delay)
1233
1234 def _incrementudc(self):
1235 "Increment upadate counter."""
1236 if not TurtleScreen._RUNNING:
1237 TurtleScreen._RUNNNING = True
1238 raise Terminator
1239 if self._tracing > 0:
1240 self._updatecounter += 1
1241 self._updatecounter %= self._tracing
1242
1243 def update(self):
1244 """Perform a TurtleScreen update.
1245 """
1246 for t in self.turtles():
1247 t._update_data()
1248 t._drawturtle()
1249 self._update()
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001250
1251 def window_width(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001252 """ Return the width of the turtle window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001253
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001254 Example (for a TurtleScreen instance named screen):
1255 >>> screen.window_width()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001256 640
1257 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001258 return self._window_size()[0]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001259
1260 def window_height(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001261 """ Return the height of the turtle window.
1262
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001263 Example (for a TurtleScreen instance named screen):
1264 >>> screen.window_height()
1265 480
Thomas Wouters477c8d52006-05-27 19:21:47 +00001266 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001267 return self._window_size()[1]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001268
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001269 def getcanvas(self):
1270 """Return the Canvas of this TurtleScreen.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001271
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001272 No argument.
1273
1274 Example (for a Screen instance named screen):
1275 >>> cv = screen.getcanvas()
1276 >>> cv
1277 <turtle.ScrolledCanvas instance at 0x010742D8>
Thomas Wouters477c8d52006-05-27 19:21:47 +00001278 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001279 return self.cv
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001280
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001281 def getshapes(self):
1282 """Return a list of names of all currently available turtle shapes.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001283
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001284 No argument.
1285
1286 Example (for a TurtleScreen instance named screen):
1287 >>> screen.getshapes()
1288 ['arrow', 'blank', 'circle', ... , 'turtle']
1289 """
1290 return sorted(self._shapes.keys())
1291
1292 def onclick(self, fun, btn=1, add=None):
1293 """Bind fun to mouse-click event on canvas.
1294
1295 Arguments:
1296 fun -- a function with two arguments, the coordinates of the
1297 clicked point on the canvas.
1298 num -- the number of the mouse-button, defaults to 1
1299
1300 Example (for a TurtleScreen instance named screen
1301 and a Turtle instance named turtle):
1302
1303 >>> screen.onclick(turtle.goto)
1304
1305 ### Subsequently clicking into the TurtleScreen will
1306 ### make the turtle move to the clicked point.
1307 >>> screen.onclick(None)
1308
1309 ### event-binding will be removed
1310 """
1311 self._onscreenclick(fun, btn, add)
1312
1313 def onkey(self, fun, key):
1314 """Bind fun to key-release event of key.
1315
1316 Arguments:
1317 fun -- a function with no arguments
1318 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1319
1320 In order ro be able to register key-events, TurtleScreen
1321 must have focus. (See method listen.)
1322
1323 Example (for a TurtleScreen instance named screen
1324 and a Turtle instance named turtle):
1325
1326 >>> def f():
1327 fd(50)
1328 lt(60)
1329
1330
1331 >>> screen.onkey(f, "Up")
1332 >>> screen.listen()
1333
1334 ### Subsequently the turtle can be moved by
1335 ### repeatedly pressing the up-arrow key,
1336 ### consequently drawing a hexagon
1337 """
1338 if fun == None:
1339 self._keys.remove(key)
1340 elif key not in self._keys:
1341 self._keys.append(key)
1342 self._onkey(fun, key)
1343
1344 def listen(self, xdummy=None, ydummy=None):
1345 """Set focus on TurtleScreen (in order to collect key-events)
1346
1347 No arguments.
1348 Dummy arguments are provided in order
1349 to be able to pass listen to the onclick method.
1350
1351 Example (for a TurtleScreen instance named screen):
1352 >>> screen.listen()
1353 """
1354 self._listen()
1355
1356 def ontimer(self, fun, t=0):
1357 """Install a timer, which calls fun after t milliseconds.
1358
1359 Arguments:
1360 fun -- a function with no arguments.
1361 t -- a number >= 0
1362
1363 Example (for a TurtleScreen instance named screen):
1364
1365 >>> running = True
1366 >>> def f():
1367 if running:
1368 fd(50)
1369 lt(60)
1370 screen.ontimer(f, 250)
1371
1372 >>> f() ### makes the turtle marching around
1373 >>> running = False
1374 """
1375 self._ontimer(fun, t)
1376
1377 def bgpic(self, picname=None):
1378 """Set background image or return name of current backgroundimage.
1379
1380 Optional argument:
1381 picname -- a string, name of a gif-file or "nopic".
1382
1383 If picname is a filename, set the corresponing image as background.
1384 If picname is "nopic", delete backgroundimage, if present.
1385 If picname is None, return the filename of the current backgroundimage.
1386
1387 Example (for a TurtleScreen instance named screen):
1388 >>> screen.bgpic()
1389 'nopic'
1390 >>> screen.bgpic("landscape.gif")
1391 >>> screen.bgpic()
1392 'landscape.gif'
1393 """
1394 if picname is None:
1395 return self._bgpicname
1396 if picname not in self._bgpics:
1397 self._bgpics[picname] = self._image(picname)
1398 self._setbgpic(self._bgpic, self._bgpics[picname])
1399 self._bgpicname = picname
1400
1401 def screensize(self, canvwidth=None, canvheight=None, bg=None):
1402 """Resize the canvas, the turtles are drawing on.
1403
1404 Optional arguments:
1405 canvwidth -- positive integer, new width of canvas in pixels
1406 canvheight -- positive integer, new height of canvas in pixels
1407 bg -- colorstring or color-tupel, new backgroundcolor
1408 If no arguments are given, return current (canvaswidth, canvasheight)
1409
1410 Do not alter the drawing window. To observe hidden parts of
1411 the canvas use the scrollbars. (Can make visible those parts
1412 of a drawing, which were outside the canvas before!)
1413
1414 Example (for a Turtle instance named turtle):
1415 >>> turtle.screensize(2000,1500)
1416 ### e. g. to search for an erroneously escaped turtle ;-)
1417 """
1418 return self._resize(canvwidth, canvheight, bg)
1419
1420 onscreenclick = onclick
1421 resetscreen = reset
1422 clearscreen = clear
1423 addshape = register_shape
1424
1425class TNavigator(object):
1426 """Navigation part of the RawTurtle.
1427 Implements methods for turtle movement.
1428 """
1429 START_ORIENTATION = {
1430 "standard": Vec2D(1.0, 0.0),
1431 "world" : Vec2D(1.0, 0.0),
1432 "logo" : Vec2D(0.0, 1.0) }
1433 DEFAULT_MODE = "standard"
1434 DEFAULT_ANGLEOFFSET = 0
1435 DEFAULT_ANGLEORIENT = 1
1436
1437 def __init__(self, mode=DEFAULT_MODE):
1438 self._angleOffset = self.DEFAULT_ANGLEOFFSET
1439 self._angleOrient = self.DEFAULT_ANGLEORIENT
1440 self._mode = mode
1441 self.undobuffer = None
1442 self.degrees()
1443 self._mode = None
1444 self._setmode(mode)
1445 TNavigator.reset(self)
1446
1447 def reset(self):
1448 """reset turtle to its initial values
1449
1450 Will be overwritten by parent class
1451 """
1452 self._position = Vec2D(0.0, 0.0)
1453 self._orient = TNavigator.START_ORIENTATION[self._mode]
1454
1455 def _setmode(self, mode=None):
1456 """Set turtle-mode to 'standard', 'world' or 'logo'.
1457 """
1458 if mode == None:
1459 return self._mode
1460 if mode not in ["standard", "logo", "world"]:
1461 return
1462 self._mode = mode
1463 if mode in ["standard", "world"]:
1464 self._angleOffset = 0
1465 self._angleOrient = 1
1466 else: # mode == "logo":
1467 self._angleOffset = self._fullcircle/4.
1468 self._angleOrient = -1
1469
1470 def _setDegreesPerAU(self, fullcircle):
1471 """Helper function for degrees() and radians()"""
1472 self._fullcircle = fullcircle
1473 self._degreesPerAU = 360/fullcircle
1474 if self._mode == "standard":
1475 self._angleOffset = 0
1476 else:
1477 self._angleOffset = fullcircle/4.
1478
1479 def degrees(self, fullcircle=360.0):
1480 """ Set angle measurement units to degrees.
1481
1482 Optional argument:
1483 fullcircle - a number
1484
1485 Set angle measurement units, i. e. set number
1486 of 'degrees' for a full circle. Dafault value is
1487 360 degrees.
1488
1489 Example (for a Turtle instance named turtle):
1490 >>> turtle.left(90)
1491 >>> turtle.heading()
1492 90
1493 >>> turtle.degrees(400.0) # angle measurement in gon
1494 >>> turtle.heading()
1495 100
1496
1497 """
1498 self._setDegreesPerAU(fullcircle)
1499
1500 def radians(self):
1501 """ Set the angle measurement units to radians.
1502
1503 No arguments.
1504
1505 Example (for a Turtle instance named turtle):
1506 >>> turtle.heading()
1507 90
1508 >>> turtle.radians()
1509 >>> turtle.heading()
1510 1.5707963267948966
1511 """
1512 self._setDegreesPerAU(2*math.pi)
1513
1514 def _go(self, distance):
1515 """move turtle forward by specified distance"""
1516 ende = self._position + self._orient * distance
1517 self._goto(ende)
1518
1519 def _rotate(self, angle):
1520 """Turn turtle counterclockwise by specified angle if angle > 0."""
1521 angle *= self._degreesPerAU
1522 self._orient = self._orient.rotate(angle)
1523
1524 def _goto(self, end):
1525 """move turtle to position end."""
1526 self._position = end
1527
1528 def forward(self, distance):
1529 """Move the turtle forward by the specified distance.
1530
1531 Aliases: forward | fd
1532
1533 Argument:
1534 distance -- a number (integer or float)
1535
1536 Move the turtle forward by the specified distance, in the direction
1537 the turtle is headed.
1538
1539 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001540 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001541 (0.00, 0.00)
1542 >>> turtle.forward(25)
1543 >>> turtle.position()
1544 (25.00,0.00)
1545 >>> turtle.forward(-75)
1546 >>> turtle.position()
1547 (-50.00,0.00)
1548 """
1549 self._go(distance)
1550
1551 def back(self, distance):
1552 """Move the turtle backward by distance.
1553
1554 Aliases: back | backward | bk
1555
1556 Argument:
1557 distance -- a number
1558
1559 Move the turtle backward by distance ,opposite to the direction the
1560 turtle is headed. Do not change the turtle's heading.
1561
1562 Example (for a Turtle instance named turtle):
1563 >>> turtle.position()
1564 (0.00, 0.00)
1565 >>> turtle.backward(30)
1566 >>> turtle.position()
1567 (-30.00, 0.00)
1568 """
1569 self._go(-distance)
1570
1571 def right(self, angle):
1572 """Turn turtle right by angle units.
1573
1574 Aliases: right | rt
1575
1576 Argument:
1577 angle -- a number (integer or float)
1578
1579 Turn turtle right by angle units. (Units are by default degrees,
1580 but can be set via the degrees() and radians() functions.)
1581 Angle orientation depends on mode. (See this.)
1582
1583 Example (for a Turtle instance named turtle):
1584 >>> turtle.heading()
1585 22.0
1586 >>> turtle.right(45)
1587 >>> turtle.heading()
1588 337.0
1589 """
1590 self._rotate(-angle)
1591
1592 def left(self, angle):
1593 """Turn turtle left by angle units.
1594
1595 Aliases: left | lt
1596
1597 Argument:
1598 angle -- a number (integer or float)
1599
1600 Turn turtle left by angle units. (Units are by default degrees,
1601 but can be set via the degrees() and radians() functions.)
1602 Angle orientation depends on mode. (See this.)
1603
1604 Example (for a Turtle instance named turtle):
1605 >>> turtle.heading()
1606 22.0
1607 >>> turtle.left(45)
1608 >>> turtle.heading()
1609 67.0
1610 """
1611 self._rotate(angle)
1612
1613 def pos(self):
1614 """Return the turtle's current location (x,y), as a Vec2D-vector.
1615
1616 Aliases: pos | position
1617
1618 No arguments.
1619
1620 Example (for a Turtle instance named turtle):
1621 >>> turtle.pos()
1622 (0.00, 240.00)
1623 """
1624 return self._position
1625
1626 def xcor(self):
1627 """ Return the turtle's x coordinate.
1628
1629 No arguments.
1630
1631 Example (for a Turtle instance named turtle):
1632 >>> reset()
1633 >>> turtle.left(60)
1634 >>> turtle.forward(100)
1635 >>> print turtle.xcor()
1636 50.0
1637 """
1638 return self._position[0]
1639
1640 def ycor(self):
1641 """ Return the turtle's y coordinate
1642 ---
1643 No arguments.
1644
1645 Example (for a Turtle instance named turtle):
1646 >>> reset()
1647 >>> turtle.left(60)
1648 >>> turtle.forward(100)
1649 >>> print turtle.ycor()
1650 86.6025403784
1651 """
1652 return self._position[1]
1653
1654
1655 def goto(self, x, y=None):
1656 """Move turtle to an absolute position.
1657
1658 Aliases: setpos | setposition | goto:
1659
1660 Arguments:
1661 x -- a number or a pair/vector of numbers
1662 y -- a number None
1663
1664 call: goto(x, y) # two coordinates
1665 --or: goto((x, y)) # a pair (tuple) of coordinates
1666 --or: goto(vec) # e.g. as returned by pos()
1667
1668 Move turtle to an absolute position. If the pen is down,
1669 a line will be drawn. The turtle's orientation does not change.
1670
1671 Example (for a Turtle instance named turtle):
1672 >>> tp = turtle.pos()
1673 >>> tp
1674 (0.00, 0.00)
1675 >>> turtle.setpos(60,30)
1676 >>> turtle.pos()
1677 (60.00,30.00)
1678 >>> turtle.setpos((20,80))
1679 >>> turtle.pos()
1680 (20.00,80.00)
1681 >>> turtle.setpos(tp)
1682 >>> turtle.pos()
1683 (0.00,0.00)
1684 """
1685 if y is None:
1686 self._goto(Vec2D(*x))
1687 else:
1688 self._goto(Vec2D(x, y))
1689
1690 def home(self):
1691 """Move turtle to the origin - coordinates (0,0).
1692
1693 No arguments.
1694
1695 Move turtle to the origin - coordinates (0,0) and set it's
1696 heading to it's start-orientation (which depends on mode).
1697
1698 Example (for a Turtle instance named turtle):
1699 >>> turtle.home()
1700 """
1701 self.goto(0, 0)
1702 self.setheading(0)
1703
1704 def setx(self, x):
1705 """Set the turtle's first coordinate to x
1706
1707 Argument:
1708 x -- a number (integer or float)
1709
1710 Set the turtle's first coordinate to x, leave second coordinate
1711 unchanged.
1712
1713 Example (for a Turtle instance named turtle):
1714 >>> turtle.position()
1715 (0.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001716 >>> turtle.setx(10)
1717 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001718 (10.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001719 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001720 self._goto(Vec2D(x, self._position[1]))
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001721
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001722 def sety(self, y):
1723 """Set the turtle's second coordinate to y
Thomas Wouters477c8d52006-05-27 19:21:47 +00001724
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001725 Argument:
1726 y -- a number (integer or float)
1727
1728 Set the turtle's first coordinate to x, second coordinate remains
1729 unchanged.
1730
1731 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001732 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001733 (0.00, 40.00)
1734 >>> turtle.sety(-10)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001735 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001736 (0.00, -10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001737 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001738 self._goto(Vec2D(self._position[0], y))
Guido van Rossumb241b671998-12-04 16:42:46 +00001739
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001740 def distance(self, x, y=None):
1741 """Return the distance from the turtle to (x,y) in turtle step units.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001742
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001743 Arguments:
1744 x -- a number or a pair/vector of numbers or a turtle instance
1745 y -- a number None None
Thomas Wouters477c8d52006-05-27 19:21:47 +00001746
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001747 call: distance(x, y) # two coordinates
1748 --or: distance((x, y)) # a pair (tuple) of coordinates
1749 --or: distance(vec) # e.g. as returned by pos()
1750 --or: distance(mypen) # where mypen is another turtle
1751
1752 Example (for a Turtle instance named turtle):
1753 >>> turtle.pos()
1754 (0.00, 0.00)
1755 >>> turtle.distance(30,40)
1756 50.0
1757 >>> pen = Turtle()
1758 >>> pen.forward(77)
1759 >>> turtle.distance(pen)
1760 77.0
1761 """
1762 if y is not None:
1763 pos = Vec2D(x, y)
1764 if isinstance(x, Vec2D):
1765 pos = x
1766 elif isinstance(x, tuple):
1767 pos = Vec2D(*x)
1768 elif isinstance(x, TNavigator):
1769 pos = x._position
1770 return abs(pos - self._position)
1771
1772 def towards(self, x, y=None):
1773 """Return the angle of the line from the turtle's position to (x, y).
1774
1775 Arguments:
1776 x -- a number or a pair/vector of numbers or a turtle instance
1777 y -- a number None None
1778
1779 call: distance(x, y) # two coordinates
1780 --or: distance((x, y)) # a pair (tuple) of coordinates
1781 --or: distance(vec) # e.g. as returned by pos()
1782 --or: distance(mypen) # where mypen is another turtle
1783
1784 Return the angle, between the line from turtle-position to position
1785 specified by x, y and the turtle's start orientation. (Depends on
1786 modes - "standard" or "logo")
1787
1788 Example (for a Turtle instance named turtle):
1789 >>> turtle.pos()
1790 (10.00, 10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001791 >>> turtle.towards(0,0)
1792 225.0
1793 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001794 if y is not None:
1795 pos = Vec2D(x, y)
1796 if isinstance(x, Vec2D):
1797 pos = x
1798 elif isinstance(x, tuple):
1799 pos = Vec2D(*x)
1800 elif isinstance(x, TNavigator):
1801 pos = x._position
1802 x, y = pos - self._position
1803 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1804 result /= self._degreesPerAU
1805 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1806
1807 def heading(self):
1808 """ Return the turtle's current heading.
1809
1810 No arguments.
1811
1812 Example (for a Turtle instance named turtle):
1813 >>> turtle.left(67)
1814 >>> turtle.heading()
1815 67.0
1816 """
1817 x, y = self._orient
1818 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1819 result /= self._degreesPerAU
1820 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1821
1822 def setheading(self, to_angle):
1823 """Set the orientation of the turtle to to_angle.
1824
1825 Aliases: setheading | seth
1826
1827 Argument:
1828 to_angle -- a number (integer or float)
1829
1830 Set the orientation of the turtle to to_angle.
1831 Here are some common directions in degrees:
1832
1833 standard - mode: logo-mode:
1834 -------------------|--------------------
1835 0 - east 0 - north
1836 90 - north 90 - east
1837 180 - west 180 - south
1838 270 - south 270 - west
1839
1840 Example (for a Turtle instance named turtle):
1841 >>> turtle.setheading(90)
1842 >>> turtle.heading()
1843 90
1844 """
1845 angle = (to_angle - self.heading())*self._angleOrient
1846 full = self._fullcircle
1847 angle = (angle+full/2.)%full - full/2.
1848 self._rotate(angle)
1849
1850 def circle(self, radius, extent = None, steps = None):
1851 """ Draw a circle with given radius.
1852
1853 Arguments:
1854 radius -- a number
1855 extent (optional) -- a number
1856 steps (optional) -- an integer
1857
1858 Draw a circle with given radius. The center is radius units left
1859 of the turtle; extent - an angle - determines which part of the
1860 circle is drawn. If extent is not given, draw the entire circle.
1861 If extent is not a full circle, one endpoint of the arc is the
1862 current pen position. Draw the arc in counterclockwise direction
1863 if radius is positive, otherwise in clockwise direction. Finally
1864 the direction of the turtle is changed by the amount of extent.
1865
1866 As the circle is approximated by an inscribed regular polygon,
1867 steps determines the number of steps to use. If not given,
1868 it will be calculated automatically. Maybe used to draw regular
1869 polygons.
1870
1871 call: circle(radius) # full circle
1872 --or: circle(radius, extent) # arc
1873 --or: circle(radius, extent, steps)
1874 --or: circle(radius, steps=6) # 6-sided polygon
1875
1876 Example (for a Turtle instance named turtle):
1877 >>> turtle.circle(50)
1878 >>> turtle.circle(120, 180) # semicircle
1879 """
1880 if self.undobuffer:
1881 self.undobuffer.push(["seq"])
1882 self.undobuffer.cumulate = True
1883 speed = self.speed()
1884 if extent is None:
1885 extent = self._fullcircle
1886 if steps is None:
1887 frac = abs(extent)/self._fullcircle
1888 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1889 w = 1.0 * extent / steps
1890 w2 = 0.5 * w
1891 l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1892 if radius < 0:
1893 l, w, w2 = -l, -w, -w2
1894 tr = self._tracer()
1895 dl = self._delay()
1896 if speed == 0:
1897 self._tracer(0, 0)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001898 else:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001899 self.speed(0)
1900 self._rotate(w2)
1901 for i in range(steps):
1902 self.speed(speed)
1903 self._go(l)
1904 self.speed(0)
1905 self._rotate(w)
1906 self._rotate(-w2)
1907 if speed == 0:
1908 self._tracer(tr, dl)
1909 self.speed(speed)
1910 if self.undobuffer:
1911 self.undobuffer.cumulate = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001912
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001913## three dummy methods to be implemented by child class:
Thomas Wouters477c8d52006-05-27 19:21:47 +00001914
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001915 def speed(self, s=0):
1916 """dummy method - to be overwritten by child class"""
1917 def _tracer(self, a=None, b=None):
1918 """dummy method - to be overwritten by child class"""
1919 def _delay(self, n=None):
1920 """dummy method - to be overwritten by child class"""
Thomas Wouters477c8d52006-05-27 19:21:47 +00001921
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001922 fd = forward
1923 bk = back
1924 backward = back
1925 rt = right
1926 lt = left
1927 position = pos
1928 setpos = goto
1929 setposition = goto
1930 seth = setheading
Thomas Wouters477c8d52006-05-27 19:21:47 +00001931
Thomas Wouters477c8d52006-05-27 19:21:47 +00001932
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001933class TPen(object):
1934 """Drawing part of the RawTurtle.
1935 Implements drawing properties.
1936 """
1937 def __init__(self, resizemode=_CFG["resizemode"]):
1938 self._resizemode = resizemode # or "user" or "noresize"
1939 self.undobuffer = None
1940 TPen._reset(self)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001941
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001942 def _reset(self, pencolor=_CFG["pencolor"],
1943 fillcolor=_CFG["fillcolor"]):
1944 self._pensize = 1
1945 self._shown = True
1946 self._pencolor = pencolor
1947 self._fillcolor = fillcolor
1948 self._drawing = True
1949 self._speed = 3
1950 self._stretchfactor = (1, 1)
1951 self._tilt = 0
1952 self._outlinewidth = 1
1953 ### self.screen = None # to override by child class
1954
1955 def resizemode(self, rmode=None):
1956 """Set resizemode to one of the values: "auto", "user", "noresize".
1957
1958 (Optional) Argument:
1959 rmode -- one of the strings "auto", "user", "noresize"
1960
1961 Different resizemodes have the following effects:
1962 - "auto" adapts the appearance of the turtle
1963 corresponding to the value of pensize.
1964 - "user" adapts the appearance of the turtle according to the
1965 values of stretchfactor and outlinewidth (outline),
1966 which are set by shapesize()
1967 - "noresize" no adaption of the turtle's appearance takes place.
1968 If no argument is given, return current resizemode.
1969 resizemode("user") is called by a call of shapesize with arguments.
1970
1971
1972 Examples (for a Turtle instance named turtle):
1973 >>> turtle.resizemode("noresize")
1974 >>> turtle.resizemode()
1975 'noresize'
Thomas Wouters477c8d52006-05-27 19:21:47 +00001976 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001977 if rmode is None:
1978 return self._resizemode
1979 rmode = rmode.lower()
1980 if rmode in ["auto", "user", "noresize"]:
1981 self.pen(resizemode=rmode)
Guido van Rossumb241b671998-12-04 16:42:46 +00001982
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001983 def pensize(self, width=None):
1984 """Set or return the line thickness.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00001985
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001986 Aliases: pensize | width
Thomas Wouters477c8d52006-05-27 19:21:47 +00001987
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001988 Argument:
1989 width -- positive number
Thomas Wouters477c8d52006-05-27 19:21:47 +00001990
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001991 Set the line thickness to width or return it. If resizemode is set
1992 to "auto" and turtleshape is a polygon, that polygon is drawn with
1993 the same line thickness. If no argument is given, current pensize
1994 is returned.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001995
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001996 Example (for a Turtle instance named turtle):
1997 >>> turtle.pensize()
1998 1
1999 turtle.pensize(10) # from here on lines of width 10 are drawn
Thomas Wouters477c8d52006-05-27 19:21:47 +00002000 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002001 if width is None:
2002 return self._pensize
2003 self.pen(pensize=width)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002004
2005
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002006 def penup(self):
2007 """Pull the pen up -- no drawing when moving.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002008
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002009 Aliases: penup | pu | up
Thomas Wouters477c8d52006-05-27 19:21:47 +00002010
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002011 No argument
2012
2013 Example (for a Turtle instance named turtle):
2014 >>> turtle.penup()
Thomas Wouters477c8d52006-05-27 19:21:47 +00002015 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002016 if not self._drawing:
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002017 return
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002018 self.pen(pendown=False)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002019
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002020 def pendown(self):
2021 """Pull the pen down -- drawing when moving.
2022
2023 Aliases: pendown | pd | down
2024
2025 No argument.
2026
2027 Example (for a Turtle instance named turtle):
2028 >>> turtle.pendown()
2029 """
2030 if self._drawing:
2031 return
2032 self.pen(pendown=True)
2033
2034 def isdown(self):
2035 """Return True if pen is down, False if it's up.
2036
2037 No argument.
2038
2039 Example (for a Turtle instance named turtle):
2040 >>> turtle.penup()
2041 >>> turtle.isdown()
2042 False
2043 >>> turtle.pendown()
2044 >>> turtle.isdown()
2045 True
2046 """
2047 return self._drawing
2048
2049 def speed(self, speed=None):
2050 """ Return or set the turtle's speed.
2051
2052 Optional argument:
2053 speed -- an integer in the range 0..10 or a speedstring (see below)
2054
2055 Set the turtle's speed to an integer value in the range 0 .. 10.
2056 If no argument is given: return current speed.
2057
2058 If input is a number greater than 10 or smaller than 0.5,
2059 speed is set to 0.
2060 Speedstrings are mapped to speedvalues in the following way:
2061 'fastest' : 0
2062 'fast' : 10
2063 'normal' : 6
2064 'slow' : 3
2065 'slowest' : 1
2066 speeds from 1 to 10 enforce increasingly faster animation of
2067 line drawing and turtle turning.
2068
2069 Attention:
2070 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2071 and likewise left/right make the turtle turn instantly.
2072
2073 Example (for a Turtle instance named turtle):
2074 >>> turtle.speed(3)
2075 """
2076 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2077 if speed is None:
2078 return self._speed
2079 if speed in speeds:
2080 speed = speeds[speed]
2081 elif 0.5 < speed < 10.5:
2082 speed = int(round(speed))
2083 else:
2084 speed = 0
2085 self.pen(speed=speed)
2086
2087 def color(self, *args):
2088 """Return or set the pencolor and fillcolor.
2089
2090 Arguments:
2091 Several input formats are allowed.
2092 They use 0, 1, 2, or 3 arguments as follows:
2093
2094 color()
2095 Return the current pencolor and the current fillcolor
2096 as a pair of color specification strings as are returned
2097 by pencolor and fillcolor.
2098 color(colorstring), color((r,g,b)), color(r,g,b)
2099 inputs as in pencolor, set both, fillcolor and pencolor,
2100 to the given value.
2101 color(colorstring1, colorstring2),
2102 color((r1,g1,b1), (r2,g2,b2))
2103 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2104 and analogously, if the other input format is used.
2105
2106 If turtleshape is a polygon, outline and interior of that polygon
2107 is drawn with the newly set colors.
2108 For mor info see: pencolor, fillcolor
2109
2110 Example (for a Turtle instance named turtle):
2111 >>> turtle.color('red', 'green')
2112 >>> turtle.color()
2113 ('red', 'green')
2114 >>> colormode(255)
2115 >>> color((40, 80, 120), (160, 200, 240))
2116 >>> color()
2117 ('#285078', '#a0c8f0')
2118 """
2119 if args:
2120 l = len(args)
2121 if l == 1:
2122 pcolor = fcolor = args[0]
2123 elif l == 2:
2124 pcolor, fcolor = args
2125 elif l == 3:
2126 pcolor = fcolor = args
2127 pcolor = self._colorstr(pcolor)
2128 fcolor = self._colorstr(fcolor)
2129 self.pen(pencolor=pcolor, fillcolor=fcolor)
2130 else:
2131 return self._color(self._pencolor), self._color(self._fillcolor)
2132
2133 def pencolor(self, *args):
2134 """ Return or set the pencolor.
2135
2136 Arguments:
2137 Four input formats are allowed:
2138 - pencolor()
2139 Return the current pencolor as color specification string,
2140 possibly in hex-number format (see example).
2141 May be used as input to another color/pencolor/fillcolor call.
2142 - pencolor(colorstring)
2143 s is a Tk color specification string, such as "red" or "yellow"
2144 - pencolor((r, g, b))
2145 *a tuple* of r, g, and b, which represent, an RGB color,
2146 and each of r, g, and b are in the range 0..colormode,
2147 where colormode is either 1.0 or 255
2148 - pencolor(r, g, b)
2149 r, g, and b represent an RGB color, and each of r, g, and b
2150 are in the range 0..colormode
2151
2152 If turtleshape is a polygon, the outline of that polygon is drawn
2153 with the newly set pencolor.
2154
2155 Example (for a Turtle instance named turtle):
2156 >>> turtle.pencolor('brown')
2157 >>> tup = (0.2, 0.8, 0.55)
2158 >>> turtle.pencolor(tup)
2159 >>> turtle.pencolor()
2160 '#33cc8c'
2161 """
2162 if args:
2163 color = self._colorstr(args)
2164 if color == self._pencolor:
2165 return
2166 self.pen(pencolor=color)
2167 else:
2168 return self._color(self._pencolor)
2169
2170 def fillcolor(self, *args):
2171 """ Return or set the fillcolor.
2172
2173 Arguments:
2174 Four input formats are allowed:
2175 - fillcolor()
2176 Return the current fillcolor as color specification string,
2177 possibly in hex-number format (see example).
2178 May be used as input to another color/pencolor/fillcolor call.
2179 - fillcolor(colorstring)
2180 s is a Tk color specification string, such as "red" or "yellow"
2181 - fillcolor((r, g, b))
2182 *a tuple* of r, g, and b, which represent, an RGB color,
2183 and each of r, g, and b are in the range 0..colormode,
2184 where colormode is either 1.0 or 255
2185 - fillcolor(r, g, b)
2186 r, g, and b represent an RGB color, and each of r, g, and b
2187 are in the range 0..colormode
2188
2189 If turtleshape is a polygon, the interior of that polygon is drawn
2190 with the newly set fillcolor.
2191
2192 Example (for a Turtle instance named turtle):
2193 >>> turtle.fillcolor('violet')
2194 >>> col = turtle.pencolor()
2195 >>> turtle.fillcolor(col)
2196 >>> turtle.fillcolor(0, .5, 0)
2197 """
2198 if args:
2199 color = self._colorstr(args)
2200 if color == self._fillcolor:
2201 return
2202 self.pen(fillcolor=color)
2203 else:
2204 return self._color(self._fillcolor)
2205
2206 def showturtle(self):
2207 """Makes the turtle visible.
2208
2209 Aliases: showturtle | st
2210
2211 No argument.
2212
2213 Example (for a Turtle instance named turtle):
2214 >>> turtle.hideturtle()
2215 >>> turtle.showturtle()
2216 """
2217 self.pen(shown=True)
2218
2219 def hideturtle(self):
2220 """Makes the turtle invisible.
2221
2222 Aliases: hideturtle | ht
2223
2224 No argument.
2225
2226 It's a good idea to do this while you're in the
2227 middle of a complicated drawing, because hiding
2228 the turtle speeds up the drawing observably.
2229
2230 Example (for a Turtle instance named turtle):
2231 >>> turtle.hideturtle()
2232 """
2233 self.pen(shown=False)
2234
2235 def isvisible(self):
2236 """Return True if the Turtle is shown, False if it's hidden.
2237
2238 No argument.
2239
2240 Example (for a Turtle instance named turtle):
2241 >>> turtle.hideturtle()
2242 >>> print turtle.isvisible():
2243 False
2244 """
2245 return self._shown
2246
2247 def pen(self, pen=None, **pendict):
2248 """Return or set the pen's attributes.
2249
2250 Arguments:
2251 pen -- a dictionary with some or all of the below listed keys.
2252 **pendict -- one or more keyword-arguments with the below
2253 listed keys as keywords.
2254
2255 Return or set the pen's attributes in a 'pen-dictionary'
2256 with the following key/value pairs:
2257 "shown" : True/False
2258 "pendown" : True/False
2259 "pencolor" : color-string or color-tuple
2260 "fillcolor" : color-string or color-tuple
2261 "pensize" : positive number
2262 "speed" : number in range 0..10
2263 "resizemode" : "auto" or "user" or "noresize"
2264 "stretchfactor": (positive number, positive number)
2265 "outline" : positive number
2266 "tilt" : number
2267
2268 This dicionary can be used as argument for a subsequent
2269 pen()-call to restore the former pen-state. Moreover one
2270 or more of these attributes can be provided as keyword-arguments.
2271 This can be used to set several pen attributes in one statement.
Guido van Rossumb241b671998-12-04 16:42:46 +00002272
2273
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002274 Examples (for a Turtle instance named turtle):
2275 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2276 >>> turtle.pen()
2277 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2278 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
2279 'stretchfactor': (1,1), 'speed': 3}
2280 >>> penstate=turtle.pen()
2281 >>> turtle.color("yellow","")
2282 >>> turtle.penup()
2283 >>> turtle.pen()
2284 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2285 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
2286 'stretchfactor': (1,1), 'speed': 3}
2287 >>> p.pen(penstate, fillcolor="green")
2288 >>> p.pen()
2289 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2290 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
2291 'stretchfactor': (1,1), 'speed': 3}
2292 """
2293 _pd = {"shown" : self._shown,
2294 "pendown" : self._drawing,
2295 "pencolor" : self._pencolor,
2296 "fillcolor" : self._fillcolor,
2297 "pensize" : self._pensize,
2298 "speed" : self._speed,
2299 "resizemode" : self._resizemode,
2300 "stretchfactor" : self._stretchfactor,
2301 "outline" : self._outlinewidth,
2302 "tilt" : self._tilt
2303 }
Guido van Rossumb241b671998-12-04 16:42:46 +00002304
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002305 if not (pen or pendict):
2306 return _pd
2307
2308 if isinstance(pen, dict):
2309 p = pen
2310 else:
2311 p = {}
2312 p.update(pendict)
2313
2314 _p_buf = {}
2315 for key in p:
2316 _p_buf[key] = _pd[key]
2317
2318 if self.undobuffer:
2319 self.undobuffer.push(("pen", _p_buf))
2320
2321 newLine = False
2322 if "pendown" in p:
2323 if self._drawing != p["pendown"]:
2324 newLine = True
2325 if "pencolor" in p:
2326 if isinstance(p["pencolor"], tuple):
2327 p["pencolor"] = self._colorstr((p["pencolor"],))
2328 if self._pencolor != p["pencolor"]:
2329 newLine = True
2330 if "pensize" in p:
2331 if self._pensize != p["pensize"]:
2332 newLine = True
2333 if newLine:
2334 self._newLine()
2335 if "pendown" in p:
2336 self._drawing = p["pendown"]
2337 if "pencolor" in p:
2338 self._pencolor = p["pencolor"]
2339 if "pensize" in p:
2340 self._pensize = p["pensize"]
2341 if "fillcolor" in p:
2342 if isinstance(p["fillcolor"], tuple):
2343 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2344 self._fillcolor = p["fillcolor"]
2345 if "speed" in p:
2346 self._speed = p["speed"]
2347 if "resizemode" in p:
2348 self._resizemode = p["resizemode"]
2349 if "stretchfactor" in p:
2350 sf = p["stretchfactor"]
2351 if isinstance(sf, (int, float)):
2352 sf = (sf, sf)
2353 self._stretchfactor = sf
2354 if "outline" in p:
2355 self._outlinewidth = p["outline"]
2356 if "shown" in p:
2357 self._shown = p["shown"]
2358 if "tilt" in p:
2359 self._tilt = p["tilt"]
2360 self._update()
2361
2362## three dummy methods to be implemented by child class:
2363
2364 def _newLine(self, usePos = True):
2365 """dummy method - to be overwritten by child class"""
2366 def _update(self, count=True, forced=False):
2367 """dummy method - to be overwritten by child class"""
2368 def _color(self, args):
2369 """dummy method - to be overwritten by child class"""
2370 def _colorstr(self, args):
2371 """dummy method - to be overwritten by child class"""
2372
2373 width = pensize
2374 up = penup
2375 pu = penup
2376 pd = pendown
2377 down = pendown
2378 st = showturtle
2379 ht = hideturtle
2380
2381
2382class _TurtleImage(object):
2383 """Helper class: Datatype to store Turtle attributes
2384 """
2385
2386 def __init__(self, screen, shapeIndex):
2387 self.screen = screen
2388 self._type = None
2389 self._setshape(shapeIndex)
2390
2391 def _setshape(self, shapeIndex):
2392 screen = self.screen # RawTurtle.screens[self.screenIndex]
2393 self.shapeIndex = shapeIndex
2394 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2395 return
2396 if self._type == "image" == screen._shapes[shapeIndex]._type:
2397 return
2398 if self._type in ["image", "polygon"]:
2399 screen._delete(self._item)
2400 elif self._type == "compound":
2401 for item in self._item:
2402 screen._delete(item)
2403 self._type = screen._shapes[shapeIndex]._type
2404 if self._type == "polygon":
2405 self._item = screen._createpoly()
2406 elif self._type == "image":
2407 self._item = screen._createimage(screen._shapes["blank"]._data)
2408 elif self._type == "compound":
2409 self._item = [screen._createpoly() for item in
2410 screen._shapes[shapeIndex]._data]
2411
2412
2413class RawTurtle(TPen, TNavigator):
2414 """Animation part of the RawTurtle.
2415 Puts RawTurtle upon a TurtleScreen and provides tools for
2416 it's animation.
2417 """
2418 screens = []
2419
2420 def __init__(self, canvas=None,
2421 shape=_CFG["shape"],
2422 undobuffersize=_CFG["undobuffersize"],
2423 visible=_CFG["visible"]):
Martin v. Löwis601149b2008-09-29 22:19:08 +00002424 if isinstance(canvas, _Screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002425 self.screen = canvas
2426 elif isinstance(canvas, TurtleScreen):
2427 if canvas not in RawTurtle.screens:
2428 RawTurtle.screens.append(canvas)
2429 self.screen = canvas
2430 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2431 for screen in RawTurtle.screens:
2432 if screen.cv == canvas:
2433 self.screen = screen
2434 break
2435 else:
2436 self.screen = TurtleScreen(canvas)
2437 RawTurtle.screens.append(self.screen)
2438 else:
2439 raise TurtleGraphicsError("bad cavas argument %s" % canvas)
2440
2441 screen = self.screen
2442 TNavigator.__init__(self, screen.mode())
2443 TPen.__init__(self)
2444 screen._turtles.append(self)
2445 self.drawingLineItem = screen._createline()
2446 self.turtle = _TurtleImage(screen, shape)
2447 self._poly = None
2448 self._creatingPoly = False
2449 self._fillitem = self._fillpath = None
2450 self._shown = visible
2451 self._hidden_from_screen = False
2452 self.currentLineItem = screen._createline()
2453 self.currentLine = [self._position]
2454 self.items = [self.currentLineItem]
2455 self.stampItems = []
2456 self._undobuffersize = undobuffersize
2457 self.undobuffer = Tbuffer(undobuffersize)
2458 self._update()
2459
2460 def reset(self):
2461 """Delete the turtle's drawings and restore it's default values.
2462
2463 No argument.
2464,
2465 Delete the turtle's drawings from the screen, re-center the turtle
2466 and set variables to the default values.
2467
2468 Example (for a Turtle instance named turtle):
2469 >>> turtle.position()
2470 (0.00,-22.00)
2471 >>> turtle.heading()
2472 100.0
2473 >>> turtle.reset()
2474 >>> turtle.position()
2475 (0.00,0.00)
2476 >>> turtle.heading()
2477 0.0
2478 """
2479 TNavigator.reset(self)
2480 TPen._reset(self)
2481 self._clear()
2482 self._drawturtle()
2483 self._update()
2484
2485 def setundobuffer(self, size):
2486 """Set or disable undobuffer.
2487
2488 Argument:
2489 size -- an integer or None
2490
2491 If size is an integer an empty undobuffer of given size is installed.
2492 Size gives the maximum number of turtle-actions that can be undone
2493 by the undo() function.
2494 If size is None, no undobuffer is present.
2495
2496 Example (for a Turtle instance named turtle):
2497 >>> turtle.setundobuffer(42)
2498 """
2499 if size is None:
2500 self.undobuffer = None
2501 else:
2502 self.undobuffer = Tbuffer(size)
2503
2504 def undobufferentries(self):
2505 """Return count of entries in the undobuffer.
2506
2507 No argument.
2508
2509 Example (for a Turtle instance named turtle):
2510 >>> while undobufferentries():
2511 undo()
2512 """
2513 if self.undobuffer is None:
2514 return 0
2515 return self.undobuffer.nr_of_items()
2516
2517 def _clear(self):
2518 """Delete all of pen's drawings"""
2519 self._fillitem = self._fillpath = None
2520 for item in self.items:
2521 self.screen._delete(item)
2522 self.currentLineItem = self.screen._createline()
2523 self.currentLine = []
2524 if self._drawing:
2525 self.currentLine.append(self._position)
2526 self.items = [self.currentLineItem]
2527 self.clearstamps()
2528 self.setundobuffer(self._undobuffersize)
2529
2530
2531 def clear(self):
2532 """Delete the turtle's drawings from the screen. Do not move turtle.
2533
2534 No arguments.
2535
2536 Delete the turtle's drawings from the screen. Do not move turtle.
2537 State and position of the turtle as well as drawings of other
2538 turtles are not affected.
2539
2540 Examples (for a Turtle instance named turtle):
2541 >>> turtle.clear()
2542 """
2543 self._clear()
2544 self._update()
2545
2546 def _update_data(self):
2547 self.screen._incrementudc()
2548 if self.screen._updatecounter != 0:
2549 return
2550 if len(self.currentLine)>1:
2551 self.screen._drawline(self.currentLineItem, self.currentLine,
2552 self._pencolor, self._pensize)
2553
2554 def _update(self):
2555 """Perform a Turtle-data update.
2556 """
2557 screen = self.screen
2558 if screen._tracing == 0:
2559 return
2560 elif screen._tracing == 1:
2561 self._update_data()
2562 self._drawturtle()
2563 screen._update() # TurtleScreenBase
2564 screen._delay(screen._delayvalue) # TurtleScreenBase
2565 else:
2566 self._update_data()
2567 if screen._updatecounter == 0:
2568 for t in screen.turtles():
2569 t._drawturtle()
2570 screen._update()
2571
2572 def _tracer(self, flag=None, delay=None):
2573 """Turns turtle animation on/off and set delay for update drawings.
2574
2575 Optional arguments:
2576 n -- nonnegative integer
2577 delay -- nonnegative integer
2578
2579 If n is given, only each n-th regular screen update is really performed.
2580 (Can be used to accelerate the drawing of complex graphics.)
2581 Second arguments sets delay value (see RawTurtle.delay())
2582
2583 Example (for a Turtle instance named turtle):
2584 >>> turtle.tracer(8, 25)
2585 >>> dist = 2
2586 >>> for i in range(200):
2587 turtle.fd(dist)
2588 turtle.rt(90)
2589 dist += 2
2590 """
2591 return self.screen.tracer(flag, delay)
2592
2593 def _color(self, args):
2594 return self.screen._color(args)
2595
2596 def _colorstr(self, args):
2597 return self.screen._colorstr(args)
2598
2599 def _cc(self, args):
2600 """Convert colortriples to hexstrings.
2601 """
2602 if isinstance(args, str):
2603 return args
2604 try:
2605 r, g, b = args
2606 except:
2607 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2608 if self.screen._colormode == 1.0:
2609 r, g, b = [round(255.0*x) for x in (r, g, b)]
2610 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2611 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2612 return "#%02x%02x%02x" % (r, g, b)
2613
2614 def clone(self):
2615 """Create and return a clone of the turtle.
2616
2617 No argument.
2618
2619 Create and return a clone of the turtle with same position, heading
2620 and turtle properties.
2621
2622 Example (for a Turtle instance named mick):
2623 mick = Turtle()
2624 joe = mick.clone()
2625 """
2626 screen = self.screen
2627 self._newLine(self._drawing)
2628
2629 turtle = self.turtle
2630 self.screen = None
2631 self.turtle = None # too make self deepcopy-able
2632
2633 q = deepcopy(self)
2634
2635 self.screen = screen
2636 self.turtle = turtle
2637
2638 q.screen = screen
2639 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2640
2641 screen._turtles.append(q)
2642 ttype = screen._shapes[self.turtle.shapeIndex]._type
2643 if ttype == "polygon":
2644 q.turtle._item = screen._createpoly()
2645 elif ttype == "image":
2646 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2647 elif ttype == "compound":
2648 q.turtle._item = [screen._createpoly() for item in
2649 screen._shapes[self.turtle.shapeIndex]._data]
2650 q.currentLineItem = screen._createline()
2651 q._update()
2652 return q
2653
2654 def shape(self, name=None):
2655 """Set turtle shape to shape with given name / return current shapename.
2656
2657 Optional argument:
2658 name -- a string, which is a valid shapename
2659
2660 Set turtle shape to shape with given name or, if name is not given,
2661 return name of current shape.
2662 Shape with name must exist in the TurtleScreen's shape dictionary.
2663 Initially there are the following polygon shapes:
2664 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2665 To learn about how to deal with shapes see Screen-method register_shape.
2666
2667 Example (for a Turtle instance named turtle):
2668 >>> turtle.shape()
2669 'arrow'
2670 >>> turtle.shape("turtle")
2671 >>> turtle.shape()
2672 'turtle'
2673 """
2674 if name is None:
2675 return self.turtle.shapeIndex
2676 if not name in self.screen.getshapes():
2677 raise TurtleGraphicsError("There is no shape named %s" % name)
2678 self.turtle._setshape(name)
2679 self._update()
2680
2681 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2682 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2683
2684 Optinonal arguments:
2685 stretch_wid : positive number
2686 stretch_len : positive number
2687 outline : positive number
2688
2689 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2690 Set resizemode to "user".
2691 If and only if resizemode is set to "user", the turtle will be displayed
2692 stretched according to its stretchfactors:
2693 stretch_wid is stretchfactor perpendicular to orientation
2694 stretch_len is stretchfactor in direction of turtles orientation.
2695 outline determines the width of the shapes's outline.
2696
2697 Examples (for a Turtle instance named turtle):
2698 >>> turtle.resizemode("user")
2699 >>> turtle.shapesize(5, 5, 12)
2700 >>> turtle.shapesize(outline=8)
2701 """
2702 if stretch_wid is None and stretch_len is None and outline == None:
2703 stretch_wid, stretch_len = self._stretchfactor
2704 return stretch_wid, stretch_len, self._outlinewidth
2705 if stretch_wid is not None:
2706 if stretch_len is None:
2707 stretchfactor = stretch_wid, stretch_wid
2708 else:
2709 stretchfactor = stretch_wid, stretch_len
2710 elif stretch_len is not None:
2711 stretchfactor = self._stretchfactor[0], stretch_len
2712 else:
2713 stretchfactor = self._stretchfactor
2714 if outline is None:
2715 outline = self._outlinewidth
2716 self.pen(resizemode="user",
2717 stretchfactor=stretchfactor, outline=outline)
2718
2719 def settiltangle(self, angle):
2720 """Rotate the turtleshape to point in the specified direction
2721
2722 Optional argument:
2723 angle -- number
2724
2725 Rotate the turtleshape to point in the direction specified by angle,
2726 regardless of its current tilt-angle. DO NOT change the turtle's
2727 heading (direction of movement).
2728
2729
2730 Examples (for a Turtle instance named turtle):
2731 >>> turtle.shape("circle")
2732 >>> turtle.shapesize(5,2)
2733 >>> turtle.settiltangle(45)
2734 >>> stamp()
2735 >>> turtle.fd(50)
2736 >>> turtle.settiltangle(-45)
2737 >>> stamp()
2738 >>> turtle.fd(50)
2739 """
2740 tilt = -angle * self._degreesPerAU * self._angleOrient
2741 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2742 self.pen(resizemode="user", tilt=tilt)
2743
2744 def tiltangle(self):
2745 """Return the current tilt-angle.
2746
2747 No argument.
2748
2749 Return the current tilt-angle, i. e. the angle between the
2750 orientation of the turtleshape and the heading of the turtle
2751 (it's direction of movement).
2752
2753 Examples (for a Turtle instance named turtle):
2754 >>> turtle.shape("circle")
2755 >>> turtle.shapesize(5,2)
2756 >>> turtle.tilt(45)
2757 >>> turtle.tiltangle()
2758 >>>
2759 """
2760 tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2761 return (tilt / self._degreesPerAU) % self._fullcircle
2762
2763 def tilt(self, angle):
2764 """Rotate the turtleshape by angle.
2765
2766 Argument:
2767 angle - a number
2768
2769 Rotate the turtleshape by angle from its current tilt-angle,
2770 but do NOT change the turtle's heading (direction of movement).
2771
2772 Examples (for a Turtle instance named turtle):
2773 >>> turtle.shape("circle")
2774 >>> turtle.shapesize(5,2)
2775 >>> turtle.tilt(30)
2776 >>> turtle.fd(50)
2777 >>> turtle.tilt(30)
2778 >>> turtle.fd(50)
2779 """
2780 self.settiltangle(angle + self.tiltangle())
2781
2782 def _polytrafo(self, poly):
2783 """Computes transformed polygon shapes from a shape
2784 according to current position and heading.
2785 """
2786 screen = self.screen
2787 p0, p1 = self._position
2788 e0, e1 = self._orient
2789 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2790 e0, e1 = (1.0 / abs(e)) * e
2791 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2792 for (x, y) in poly]
2793
2794 def _drawturtle(self):
2795 """Manages the correct rendering of the turtle with respect to
2796 it's shape, resizemode, strech and tilt etc."""
2797 screen = self.screen
2798 shape = screen._shapes[self.turtle.shapeIndex]
2799 ttype = shape._type
2800 titem = self.turtle._item
2801 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
2802 self._hidden_from_screen = False
2803 tshape = shape._data
2804 if ttype == "polygon":
2805 if self._resizemode == "noresize":
2806 w = 1
2807 shape = tshape
2808 else:
2809 if self._resizemode == "auto":
2810 lx = ly = max(1, self._pensize/5.0)
2811 w = self._pensize
2812 tiltangle = 0
2813 elif self._resizemode == "user":
2814 lx, ly = self._stretchfactor
2815 w = self._outlinewidth
2816 tiltangle = self._tilt
2817 shape = [(lx*x, ly*y) for (x, y) in tshape]
2818 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2819 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2820 shape = self._polytrafo(shape)
2821 fc, oc = self._fillcolor, self._pencolor
2822 screen._drawpoly(titem, shape, fill=fc, outline=oc,
2823 width=w, top=True)
2824 elif ttype == "image":
2825 screen._drawimage(titem, self._position, tshape)
2826 elif ttype == "compound":
2827 lx, ly = self._stretchfactor
2828 w = self._outlinewidth
2829 for item, (poly, fc, oc) in zip(titem, tshape):
2830 poly = [(lx*x, ly*y) for (x, y) in poly]
2831 poly = self._polytrafo(poly)
2832 screen._drawpoly(item, poly, fill=self._cc(fc),
2833 outline=self._cc(oc), width=w, top=True)
2834 else:
2835 if self._hidden_from_screen:
2836 return
2837 if ttype == "polygon":
2838 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
2839 elif ttype == "image":
2840 screen._drawimage(titem, self._position,
2841 screen._shapes["blank"]._data)
2842 elif ttype == "compound":
2843 for item in titem:
2844 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
2845 self._hidden_from_screen = True
2846
2847############################## stamp stuff ###############################
2848
2849 def stamp(self):
2850 """Stamp a copy of the turtleshape onto the canvas and return it's id.
2851
2852 No argument.
2853
2854 Stamp a copy of the turtle shape onto the canvas at the current
2855 turtle position. Return a stamp_id for that stamp, which can be
2856 used to delete it by calling clearstamp(stamp_id).
2857
2858 Example (for a Turtle instance named turtle):
2859 >>> turtle.color("blue")
2860 >>> turtle.stamp()
2861 13
2862 >>> turtle.fd(50)
2863 """
2864 screen = self.screen
2865 shape = screen._shapes[self.turtle.shapeIndex]
2866 ttype = shape._type
2867 tshape = shape._data
2868 if ttype == "polygon":
2869 stitem = screen._createpoly()
2870 if self._resizemode == "noresize":
2871 w = 1
2872 shape = tshape
2873 else:
2874 if self._resizemode == "auto":
2875 lx = ly = max(1, self._pensize/5.0)
2876 w = self._pensize
2877 tiltangle = 0
2878 elif self._resizemode == "user":
2879 lx, ly = self._stretchfactor
2880 w = self._outlinewidth
2881 tiltangle = self._tilt
2882 shape = [(lx*x, ly*y) for (x, y) in tshape]
2883 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2884 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2885 shape = self._polytrafo(shape)
2886 fc, oc = self._fillcolor, self._pencolor
2887 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
2888 width=w, top=True)
2889 elif ttype == "image":
2890 stitem = screen._createimage("")
2891 screen._drawimage(stitem, self._position, tshape)
2892 elif ttype == "compound":
2893 stitem = []
2894 for element in tshape:
2895 item = screen._createpoly()
2896 stitem.append(item)
2897 stitem = tuple(stitem)
2898 lx, ly = self._stretchfactor
2899 w = self._outlinewidth
2900 for item, (poly, fc, oc) in zip(stitem, tshape):
2901 poly = [(lx*x, ly*y) for (x, y) in poly]
2902 poly = self._polytrafo(poly)
2903 screen._drawpoly(item, poly, fill=self._cc(fc),
2904 outline=self._cc(oc), width=w, top=True)
2905 self.stampItems.append(stitem)
2906 self.undobuffer.push(("stamp", stitem))
2907 return stitem
2908
2909 def _clearstamp(self, stampid):
2910 """does the work for clearstamp() and clearstamps()
2911 """
2912 if stampid in self.stampItems:
2913 if isinstance(stampid, tuple):
2914 for subitem in stampid:
2915 self.screen._delete(subitem)
2916 else:
2917 self.screen._delete(stampid)
2918 self.stampItems.remove(stampid)
2919 # Delete stampitem from undobuffer if necessary
2920 # if clearstamp is called directly.
2921 item = ("stamp", stampid)
2922 buf = self.undobuffer
2923 if item not in buf.buffer:
2924 return
2925 index = buf.buffer.index(item)
2926 buf.buffer.remove(item)
2927 if index <= buf.ptr:
2928 buf.ptr = (buf.ptr - 1) % buf.bufsize
2929 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
2930
2931 def clearstamp(self, stampid):
2932 """Delete stamp with given stampid
2933
2934 Argument:
2935 stampid - an integer, must be return value of previous stamp() call.
2936
2937 Example (for a Turtle instance named turtle):
2938 >>> turtle.color("blue")
2939 >>> astamp = turtle.stamp()
2940 >>> turtle.fd(50)
2941 >>> turtle.clearstamp(astamp)
2942 """
2943 self._clearstamp(stampid)
2944 self._update()
2945
2946 def clearstamps(self, n=None):
2947 """Delete all or first/last n of turtle's stamps.
2948
2949 Optional argument:
2950 n -- an integer
2951
2952 If n is None, delete all of pen's stamps,
2953 else if n > 0 delete first n stamps
2954 else if n < 0 delete last n stamps.
2955
2956 Example (for a Turtle instance named turtle):
2957 >>> for i in range(8):
2958 turtle.stamp(); turtle.fd(30)
2959 ...
2960 >>> turtle.clearstamps(2)
2961 >>> turtle.clearstamps(-2)
2962 >>> turtle.clearstamps()
2963 """
2964 if n is None:
2965 toDelete = self.stampItems[:]
2966 elif n >= 0:
2967 toDelete = self.stampItems[:n]
2968 else:
2969 toDelete = self.stampItems[n:]
2970 for item in toDelete:
2971 self._clearstamp(item)
2972 self._update()
2973
2974 def _goto(self, end):
2975 """Move the pen to the point end, thereby drawing a line
2976 if pen is down. All other methodes for turtle movement depend
2977 on this one.
2978 """
2979 ## Version mit undo-stuff
2980 go_modes = ( self._drawing,
2981 self._pencolor,
2982 self._pensize,
2983 isinstance(self._fillpath, list))
2984 screen = self.screen
2985 undo_entry = ("go", self._position, end, go_modes,
2986 (self.currentLineItem,
2987 self.currentLine[:],
2988 screen._pointlist(self.currentLineItem),
2989 self.items[:])
2990 )
2991 if self.undobuffer:
2992 self.undobuffer.push(undo_entry)
2993 start = self._position
2994 if self._speed and screen._tracing == 1:
2995 diff = (end-start)
2996 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
2997 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
2998 delta = diff * (1.0/nhops)
2999 for n in range(1, nhops):
3000 if n == 1:
3001 top = True
3002 else:
3003 top = False
3004 self._position = start + delta * n
3005 if self._drawing:
3006 screen._drawline(self.drawingLineItem,
3007 (start, self._position),
3008 self._pencolor, self._pensize, top)
3009 self._update()
3010 if self._drawing:
3011 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3012 fill="", width=self._pensize)
3013 # Turtle now at end,
3014 if self._drawing: # now update currentLine
3015 self.currentLine.append(end)
3016 if isinstance(self._fillpath, list):
3017 self._fillpath.append(end)
3018 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3019 self._position = end
3020 if self._creatingPoly:
3021 self._poly.append(end)
3022 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3023 # of life, the universe and everything
3024 self._newLine()
3025 self._update() #count=True)
3026
3027 def _undogoto(self, entry):
3028 """Reverse a _goto. Used for undo()
3029 """
3030 old, new, go_modes, coodata = entry
3031 drawing, pc, ps, filling = go_modes
3032 cLI, cL, pl, items = coodata
3033 screen = self.screen
3034 if abs(self._position - new) > 0.5:
3035 print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3036 # restore former situation
3037 self.currentLineItem = cLI
3038 self.currentLine = cL
3039
3040 if pl == [(0, 0), (0, 0)]:
3041 usepc = ""
3042 else:
3043 usepc = pc
3044 screen._drawline(cLI, pl, fill=usepc, width=ps)
3045
3046 todelete = [i for i in self.items if (i not in items) and
3047 (screen._type(i) == "line")]
3048 for i in todelete:
3049 screen._delete(i)
3050 self.items.remove(i)
3051
3052 start = old
3053 if self._speed and screen._tracing == 1:
3054 diff = old - new
3055 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3056 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3057 delta = diff * (1.0/nhops)
3058 for n in range(1, nhops):
3059 if n == 1:
3060 top = True
3061 else:
3062 top = False
3063 self._position = new + delta * n
3064 if drawing:
3065 screen._drawline(self.drawingLineItem,
3066 (start, self._position),
3067 pc, ps, top)
3068 self._update()
3069 if drawing:
3070 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3071 fill="", width=ps)
3072 # Turtle now at position old,
3073 self._position = old
3074 ## if undo is done during crating a polygon, the last vertex
3075 ## will be deleted. if the polygon is entirel deleted,
3076 ## creatigPoly will be set to False.
3077 ## Polygons created before the last one will not be affected by undo()
3078 if self._creatingPoly:
3079 if len(self._poly) > 0:
3080 self._poly.pop()
3081 if self._poly == []:
3082 self._creatingPoly = False
3083 self._poly = None
3084 if filling:
3085 if self._fillpath == []:
3086 self._fillpath = None
3087 print("Unwahrscheinlich in _undogoto!")
3088 elif self._fillpath is not None:
3089 self._fillpath.pop()
3090 self._update() #count=True)
3091
3092 def _rotate(self, angle):
3093 """Turns pen clockwise by angle.
3094 """
3095 if self.undobuffer:
3096 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3097 angle *= self._degreesPerAU
3098 neworient = self._orient.rotate(angle)
3099 tracing = self.screen._tracing
3100 if tracing == 1 and self._speed > 0:
3101 anglevel = 3.0 * self._speed
3102 steps = 1 + int(abs(angle)/anglevel)
3103 delta = 1.0*angle/steps
3104 for _ in range(steps):
3105 self._orient = self._orient.rotate(delta)
3106 self._update()
3107 self._orient = neworient
3108 self._update()
3109
3110 def _newLine(self, usePos=True):
3111 """Closes current line item and starts a new one.
3112 Remark: if current line became too long, animation
3113 performance (via _drawline) slowed down considerably.
3114 """
3115 if len(self.currentLine) > 1:
3116 self.screen._drawline(self.currentLineItem, self.currentLine,
3117 self._pencolor, self._pensize)
3118 self.currentLineItem = self.screen._createline()
3119 self.items.append(self.currentLineItem)
3120 else:
3121 self.screen._drawline(self.currentLineItem, top=True)
3122 self.currentLine = []
3123 if usePos:
3124 self.currentLine = [self._position]
3125
3126 def filling(self):
3127 """Return fillstate (True if filling, False else).
3128
3129 No argument.
3130
3131 Example (for a Turtle instance named turtle):
3132 >>> turtle.begin_fill()
3133 >>> if turtle.filling():
3134 turtle.pensize(5)
3135 else:
3136 turtle.pensize(3)
3137 """
3138 return isinstance(self._fillpath, list)
3139
3140## def fill(self, flag=None):
3141## """Call fill(True) before drawing a shape to fill, fill(False) when done.
3142##
3143## Optional argument:
3144## flag -- True/False (or 1/0 respectively)
3145##
3146## Call fill(True) before drawing the shape you want to fill,
3147## and fill(False) when done.
3148## When used without argument: return fillstate (True if filling,
3149## False else)
3150##
3151## Example (for a Turtle instance named turtle):
3152## >>> turtle.fill(True)
3153## >>> turtle.forward(100)
3154## >>> turtle.left(90)
3155## >>> turtle.forward(100)
3156## >>> turtle.left(90)
3157## >>> turtle.forward(100)
3158## >>> turtle.left(90)
3159## >>> turtle.forward(100)
3160## >>> turtle.fill(False)
3161## """
3162## filling = isinstance(self._fillpath, list)
3163## if flag is None:
3164## return filling
3165## screen = self.screen
3166## entry1 = entry2 = ()
3167## if filling:
3168## if len(self._fillpath) > 2:
3169## self.screen._drawpoly(self._fillitem, self._fillpath,
3170## fill=self._fillcolor)
3171## entry1 = ("dofill", self._fillitem)
3172## if flag:
3173## self._fillitem = self.screen._createpoly()
3174## self.items.append(self._fillitem)
3175## self._fillpath = [self._position]
3176## entry2 = ("beginfill", self._fillitem) # , self._fillpath)
3177## self._newLine()
3178## else:
3179## self._fillitem = self._fillpath = None
3180## if self.undobuffer:
3181## if entry1 == ():
3182## if entry2 != ():
3183## self.undobuffer.push(entry2)
3184## else:
3185## if entry2 == ():
3186## self.undobuffer.push(entry1)
3187## else:
3188## self.undobuffer.push(["seq", entry1, entry2])
3189## self._update()
3190
3191 def begin_fill(self):
3192 """Called just before drawing a shape to be filled.
3193
3194 No argument.
3195
3196 Example (for a Turtle instance named turtle):
3197 >>> turtle.color("black", "red")
3198 >>> turtle.begin_fill()
3199 >>> turtle.circle(60)
3200 >>> turtle.end_fill()
3201 """
3202 if not self.filling():
3203 self._fillitem = self.screen._createpoly()
3204 self.items.append(self._fillitem)
3205 self._fillpath = [self._position]
3206 self._newLine()
3207 if self.undobuffer:
3208 self.undobuffer.push(("beginfill", self._fillitem))
3209 self._update()
3210
3211
3212 def end_fill(self):
3213 """Fill the shape drawn after the call begin_fill().
3214
3215 No argument.
3216
3217 Example (for a Turtle instance named turtle):
3218 >>> turtle.color("black", "red")
3219 >>> turtle.begin_fill()
3220 >>> turtle.circle(60)
3221 >>> turtle.end_fill()
3222 """
3223 if self.filling():
3224 if len(self._fillpath) > 2:
3225 self.screen._drawpoly(self._fillitem, self._fillpath,
3226 fill=self._fillcolor)
3227 if self.undobuffer:
3228 self.undobuffer.push(("dofill", self._fillitem))
3229 self._fillitem = self._fillpath = None
3230 self._update()
3231
3232 def dot(self, size=None, *color):
3233 """Draw a dot with diameter size, using color.
3234
3235 Optional argumentS:
3236 size -- an integer >= 1 (if given)
3237 color -- a colorstring or a numeric color tuple
3238
3239 Draw a circular dot with diameter size, using color.
3240 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3241
3242 Example (for a Turtle instance named turtle):
3243 >>> turtle.dot()
3244 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3245 """
3246 #print "dot-1:", size, color
3247 if not color:
3248 if isinstance(size, (str, tuple)):
3249 color = self._colorstr(size)
3250 size = self._pensize + max(self._pensize, 4)
3251 else:
3252 color = self._pencolor
3253 if not size:
3254 size = self._pensize + max(self._pensize, 4)
3255 else:
3256 if size is None:
3257 size = self._pensize + max(self._pensize, 4)
3258 color = self._colorstr(color)
3259 #print "dot-2:", size, color
3260 if hasattr(self.screen, "_dot"):
3261 item = self.screen._dot(self._position, size, color)
3262 #print "dot:", size, color, "item:", item
3263 self.items.append(item)
3264 if self.undobuffer:
3265 self.undobuffer.push(("dot", item))
3266 else:
3267 pen = self.pen()
3268 if self.undobuffer:
3269 self.undobuffer.push(["seq"])
3270 self.undobuffer.cumulate = True
3271 try:
3272 if self.resizemode() == 'auto':
3273 self.ht()
3274 self.pendown()
3275 self.pensize(size)
3276 self.pencolor(color)
3277 self.forward(0)
3278 finally:
3279 self.pen(pen)
3280 if self.undobuffer:
3281 self.undobuffer.cumulate = False
3282
3283 def _write(self, txt, align, font):
3284 """Performs the writing for write()
3285 """
3286 item, end = self.screen._write(self._position, txt, align, font,
3287 self._pencolor)
3288 self.items.append(item)
3289 if self.undobuffer:
3290 self.undobuffer.push(("wri", item))
3291 return end
3292
3293 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3294 """Write text at the current turtle position.
3295
3296 Arguments:
3297 arg -- info, which is to be written to the TurtleScreen
3298 move (optional) -- True/False
3299 align (optional) -- one of the strings "left", "center" or right"
3300 font (optional) -- a triple (fontname, fontsize, fonttype)
3301
3302 Write text - the string representation of arg - at the current
3303 turtle position according to align ("left", "center" or right")
3304 and with the given font.
3305 If move is True, the pen is moved to the bottom-right corner
3306 of the text. By default, move is False.
3307
3308 Example (for a Turtle instance named turtle):
3309 >>> turtle.write('Home = ', True, align="center")
3310 >>> turtle.write((0,0), True)
3311 """
3312 if self.undobuffer:
3313 self.undobuffer.push(["seq"])
3314 self.undobuffer.cumulate = True
3315 end = self._write(str(arg), align.lower(), font)
3316 if move:
3317 x, y = self.pos()
3318 self.setpos(end, y)
3319 if self.undobuffer:
3320 self.undobuffer.cumulate = False
3321
3322 def begin_poly(self):
3323 """Start recording the vertices of a polygon.
3324
3325 No argument.
3326
3327 Start recording the vertices of a polygon. Current turtle position
3328 is first point of polygon.
3329
3330 Example (for a Turtle instance named turtle):
3331 >>> turtle.begin_poly()
3332 """
3333 self._poly = [self._position]
3334 self._creatingPoly = True
3335
3336 def end_poly(self):
3337 """Stop recording the vertices of a polygon.
3338
3339 No argument.
3340
3341 Stop recording the vertices of a polygon. Current turtle position is
3342 last point of polygon. This will be connected with the first point.
3343
3344 Example (for a Turtle instance named turtle):
3345 >>> turtle.end_poly()
3346 """
3347 self._creatingPoly = False
3348
3349 def get_poly(self):
3350 """Return the lastly recorded polygon.
3351
3352 No argument.
3353
3354 Example (for a Turtle instance named turtle):
3355 >>> p = turtle.get_poly()
3356 >>> turtle.register_shape("myFavouriteShape", p)
3357 """
3358 ## check if there is any poly? -- 1st solution:
3359 if self._poly is not None:
3360 return tuple(self._poly)
3361
3362 def getscreen(self):
3363 """Return the TurtleScreen object, the turtle is drawing on.
3364
3365 No argument.
3366
3367 Return the TurtleScreen object, the turtle is drawing on.
3368 So TurtleScreen-methods can be called for that object.
3369
3370 Example (for a Turtle instance named turtle):
3371 >>> ts = turtle.getscreen()
3372 >>> ts
3373 <turtle.TurtleScreen object at 0x0106B770>
3374 >>> ts.bgcolor("pink")
3375 """
3376 return self.screen
3377
3378 def getturtle(self):
3379 """Return the Turtleobject itself.
3380
3381 No argument.
3382
3383 Only reasonable use: as a function to return the 'anonymous turtle':
3384
3385 Example:
3386 >>> pet = getturtle()
3387 >>> pet.fd(50)
3388 >>> pet
3389 <turtle.Turtle object at 0x0187D810>
3390 >>> turtles()
3391 [<turtle.Turtle object at 0x0187D810>]
3392 """
3393 return self
3394
3395 getpen = getturtle
3396
3397
3398 ################################################################
3399 ### screen oriented methods recurring to methods of TurtleScreen
3400 ################################################################
3401
3402## def window_width(self):
3403## """ Returns the width of the turtle window.
3404##
3405## No argument.
3406##
3407## Example (for a TurtleScreen instance named screen):
3408## >>> screen.window_width()
3409## 640
3410## """
3411## return self.screen._window_size()[0]
3412##
3413## def window_height(self):
3414## """ Return the height of the turtle window.
3415##
3416## No argument.
3417##
3418## Example (for a TurtleScreen instance named screen):
3419## >>> screen.window_height()
3420## 480
3421## """
3422## return self.screen._window_size()[1]
3423
3424 def _delay(self, delay=None):
3425 """Set delay value which determines speed of turtle animation.
3426 """
3427 return self.screen.delay(delay)
3428
3429 ##### event binding methods #####
3430
3431 def onclick(self, fun, btn=1, add=None):
3432 """Bind fun to mouse-click event on this turtle on canvas.
3433
3434 Arguments:
3435 fun -- a function with two arguments, to which will be assigned
3436 the coordinates of the clicked point on the canvas.
3437 num -- number of the mouse-button defaults to 1 (left mouse button).
3438 add -- True or False. If True, new binding will be added, otherwise
3439 it will replace a former binding.
3440
3441 Example for the anonymous turtle, i. e. the procedural way:
3442
3443 >>> def turn(x, y):
3444 left(360)
3445
3446 >>> onclick(turn) # Now clicking into the turtle will turn it.
3447 >>> onclick(None) # event-binding will be removed
3448 """
3449 self.screen._onclick(self.turtle._item, fun, btn, add)
3450 self._update()
3451
3452 def onrelease(self, fun, btn=1, add=None):
3453 """Bind fun to mouse-button-release event on this turtle on canvas.
3454
3455 Arguments:
3456 fun -- a function with two arguments, to which will be assigned
3457 the coordinates of the clicked point on the canvas.
3458 num -- number of the mouse-button defaults to 1 (left mouse button).
3459
3460 Example (for a MyTurtle instance named joe):
3461 >>> class MyTurtle(Turtle):
3462 def glow(self,x,y):
3463 self.fillcolor("red")
3464 def unglow(self,x,y):
3465 self.fillcolor("")
3466
3467 >>> joe = MyTurtle()
3468 >>> joe.onclick(joe.glow)
3469 >>> joe.onrelease(joe.unglow)
3470 ### clicking on joe turns fillcolor red,
3471 ### unclicking turns it to transparent.
3472 """
3473 self.screen._onrelease(self.turtle._item, fun, btn, add)
3474 self._update()
3475
3476 def ondrag(self, fun, btn=1, add=None):
3477 """Bind fun to mouse-move event on this turtle on canvas.
3478
3479 Arguments:
3480 fun -- a function with two arguments, to which will be assigned
3481 the coordinates of the clicked point on the canvas.
3482 num -- number of the mouse-button defaults to 1 (left mouse button).
3483
3484 Every sequence of mouse-move-events on a turtle is preceded by a
3485 mouse-click event on that turtle.
3486
3487 Example (for a Turtle instance named turtle):
3488 >>> turtle.ondrag(turtle.goto)
3489
3490 ### Subsequently clicking and dragging a Turtle will
3491 ### move it across the screen thereby producing handdrawings
3492 ### (if pen is down).
3493 """
3494 self.screen._ondrag(self.turtle._item, fun, btn, add)
3495
3496
3497 def _undo(self, action, data):
3498 """Does the main part of the work for undo()
3499 """
3500 if self.undobuffer is None:
3501 return
3502 if action == "rot":
3503 angle, degPAU = data
3504 self._rotate(-angle*degPAU/self._degreesPerAU)
3505 dummy = self.undobuffer.pop()
3506 elif action == "stamp":
3507 stitem = data[0]
3508 self.clearstamp(stitem)
3509 elif action == "go":
3510 self._undogoto(data)
3511 elif action in ["wri", "dot"]:
3512 item = data[0]
3513 self.screen._delete(item)
3514 self.items.remove(item)
3515 elif action == "dofill":
3516 item = data[0]
3517 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3518 fill="", outline="")
3519 elif action == "beginfill":
3520 item = data[0]
3521 self._fillitem = self._fillpath = None
3522 if item in self.items:
3523 self.screen._delete(item)
3524 self.items.remove(item)
3525 elif action == "pen":
3526 TPen.pen(self, data[0])
3527 self.undobuffer.pop()
3528
3529 def undo(self):
3530 """undo (repeatedly) the last turtle action.
3531
3532 No argument.
3533
3534 undo (repeatedly) the last turtle action.
3535 Number of available undo actions is determined by the size of
3536 the undobuffer.
3537
3538 Example (for a Turtle instance named turtle):
3539 >>> for i in range(4):
3540 turtle.fd(50); turtle.lt(80)
3541
3542 >>> for i in range(8):
3543 turtle.undo()
3544 """
3545 if self.undobuffer is None:
3546 return
3547 item = self.undobuffer.pop()
3548 action = item[0]
3549 data = item[1:]
3550 if action == "seq":
3551 while data:
3552 item = data.pop()
3553 self._undo(item[0], item[1:])
3554 else:
3555 self._undo(action, data)
3556
3557 turtlesize = shapesize
3558
3559RawPen = RawTurtle
3560
Martin v. Löwis601149b2008-09-29 22:19:08 +00003561### Screen - Singleton ########################
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003562
Martin v. Löwis601149b2008-09-29 22:19:08 +00003563def Screen():
3564 """Return the singleton screen object.
3565 If none exists at the moment, create a new one and return it,
3566 else return the existing one."""
3567 if Turtle._screen is None:
3568 Turtle._screen = _Screen()
3569 return Turtle._screen
3570
3571class _Screen(TurtleScreen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003572
3573 _root = None
3574 _canvas = None
3575 _title = _CFG["title"]
3576
Guido van Rossumb241b671998-12-04 16:42:46 +00003577 def __init__(self):
Martin v. Löwis601149b2008-09-29 22:19:08 +00003578 # XXX there is no need for this code to be conditional,
3579 # as there will be only a single _Screen instance, anyway
3580 # XXX actually, the turtle demo is injecting root window,
3581 # so perhaps the conditional creation of a root should be
3582 # preserved (perhaps by passing it as an optional parameter)
3583 if _Screen._root is None:
3584 _Screen._root = self._root = _Root()
3585 self._root.title(_Screen._title)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003586 self._root.ondestroy(self._destroy)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003587 if _Screen._canvas is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003588 width = _CFG["width"]
3589 height = _CFG["height"]
3590 canvwidth = _CFG["canvwidth"]
3591 canvheight = _CFG["canvheight"]
3592 leftright = _CFG["leftright"]
3593 topbottom = _CFG["topbottom"]
3594 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003595 _Screen._canvas = self._root._getcanvas()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003596 self.setup(width, height, leftright, topbottom)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003597 TurtleScreen.__init__(self, _Screen._canvas)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003598
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003599 def setup(self, width=_CFG["width"], height=_CFG["height"],
3600 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3601 """ Set the size and position of the main window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003602
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003603 Arguments:
3604 width: as integer a size in pixels, as float a fraction of the screen.
3605 Default is 50% of screen.
3606 height: as integer the height in pixels, as float a fraction of the
3607 screen. Default is 75% of screen.
3608 startx: if positive, starting position in pixels from the left
3609 edge of the screen, if negative from the right edge
3610 Default, startx=None is to center window horizontally.
3611 starty: if positive, starting position in pixels from the top
3612 edge of the screen, if negative from the bottom edge
3613 Default, starty=None is to center window vertically.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003614
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003615 Examples (for a Screen instance named screen):
3616 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3617
3618 sets window to 200x200 pixels, in upper left of screen
3619
3620 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3621
3622 sets window to 75% of screen by 50% of screen and centers
3623 """
3624 if not hasattr(self._root, "set_geometry"):
3625 return
3626 sw = self._root.win_width()
3627 sh = self._root.win_height()
3628 if isinstance(width, float) and 0 <= width <= 1:
3629 width = sw*width
3630 if startx is None:
3631 startx = (sw - width) / 2
3632 if isinstance(height, float) and 0 <= height <= 1:
3633 height = sh*height
3634 if starty is None:
3635 starty = (sh - height) / 2
3636 self._root.set_geometry(width, height, startx, starty)
3637
3638 def title(self, titlestring):
3639 """Set title of turtle-window
3640
3641 Argument:
3642 titlestring -- a string, to appear in the titlebar of the
3643 turtle graphics window.
3644
3645 This is a method of Screen-class. Not available for TurtleScreen-
3646 objects.
3647
3648 Example (for a Screen instance named screen):
3649 >>> screen.title("Welcome to the turtle-zoo!")
3650 """
Martin v. Löwis601149b2008-09-29 22:19:08 +00003651 if _Screen._root is not None:
3652 _Screen._root.title(titlestring)
3653 _Screen._title = titlestring
Guido van Rossumb241b671998-12-04 16:42:46 +00003654
3655 def _destroy(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003656 root = self._root
Martin v. Löwis601149b2008-09-29 22:19:08 +00003657 if root is _Screen._root:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003658 Turtle._pen = None
3659 Turtle._screen = None
Martin v. Löwis601149b2008-09-29 22:19:08 +00003660 _Screen._root = None
3661 _Screen._canvas = None
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003662 TurtleScreen._RUNNING = True
Guido van Rossumb241b671998-12-04 16:42:46 +00003663 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +00003664
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003665 def bye(self):
3666 """Shut the turtlegraphics window.
3667
3668 Example (for a TurtleScreen instance named screen):
3669 >>> screen.bye()
3670 """
3671 self._destroy()
3672
3673 def exitonclick(self):
3674 """Go into mainloop until the mouse is clicked.
3675
3676 No arguments.
3677
3678 Bind bye() method to mouseclick on TurtleScreen.
3679 If "using_IDLE" - value in configuration dictionary is False
3680 (default value), enter mainloop.
3681 If IDLE with -n switch (no subprocess) is used, this value should be
3682 set to True in turtle.cfg. In this case IDLE's mainloop
3683 is active also for the client script.
3684
3685 This is a method of the Screen-class and not available for
3686 TurtleScreen instances.
3687
3688 Example (for a Screen instance named screen):
3689 >>> screen.exitonclick()
3690
3691 """
3692 def exitGracefully(x, y):
3693 """Screen.bye() with two dummy-parameters"""
3694 self.bye()
3695 self.onclick(exitGracefully)
3696 if _CFG["using_IDLE"]:
3697 return
3698 try:
3699 mainloop()
3700 except AttributeError:
3701 exit(0)
3702
3703
3704class Turtle(RawTurtle):
3705 """RawTurtle auto-crating (scrolled) canvas.
3706
3707 When a Turtle object is created or a function derived from some
3708 Turtle method is called a TurtleScreen object is automatically created.
3709 """
3710 _pen = None
3711 _screen = None
3712
3713 def __init__(self,
3714 shape=_CFG["shape"],
3715 undobuffersize=_CFG["undobuffersize"],
3716 visible=_CFG["visible"]):
3717 if Turtle._screen is None:
3718 Turtle._screen = Screen()
3719 RawTurtle.__init__(self, Turtle._screen,
3720 shape=shape,
3721 undobuffersize=undobuffersize,
3722 visible=visible)
3723
3724Pen = Turtle
3725
Guido van Rossumb241b671998-12-04 16:42:46 +00003726def _getpen():
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003727 """Create the 'anonymous' turtle if not already present."""
3728 if Turtle._pen is None:
3729 Turtle._pen = Turtle()
3730 return Turtle._pen
Thomas Wouters477c8d52006-05-27 19:21:47 +00003731
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003732def _getscreen():
3733 """Create a TurtleScreen if not already present."""
3734 if Turtle._screen is None:
3735 Turtle._screen = Screen()
3736 return Turtle._screen
Thomas Wouters477c8d52006-05-27 19:21:47 +00003737
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003738def write_docstringdict(filename="turtle_docstringdict"):
3739 """Create and write docstring-dictionary to file.
Guido van Rossumb241b671998-12-04 16:42:46 +00003740
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003741 Optional argument:
3742 filename -- a string, used as filename
3743 default value is turtle_docstringdict
Thomas Wouters477c8d52006-05-27 19:21:47 +00003744
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003745 Has to be called explicitely, (not used by the turtle-graphics classes)
3746 The docstring dictionary will be written to the Python script <filname>.py
3747 It is intended to serve as a template for translation of the docstrings
3748 into different languages.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003749 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003750 docsdict = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +00003751
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003752 for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003753 key = "_Screen."+methodname
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003754 docsdict[key] = eval(key).__doc__
3755 for methodname in _tg_turtle_functions:
3756 key = "Turtle."+methodname
3757 docsdict[key] = eval(key).__doc__
Thomas Wouters477c8d52006-05-27 19:21:47 +00003758
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003759 f = open("%s.py" % filename,"w")
3760 keys = sorted([x for x in docsdict.keys()
3761 if x.split('.')[1] not in _alias_list])
3762 f.write('docsdict = {\n\n')
3763 for key in keys[:-1]:
3764 f.write('%s :\n' % repr(key))
3765 f.write(' """%s\n""",\n\n' % docsdict[key])
3766 key = keys[-1]
3767 f.write('%s :\n' % repr(key))
3768 f.write(' """%s\n"""\n\n' % docsdict[key])
3769 f.write("}\n")
3770 f.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00003771
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003772def read_docstrings(lang):
3773 """Read in docstrings from lang-specific docstring dictionary.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003774
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003775 Transfer docstrings, translated to lang, from a dictionary-file
3776 to the methods of classes Screen and Turtle and - in revised form -
3777 to the corresponding functions.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003778 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003779 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3780 module = __import__(modname)
3781 docsdict = module.docsdict
3782 for key in docsdict:
3783 try:
3784# eval(key).im_func.__doc__ = docsdict[key]
3785 eval(key).__doc__ = docsdict[key]
3786 except:
3787 print("Bad docstring-entry: %s" % key)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003788
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003789_LANGUAGE = _CFG["language"]
Guido van Rossumb241b671998-12-04 16:42:46 +00003790
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003791try:
3792 if _LANGUAGE != "english":
3793 read_docstrings(_LANGUAGE)
3794except ImportError:
3795 print("Cannot find docsdict for", _LANGUAGE)
3796except:
3797 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3798 _LANGUAGE)
3799
3800
3801def getmethparlist(ob):
3802 "Get strings describing the arguments for the given object"
3803 argText1 = argText2 = ""
3804 # bit of a hack for methods - turn it into a function
3805 # but we drop the "self" param.
3806## if type(ob)==types.MethodType:
3807## fob = ob.im_func
3808## argOffset = 1
3809## else:
3810## fob = ob
3811## argOffset = 0
3812 # Try and build one for Python defined functions
3813 argOffset = 1
3814## if type(fob) in [types.FunctionType, types.LambdaType]:
3815## try:
3816 counter = ob.__code__.co_argcount
3817 items2 = list(ob.__code__.co_varnames[argOffset:counter])
3818 realArgs = ob.__code__.co_varnames[argOffset:counter]
3819 defaults = ob.__defaults__ or []
3820 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
3821 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3822 items1 = list(map(lambda arg, dflt: arg+dflt, realArgs, defaults))
3823 if ob.__code__.co_flags & 0x4:
3824 items1.append("*"+ob.__code__.co_varnames[counter])
3825 items2.append("*"+ob.__code__.co_varnames[counter])
3826 counter += 1
3827 if ob.__code__.co_flags & 0x8:
3828 items1.append("**"+ob.__code__.co_varnames[counter])
3829 items2.append("**"+ob.__code__.co_varnames[counter])
3830 argText1 = ", ".join(items1)
3831 argText1 = "(%s)" % argText1
3832 argText2 = ", ".join(items2)
3833 argText2 = "(%s)" % argText2
3834## except:
3835## pass
3836 return argText1, argText2
3837
3838def _turtle_docrevise(docstr):
3839 """To reduce docstrings from RawTurtle class for functions
3840 """
3841 import re
3842 if docstr is None:
3843 return None
3844 turtlename = _CFG["exampleturtle"]
3845 newdocstr = docstr.replace("%s." % turtlename,"")
3846 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3847 newdocstr = parexp.sub(":", newdocstr)
3848 return newdocstr
3849
3850def _screen_docrevise(docstr):
3851 """To reduce docstrings from TurtleScreen class for functions
3852 """
3853 import re
3854 if docstr is None:
3855 return None
3856 screenname = _CFG["examplescreen"]
3857 newdocstr = docstr.replace("%s." % screenname,"")
3858 parexp = re.compile(r' \(.+ %s\):' % screenname)
3859 newdocstr = parexp.sub(":", newdocstr)
3860 return newdocstr
3861
3862## The following mechanism makes all methods of RawTurtle and Turtle available
3863## as functions. So we can enhance, change, add, delete methods to these
3864## classes and do not need to change anything here.
3865
3866
3867for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003868 pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003869 if pl1 == "":
3870 print(">>>>>>", pl1, pl2)
3871 continue
3872 defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
3873 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3874## print("Screen:", defstr)
3875 exec(defstr)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003876 eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003877
3878for methodname in _tg_turtle_functions:
3879 pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
3880 if pl1 == "":
3881 print(">>>>>>", pl1, pl2)
3882 continue
3883 defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
3884 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
3885## print("Turtle:", defstr)
3886 exec(defstr)
3887 eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
3888
3889
3890done = mainloop = TK.mainloop
3891#del pl1, pl2, defstr
3892
3893if __name__ == "__main__":
3894 def switchpen():
3895 if isdown():
3896 pu()
3897 else:
3898 pd()
3899
3900 def demo1():
3901 """Demo of old turtle.py - module"""
3902 reset()
3903 tracer(True)
3904 up()
3905 backward(100)
3906 down()
3907 # draw 3 squares; the last filled
3908 width(3)
3909 for i in range(3):
3910 if i == 2:
3911 begin_fill()
3912 for _ in range(4):
3913 forward(20)
3914 left(90)
3915 if i == 2:
3916 color("maroon")
3917 end_fill()
3918 up()
3919 forward(30)
3920 down()
3921 width(1)
3922 color("black")
3923 # move out of the way
3924 tracer(False)
3925 up()
3926 right(90)
3927 forward(100)
3928 right(90)
3929 forward(100)
3930 right(180)
3931 down()
3932 # some text
3933 write("startstart", 1)
3934 write("start", 1)
3935 color("red")
3936 # staircase
3937 for i in range(5):
Guido van Rossumb241b671998-12-04 16:42:46 +00003938 forward(20)
3939 left(90)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003940 forward(20)
3941 right(90)
3942 # filled staircase
3943 tracer(True)
3944 begin_fill()
3945 for i in range(5):
3946 forward(20)
3947 left(90)
3948 forward(20)
3949 right(90)
3950 end_fill()
3951 # more text
Thomas Wouters477c8d52006-05-27 19:21:47 +00003952
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003953 def demo2():
3954 """Demo of some new features."""
3955 speed(1)
3956 st()
3957 pensize(3)
3958 setheading(towards(0, 0))
3959 radius = distance(0, 0)/2.0
3960 rt(90)
3961 for _ in range(18):
3962 switchpen()
3963 circle(radius, 10)
3964 write("wait a moment...")
3965 while undobufferentries():
3966 undo()
3967 reset()
3968 lt(90)
3969 colormode(255)
3970 laenge = 10
3971 pencolor("green")
3972 pensize(3)
3973 lt(180)
3974 for i in range(-2, 16):
3975 if i > 0:
3976 begin_fill()
3977 fillcolor(255-15*i, 0, 15*i)
3978 for _ in range(3):
3979 fd(laenge)
3980 lt(120)
3981 end_fill()
3982 laenge += 10
3983 lt(15)
3984 speed((speed()+1)%12)
3985 #end_fill()
Thomas Wouters477c8d52006-05-27 19:21:47 +00003986
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003987 lt(120)
3988 pu()
3989 fd(70)
3990 rt(30)
3991 pd()
3992 color("red","yellow")
3993 speed(0)
3994 begin_fill()
3995 for _ in range(4):
3996 circle(50, 90)
3997 rt(90)
3998 fd(30)
3999 rt(90)
4000 end_fill()
4001 lt(90)
4002 pu()
4003 fd(30)
4004 pd()
4005 shape("turtle")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004006
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004007 tri = getturtle()
4008 tri.resizemode("auto")
4009 turtle = Turtle()
4010 turtle.resizemode("auto")
4011 turtle.shape("turtle")
4012 turtle.reset()
4013 turtle.left(90)
4014 turtle.speed(0)
4015 turtle.up()
4016 turtle.goto(280, 40)
4017 turtle.lt(30)
4018 turtle.down()
4019 turtle.speed(6)
4020 turtle.color("blue","orange")
4021 turtle.pensize(2)
4022 tri.speed(6)
Thomas Wouters477c8d52006-05-27 19:21:47 +00004023 setheading(towards(turtle))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004024 count = 1
4025 while tri.distance(turtle) > 4:
4026 turtle.fd(3.5)
4027 turtle.lt(0.6)
4028 tri.setheading(tri.towards(turtle))
4029 tri.fd(4)
4030 if count % 20 == 0:
4031 turtle.stamp()
4032 tri.stamp()
4033 switchpen()
4034 count += 1
4035 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4036 tri.pencolor("black")
4037 tri.pencolor("red")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004038
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004039 def baba(xdummy, ydummy):
4040 clearscreen()
4041 bye()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004042
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004043 time.sleep(2)
Guido van Rossumb241b671998-12-04 16:42:46 +00004044
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004045 while undobufferentries():
4046 tri.undo()
4047 turtle.undo()
4048 tri.fd(50)
4049 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4050 tri.onclick(baba, 1)
4051
4052 demo1()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004053 demo2()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004054 exitonclick()