blob: ad7518336f8ebc66b199405cf0716f0f16d2e994 [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
Sandro Tosi2a389e42011-08-07 17:12:19 +020030Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it
Thomas Wouters477c8d52006-05-27 19:21:47 +000031the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
32the direction it is facing, drawing a line as it moves. Give it the
Sandro Tosi2a389e42011-08-07 17:12:19 +020033command turtle.right(25), and it rotates in-place 25 degrees clockwise.
Thomas Wouters477c8d52006-05-27 19:21:47 +000034
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
Alexander Belopolskyc1a68362010-10-27 13:25:45 +0000111import inspect
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 >>> """
Alexander Belopolsky022f0492010-11-22 19:40:51 +0000754 cl = self.cv.coords(item)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000755 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
Ezio Melotti42da6632011-03-15 05:18:48 +02001466 If picname is a filename, set the corresponding image as background.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001467 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
Ezio Melotti13925002011-03-16 11:05:33 +02001490 bg -- colorstring or color-tuple, new backgroundcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001491 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
Alexander Belopolsky3cdfb122010-10-29 17:16:49 +00001577
1578 Change angle measurement unit to grad (also known as gon,
1579 grade, or gradian and equals 1/100-th of the right angle.)
1580 >>> turtle.degrees(400.0)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001581 >>> turtle.heading()
1582 100
1583
1584 """
1585 self._setDegreesPerAU(fullcircle)
1586
1587 def radians(self):
1588 """ Set the angle measurement units to radians.
1589
1590 No arguments.
1591
1592 Example (for a Turtle instance named turtle):
1593 >>> turtle.heading()
1594 90
1595 >>> turtle.radians()
1596 >>> turtle.heading()
1597 1.5707963267948966
1598 """
1599 self._setDegreesPerAU(2*math.pi)
1600
1601 def _go(self, distance):
1602 """move turtle forward by specified distance"""
1603 ende = self._position + self._orient * distance
1604 self._goto(ende)
1605
1606 def _rotate(self, angle):
1607 """Turn turtle counterclockwise by specified angle if angle > 0."""
1608 angle *= self._degreesPerAU
1609 self._orient = self._orient.rotate(angle)
1610
1611 def _goto(self, end):
1612 """move turtle to position end."""
1613 self._position = end
1614
1615 def forward(self, distance):
1616 """Move the turtle forward by the specified distance.
1617
1618 Aliases: forward | fd
1619
1620 Argument:
1621 distance -- a number (integer or float)
1622
1623 Move the turtle forward by the specified distance, in the direction
1624 the turtle is headed.
1625
1626 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001627 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001628 (0.00, 0.00)
1629 >>> turtle.forward(25)
1630 >>> turtle.position()
1631 (25.00,0.00)
1632 >>> turtle.forward(-75)
1633 >>> turtle.position()
1634 (-50.00,0.00)
1635 """
1636 self._go(distance)
1637
1638 def back(self, distance):
1639 """Move the turtle backward by distance.
1640
1641 Aliases: back | backward | bk
1642
1643 Argument:
1644 distance -- a number
1645
1646 Move the turtle backward by distance ,opposite to the direction the
1647 turtle is headed. Do not change the turtle's heading.
1648
1649 Example (for a Turtle instance named turtle):
1650 >>> turtle.position()
1651 (0.00, 0.00)
1652 >>> turtle.backward(30)
1653 >>> turtle.position()
1654 (-30.00, 0.00)
1655 """
1656 self._go(-distance)
1657
1658 def right(self, angle):
1659 """Turn turtle right by angle units.
1660
1661 Aliases: right | rt
1662
1663 Argument:
1664 angle -- a number (integer or float)
1665
1666 Turn turtle right by angle units. (Units are by default degrees,
1667 but can be set via the degrees() and radians() functions.)
1668 Angle orientation depends on mode. (See this.)
1669
1670 Example (for a Turtle instance named turtle):
1671 >>> turtle.heading()
1672 22.0
1673 >>> turtle.right(45)
1674 >>> turtle.heading()
1675 337.0
1676 """
1677 self._rotate(-angle)
1678
1679 def left(self, angle):
1680 """Turn turtle left by angle units.
1681
1682 Aliases: left | lt
1683
1684 Argument:
1685 angle -- a number (integer or float)
1686
1687 Turn turtle left by angle units. (Units are by default degrees,
1688 but can be set via the degrees() and radians() functions.)
1689 Angle orientation depends on mode. (See this.)
1690
1691 Example (for a Turtle instance named turtle):
1692 >>> turtle.heading()
1693 22.0
1694 >>> turtle.left(45)
1695 >>> turtle.heading()
1696 67.0
1697 """
1698 self._rotate(angle)
1699
1700 def pos(self):
1701 """Return the turtle's current location (x,y), as a Vec2D-vector.
1702
1703 Aliases: pos | position
1704
1705 No arguments.
1706
1707 Example (for a Turtle instance named turtle):
1708 >>> turtle.pos()
1709 (0.00, 240.00)
1710 """
1711 return self._position
1712
1713 def xcor(self):
1714 """ Return the turtle's x coordinate.
1715
1716 No arguments.
1717
1718 Example (for a Turtle instance named turtle):
1719 >>> reset()
1720 >>> turtle.left(60)
1721 >>> turtle.forward(100)
1722 >>> print turtle.xcor()
1723 50.0
1724 """
1725 return self._position[0]
1726
1727 def ycor(self):
1728 """ Return the turtle's y coordinate
1729 ---
1730 No arguments.
1731
1732 Example (for a Turtle instance named turtle):
1733 >>> reset()
1734 >>> turtle.left(60)
1735 >>> turtle.forward(100)
1736 >>> print turtle.ycor()
1737 86.6025403784
1738 """
1739 return self._position[1]
1740
1741
1742 def goto(self, x, y=None):
1743 """Move turtle to an absolute position.
1744
1745 Aliases: setpos | setposition | goto:
1746
1747 Arguments:
1748 x -- a number or a pair/vector of numbers
1749 y -- a number None
1750
1751 call: goto(x, y) # two coordinates
1752 --or: goto((x, y)) # a pair (tuple) of coordinates
1753 --or: goto(vec) # e.g. as returned by pos()
1754
1755 Move turtle to an absolute position. If the pen is down,
1756 a line will be drawn. The turtle's orientation does not change.
1757
1758 Example (for a Turtle instance named turtle):
1759 >>> tp = turtle.pos()
1760 >>> tp
1761 (0.00, 0.00)
1762 >>> turtle.setpos(60,30)
1763 >>> turtle.pos()
1764 (60.00,30.00)
1765 >>> turtle.setpos((20,80))
1766 >>> turtle.pos()
1767 (20.00,80.00)
1768 >>> turtle.setpos(tp)
1769 >>> turtle.pos()
1770 (0.00,0.00)
1771 """
1772 if y is None:
1773 self._goto(Vec2D(*x))
1774 else:
1775 self._goto(Vec2D(x, y))
1776
1777 def home(self):
1778 """Move turtle to the origin - coordinates (0,0).
1779
1780 No arguments.
1781
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001782 Move turtle to the origin - coordinates (0,0) and set its
1783 heading to its start-orientation (which depends on mode).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001784
1785 Example (for a Turtle instance named turtle):
1786 >>> turtle.home()
1787 """
1788 self.goto(0, 0)
1789 self.setheading(0)
1790
1791 def setx(self, x):
1792 """Set the turtle's first coordinate to x
1793
1794 Argument:
1795 x -- a number (integer or float)
1796
1797 Set the turtle's first coordinate to x, leave second coordinate
1798 unchanged.
1799
1800 Example (for a Turtle instance named turtle):
1801 >>> turtle.position()
1802 (0.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001803 >>> turtle.setx(10)
1804 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001805 (10.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001806 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001807 self._goto(Vec2D(x, self._position[1]))
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001808
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001809 def sety(self, y):
1810 """Set the turtle's second coordinate to y
Thomas Wouters477c8d52006-05-27 19:21:47 +00001811
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001812 Argument:
1813 y -- a number (integer or float)
1814
1815 Set the turtle's first coordinate to x, second coordinate remains
1816 unchanged.
1817
1818 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001819 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001820 (0.00, 40.00)
1821 >>> turtle.sety(-10)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001822 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001823 (0.00, -10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001824 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001825 self._goto(Vec2D(self._position[0], y))
Guido van Rossumb241b671998-12-04 16:42:46 +00001826
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001827 def distance(self, x, y=None):
1828 """Return the distance from the turtle to (x,y) in turtle step units.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001829
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001830 Arguments:
1831 x -- a number or a pair/vector of numbers or a turtle instance
1832 y -- a number None None
Thomas Wouters477c8d52006-05-27 19:21:47 +00001833
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001834 call: distance(x, y) # two coordinates
1835 --or: distance((x, y)) # a pair (tuple) of coordinates
1836 --or: distance(vec) # e.g. as returned by pos()
1837 --or: distance(mypen) # where mypen is another turtle
1838
1839 Example (for a Turtle instance named turtle):
1840 >>> turtle.pos()
1841 (0.00, 0.00)
1842 >>> turtle.distance(30,40)
1843 50.0
1844 >>> pen = Turtle()
1845 >>> pen.forward(77)
1846 >>> turtle.distance(pen)
1847 77.0
1848 """
1849 if y is not None:
1850 pos = Vec2D(x, y)
1851 if isinstance(x, Vec2D):
1852 pos = x
1853 elif isinstance(x, tuple):
1854 pos = Vec2D(*x)
1855 elif isinstance(x, TNavigator):
1856 pos = x._position
1857 return abs(pos - self._position)
1858
1859 def towards(self, x, y=None):
1860 """Return the angle of the line from the turtle's position to (x, y).
1861
1862 Arguments:
1863 x -- a number or a pair/vector of numbers or a turtle instance
1864 y -- a number None None
1865
1866 call: distance(x, y) # two coordinates
1867 --or: distance((x, y)) # a pair (tuple) of coordinates
1868 --or: distance(vec) # e.g. as returned by pos()
1869 --or: distance(mypen) # where mypen is another turtle
1870
1871 Return the angle, between the line from turtle-position to position
1872 specified by x, y and the turtle's start orientation. (Depends on
1873 modes - "standard" or "logo")
1874
1875 Example (for a Turtle instance named turtle):
1876 >>> turtle.pos()
1877 (10.00, 10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001878 >>> turtle.towards(0,0)
1879 225.0
1880 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001881 if y is not None:
1882 pos = Vec2D(x, y)
1883 if isinstance(x, Vec2D):
1884 pos = x
1885 elif isinstance(x, tuple):
1886 pos = Vec2D(*x)
1887 elif isinstance(x, TNavigator):
1888 pos = x._position
1889 x, y = pos - self._position
1890 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1891 result /= self._degreesPerAU
1892 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1893
1894 def heading(self):
1895 """ Return the turtle's current heading.
1896
1897 No arguments.
1898
1899 Example (for a Turtle instance named turtle):
1900 >>> turtle.left(67)
1901 >>> turtle.heading()
1902 67.0
1903 """
1904 x, y = self._orient
1905 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1906 result /= self._degreesPerAU
1907 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1908
1909 def setheading(self, to_angle):
1910 """Set the orientation of the turtle to to_angle.
1911
1912 Aliases: setheading | seth
1913
1914 Argument:
1915 to_angle -- a number (integer or float)
1916
1917 Set the orientation of the turtle to to_angle.
1918 Here are some common directions in degrees:
1919
1920 standard - mode: logo-mode:
1921 -------------------|--------------------
1922 0 - east 0 - north
1923 90 - north 90 - east
1924 180 - west 180 - south
1925 270 - south 270 - west
1926
1927 Example (for a Turtle instance named turtle):
1928 >>> turtle.setheading(90)
1929 >>> turtle.heading()
1930 90
1931 """
1932 angle = (to_angle - self.heading())*self._angleOrient
1933 full = self._fullcircle
1934 angle = (angle+full/2.)%full - full/2.
1935 self._rotate(angle)
1936
1937 def circle(self, radius, extent = None, steps = None):
1938 """ Draw a circle with given radius.
1939
1940 Arguments:
1941 radius -- a number
1942 extent (optional) -- a number
1943 steps (optional) -- an integer
1944
1945 Draw a circle with given radius. The center is radius units left
1946 of the turtle; extent - an angle - determines which part of the
1947 circle is drawn. If extent is not given, draw the entire circle.
1948 If extent is not a full circle, one endpoint of the arc is the
1949 current pen position. Draw the arc in counterclockwise direction
1950 if radius is positive, otherwise in clockwise direction. Finally
1951 the direction of the turtle is changed by the amount of extent.
1952
1953 As the circle is approximated by an inscribed regular polygon,
1954 steps determines the number of steps to use. If not given,
1955 it will be calculated automatically. Maybe used to draw regular
1956 polygons.
1957
1958 call: circle(radius) # full circle
1959 --or: circle(radius, extent) # arc
1960 --or: circle(radius, extent, steps)
1961 --or: circle(radius, steps=6) # 6-sided polygon
1962
1963 Example (for a Turtle instance named turtle):
1964 >>> turtle.circle(50)
1965 >>> turtle.circle(120, 180) # semicircle
1966 """
1967 if self.undobuffer:
1968 self.undobuffer.push(["seq"])
1969 self.undobuffer.cumulate = True
1970 speed = self.speed()
1971 if extent is None:
1972 extent = self._fullcircle
1973 if steps is None:
1974 frac = abs(extent)/self._fullcircle
1975 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1976 w = 1.0 * extent / steps
1977 w2 = 0.5 * w
1978 l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1979 if radius < 0:
1980 l, w, w2 = -l, -w, -w2
1981 tr = self._tracer()
1982 dl = self._delay()
1983 if speed == 0:
1984 self._tracer(0, 0)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001985 else:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001986 self.speed(0)
1987 self._rotate(w2)
1988 for i in range(steps):
1989 self.speed(speed)
1990 self._go(l)
1991 self.speed(0)
1992 self._rotate(w)
1993 self._rotate(-w2)
1994 if speed == 0:
1995 self._tracer(tr, dl)
1996 self.speed(speed)
1997 if self.undobuffer:
1998 self.undobuffer.cumulate = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001999
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002000## three dummy methods to be implemented by child class:
Thomas Wouters477c8d52006-05-27 19:21:47 +00002001
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002002 def speed(self, s=0):
2003 """dummy method - to be overwritten by child class"""
2004 def _tracer(self, a=None, b=None):
2005 """dummy method - to be overwritten by child class"""
2006 def _delay(self, n=None):
2007 """dummy method - to be overwritten by child class"""
Thomas Wouters477c8d52006-05-27 19:21:47 +00002008
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002009 fd = forward
2010 bk = back
2011 backward = back
2012 rt = right
2013 lt = left
2014 position = pos
2015 setpos = goto
2016 setposition = goto
2017 seth = setheading
Thomas Wouters477c8d52006-05-27 19:21:47 +00002018
Thomas Wouters477c8d52006-05-27 19:21:47 +00002019
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002020class TPen(object):
2021 """Drawing part of the RawTurtle.
2022 Implements drawing properties.
2023 """
2024 def __init__(self, resizemode=_CFG["resizemode"]):
2025 self._resizemode = resizemode # or "user" or "noresize"
2026 self.undobuffer = None
2027 TPen._reset(self)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002028
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002029 def _reset(self, pencolor=_CFG["pencolor"],
2030 fillcolor=_CFG["fillcolor"]):
2031 self._pensize = 1
2032 self._shown = True
2033 self._pencolor = pencolor
2034 self._fillcolor = fillcolor
2035 self._drawing = True
2036 self._speed = 3
Georg Brandleaa84ef2009-05-05 08:14:33 +00002037 self._stretchfactor = (1., 1.)
2038 self._shearfactor = 0.
2039 self._tilt = 0.
2040 self._shapetrafo = (1., 0., 0., 1.)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002041 self._outlinewidth = 1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002042
2043 def resizemode(self, rmode=None):
2044 """Set resizemode to one of the values: "auto", "user", "noresize".
2045
2046 (Optional) Argument:
2047 rmode -- one of the strings "auto", "user", "noresize"
2048
2049 Different resizemodes have the following effects:
2050 - "auto" adapts the appearance of the turtle
2051 corresponding to the value of pensize.
2052 - "user" adapts the appearance of the turtle according to the
2053 values of stretchfactor and outlinewidth (outline),
2054 which are set by shapesize()
2055 - "noresize" no adaption of the turtle's appearance takes place.
2056 If no argument is given, return current resizemode.
2057 resizemode("user") is called by a call of shapesize with arguments.
2058
2059
2060 Examples (for a Turtle instance named turtle):
2061 >>> turtle.resizemode("noresize")
2062 >>> turtle.resizemode()
2063 'noresize'
Thomas Wouters477c8d52006-05-27 19:21:47 +00002064 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002065 if rmode is None:
2066 return self._resizemode
2067 rmode = rmode.lower()
2068 if rmode in ["auto", "user", "noresize"]:
2069 self.pen(resizemode=rmode)
Guido van Rossumb241b671998-12-04 16:42:46 +00002070
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002071 def pensize(self, width=None):
2072 """Set or return the line thickness.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002073
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002074 Aliases: pensize | width
Thomas Wouters477c8d52006-05-27 19:21:47 +00002075
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002076 Argument:
2077 width -- positive number
Thomas Wouters477c8d52006-05-27 19:21:47 +00002078
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002079 Set the line thickness to width or return it. If resizemode is set
2080 to "auto" and turtleshape is a polygon, that polygon is drawn with
2081 the same line thickness. If no argument is given, current pensize
2082 is returned.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002083
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002084 Example (for a Turtle instance named turtle):
2085 >>> turtle.pensize()
2086 1
2087 turtle.pensize(10) # from here on lines of width 10 are drawn
Thomas Wouters477c8d52006-05-27 19:21:47 +00002088 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002089 if width is None:
2090 return self._pensize
2091 self.pen(pensize=width)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002092
2093
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002094 def penup(self):
2095 """Pull the pen up -- no drawing when moving.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002096
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002097 Aliases: penup | pu | up
Thomas Wouters477c8d52006-05-27 19:21:47 +00002098
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002099 No argument
2100
2101 Example (for a Turtle instance named turtle):
2102 >>> turtle.penup()
Thomas Wouters477c8d52006-05-27 19:21:47 +00002103 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002104 if not self._drawing:
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002105 return
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002106 self.pen(pendown=False)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002107
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002108 def pendown(self):
2109 """Pull the pen down -- drawing when moving.
2110
2111 Aliases: pendown | pd | down
2112
2113 No argument.
2114
2115 Example (for a Turtle instance named turtle):
2116 >>> turtle.pendown()
2117 """
2118 if self._drawing:
2119 return
2120 self.pen(pendown=True)
2121
2122 def isdown(self):
2123 """Return True if pen is down, False if it's up.
2124
2125 No argument.
2126
2127 Example (for a Turtle instance named turtle):
2128 >>> turtle.penup()
2129 >>> turtle.isdown()
2130 False
2131 >>> turtle.pendown()
2132 >>> turtle.isdown()
2133 True
2134 """
2135 return self._drawing
2136
2137 def speed(self, speed=None):
2138 """ Return or set the turtle's speed.
2139
2140 Optional argument:
2141 speed -- an integer in the range 0..10 or a speedstring (see below)
2142
2143 Set the turtle's speed to an integer value in the range 0 .. 10.
2144 If no argument is given: return current speed.
2145
2146 If input is a number greater than 10 or smaller than 0.5,
2147 speed is set to 0.
2148 Speedstrings are mapped to speedvalues in the following way:
2149 'fastest' : 0
2150 'fast' : 10
2151 'normal' : 6
2152 'slow' : 3
2153 'slowest' : 1
2154 speeds from 1 to 10 enforce increasingly faster animation of
2155 line drawing and turtle turning.
2156
2157 Attention:
2158 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2159 and likewise left/right make the turtle turn instantly.
2160
2161 Example (for a Turtle instance named turtle):
2162 >>> turtle.speed(3)
2163 """
2164 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2165 if speed is None:
2166 return self._speed
2167 if speed in speeds:
2168 speed = speeds[speed]
2169 elif 0.5 < speed < 10.5:
2170 speed = int(round(speed))
2171 else:
2172 speed = 0
2173 self.pen(speed=speed)
2174
2175 def color(self, *args):
2176 """Return or set the pencolor and fillcolor.
2177
2178 Arguments:
2179 Several input formats are allowed.
2180 They use 0, 1, 2, or 3 arguments as follows:
2181
2182 color()
2183 Return the current pencolor and the current fillcolor
2184 as a pair of color specification strings as are returned
2185 by pencolor and fillcolor.
2186 color(colorstring), color((r,g,b)), color(r,g,b)
2187 inputs as in pencolor, set both, fillcolor and pencolor,
2188 to the given value.
2189 color(colorstring1, colorstring2),
2190 color((r1,g1,b1), (r2,g2,b2))
2191 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2192 and analogously, if the other input format is used.
2193
2194 If turtleshape is a polygon, outline and interior of that polygon
2195 is drawn with the newly set colors.
2196 For mor info see: pencolor, fillcolor
2197
2198 Example (for a Turtle instance named turtle):
2199 >>> turtle.color('red', 'green')
2200 >>> turtle.color()
2201 ('red', 'green')
2202 >>> colormode(255)
2203 >>> color((40, 80, 120), (160, 200, 240))
2204 >>> color()
2205 ('#285078', '#a0c8f0')
2206 """
2207 if args:
2208 l = len(args)
2209 if l == 1:
2210 pcolor = fcolor = args[0]
2211 elif l == 2:
2212 pcolor, fcolor = args
2213 elif l == 3:
2214 pcolor = fcolor = args
2215 pcolor = self._colorstr(pcolor)
2216 fcolor = self._colorstr(fcolor)
2217 self.pen(pencolor=pcolor, fillcolor=fcolor)
2218 else:
2219 return self._color(self._pencolor), self._color(self._fillcolor)
2220
2221 def pencolor(self, *args):
2222 """ Return or set the pencolor.
2223
2224 Arguments:
2225 Four input formats are allowed:
2226 - pencolor()
2227 Return the current pencolor as color specification string,
2228 possibly in hex-number format (see example).
2229 May be used as input to another color/pencolor/fillcolor call.
2230 - pencolor(colorstring)
2231 s is a Tk color specification string, such as "red" or "yellow"
2232 - pencolor((r, g, b))
2233 *a tuple* of r, g, and b, which represent, an RGB color,
2234 and each of r, g, and b are in the range 0..colormode,
2235 where colormode is either 1.0 or 255
2236 - pencolor(r, g, b)
2237 r, g, and b represent an RGB color, and each of r, g, and b
2238 are in the range 0..colormode
2239
2240 If turtleshape is a polygon, the outline of that polygon is drawn
2241 with the newly set pencolor.
2242
2243 Example (for a Turtle instance named turtle):
2244 >>> turtle.pencolor('brown')
2245 >>> tup = (0.2, 0.8, 0.55)
2246 >>> turtle.pencolor(tup)
2247 >>> turtle.pencolor()
2248 '#33cc8c'
2249 """
2250 if args:
2251 color = self._colorstr(args)
2252 if color == self._pencolor:
2253 return
2254 self.pen(pencolor=color)
2255 else:
2256 return self._color(self._pencolor)
2257
2258 def fillcolor(self, *args):
2259 """ Return or set the fillcolor.
2260
2261 Arguments:
2262 Four input formats are allowed:
2263 - fillcolor()
2264 Return the current fillcolor as color specification string,
2265 possibly in hex-number format (see example).
2266 May be used as input to another color/pencolor/fillcolor call.
2267 - fillcolor(colorstring)
2268 s is a Tk color specification string, such as "red" or "yellow"
2269 - fillcolor((r, g, b))
2270 *a tuple* of r, g, and b, which represent, an RGB color,
2271 and each of r, g, and b are in the range 0..colormode,
2272 where colormode is either 1.0 or 255
2273 - fillcolor(r, g, b)
2274 r, g, and b represent an RGB color, and each of r, g, and b
2275 are in the range 0..colormode
2276
2277 If turtleshape is a polygon, the interior of that polygon is drawn
2278 with the newly set fillcolor.
2279
2280 Example (for a Turtle instance named turtle):
2281 >>> turtle.fillcolor('violet')
2282 >>> col = turtle.pencolor()
2283 >>> turtle.fillcolor(col)
2284 >>> turtle.fillcolor(0, .5, 0)
2285 """
2286 if args:
2287 color = self._colorstr(args)
2288 if color == self._fillcolor:
2289 return
2290 self.pen(fillcolor=color)
2291 else:
2292 return self._color(self._fillcolor)
2293
2294 def showturtle(self):
2295 """Makes the turtle visible.
2296
2297 Aliases: showturtle | st
2298
2299 No argument.
2300
2301 Example (for a Turtle instance named turtle):
2302 >>> turtle.hideturtle()
2303 >>> turtle.showturtle()
2304 """
2305 self.pen(shown=True)
2306
2307 def hideturtle(self):
2308 """Makes the turtle invisible.
2309
2310 Aliases: hideturtle | ht
2311
2312 No argument.
2313
2314 It's a good idea to do this while you're in the
2315 middle of a complicated drawing, because hiding
2316 the turtle speeds up the drawing observably.
2317
2318 Example (for a Turtle instance named turtle):
2319 >>> turtle.hideturtle()
2320 """
2321 self.pen(shown=False)
2322
2323 def isvisible(self):
2324 """Return True if the Turtle is shown, False if it's hidden.
2325
2326 No argument.
2327
2328 Example (for a Turtle instance named turtle):
2329 >>> turtle.hideturtle()
2330 >>> print turtle.isvisible():
2331 False
2332 """
2333 return self._shown
2334
2335 def pen(self, pen=None, **pendict):
2336 """Return or set the pen's attributes.
2337
2338 Arguments:
2339 pen -- a dictionary with some or all of the below listed keys.
2340 **pendict -- one or more keyword-arguments with the below
2341 listed keys as keywords.
2342
2343 Return or set the pen's attributes in a 'pen-dictionary'
2344 with the following key/value pairs:
2345 "shown" : True/False
2346 "pendown" : True/False
2347 "pencolor" : color-string or color-tuple
2348 "fillcolor" : color-string or color-tuple
2349 "pensize" : positive number
2350 "speed" : number in range 0..10
2351 "resizemode" : "auto" or "user" or "noresize"
2352 "stretchfactor": (positive number, positive number)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002353 "shearfactor": number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002354 "outline" : positive number
2355 "tilt" : number
2356
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002357 This dictionary can be used as argument for a subsequent
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002358 pen()-call to restore the former pen-state. Moreover one
2359 or more of these attributes can be provided as keyword-arguments.
2360 This can be used to set several pen attributes in one statement.
Guido van Rossumb241b671998-12-04 16:42:46 +00002361
2362
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002363 Examples (for a Turtle instance named turtle):
2364 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2365 >>> turtle.pen()
2366 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2367 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002368 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002369 >>> penstate=turtle.pen()
2370 >>> turtle.color("yellow","")
2371 >>> turtle.penup()
2372 >>> turtle.pen()
2373 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2374 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002375 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002376 >>> p.pen(penstate, fillcolor="green")
2377 >>> p.pen()
2378 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2379 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002380 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002381 """
2382 _pd = {"shown" : self._shown,
2383 "pendown" : self._drawing,
2384 "pencolor" : self._pencolor,
2385 "fillcolor" : self._fillcolor,
2386 "pensize" : self._pensize,
2387 "speed" : self._speed,
2388 "resizemode" : self._resizemode,
2389 "stretchfactor" : self._stretchfactor,
Georg Brandleaa84ef2009-05-05 08:14:33 +00002390 "shearfactor" : self._shearfactor,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002391 "outline" : self._outlinewidth,
2392 "tilt" : self._tilt
2393 }
Guido van Rossumb241b671998-12-04 16:42:46 +00002394
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002395 if not (pen or pendict):
2396 return _pd
2397
2398 if isinstance(pen, dict):
2399 p = pen
2400 else:
2401 p = {}
2402 p.update(pendict)
2403
2404 _p_buf = {}
2405 for key in p:
2406 _p_buf[key] = _pd[key]
2407
2408 if self.undobuffer:
2409 self.undobuffer.push(("pen", _p_buf))
2410
2411 newLine = False
2412 if "pendown" in p:
2413 if self._drawing != p["pendown"]:
2414 newLine = True
2415 if "pencolor" in p:
2416 if isinstance(p["pencolor"], tuple):
2417 p["pencolor"] = self._colorstr((p["pencolor"],))
2418 if self._pencolor != p["pencolor"]:
2419 newLine = True
2420 if "pensize" in p:
2421 if self._pensize != p["pensize"]:
2422 newLine = True
2423 if newLine:
2424 self._newLine()
2425 if "pendown" in p:
2426 self._drawing = p["pendown"]
2427 if "pencolor" in p:
2428 self._pencolor = p["pencolor"]
2429 if "pensize" in p:
2430 self._pensize = p["pensize"]
2431 if "fillcolor" in p:
2432 if isinstance(p["fillcolor"], tuple):
2433 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2434 self._fillcolor = p["fillcolor"]
2435 if "speed" in p:
2436 self._speed = p["speed"]
2437 if "resizemode" in p:
2438 self._resizemode = p["resizemode"]
2439 if "stretchfactor" in p:
2440 sf = p["stretchfactor"]
2441 if isinstance(sf, (int, float)):
2442 sf = (sf, sf)
2443 self._stretchfactor = sf
Georg Brandleaa84ef2009-05-05 08:14:33 +00002444 if "shearfactor" in p:
2445 self._shearfactor = p["shearfactor"]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002446 if "outline" in p:
2447 self._outlinewidth = p["outline"]
2448 if "shown" in p:
2449 self._shown = p["shown"]
2450 if "tilt" in p:
2451 self._tilt = p["tilt"]
Georg Brandleaa84ef2009-05-05 08:14:33 +00002452 if "stretchfactor" in p or "tilt" in p or "shearfactor" in p:
2453 scx, scy = self._stretchfactor
2454 shf = self._shearfactor
2455 sa, ca = math.sin(self._tilt), math.cos(self._tilt)
2456 self._shapetrafo = ( scx*ca, scy*(shf*ca + sa),
2457 -scx*sa, scy*(ca - shf*sa))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002458 self._update()
2459
2460## three dummy methods to be implemented by child class:
2461
2462 def _newLine(self, usePos = True):
2463 """dummy method - to be overwritten by child class"""
2464 def _update(self, count=True, forced=False):
2465 """dummy method - to be overwritten by child class"""
2466 def _color(self, args):
2467 """dummy method - to be overwritten by child class"""
2468 def _colorstr(self, args):
2469 """dummy method - to be overwritten by child class"""
2470
2471 width = pensize
2472 up = penup
2473 pu = penup
2474 pd = pendown
2475 down = pendown
2476 st = showturtle
2477 ht = hideturtle
2478
2479
2480class _TurtleImage(object):
2481 """Helper class: Datatype to store Turtle attributes
2482 """
2483
2484 def __init__(self, screen, shapeIndex):
2485 self.screen = screen
2486 self._type = None
2487 self._setshape(shapeIndex)
2488
2489 def _setshape(self, shapeIndex):
Georg Brandleaa84ef2009-05-05 08:14:33 +00002490 screen = self.screen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002491 self.shapeIndex = shapeIndex
2492 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2493 return
2494 if self._type == "image" == screen._shapes[shapeIndex]._type:
2495 return
2496 if self._type in ["image", "polygon"]:
2497 screen._delete(self._item)
2498 elif self._type == "compound":
2499 for item in self._item:
2500 screen._delete(item)
2501 self._type = screen._shapes[shapeIndex]._type
2502 if self._type == "polygon":
2503 self._item = screen._createpoly()
2504 elif self._type == "image":
2505 self._item = screen._createimage(screen._shapes["blank"]._data)
2506 elif self._type == "compound":
2507 self._item = [screen._createpoly() for item in
2508 screen._shapes[shapeIndex]._data]
2509
2510
2511class RawTurtle(TPen, TNavigator):
2512 """Animation part of the RawTurtle.
2513 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002514 its animation.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002515 """
2516 screens = []
2517
2518 def __init__(self, canvas=None,
2519 shape=_CFG["shape"],
2520 undobuffersize=_CFG["undobuffersize"],
2521 visible=_CFG["visible"]):
Martin v. Löwis601149b2008-09-29 22:19:08 +00002522 if isinstance(canvas, _Screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002523 self.screen = canvas
2524 elif isinstance(canvas, TurtleScreen):
2525 if canvas not in RawTurtle.screens:
2526 RawTurtle.screens.append(canvas)
2527 self.screen = canvas
2528 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2529 for screen in RawTurtle.screens:
2530 if screen.cv == canvas:
2531 self.screen = screen
2532 break
2533 else:
2534 self.screen = TurtleScreen(canvas)
2535 RawTurtle.screens.append(self.screen)
2536 else:
2537 raise TurtleGraphicsError("bad cavas argument %s" % canvas)
2538
2539 screen = self.screen
2540 TNavigator.__init__(self, screen.mode())
2541 TPen.__init__(self)
2542 screen._turtles.append(self)
2543 self.drawingLineItem = screen._createline()
2544 self.turtle = _TurtleImage(screen, shape)
2545 self._poly = None
2546 self._creatingPoly = False
2547 self._fillitem = self._fillpath = None
2548 self._shown = visible
2549 self._hidden_from_screen = False
2550 self.currentLineItem = screen._createline()
2551 self.currentLine = [self._position]
2552 self.items = [self.currentLineItem]
2553 self.stampItems = []
2554 self._undobuffersize = undobuffersize
2555 self.undobuffer = Tbuffer(undobuffersize)
2556 self._update()
2557
2558 def reset(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002559 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002560
2561 No argument.
2562,
2563 Delete the turtle's drawings from the screen, re-center the turtle
2564 and set variables to the default values.
2565
2566 Example (for a Turtle instance named turtle):
2567 >>> turtle.position()
2568 (0.00,-22.00)
2569 >>> turtle.heading()
2570 100.0
2571 >>> turtle.reset()
2572 >>> turtle.position()
2573 (0.00,0.00)
2574 >>> turtle.heading()
2575 0.0
2576 """
2577 TNavigator.reset(self)
2578 TPen._reset(self)
2579 self._clear()
2580 self._drawturtle()
2581 self._update()
2582
2583 def setundobuffer(self, size):
2584 """Set or disable undobuffer.
2585
2586 Argument:
2587 size -- an integer or None
2588
2589 If size is an integer an empty undobuffer of given size is installed.
2590 Size gives the maximum number of turtle-actions that can be undone
2591 by the undo() function.
2592 If size is None, no undobuffer is present.
2593
2594 Example (for a Turtle instance named turtle):
2595 >>> turtle.setundobuffer(42)
2596 """
2597 if size is None:
2598 self.undobuffer = None
2599 else:
2600 self.undobuffer = Tbuffer(size)
2601
2602 def undobufferentries(self):
2603 """Return count of entries in the undobuffer.
2604
2605 No argument.
2606
2607 Example (for a Turtle instance named turtle):
2608 >>> while undobufferentries():
2609 undo()
2610 """
2611 if self.undobuffer is None:
2612 return 0
2613 return self.undobuffer.nr_of_items()
2614
2615 def _clear(self):
2616 """Delete all of pen's drawings"""
2617 self._fillitem = self._fillpath = None
2618 for item in self.items:
2619 self.screen._delete(item)
2620 self.currentLineItem = self.screen._createline()
2621 self.currentLine = []
2622 if self._drawing:
2623 self.currentLine.append(self._position)
2624 self.items = [self.currentLineItem]
2625 self.clearstamps()
2626 self.setundobuffer(self._undobuffersize)
2627
2628
2629 def clear(self):
2630 """Delete the turtle's drawings from the screen. Do not move turtle.
2631
2632 No arguments.
2633
2634 Delete the turtle's drawings from the screen. Do not move turtle.
2635 State and position of the turtle as well as drawings of other
2636 turtles are not affected.
2637
2638 Examples (for a Turtle instance named turtle):
2639 >>> turtle.clear()
2640 """
2641 self._clear()
2642 self._update()
2643
2644 def _update_data(self):
2645 self.screen._incrementudc()
2646 if self.screen._updatecounter != 0:
2647 return
2648 if len(self.currentLine)>1:
2649 self.screen._drawline(self.currentLineItem, self.currentLine,
2650 self._pencolor, self._pensize)
2651
2652 def _update(self):
2653 """Perform a Turtle-data update.
2654 """
2655 screen = self.screen
2656 if screen._tracing == 0:
2657 return
2658 elif screen._tracing == 1:
2659 self._update_data()
2660 self._drawturtle()
2661 screen._update() # TurtleScreenBase
2662 screen._delay(screen._delayvalue) # TurtleScreenBase
2663 else:
2664 self._update_data()
2665 if screen._updatecounter == 0:
2666 for t in screen.turtles():
2667 t._drawturtle()
2668 screen._update()
2669
2670 def _tracer(self, flag=None, delay=None):
2671 """Turns turtle animation on/off and set delay for update drawings.
2672
2673 Optional arguments:
2674 n -- nonnegative integer
2675 delay -- nonnegative integer
2676
2677 If n is given, only each n-th regular screen update is really performed.
2678 (Can be used to accelerate the drawing of complex graphics.)
2679 Second arguments sets delay value (see RawTurtle.delay())
2680
2681 Example (for a Turtle instance named turtle):
2682 >>> turtle.tracer(8, 25)
2683 >>> dist = 2
2684 >>> for i in range(200):
2685 turtle.fd(dist)
2686 turtle.rt(90)
2687 dist += 2
2688 """
2689 return self.screen.tracer(flag, delay)
2690
2691 def _color(self, args):
2692 return self.screen._color(args)
2693
2694 def _colorstr(self, args):
2695 return self.screen._colorstr(args)
2696
2697 def _cc(self, args):
2698 """Convert colortriples to hexstrings.
2699 """
2700 if isinstance(args, str):
2701 return args
2702 try:
2703 r, g, b = args
2704 except:
2705 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2706 if self.screen._colormode == 1.0:
2707 r, g, b = [round(255.0*x) for x in (r, g, b)]
2708 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2709 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2710 return "#%02x%02x%02x" % (r, g, b)
2711
2712 def clone(self):
2713 """Create and return a clone of the turtle.
2714
2715 No argument.
2716
2717 Create and return a clone of the turtle with same position, heading
2718 and turtle properties.
2719
2720 Example (for a Turtle instance named mick):
2721 mick = Turtle()
2722 joe = mick.clone()
2723 """
2724 screen = self.screen
2725 self._newLine(self._drawing)
2726
2727 turtle = self.turtle
2728 self.screen = None
2729 self.turtle = None # too make self deepcopy-able
2730
2731 q = deepcopy(self)
2732
2733 self.screen = screen
2734 self.turtle = turtle
2735
2736 q.screen = screen
2737 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2738
2739 screen._turtles.append(q)
2740 ttype = screen._shapes[self.turtle.shapeIndex]._type
2741 if ttype == "polygon":
2742 q.turtle._item = screen._createpoly()
2743 elif ttype == "image":
2744 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2745 elif ttype == "compound":
2746 q.turtle._item = [screen._createpoly() for item in
2747 screen._shapes[self.turtle.shapeIndex]._data]
2748 q.currentLineItem = screen._createline()
2749 q._update()
2750 return q
2751
2752 def shape(self, name=None):
2753 """Set turtle shape to shape with given name / return current shapename.
2754
2755 Optional argument:
2756 name -- a string, which is a valid shapename
2757
2758 Set turtle shape to shape with given name or, if name is not given,
2759 return name of current shape.
2760 Shape with name must exist in the TurtleScreen's shape dictionary.
2761 Initially there are the following polygon shapes:
2762 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2763 To learn about how to deal with shapes see Screen-method register_shape.
2764
2765 Example (for a Turtle instance named turtle):
2766 >>> turtle.shape()
2767 'arrow'
2768 >>> turtle.shape("turtle")
2769 >>> turtle.shape()
2770 'turtle'
2771 """
2772 if name is None:
2773 return self.turtle.shapeIndex
2774 if not name in self.screen.getshapes():
2775 raise TurtleGraphicsError("There is no shape named %s" % name)
2776 self.turtle._setshape(name)
2777 self._update()
2778
2779 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2780 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2781
2782 Optinonal arguments:
2783 stretch_wid : positive number
2784 stretch_len : positive number
2785 outline : positive number
2786
2787 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2788 Set resizemode to "user".
2789 If and only if resizemode is set to "user", the turtle will be displayed
2790 stretched according to its stretchfactors:
2791 stretch_wid is stretchfactor perpendicular to orientation
2792 stretch_len is stretchfactor in direction of turtles orientation.
2793 outline determines the width of the shapes's outline.
2794
2795 Examples (for a Turtle instance named turtle):
2796 >>> turtle.resizemode("user")
2797 >>> turtle.shapesize(5, 5, 12)
2798 >>> turtle.shapesize(outline=8)
2799 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00002800 if stretch_wid is stretch_len is outline is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002801 stretch_wid, stretch_len = self._stretchfactor
2802 return stretch_wid, stretch_len, self._outlinewidth
Georg Brandleaa84ef2009-05-05 08:14:33 +00002803 if stretch_wid == 0 or stretch_len == 0:
2804 raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero")
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002805 if stretch_wid is not None:
2806 if stretch_len is None:
2807 stretchfactor = stretch_wid, stretch_wid
2808 else:
2809 stretchfactor = stretch_wid, stretch_len
2810 elif stretch_len is not None:
2811 stretchfactor = self._stretchfactor[0], stretch_len
2812 else:
2813 stretchfactor = self._stretchfactor
2814 if outline is None:
2815 outline = self._outlinewidth
2816 self.pen(resizemode="user",
2817 stretchfactor=stretchfactor, outline=outline)
2818
Georg Brandleaa84ef2009-05-05 08:14:33 +00002819 def shearfactor(self, shear=None):
2820 """Set or return the current shearfactor.
2821
2822 Optional argument: shear -- number, tangent of the shear angle
2823
2824 Shear the turtleshape according to the given shearfactor shear,
2825 which is the tangent of the shear angle. DO NOT change the
2826 turtle's heading (direction of movement).
2827 If shear is not given: return the current shearfactor, i. e. the
2828 tangent of the shear angle, by which lines parallel to the
2829 heading of the turtle are sheared.
2830
2831 Examples (for a Turtle instance named turtle):
2832 >>> turtle.shape("circle")
2833 >>> turtle.shapesize(5,2)
2834 >>> turtle.shearfactor(0.5)
2835 >>> turtle.shearfactor()
2836 >>> 0.5
2837 """
2838 if shear is None:
2839 return self._shearfactor
2840 self.pen(resizemode="user", shearfactor=shear)
2841
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002842 def settiltangle(self, angle):
2843 """Rotate the turtleshape to point in the specified direction
2844
Georg Brandleaa84ef2009-05-05 08:14:33 +00002845 Argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002846
2847 Rotate the turtleshape to point in the direction specified by angle,
2848 regardless of its current tilt-angle. DO NOT change the turtle's
2849 heading (direction of movement).
2850
2851
2852 Examples (for a Turtle instance named turtle):
2853 >>> turtle.shape("circle")
2854 >>> turtle.shapesize(5,2)
2855 >>> turtle.settiltangle(45)
2856 >>> stamp()
2857 >>> turtle.fd(50)
2858 >>> turtle.settiltangle(-45)
2859 >>> stamp()
2860 >>> turtle.fd(50)
2861 """
2862 tilt = -angle * self._degreesPerAU * self._angleOrient
2863 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2864 self.pen(resizemode="user", tilt=tilt)
2865
Georg Brandleaa84ef2009-05-05 08:14:33 +00002866 def tiltangle(self, angle=None):
2867 """Set or return the current tilt-angle.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002868
Georg Brandleaa84ef2009-05-05 08:14:33 +00002869 Optional argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002870
Georg Brandleaa84ef2009-05-05 08:14:33 +00002871 Rotate the turtleshape to point in the direction specified by angle,
2872 regardless of its current tilt-angle. DO NOT change the turtle's
2873 heading (direction of movement).
2874 If angle is not given: return the current tilt-angle, i. e. the angle
2875 between the orientation of the turtleshape and the heading of the
2876 turtle (its direction of movement).
2877
2878 Deprecated since Python 3.1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002879
2880 Examples (for a Turtle instance named turtle):
2881 >>> turtle.shape("circle")
2882 >>> turtle.shapesize(5,2)
2883 >>> turtle.tilt(45)
2884 >>> turtle.tiltangle()
2885 >>>
2886 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00002887 if angle is None:
2888 tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2889 return (tilt / self._degreesPerAU) % self._fullcircle
2890 else:
2891 self.settiltangle(angle)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002892
2893 def tilt(self, angle):
2894 """Rotate the turtleshape by angle.
2895
2896 Argument:
2897 angle - a number
2898
2899 Rotate the turtleshape by angle from its current tilt-angle,
2900 but do NOT change the turtle's heading (direction of movement).
2901
2902 Examples (for a Turtle instance named turtle):
2903 >>> turtle.shape("circle")
2904 >>> turtle.shapesize(5,2)
2905 >>> turtle.tilt(30)
2906 >>> turtle.fd(50)
2907 >>> turtle.tilt(30)
2908 >>> turtle.fd(50)
2909 """
2910 self.settiltangle(angle + self.tiltangle())
2911
Georg Brandleaa84ef2009-05-05 08:14:33 +00002912 def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2913 """Set or return the current transformation matrix of the turtle shape.
2914
2915 Optional arguments: t11, t12, t21, t22 -- numbers.
2916
2917 If none of the matrix elements are given, return the transformation
2918 matrix.
2919 Otherwise set the given elements and transform the turtleshape
2920 according to the matrix consisting of first row t11, t12 and
2921 second row t21, 22.
2922 Modify stretchfactor, shearfactor and tiltangle according to the
2923 given matrix.
2924
2925 Examples (for a Turtle instance named turtle):
2926 >>> turtle.shape("square")
2927 >>> turtle.shapesize(4,2)
2928 >>> turtle.shearfactor(-0.5)
2929 >>> turtle.shapetransform()
2930 >>> (4.0, -1.0, -0.0, 2.0)
2931 """
2932 if t11 is t12 is t21 is t22 is None:
2933 return self._shapetrafo
2934 m11, m12, m21, m22 = self._shapetrafo
2935 if t11 is not None: m11 = t11
2936 if t12 is not None: m12 = t12
2937 if t21 is not None: m21 = t21
2938 if t22 is not None: m22 = t22
2939 if t11 * t22 - t12 * t21 == 0:
2940 raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2941 self._shapetrafo = (m11, m12, m21, m22)
2942 alfa = math.atan2(-m21, m11) % (2 * math.pi)
2943 sa, ca = math.sin(alfa), math.cos(alfa)
2944 a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2945 sa*m11 + ca*m21, sa*m12 + ca*m22)
2946 self._stretchfactor = a11, a22
2947 self._shearfactor = a12/a22
2948 self._tilt = alfa
2949 self._update()
2950
2951
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002952 def _polytrafo(self, poly):
2953 """Computes transformed polygon shapes from a shape
2954 according to current position and heading.
2955 """
2956 screen = self.screen
2957 p0, p1 = self._position
2958 e0, e1 = self._orient
2959 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2960 e0, e1 = (1.0 / abs(e)) * e
2961 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2962 for (x, y) in poly]
2963
Georg Brandleaa84ef2009-05-05 08:14:33 +00002964 def get_shapepoly(self):
2965 """Return the current shape polygon as tuple of coordinate pairs.
2966
2967 No argument.
2968
2969 Examples (for a Turtle instance named turtle):
2970 >>> turtle.shape("square")
2971 >>> turtle.shapetransform(4, -1, 0, 2)
2972 >>> turtle.get_shapepoly()
2973 ((50, -20), (30, 20), (-50, 20), (-30, -20))
2974
2975 """
2976 shape = self.screen._shapes[self.turtle.shapeIndex]
2977 if shape._type == "polygon":
2978 return self._getshapepoly(shape._data, shape._type == "compound")
2979 # else return None
2980
2981 def _getshapepoly(self, polygon, compound=False):
2982 """Calculate transformed shape polygon according to resizemode
2983 and shapetransform.
2984 """
2985 if self._resizemode == "user" or compound:
2986 t11, t12, t21, t22 = self._shapetrafo
2987 elif self._resizemode == "auto":
2988 l = max(1, self._pensize/5.0)
2989 t11, t12, t21, t22 = l, 0, 0, l
2990 elif self._resizemode == "noresize":
2991 return polygon
2992 return tuple([(t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon])
2993
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002994 def _drawturtle(self):
2995 """Manages the correct rendering of the turtle with respect to
Mark Dickinson934896d2009-02-21 20:59:32 +00002996 its shape, resizemode, stretch and tilt etc."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002997 screen = self.screen
2998 shape = screen._shapes[self.turtle.shapeIndex]
2999 ttype = shape._type
3000 titem = self.turtle._item
3001 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3002 self._hidden_from_screen = False
3003 tshape = shape._data
3004 if ttype == "polygon":
Georg Brandleaa84ef2009-05-05 08:14:33 +00003005 if self._resizemode == "noresize": w = 1
3006 elif self._resizemode == "auto": w = self._pensize
3007 else: w =self._outlinewidth
3008 shape = self._polytrafo(self._getshapepoly(tshape))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003009 fc, oc = self._fillcolor, self._pencolor
3010 screen._drawpoly(titem, shape, fill=fc, outline=oc,
3011 width=w, top=True)
3012 elif ttype == "image":
3013 screen._drawimage(titem, self._position, tshape)
3014 elif ttype == "compound":
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003015 for item, (poly, fc, oc) in zip(titem, tshape):
Georg Brandleaa84ef2009-05-05 08:14:33 +00003016 poly = self._polytrafo(self._getshapepoly(poly, True))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003017 screen._drawpoly(item, poly, fill=self._cc(fc),
Georg Brandleaa84ef2009-05-05 08:14:33 +00003018 outline=self._cc(oc), width=self._outlinewidth, top=True)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003019 else:
3020 if self._hidden_from_screen:
3021 return
3022 if ttype == "polygon":
3023 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3024 elif ttype == "image":
3025 screen._drawimage(titem, self._position,
3026 screen._shapes["blank"]._data)
3027 elif ttype == "compound":
3028 for item in titem:
3029 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3030 self._hidden_from_screen = True
3031
3032############################## stamp stuff ###############################
3033
3034 def stamp(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00003035 """Stamp a copy of the turtleshape onto the canvas and return its id.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003036
3037 No argument.
3038
3039 Stamp a copy of the turtle shape onto the canvas at the current
3040 turtle position. Return a stamp_id for that stamp, which can be
3041 used to delete it by calling clearstamp(stamp_id).
3042
3043 Example (for a Turtle instance named turtle):
3044 >>> turtle.color("blue")
3045 >>> turtle.stamp()
3046 13
3047 >>> turtle.fd(50)
3048 """
3049 screen = self.screen
3050 shape = screen._shapes[self.turtle.shapeIndex]
3051 ttype = shape._type
3052 tshape = shape._data
3053 if ttype == "polygon":
3054 stitem = screen._createpoly()
Georg Brandleaa84ef2009-05-05 08:14:33 +00003055 if self._resizemode == "noresize": w = 1
3056 elif self._resizemode == "auto": w = self._pensize
3057 else: w =self._outlinewidth
3058 shape = self._polytrafo(self._getshapepoly(tshape))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003059 fc, oc = self._fillcolor, self._pencolor
3060 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3061 width=w, top=True)
3062 elif ttype == "image":
3063 stitem = screen._createimage("")
3064 screen._drawimage(stitem, self._position, tshape)
3065 elif ttype == "compound":
3066 stitem = []
3067 for element in tshape:
3068 item = screen._createpoly()
3069 stitem.append(item)
3070 stitem = tuple(stitem)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003071 for item, (poly, fc, oc) in zip(stitem, tshape):
Georg Brandleaa84ef2009-05-05 08:14:33 +00003072 poly = self._polytrafo(self._getshapepoly(poly, True))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003073 screen._drawpoly(item, poly, fill=self._cc(fc),
Georg Brandleaa84ef2009-05-05 08:14:33 +00003074 outline=self._cc(oc), width=self._outlinewidth, top=True)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003075 self.stampItems.append(stitem)
3076 self.undobuffer.push(("stamp", stitem))
3077 return stitem
3078
3079 def _clearstamp(self, stampid):
3080 """does the work for clearstamp() and clearstamps()
3081 """
3082 if stampid in self.stampItems:
3083 if isinstance(stampid, tuple):
3084 for subitem in stampid:
3085 self.screen._delete(subitem)
3086 else:
3087 self.screen._delete(stampid)
3088 self.stampItems.remove(stampid)
3089 # Delete stampitem from undobuffer if necessary
3090 # if clearstamp is called directly.
3091 item = ("stamp", stampid)
3092 buf = self.undobuffer
3093 if item not in buf.buffer:
3094 return
3095 index = buf.buffer.index(item)
3096 buf.buffer.remove(item)
3097 if index <= buf.ptr:
3098 buf.ptr = (buf.ptr - 1) % buf.bufsize
3099 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3100
3101 def clearstamp(self, stampid):
3102 """Delete stamp with given stampid
3103
3104 Argument:
3105 stampid - an integer, must be return value of previous stamp() call.
3106
3107 Example (for a Turtle instance named turtle):
3108 >>> turtle.color("blue")
3109 >>> astamp = turtle.stamp()
3110 >>> turtle.fd(50)
3111 >>> turtle.clearstamp(astamp)
3112 """
3113 self._clearstamp(stampid)
3114 self._update()
3115
3116 def clearstamps(self, n=None):
3117 """Delete all or first/last n of turtle's stamps.
3118
3119 Optional argument:
3120 n -- an integer
3121
3122 If n is None, delete all of pen's stamps,
3123 else if n > 0 delete first n stamps
3124 else if n < 0 delete last n stamps.
3125
3126 Example (for a Turtle instance named turtle):
3127 >>> for i in range(8):
3128 turtle.stamp(); turtle.fd(30)
3129 ...
3130 >>> turtle.clearstamps(2)
3131 >>> turtle.clearstamps(-2)
3132 >>> turtle.clearstamps()
3133 """
3134 if n is None:
3135 toDelete = self.stampItems[:]
3136 elif n >= 0:
3137 toDelete = self.stampItems[:n]
3138 else:
3139 toDelete = self.stampItems[n:]
3140 for item in toDelete:
3141 self._clearstamp(item)
3142 self._update()
3143
3144 def _goto(self, end):
3145 """Move the pen to the point end, thereby drawing a line
3146 if pen is down. All other methodes for turtle movement depend
3147 on this one.
3148 """
Alexander Belopolsky1842d0c2010-10-28 20:13:52 +00003149 ## Version with undo-stuff
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003150 go_modes = ( self._drawing,
3151 self._pencolor,
3152 self._pensize,
3153 isinstance(self._fillpath, list))
3154 screen = self.screen
3155 undo_entry = ("go", self._position, end, go_modes,
3156 (self.currentLineItem,
3157 self.currentLine[:],
3158 screen._pointlist(self.currentLineItem),
3159 self.items[:])
3160 )
3161 if self.undobuffer:
3162 self.undobuffer.push(undo_entry)
3163 start = self._position
3164 if self._speed and screen._tracing == 1:
3165 diff = (end-start)
3166 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3167 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3168 delta = diff * (1.0/nhops)
3169 for n in range(1, nhops):
3170 if n == 1:
3171 top = True
3172 else:
3173 top = False
3174 self._position = start + delta * n
3175 if self._drawing:
3176 screen._drawline(self.drawingLineItem,
3177 (start, self._position),
3178 self._pencolor, self._pensize, top)
3179 self._update()
3180 if self._drawing:
3181 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3182 fill="", width=self._pensize)
3183 # Turtle now at end,
3184 if self._drawing: # now update currentLine
3185 self.currentLine.append(end)
3186 if isinstance(self._fillpath, list):
3187 self._fillpath.append(end)
3188 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3189 self._position = end
3190 if self._creatingPoly:
3191 self._poly.append(end)
3192 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3193 # of life, the universe and everything
3194 self._newLine()
3195 self._update() #count=True)
3196
3197 def _undogoto(self, entry):
3198 """Reverse a _goto. Used for undo()
3199 """
3200 old, new, go_modes, coodata = entry
3201 drawing, pc, ps, filling = go_modes
3202 cLI, cL, pl, items = coodata
3203 screen = self.screen
3204 if abs(self._position - new) > 0.5:
3205 print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3206 # restore former situation
3207 self.currentLineItem = cLI
3208 self.currentLine = cL
3209
3210 if pl == [(0, 0), (0, 0)]:
3211 usepc = ""
3212 else:
3213 usepc = pc
3214 screen._drawline(cLI, pl, fill=usepc, width=ps)
3215
3216 todelete = [i for i in self.items if (i not in items) and
3217 (screen._type(i) == "line")]
3218 for i in todelete:
3219 screen._delete(i)
3220 self.items.remove(i)
3221
3222 start = old
3223 if self._speed and screen._tracing == 1:
3224 diff = old - new
3225 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3226 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3227 delta = diff * (1.0/nhops)
3228 for n in range(1, nhops):
3229 if n == 1:
3230 top = True
3231 else:
3232 top = False
3233 self._position = new + delta * n
3234 if drawing:
3235 screen._drawline(self.drawingLineItem,
3236 (start, self._position),
3237 pc, ps, top)
3238 self._update()
3239 if drawing:
3240 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3241 fill="", width=ps)
3242 # Turtle now at position old,
3243 self._position = old
Ezio Melotti13925002011-03-16 11:05:33 +02003244 ## if undo is done during creating a polygon, the last vertex
3245 ## will be deleted. if the polygon is entirely deleted,
3246 ## creatingPoly will be set to False.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003247 ## Polygons created before the last one will not be affected by undo()
3248 if self._creatingPoly:
3249 if len(self._poly) > 0:
3250 self._poly.pop()
3251 if self._poly == []:
3252 self._creatingPoly = False
3253 self._poly = None
3254 if filling:
3255 if self._fillpath == []:
3256 self._fillpath = None
3257 print("Unwahrscheinlich in _undogoto!")
3258 elif self._fillpath is not None:
3259 self._fillpath.pop()
3260 self._update() #count=True)
3261
3262 def _rotate(self, angle):
3263 """Turns pen clockwise by angle.
3264 """
3265 if self.undobuffer:
3266 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3267 angle *= self._degreesPerAU
3268 neworient = self._orient.rotate(angle)
3269 tracing = self.screen._tracing
3270 if tracing == 1 and self._speed > 0:
3271 anglevel = 3.0 * self._speed
3272 steps = 1 + int(abs(angle)/anglevel)
3273 delta = 1.0*angle/steps
3274 for _ in range(steps):
3275 self._orient = self._orient.rotate(delta)
3276 self._update()
3277 self._orient = neworient
3278 self._update()
3279
3280 def _newLine(self, usePos=True):
3281 """Closes current line item and starts a new one.
3282 Remark: if current line became too long, animation
3283 performance (via _drawline) slowed down considerably.
3284 """
3285 if len(self.currentLine) > 1:
3286 self.screen._drawline(self.currentLineItem, self.currentLine,
3287 self._pencolor, self._pensize)
3288 self.currentLineItem = self.screen._createline()
3289 self.items.append(self.currentLineItem)
3290 else:
3291 self.screen._drawline(self.currentLineItem, top=True)
3292 self.currentLine = []
3293 if usePos:
3294 self.currentLine = [self._position]
3295
3296 def filling(self):
3297 """Return fillstate (True if filling, False else).
3298
3299 No argument.
3300
3301 Example (for a Turtle instance named turtle):
3302 >>> turtle.begin_fill()
3303 >>> if turtle.filling():
3304 turtle.pensize(5)
3305 else:
3306 turtle.pensize(3)
3307 """
3308 return isinstance(self._fillpath, list)
3309
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003310 def begin_fill(self):
3311 """Called just before drawing a shape to be filled.
3312
3313 No argument.
3314
3315 Example (for a Turtle instance named turtle):
3316 >>> turtle.color("black", "red")
3317 >>> turtle.begin_fill()
3318 >>> turtle.circle(60)
3319 >>> turtle.end_fill()
3320 """
3321 if not self.filling():
3322 self._fillitem = self.screen._createpoly()
3323 self.items.append(self._fillitem)
3324 self._fillpath = [self._position]
3325 self._newLine()
3326 if self.undobuffer:
3327 self.undobuffer.push(("beginfill", self._fillitem))
3328 self._update()
3329
3330
3331 def end_fill(self):
3332 """Fill the shape drawn after the call begin_fill().
3333
3334 No argument.
3335
3336 Example (for a Turtle instance named turtle):
3337 >>> turtle.color("black", "red")
3338 >>> turtle.begin_fill()
3339 >>> turtle.circle(60)
3340 >>> turtle.end_fill()
3341 """
3342 if self.filling():
3343 if len(self._fillpath) > 2:
3344 self.screen._drawpoly(self._fillitem, self._fillpath,
3345 fill=self._fillcolor)
3346 if self.undobuffer:
3347 self.undobuffer.push(("dofill", self._fillitem))
3348 self._fillitem = self._fillpath = None
3349 self._update()
3350
3351 def dot(self, size=None, *color):
3352 """Draw a dot with diameter size, using color.
3353
Ezio Melotti42da6632011-03-15 05:18:48 +02003354 Optional arguments:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003355 size -- an integer >= 1 (if given)
3356 color -- a colorstring or a numeric color tuple
3357
3358 Draw a circular dot with diameter size, using color.
3359 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3360
3361 Example (for a Turtle instance named turtle):
3362 >>> turtle.dot()
3363 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3364 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003365 if not color:
3366 if isinstance(size, (str, tuple)):
3367 color = self._colorstr(size)
3368 size = self._pensize + max(self._pensize, 4)
3369 else:
3370 color = self._pencolor
3371 if not size:
3372 size = self._pensize + max(self._pensize, 4)
3373 else:
3374 if size is None:
3375 size = self._pensize + max(self._pensize, 4)
3376 color = self._colorstr(color)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003377 if hasattr(self.screen, "_dot"):
3378 item = self.screen._dot(self._position, size, color)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003379 self.items.append(item)
3380 if self.undobuffer:
3381 self.undobuffer.push(("dot", item))
3382 else:
3383 pen = self.pen()
3384 if self.undobuffer:
3385 self.undobuffer.push(["seq"])
3386 self.undobuffer.cumulate = True
3387 try:
3388 if self.resizemode() == 'auto':
3389 self.ht()
3390 self.pendown()
3391 self.pensize(size)
3392 self.pencolor(color)
3393 self.forward(0)
3394 finally:
3395 self.pen(pen)
3396 if self.undobuffer:
3397 self.undobuffer.cumulate = False
3398
3399 def _write(self, txt, align, font):
3400 """Performs the writing for write()
3401 """
3402 item, end = self.screen._write(self._position, txt, align, font,
3403 self._pencolor)
3404 self.items.append(item)
3405 if self.undobuffer:
3406 self.undobuffer.push(("wri", item))
3407 return end
3408
3409 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3410 """Write text at the current turtle position.
3411
3412 Arguments:
3413 arg -- info, which is to be written to the TurtleScreen
3414 move (optional) -- True/False
3415 align (optional) -- one of the strings "left", "center" or right"
3416 font (optional) -- a triple (fontname, fontsize, fonttype)
3417
3418 Write text - the string representation of arg - at the current
3419 turtle position according to align ("left", "center" or right")
3420 and with the given font.
3421 If move is True, the pen is moved to the bottom-right corner
3422 of the text. By default, move is False.
3423
3424 Example (for a Turtle instance named turtle):
3425 >>> turtle.write('Home = ', True, align="center")
3426 >>> turtle.write((0,0), True)
3427 """
3428 if self.undobuffer:
3429 self.undobuffer.push(["seq"])
3430 self.undobuffer.cumulate = True
3431 end = self._write(str(arg), align.lower(), font)
3432 if move:
3433 x, y = self.pos()
3434 self.setpos(end, y)
3435 if self.undobuffer:
3436 self.undobuffer.cumulate = False
3437
3438 def begin_poly(self):
3439 """Start recording the vertices of a polygon.
3440
3441 No argument.
3442
3443 Start recording the vertices of a polygon. Current turtle position
3444 is first point of polygon.
3445
3446 Example (for a Turtle instance named turtle):
3447 >>> turtle.begin_poly()
3448 """
3449 self._poly = [self._position]
3450 self._creatingPoly = True
3451
3452 def end_poly(self):
3453 """Stop recording the vertices of a polygon.
3454
3455 No argument.
3456
3457 Stop recording the vertices of a polygon. Current turtle position is
3458 last point of polygon. This will be connected with the first point.
3459
3460 Example (for a Turtle instance named turtle):
3461 >>> turtle.end_poly()
3462 """
3463 self._creatingPoly = False
3464
3465 def get_poly(self):
3466 """Return the lastly recorded polygon.
3467
3468 No argument.
3469
3470 Example (for a Turtle instance named turtle):
3471 >>> p = turtle.get_poly()
3472 >>> turtle.register_shape("myFavouriteShape", p)
3473 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00003474 ## check if there is any poly?
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003475 if self._poly is not None:
3476 return tuple(self._poly)
3477
3478 def getscreen(self):
3479 """Return the TurtleScreen object, the turtle is drawing on.
3480
3481 No argument.
3482
3483 Return the TurtleScreen object, the turtle is drawing on.
3484 So TurtleScreen-methods can be called for that object.
3485
3486 Example (for a Turtle instance named turtle):
3487 >>> ts = turtle.getscreen()
3488 >>> ts
3489 <turtle.TurtleScreen object at 0x0106B770>
3490 >>> ts.bgcolor("pink")
3491 """
3492 return self.screen
3493
3494 def getturtle(self):
3495 """Return the Turtleobject itself.
3496
3497 No argument.
3498
3499 Only reasonable use: as a function to return the 'anonymous turtle':
3500
3501 Example:
3502 >>> pet = getturtle()
3503 >>> pet.fd(50)
3504 >>> pet
3505 <turtle.Turtle object at 0x0187D810>
3506 >>> turtles()
3507 [<turtle.Turtle object at 0x0187D810>]
3508 """
3509 return self
3510
3511 getpen = getturtle
3512
3513
3514 ################################################################
3515 ### screen oriented methods recurring to methods of TurtleScreen
3516 ################################################################
3517
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003518 def _delay(self, delay=None):
3519 """Set delay value which determines speed of turtle animation.
3520 """
3521 return self.screen.delay(delay)
3522
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003523 def onclick(self, fun, btn=1, add=None):
3524 """Bind fun to mouse-click event on this turtle on canvas.
3525
3526 Arguments:
3527 fun -- a function with two arguments, to which will be assigned
3528 the coordinates of the clicked point on the canvas.
3529 num -- number of the mouse-button defaults to 1 (left mouse button).
3530 add -- True or False. If True, new binding will be added, otherwise
3531 it will replace a former binding.
3532
3533 Example for the anonymous turtle, i. e. the procedural way:
3534
3535 >>> def turn(x, y):
3536 left(360)
3537
3538 >>> onclick(turn) # Now clicking into the turtle will turn it.
3539 >>> onclick(None) # event-binding will be removed
3540 """
3541 self.screen._onclick(self.turtle._item, fun, btn, add)
3542 self._update()
3543
3544 def onrelease(self, fun, btn=1, add=None):
3545 """Bind fun to mouse-button-release event on this turtle on canvas.
3546
3547 Arguments:
3548 fun -- a function with two arguments, to which will be assigned
3549 the coordinates of the clicked point on the canvas.
3550 num -- number of the mouse-button defaults to 1 (left mouse button).
3551
3552 Example (for a MyTurtle instance named joe):
3553 >>> class MyTurtle(Turtle):
3554 def glow(self,x,y):
3555 self.fillcolor("red")
3556 def unglow(self,x,y):
3557 self.fillcolor("")
3558
3559 >>> joe = MyTurtle()
3560 >>> joe.onclick(joe.glow)
3561 >>> joe.onrelease(joe.unglow)
3562 ### clicking on joe turns fillcolor red,
3563 ### unclicking turns it to transparent.
3564 """
3565 self.screen._onrelease(self.turtle._item, fun, btn, add)
3566 self._update()
3567
3568 def ondrag(self, fun, btn=1, add=None):
3569 """Bind fun to mouse-move event on this turtle on canvas.
3570
3571 Arguments:
3572 fun -- a function with two arguments, to which will be assigned
3573 the coordinates of the clicked point on the canvas.
3574 num -- number of the mouse-button defaults to 1 (left mouse button).
3575
3576 Every sequence of mouse-move-events on a turtle is preceded by a
3577 mouse-click event on that turtle.
3578
3579 Example (for a Turtle instance named turtle):
3580 >>> turtle.ondrag(turtle.goto)
3581
3582 ### Subsequently clicking and dragging a Turtle will
3583 ### move it across the screen thereby producing handdrawings
3584 ### (if pen is down).
3585 """
3586 self.screen._ondrag(self.turtle._item, fun, btn, add)
3587
3588
3589 def _undo(self, action, data):
3590 """Does the main part of the work for undo()
3591 """
3592 if self.undobuffer is None:
3593 return
3594 if action == "rot":
3595 angle, degPAU = data
3596 self._rotate(-angle*degPAU/self._degreesPerAU)
3597 dummy = self.undobuffer.pop()
3598 elif action == "stamp":
3599 stitem = data[0]
3600 self.clearstamp(stitem)
3601 elif action == "go":
3602 self._undogoto(data)
3603 elif action in ["wri", "dot"]:
3604 item = data[0]
3605 self.screen._delete(item)
3606 self.items.remove(item)
3607 elif action == "dofill":
3608 item = data[0]
3609 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3610 fill="", outline="")
3611 elif action == "beginfill":
3612 item = data[0]
3613 self._fillitem = self._fillpath = None
3614 if item in self.items:
3615 self.screen._delete(item)
3616 self.items.remove(item)
3617 elif action == "pen":
3618 TPen.pen(self, data[0])
3619 self.undobuffer.pop()
3620
3621 def undo(self):
3622 """undo (repeatedly) the last turtle action.
3623
3624 No argument.
3625
3626 undo (repeatedly) the last turtle action.
3627 Number of available undo actions is determined by the size of
3628 the undobuffer.
3629
3630 Example (for a Turtle instance named turtle):
3631 >>> for i in range(4):
3632 turtle.fd(50); turtle.lt(80)
3633
3634 >>> for i in range(8):
3635 turtle.undo()
3636 """
3637 if self.undobuffer is None:
3638 return
3639 item = self.undobuffer.pop()
3640 action = item[0]
3641 data = item[1:]
3642 if action == "seq":
3643 while data:
3644 item = data.pop()
3645 self._undo(item[0], item[1:])
3646 else:
3647 self._undo(action, data)
3648
3649 turtlesize = shapesize
3650
3651RawPen = RawTurtle
3652
Martin v. Löwis601149b2008-09-29 22:19:08 +00003653### Screen - Singleton ########################
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003654
Martin v. Löwis601149b2008-09-29 22:19:08 +00003655def Screen():
3656 """Return the singleton screen object.
3657 If none exists at the moment, create a new one and return it,
3658 else return the existing one."""
3659 if Turtle._screen is None:
3660 Turtle._screen = _Screen()
3661 return Turtle._screen
3662
3663class _Screen(TurtleScreen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003664
3665 _root = None
3666 _canvas = None
3667 _title = _CFG["title"]
3668
Guido van Rossumb241b671998-12-04 16:42:46 +00003669 def __init__(self):
Martin v. Löwis601149b2008-09-29 22:19:08 +00003670 # XXX there is no need for this code to be conditional,
3671 # as there will be only a single _Screen instance, anyway
3672 # XXX actually, the turtle demo is injecting root window,
3673 # so perhaps the conditional creation of a root should be
3674 # preserved (perhaps by passing it as an optional parameter)
3675 if _Screen._root is None:
3676 _Screen._root = self._root = _Root()
3677 self._root.title(_Screen._title)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003678 self._root.ondestroy(self._destroy)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003679 if _Screen._canvas is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003680 width = _CFG["width"]
3681 height = _CFG["height"]
3682 canvwidth = _CFG["canvwidth"]
3683 canvheight = _CFG["canvheight"]
3684 leftright = _CFG["leftright"]
3685 topbottom = _CFG["topbottom"]
3686 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003687 _Screen._canvas = self._root._getcanvas()
Martin v. Löwis601149b2008-09-29 22:19:08 +00003688 TurtleScreen.__init__(self, _Screen._canvas)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003689 self.setup(width, height, leftright, topbottom)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003690
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003691 def setup(self, width=_CFG["width"], height=_CFG["height"],
3692 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3693 """ Set the size and position of the main window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003694
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003695 Arguments:
3696 width: as integer a size in pixels, as float a fraction of the screen.
3697 Default is 50% of screen.
3698 height: as integer the height in pixels, as float a fraction of the
3699 screen. Default is 75% of screen.
3700 startx: if positive, starting position in pixels from the left
3701 edge of the screen, if negative from the right edge
3702 Default, startx=None is to center window horizontally.
3703 starty: if positive, starting position in pixels from the top
3704 edge of the screen, if negative from the bottom edge
3705 Default, starty=None is to center window vertically.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003706
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003707 Examples (for a Screen instance named screen):
3708 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3709
3710 sets window to 200x200 pixels, in upper left of screen
3711
3712 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3713
3714 sets window to 75% of screen by 50% of screen and centers
3715 """
3716 if not hasattr(self._root, "set_geometry"):
3717 return
3718 sw = self._root.win_width()
3719 sh = self._root.win_height()
3720 if isinstance(width, float) and 0 <= width <= 1:
3721 width = sw*width
3722 if startx is None:
3723 startx = (sw - width) / 2
3724 if isinstance(height, float) and 0 <= height <= 1:
3725 height = sh*height
3726 if starty is None:
3727 starty = (sh - height) / 2
3728 self._root.set_geometry(width, height, startx, starty)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003729 self.update()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003730
3731 def title(self, titlestring):
3732 """Set title of turtle-window
3733
3734 Argument:
3735 titlestring -- a string, to appear in the titlebar of the
3736 turtle graphics window.
3737
3738 This is a method of Screen-class. Not available for TurtleScreen-
3739 objects.
3740
3741 Example (for a Screen instance named screen):
3742 >>> screen.title("Welcome to the turtle-zoo!")
3743 """
Martin v. Löwis601149b2008-09-29 22:19:08 +00003744 if _Screen._root is not None:
3745 _Screen._root.title(titlestring)
3746 _Screen._title = titlestring
Guido van Rossumb241b671998-12-04 16:42:46 +00003747
3748 def _destroy(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003749 root = self._root
Martin v. Löwis601149b2008-09-29 22:19:08 +00003750 if root is _Screen._root:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003751 Turtle._pen = None
3752 Turtle._screen = None
Martin v. Löwis601149b2008-09-29 22:19:08 +00003753 _Screen._root = None
3754 _Screen._canvas = None
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003755 TurtleScreen._RUNNING = True
Guido van Rossumb241b671998-12-04 16:42:46 +00003756 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +00003757
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003758 def bye(self):
3759 """Shut the turtlegraphics window.
3760
3761 Example (for a TurtleScreen instance named screen):
3762 >>> screen.bye()
3763 """
3764 self._destroy()
3765
3766 def exitonclick(self):
3767 """Go into mainloop until the mouse is clicked.
3768
3769 No arguments.
3770
3771 Bind bye() method to mouseclick on TurtleScreen.
3772 If "using_IDLE" - value in configuration dictionary is False
3773 (default value), enter mainloop.
3774 If IDLE with -n switch (no subprocess) is used, this value should be
3775 set to True in turtle.cfg. In this case IDLE's mainloop
3776 is active also for the client script.
3777
3778 This is a method of the Screen-class and not available for
3779 TurtleScreen instances.
3780
3781 Example (for a Screen instance named screen):
3782 >>> screen.exitonclick()
3783
3784 """
3785 def exitGracefully(x, y):
3786 """Screen.bye() with two dummy-parameters"""
3787 self.bye()
3788 self.onclick(exitGracefully)
3789 if _CFG["using_IDLE"]:
3790 return
3791 try:
3792 mainloop()
3793 except AttributeError:
3794 exit(0)
3795
3796
3797class Turtle(RawTurtle):
Ezio Melotti13925002011-03-16 11:05:33 +02003798 """RawTurtle auto-creating (scrolled) canvas.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003799
3800 When a Turtle object is created or a function derived from some
3801 Turtle method is called a TurtleScreen object is automatically created.
3802 """
3803 _pen = None
3804 _screen = None
3805
3806 def __init__(self,
3807 shape=_CFG["shape"],
3808 undobuffersize=_CFG["undobuffersize"],
3809 visible=_CFG["visible"]):
3810 if Turtle._screen is None:
3811 Turtle._screen = Screen()
3812 RawTurtle.__init__(self, Turtle._screen,
3813 shape=shape,
3814 undobuffersize=undobuffersize,
3815 visible=visible)
3816
3817Pen = Turtle
3818
Guido van Rossumb241b671998-12-04 16:42:46 +00003819def _getpen():
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003820 """Create the 'anonymous' turtle if not already present."""
3821 if Turtle._pen is None:
3822 Turtle._pen = Turtle()
3823 return Turtle._pen
Thomas Wouters477c8d52006-05-27 19:21:47 +00003824
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003825def _getscreen():
3826 """Create a TurtleScreen if not already present."""
3827 if Turtle._screen is None:
3828 Turtle._screen = Screen()
3829 return Turtle._screen
Thomas Wouters477c8d52006-05-27 19:21:47 +00003830
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003831def write_docstringdict(filename="turtle_docstringdict"):
3832 """Create and write docstring-dictionary to file.
Guido van Rossumb241b671998-12-04 16:42:46 +00003833
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003834 Optional argument:
3835 filename -- a string, used as filename
3836 default value is turtle_docstringdict
Thomas Wouters477c8d52006-05-27 19:21:47 +00003837
Ezio Melotti13925002011-03-16 11:05:33 +02003838 Has to be called explicitly, (not used by the turtle-graphics classes)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003839 The docstring dictionary will be written to the Python script <filname>.py
3840 It is intended to serve as a template for translation of the docstrings
3841 into different languages.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003842 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003843 docsdict = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +00003844
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003845 for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003846 key = "_Screen."+methodname
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003847 docsdict[key] = eval(key).__doc__
3848 for methodname in _tg_turtle_functions:
3849 key = "Turtle."+methodname
3850 docsdict[key] = eval(key).__doc__
Thomas Wouters477c8d52006-05-27 19:21:47 +00003851
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003852 f = open("%s.py" % filename,"w")
3853 keys = sorted([x for x in docsdict.keys()
3854 if x.split('.')[1] not in _alias_list])
3855 f.write('docsdict = {\n\n')
3856 for key in keys[:-1]:
3857 f.write('%s :\n' % repr(key))
3858 f.write(' """%s\n""",\n\n' % docsdict[key])
3859 key = keys[-1]
3860 f.write('%s :\n' % repr(key))
3861 f.write(' """%s\n"""\n\n' % docsdict[key])
3862 f.write("}\n")
3863 f.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00003864
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003865def read_docstrings(lang):
3866 """Read in docstrings from lang-specific docstring dictionary.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003867
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003868 Transfer docstrings, translated to lang, from a dictionary-file
3869 to the methods of classes Screen and Turtle and - in revised form -
3870 to the corresponding functions.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003871 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003872 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3873 module = __import__(modname)
3874 docsdict = module.docsdict
3875 for key in docsdict:
3876 try:
3877# eval(key).im_func.__doc__ = docsdict[key]
3878 eval(key).__doc__ = docsdict[key]
3879 except:
3880 print("Bad docstring-entry: %s" % key)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003881
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003882_LANGUAGE = _CFG["language"]
Guido van Rossumb241b671998-12-04 16:42:46 +00003883
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003884try:
3885 if _LANGUAGE != "english":
3886 read_docstrings(_LANGUAGE)
3887except ImportError:
3888 print("Cannot find docsdict for", _LANGUAGE)
3889except:
3890 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3891 _LANGUAGE)
3892
3893
3894def getmethparlist(ob):
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003895 """Get strings describing the arguments for the given object
3896
3897 Returns a pair of strings representing function parameter lists
3898 including parenthesis. The first string is suitable for use in
3899 function definition and the second is suitable for use in function
3900 call. The "self" parameter is not included.
3901 """
3902 defText = callText = ""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003903 # bit of a hack for methods - turn it into a function
3904 # but we drop the "self" param.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003905 # Try and build one for Python defined functions
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003906 args, varargs, varkw = inspect.getargs(ob.__code__)
3907 items2 = args[1:]
3908 realArgs = args[1:]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003909 defaults = ob.__defaults__ or []
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003910 defaults = ["=%r" % (value,) for value in defaults]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003911 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003912 items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3913 if varargs is not None:
3914 items1.append("*" + varargs)
3915 items2.append("*" + varargs)
3916 if varkw is not None:
3917 items1.append("**" + varkw)
3918 items2.append("**" + varkw)
3919 defText = ", ".join(items1)
3920 defText = "(%s)" % defText
3921 callText = ", ".join(items2)
3922 callText = "(%s)" % callText
3923 return defText, callText
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003924
3925def _turtle_docrevise(docstr):
3926 """To reduce docstrings from RawTurtle class for functions
3927 """
3928 import re
3929 if docstr is None:
3930 return None
3931 turtlename = _CFG["exampleturtle"]
3932 newdocstr = docstr.replace("%s." % turtlename,"")
3933 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3934 newdocstr = parexp.sub(":", newdocstr)
3935 return newdocstr
3936
3937def _screen_docrevise(docstr):
3938 """To reduce docstrings from TurtleScreen class for functions
3939 """
3940 import re
3941 if docstr is None:
3942 return None
3943 screenname = _CFG["examplescreen"]
3944 newdocstr = docstr.replace("%s." % screenname,"")
3945 parexp = re.compile(r' \(.+ %s\):' % screenname)
3946 newdocstr = parexp.sub(":", newdocstr)
3947 return newdocstr
3948
3949## The following mechanism makes all methods of RawTurtle and Turtle available
3950## as functions. So we can enhance, change, add, delete methods to these
3951## classes and do not need to change anything here.
3952
3953
3954for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003955 pl1, pl2 = getmethparlist(eval('_Screen.' + methodname))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003956 if pl1 == "":
3957 print(">>>>>>", pl1, pl2)
3958 continue
3959 defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
3960 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003961 exec(defstr)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003962 eval(methodname).__doc__ = _screen_docrevise(eval('_Screen.'+methodname).__doc__)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003963
3964for methodname in _tg_turtle_functions:
3965 pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
3966 if pl1 == "":
3967 print(">>>>>>", pl1, pl2)
3968 continue
3969 defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
3970 {'key':methodname, 'pl1':pl1, 'pl2':pl2})
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003971 exec(defstr)
3972 eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
3973
3974
Georg Brandleaa84ef2009-05-05 08:14:33 +00003975done = mainloop
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003976
3977if __name__ == "__main__":
3978 def switchpen():
3979 if isdown():
3980 pu()
3981 else:
3982 pd()
3983
3984 def demo1():
3985 """Demo of old turtle.py - module"""
3986 reset()
3987 tracer(True)
3988 up()
3989 backward(100)
3990 down()
3991 # draw 3 squares; the last filled
3992 width(3)
3993 for i in range(3):
3994 if i == 2:
3995 begin_fill()
3996 for _ in range(4):
3997 forward(20)
3998 left(90)
3999 if i == 2:
4000 color("maroon")
4001 end_fill()
4002 up()
4003 forward(30)
4004 down()
4005 width(1)
4006 color("black")
4007 # move out of the way
4008 tracer(False)
4009 up()
4010 right(90)
4011 forward(100)
4012 right(90)
4013 forward(100)
4014 right(180)
4015 down()
4016 # some text
4017 write("startstart", 1)
4018 write("start", 1)
4019 color("red")
4020 # staircase
4021 for i in range(5):
Guido van Rossumb241b671998-12-04 16:42:46 +00004022 forward(20)
4023 left(90)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004024 forward(20)
4025 right(90)
4026 # filled staircase
4027 tracer(True)
4028 begin_fill()
4029 for i in range(5):
4030 forward(20)
4031 left(90)
4032 forward(20)
4033 right(90)
4034 end_fill()
4035 # more text
Thomas Wouters477c8d52006-05-27 19:21:47 +00004036
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004037 def demo2():
4038 """Demo of some new features."""
4039 speed(1)
4040 st()
4041 pensize(3)
4042 setheading(towards(0, 0))
4043 radius = distance(0, 0)/2.0
4044 rt(90)
4045 for _ in range(18):
4046 switchpen()
4047 circle(radius, 10)
4048 write("wait a moment...")
4049 while undobufferentries():
4050 undo()
4051 reset()
4052 lt(90)
4053 colormode(255)
4054 laenge = 10
4055 pencolor("green")
4056 pensize(3)
4057 lt(180)
4058 for i in range(-2, 16):
4059 if i > 0:
4060 begin_fill()
4061 fillcolor(255-15*i, 0, 15*i)
4062 for _ in range(3):
4063 fd(laenge)
4064 lt(120)
4065 end_fill()
4066 laenge += 10
4067 lt(15)
4068 speed((speed()+1)%12)
4069 #end_fill()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004070
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004071 lt(120)
4072 pu()
4073 fd(70)
4074 rt(30)
4075 pd()
4076 color("red","yellow")
4077 speed(0)
4078 begin_fill()
4079 for _ in range(4):
4080 circle(50, 90)
4081 rt(90)
4082 fd(30)
4083 rt(90)
4084 end_fill()
4085 lt(90)
4086 pu()
4087 fd(30)
4088 pd()
4089 shape("turtle")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004090
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004091 tri = getturtle()
4092 tri.resizemode("auto")
4093 turtle = Turtle()
4094 turtle.resizemode("auto")
4095 turtle.shape("turtle")
4096 turtle.reset()
4097 turtle.left(90)
4098 turtle.speed(0)
4099 turtle.up()
4100 turtle.goto(280, 40)
4101 turtle.lt(30)
4102 turtle.down()
4103 turtle.speed(6)
4104 turtle.color("blue","orange")
4105 turtle.pensize(2)
4106 tri.speed(6)
Thomas Wouters477c8d52006-05-27 19:21:47 +00004107 setheading(towards(turtle))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004108 count = 1
4109 while tri.distance(turtle) > 4:
4110 turtle.fd(3.5)
4111 turtle.lt(0.6)
4112 tri.setheading(tri.towards(turtle))
4113 tri.fd(4)
4114 if count % 20 == 0:
4115 turtle.stamp()
4116 tri.stamp()
4117 switchpen()
4118 count += 1
4119 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4120 tri.pencolor("black")
4121 tri.pencolor("red")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004122
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004123 def baba(xdummy, ydummy):
4124 clearscreen()
4125 bye()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004126
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004127 time.sleep(2)
Guido van Rossumb241b671998-12-04 16:42:46 +00004128
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004129 while undobufferentries():
4130 tri.undo()
4131 turtle.undo()
4132 tri.fd(50)
4133 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4134 tri.onclick(baba, 1)
4135
4136 demo1()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004137 demo2()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004138 exitonclick()