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