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