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