blob: 9db564b7eb8bd403c452e311022149e4368806b6 [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
Ezio Melottie130a522011-10-19 10:58:56 +030099extensions 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
Ned Deily09ae5442014-04-19 19:11:14 -0700112import sys
Guido van Rossumb241b671998-12-04 16:42:46 +0000113
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000114from os.path import isfile, split, join
115from copy import deepcopy
Georg Brandleaa84ef2009-05-05 08:14:33 +0000116from tkinter import simpledialog
Guido van Rossumb241b671998-12-04 16:42:46 +0000117
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000118_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
119 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
120_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
121 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
Georg Brandleaa84ef2009-05-05 08:14:33 +0000122 'getshapes', 'listen', 'mainloop', 'mode', 'numinput',
123 'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer',
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000124 'register_shape', 'resetscreen', 'screensize', 'setup',
Georg Brandleaa84ef2009-05-05 08:14:33 +0000125 'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update',
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000126 'window_height', 'window_width']
127_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
128 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
129 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
Georg Brandl46afef32009-10-14 18:46:15 +0000130 'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly',
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000131 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
132 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
133 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
134 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
135 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
Georg Brandleaa84ef2009-05-05 08:14:33 +0000136 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle',
137 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards',
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000138 'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000139 'write', 'xcor', 'ycor']
Georg Brandleaa84ef2009-05-05 08:14:33 +0000140_tg_utilities = ['write_docstringdict', 'done']
Thomas Wouters477c8d52006-05-27 19:21:47 +0000141
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000142__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
Terry Jan Reedyf5ac57d2014-06-30 16:09:24 -0400143 _tg_utilities + ['Terminator']) # + _math_functions)
Guido van Rossumb241b671998-12-04 16:42:46 +0000144
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000145_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
146 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
147 'turtlesize', 'up', 'width']
Thomas Wouters477c8d52006-05-27 19:21:47 +0000148
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000149_CFG = {"width" : 0.5, # Screen
150 "height" : 0.75,
151 "canvwidth" : 400,
152 "canvheight": 300,
153 "leftright": None,
154 "topbottom": None,
155 "mode": "standard", # TurtleScreen
156 "colormode": 1.0,
157 "delay": 10,
158 "undobuffersize": 1000, # RawTurtle
159 "shape": "classic",
160 "pencolor" : "black",
161 "fillcolor" : "black",
162 "resizemode" : "noresize",
163 "visible" : True,
164 "language": "english", # docstrings
165 "exampleturtle": "turtle",
166 "examplescreen": "screen",
167 "title": "Python Turtle Graphics",
168 "using_IDLE": False
169 }
Guido van Rossumb241b671998-12-04 16:42:46 +0000170
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000171def config_dict(filename):
172 """Convert content of config-file into dictionary."""
Florent Xicluna7dde7922010-09-03 19:52:03 +0000173 with open(filename, "r") as f:
174 cfglines = f.readlines()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000175 cfgdict = {}
176 for line in cfglines:
177 line = line.strip()
178 if not line or line.startswith("#"):
179 continue
180 try:
181 key, value = line.split("=")
Serhiy Storchakacefa9172016-06-14 22:52:04 +0300182 except ValueError:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000183 print("Bad line in config-file %s:\n%s" % (filename,line))
184 continue
185 key = key.strip()
186 value = value.strip()
187 if value in ["True", "False", "None", "''", '""']:
188 value = eval(value)
Guido van Rossumb241b671998-12-04 16:42:46 +0000189 else:
190 try:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000191 if "." in value:
192 value = float(value)
193 else:
194 value = int(value)
Serhiy Storchakacefa9172016-06-14 22:52:04 +0300195 except ValueError:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000196 pass # value need not be converted
197 cfgdict[key] = value
198 return cfgdict
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000199
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000200def readconfig(cfgdict):
201 """Read config-files, change configuration-dict accordingly.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +0000202
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000203 If there is a turtle.cfg file in the current working directory,
204 read it from there. If this contains an importconfig-value,
205 say 'myway', construct filename turtle_mayway.cfg else use
206 turtle.cfg and read it from the import-directory, where
207 turtle.py is located.
208 Update configuration dictionary first according to config-file,
209 in the import directory, then according to config-file in the
210 current working directory.
211 If no config-file is found, the default configuration is used.
212 """
213 default_cfg = "turtle.cfg"
214 cfgdict1 = {}
215 cfgdict2 = {}
216 if isfile(default_cfg):
217 cfgdict1 = config_dict(default_cfg)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000218 if "importconfig" in cfgdict1:
219 default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
220 try:
221 head, tail = split(__file__)
222 cfg_file2 = join(head, default_cfg)
Serhiy Storchakacefa9172016-06-14 22:52:04 +0300223 except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000224 cfg_file2 = ""
225 if isfile(cfg_file2):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000226 cfgdict2 = config_dict(cfg_file2)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000227 _CFG.update(cfgdict2)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000228 _CFG.update(cfgdict1)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000229
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000230try:
231 readconfig(_CFG)
Serhiy Storchakacefa9172016-06-14 22:52:04 +0300232except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000233 print ("No configfile read, reason unknown")
234
235
236class Vec2D(tuple):
237 """A 2 dimensional vector class, used as a helper class
238 for implementing turtle graphics.
239 May be useful for turtle graphics programs also.
240 Derived from tuple, so a vector is a tuple!
241
242 Provides (for a, b vectors, k number):
243 a+b vector addition
244 a-b vector subtraction
245 a*b inner product
246 k*a and a*k multiplication with scalar
247 |a| absolute value of a
248 a.rotate(angle) rotation
249 """
250 def __new__(cls, x, y):
251 return tuple.__new__(cls, (x, y))
252 def __add__(self, other):
253 return Vec2D(self[0]+other[0], self[1]+other[1])
254 def __mul__(self, other):
255 if isinstance(other, Vec2D):
256 return self[0]*other[0]+self[1]*other[1]
257 return Vec2D(self[0]*other, self[1]*other)
258 def __rmul__(self, other):
259 if isinstance(other, int) or isinstance(other, float):
260 return Vec2D(self[0]*other, self[1]*other)
261 def __sub__(self, other):
262 return Vec2D(self[0]-other[0], self[1]-other[1])
263 def __neg__(self):
264 return Vec2D(-self[0], -self[1])
265 def __abs__(self):
266 return (self[0]**2 + self[1]**2)**0.5
267 def rotate(self, angle):
268 """rotate self counterclockwise by angle
269 """
270 perp = Vec2D(-self[1], self[0])
271 angle = angle * math.pi / 180.0
272 c, s = math.cos(angle), math.sin(angle)
273 return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
274 def __getnewargs__(self):
275 return (self[0], self[1])
276 def __repr__(self):
277 return "(%.2f,%.2f)" % self
278
279
280##############################################################################
281### From here up to line : Tkinter - Interface for turtle.py ###
Mark Dickinsonf8798f52009-02-20 20:53:56 +0000282### May be replaced by an interface to some different graphics toolkit ###
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000283##############################################################################
284
285## helper functions for Scrolled Canvas, to forward Canvas-methods
286## to ScrolledCanvas class
287
288def __methodDict(cls, _dict):
289 """helper function for Scrolled Canvas"""
290 baseList = list(cls.__bases__)
291 baseList.reverse()
292 for _super in baseList:
293 __methodDict(_super, _dict)
294 for key, value in cls.__dict__.items():
295 if type(value) == types.FunctionType:
296 _dict[key] = value
297
298def __methods(cls):
299 """helper function for Scrolled Canvas"""
300 _dict = {}
301 __methodDict(cls, _dict)
302 return _dict.keys()
303
304__stringBody = (
305 'def %(method)s(self, *args, **kw): return ' +
306 'self.%(attribute)s.%(method)s(*args, **kw)')
307
308def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
309 ### MANY CHANGES ###
310 _dict_1 = {}
311 __methodDict(toClass, _dict_1)
312 _dict = {}
313 mfc = __methods(fromClass)
314 for ex in _dict_1.keys():
315 if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
316 pass
317 else:
318 _dict[ex] = _dict_1[ex]
319
320 for method, func in _dict.items():
321 d = {'method': method, 'func': func}
322 if isinstance(toPart, str):
323 execString = \
324 __stringBody % {'method' : method, 'attribute' : toPart}
325 exec(execString, d)
326 setattr(fromClass, method, d[method]) ### NEWU!
327
328
329class ScrolledCanvas(TK.Frame):
330 """Modeled after the scrolled canvas class from Grayons's Tkinter book.
331
332 Used as the default canvas, which pops up automatically when
333 using turtle graphics functions or the Turtle class.
334 """
335 def __init__(self, master, width=500, height=350,
336 canvwidth=600, canvheight=500):
337 TK.Frame.__init__(self, master, width=width, height=height)
Martin v. Löwis22d297b2008-11-19 09:14:30 +0000338 self._rootwindow = self.winfo_toplevel()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000339 self.width, self.height = width, height
340 self.canvwidth, self.canvheight = canvwidth, canvheight
341 self.bg = "white"
342 self._canvas = TK.Canvas(master, width=width, height=height,
343 bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
344 self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
345 orient=TK.HORIZONTAL)
346 self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
347 self._canvas.configure(xscrollcommand=self.hscroll.set,
348 yscrollcommand=self.vscroll.set)
349 self.rowconfigure(0, weight=1, minsize=0)
350 self.columnconfigure(0, weight=1, minsize=0)
351 self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
352 column=0, rowspan=1, columnspan=1, sticky='news')
353 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
354 column=1, rowspan=1, columnspan=1, sticky='news')
355 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
356 column=0, rowspan=1, columnspan=1, sticky='news')
357 self.reset()
Martin v. Löwis22d297b2008-11-19 09:14:30 +0000358 self._rootwindow.bind('<Configure>', self.onResize)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000359
360 def reset(self, canvwidth=None, canvheight=None, bg = None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +0000361 """Adjust canvas and scrollbars according to given canvas size."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000362 if canvwidth:
363 self.canvwidth = canvwidth
364 if canvheight:
365 self.canvheight = canvheight
366 if bg:
367 self.bg = bg
368 self._canvas.config(bg=bg,
369 scrollregion=(-self.canvwidth//2, -self.canvheight//2,
370 self.canvwidth//2, self.canvheight//2))
371 self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
372 self.canvwidth)
373 self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
374 self.canvheight)
375 self.adjustScrolls()
376
377
378 def adjustScrolls(self):
379 """ Adjust scrollbars according to window- and canvas-size.
380 """
381 cwidth = self._canvas.winfo_width()
382 cheight = self._canvas.winfo_height()
383 self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
384 self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
385 if cwidth < self.canvwidth or cheight < self.canvheight:
386 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
387 column=0, rowspan=1, columnspan=1, sticky='news')
388 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
389 column=1, rowspan=1, columnspan=1, sticky='news')
390 else:
391 self.hscroll.grid_forget()
392 self.vscroll.grid_forget()
393
394 def onResize(self, event):
395 """self-explanatory"""
396 self.adjustScrolls()
397
398 def bbox(self, *args):
399 """ 'forward' method, which canvas itself has inherited...
400 """
401 return self._canvas.bbox(*args)
402
403 def cget(self, *args, **kwargs):
404 """ 'forward' method, which canvas itself has inherited...
405 """
406 return self._canvas.cget(*args, **kwargs)
407
408 def config(self, *args, **kwargs):
409 """ 'forward' method, which canvas itself has inherited...
410 """
411 self._canvas.config(*args, **kwargs)
412
413 def bind(self, *args, **kwargs):
414 """ 'forward' method, which canvas itself has inherited...
415 """
416 self._canvas.bind(*args, **kwargs)
417
418 def unbind(self, *args, **kwargs):
419 """ 'forward' method, which canvas itself has inherited...
420 """
421 self._canvas.unbind(*args, **kwargs)
422
423 def focus_force(self):
424 """ 'forward' method, which canvas itself has inherited...
425 """
426 self._canvas.focus_force()
427
428__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
429
430
431class _Root(TK.Tk):
432 """Root class for Screen based on Tkinter."""
433 def __init__(self):
434 TK.Tk.__init__(self)
435
436 def setupcanvas(self, width, height, cwidth, cheight):
437 self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
438 self._canvas.pack(expand=1, fill="both")
439
440 def _getcanvas(self):
441 return self._canvas
442
443 def set_geometry(self, width, height, startx, starty):
444 self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
445
446 def ondestroy(self, destroy):
447 self.wm_protocol("WM_DELETE_WINDOW", destroy)
448
449 def win_width(self):
450 return self.winfo_screenwidth()
451
452 def win_height(self):
453 return self.winfo_screenheight()
454
455Canvas = TK.Canvas
456
457
458class TurtleScreenBase(object):
459 """Provide the basic graphics functionality.
460 Interface between Tkinter and turtle.py.
461
462 To port turtle.py to some different graphics toolkit
463 a corresponding TurtleScreenBase class has to be implemented.
464 """
465
466 @staticmethod
467 def _blankimage():
468 """return a blank image object
469 """
470 img = TK.PhotoImage(width=1, height=1)
471 img.blank()
472 return img
473
474 @staticmethod
475 def _image(filename):
476 """return an image object containing the
477 imagedata from a gif-file named filename.
478 """
479 return TK.PhotoImage(file=filename)
480
481 def __init__(self, cv):
482 self.cv = cv
483 if isinstance(cv, ScrolledCanvas):
484 w = self.cv.canvwidth
485 h = self.cv.canvheight
486 else: # expected: ordinary TK.Canvas
487 w = int(self.cv.cget("width"))
488 h = int(self.cv.cget("height"))
489 self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
490 self.canvwidth = w
491 self.canvheight = h
492 self.xscale = self.yscale = 1.0
493
494 def _createpoly(self):
495 """Create an invisible polygon item on canvas self.cv)
496 """
497 return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
498
499 def _drawpoly(self, polyitem, coordlist, fill=None,
500 outline=None, width=None, top=False):
501 """Configure polygonitem polyitem according to provided
502 arguments:
503 coordlist is sequence of coordinates
504 fill is filling color
505 outline is outline color
506 top is a boolean value, which specifies if polyitem
507 will be put on top of the canvas' displaylist so it
508 will not be covered by other items.
509 """
510 cl = []
511 for x, y in coordlist:
512 cl.append(x * self.xscale)
513 cl.append(-y * self.yscale)
514 self.cv.coords(polyitem, *cl)
515 if fill is not None:
516 self.cv.itemconfigure(polyitem, fill=fill)
517 if outline is not None:
518 self.cv.itemconfigure(polyitem, outline=outline)
519 if width is not None:
520 self.cv.itemconfigure(polyitem, width=width)
521 if top:
522 self.cv.tag_raise(polyitem)
523
524 def _createline(self):
525 """Create an invisible line item on canvas self.cv)
526 """
527 return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
528 capstyle = TK.ROUND)
529
530 def _drawline(self, lineitem, coordlist=None,
531 fill=None, width=None, top=False):
532 """Configure lineitem according to provided arguments:
533 coordlist is sequence of coordinates
534 fill is drawing color
535 width is width of drawn line.
536 top is a boolean value, which specifies if polyitem
537 will be put on top of the canvas' displaylist so it
538 will not be covered by other items.
539 """
540 if coordlist is not None:
541 cl = []
542 for x, y in coordlist:
543 cl.append(x * self.xscale)
544 cl.append(-y * self.yscale)
545 self.cv.coords(lineitem, *cl)
546 if fill is not None:
547 self.cv.itemconfigure(lineitem, fill=fill)
548 if width is not None:
549 self.cv.itemconfigure(lineitem, width=width)
550 if top:
551 self.cv.tag_raise(lineitem)
552
553 def _delete(self, item):
554 """Delete graphics item from canvas.
555 If item is"all" delete all graphics items.
556 """
557 self.cv.delete(item)
558
559 def _update(self):
560 """Redraw graphics items on canvas
561 """
562 self.cv.update()
563
564 def _delay(self, delay):
565 """Delay subsequent canvas actions for delay ms."""
566 self.cv.after(delay)
567
568 def _iscolorstring(self, color):
569 """Check if the string color is a legal Tkinter color string.
570 """
571 try:
572 rgb = self.cv.winfo_rgb(color)
573 ok = True
574 except TK.TclError:
575 ok = False
576 return ok
577
578 def _bgcolor(self, color=None):
579 """Set canvas' backgroundcolor if color is not None,
580 else return backgroundcolor."""
581 if color is not None:
582 self.cv.config(bg = color)
583 self._update()
584 else:
585 return self.cv.cget("bg")
586
587 def _write(self, pos, txt, align, font, pencolor):
588 """Write txt at pos in canvas with specified font
589 and color.
590 Return text item and x-coord of right bottom corner
591 of text's bounding box."""
592 x, y = pos
593 x = x * self.xscale
594 y = y * self.yscale
595 anchor = {"left":"sw", "center":"s", "right":"se" }
596 item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
597 fill = pencolor, font = font)
598 x0, y0, x1, y1 = self.cv.bbox(item)
599 self.cv.update()
600 return item, x1-1
601
602## def _dot(self, pos, size, color):
603## """may be implemented for some other graphics toolkit"""
604
605 def _onclick(self, item, fun, num=1, add=None):
606 """Bind fun to mouse-click event on turtle.
607 fun must be a function with two arguments, the coordinates
608 of the clicked point on the canvas.
609 num, the number of the mouse-button defaults to 1
610 """
611 if fun is None:
612 self.cv.tag_unbind(item, "<Button-%s>" % num)
613 else:
614 def eventfun(event):
615 x, y = (self.cv.canvasx(event.x)/self.xscale,
616 -self.cv.canvasy(event.y)/self.yscale)
617 fun(x, y)
618 self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
619
620 def _onrelease(self, item, fun, num=1, add=None):
621 """Bind fun to mouse-button-release event on turtle.
622 fun must be a function with two arguments, the coordinates
623 of the point on the canvas where mouse button is released.
624 num, the number of the mouse-button defaults to 1
625
626 If a turtle is clicked, first _onclick-event will be performed,
627 then _onscreensclick-event.
628 """
629 if fun is None:
630 self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
631 else:
632 def eventfun(event):
633 x, y = (self.cv.canvasx(event.x)/self.xscale,
634 -self.cv.canvasy(event.y)/self.yscale)
635 fun(x, y)
636 self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
637 eventfun, add)
638
639 def _ondrag(self, item, fun, num=1, add=None):
640 """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
641 fun must be a function with two arguments, the coordinates of the
642 actual mouse position on the canvas.
643 num, the number of the mouse-button defaults to 1
644
645 Every sequence of mouse-move-events on a turtle is preceded by a
646 mouse-click event on that turtle.
647 """
648 if fun is None:
649 self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
650 else:
651 def eventfun(event):
652 try:
653 x, y = (self.cv.canvasx(event.x)/self.xscale,
654 -self.cv.canvasy(event.y)/self.yscale)
655 fun(x, y)
Serhiy Storchakacefa9172016-06-14 22:52:04 +0300656 except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000657 pass
658 self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
659
660 def _onscreenclick(self, fun, num=1, add=None):
661 """Bind fun to mouse-click event on canvas.
662 fun must be a function with two arguments, the coordinates
663 of the clicked point on the canvas.
664 num, the number of the mouse-button defaults to 1
665
666 If a turtle is clicked, first _onclick-event will be performed,
667 then _onscreensclick-event.
668 """
669 if fun is None:
670 self.cv.unbind("<Button-%s>" % num)
671 else:
672 def eventfun(event):
673 x, y = (self.cv.canvasx(event.x)/self.xscale,
674 -self.cv.canvasy(event.y)/self.yscale)
675 fun(x, y)
676 self.cv.bind("<Button-%s>" % num, eventfun, add)
677
Georg Brandleaa84ef2009-05-05 08:14:33 +0000678 def _onkeyrelease(self, fun, key):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000679 """Bind fun to key-release event of key.
680 Canvas must have focus. See method listen
681 """
682 if fun is None:
683 self.cv.unbind("<KeyRelease-%s>" % key, None)
684 else:
685 def eventfun(event):
686 fun()
687 self.cv.bind("<KeyRelease-%s>" % key, eventfun)
688
Georg Brandleaa84ef2009-05-05 08:14:33 +0000689 def _onkeypress(self, fun, key=None):
690 """If key is given, bind fun to key-press event of key.
691 Otherwise bind fun to any key-press.
692 Canvas must have focus. See method listen.
693 """
694 if fun is None:
695 if key is None:
696 self.cv.unbind("<KeyPress>", None)
697 else:
698 self.cv.unbind("<KeyPress-%s>" % key, None)
699 else:
700 def eventfun(event):
701 fun()
702 if key is None:
703 self.cv.bind("<KeyPress>", eventfun)
704 else:
705 self.cv.bind("<KeyPress-%s>" % key, eventfun)
706
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000707 def _listen(self):
708 """Set focus on canvas (in order to collect key-events)
709 """
710 self.cv.focus_force()
711
712 def _ontimer(self, fun, t):
713 """Install a timer, which calls fun after t milliseconds.
714 """
715 if t == 0:
716 self.cv.after_idle(fun)
717 else:
718 self.cv.after(t, fun)
719
720 def _createimage(self, image):
721 """Create and return image item on canvas.
722 """
723 return self.cv.create_image(0, 0, image=image)
724
725 def _drawimage(self, item, pos, image):
726 """Configure image item as to draw image object
727 at position (x,y) on canvas)
728 """
729 x, y = pos
730 self.cv.coords(item, (x * self.xscale, -y * self.yscale))
731 self.cv.itemconfig(item, image=image)
732
733 def _setbgpic(self, item, image):
734 """Configure image item as to draw image object
735 at center of canvas. Set item to the first item
736 in the displaylist, so it will be drawn below
737 any other item ."""
738 self.cv.itemconfig(item, image=image)
739 self.cv.tag_lower(item)
740
741 def _type(self, item):
742 """Return 'line' or 'polygon' or 'image' depending on
743 type of item.
744 """
745 return self.cv.type(item)
746
747 def _pointlist(self, item):
748 """returns list of coordinate-pairs of points of item
749 Example (for insiders):
750 >>> from turtle import *
751 >>> getscreen()._pointlist(getturtle().turtle._item)
752 [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
753 (9.9999999999999982, 0.0)]
754 >>> """
Alexander Belopolsky022f0492010-11-22 19:40:51 +0000755 cl = self.cv.coords(item)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000756 pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
757 return pl
758
759 def _setscrollregion(self, srx1, sry1, srx2, sry2):
760 self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
761
762 def _rescale(self, xscalefactor, yscalefactor):
763 items = self.cv.find_all()
764 for item in items:
765 coordinates = list(self.cv.coords(item))
766 newcoordlist = []
767 while coordinates:
768 x, y = coordinates[:2]
769 newcoordlist.append(x * xscalefactor)
770 newcoordlist.append(y * yscalefactor)
771 coordinates = coordinates[2:]
772 self.cv.coords(item, *newcoordlist)
773
774 def _resize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +0000775 """Resize the canvas the turtles are drawing on. Does
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000776 not alter the drawing window.
777 """
778 # needs amendment
779 if not isinstance(self.cv, ScrolledCanvas):
780 return self.canvwidth, self.canvheight
Florent Xiclunafd1b0932010-03-28 00:25:02 +0000781 if canvwidth is canvheight is bg is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000782 return self.cv.canvwidth, self.cv.canvheight
783 if canvwidth is not None:
784 self.canvwidth = canvwidth
785 if canvheight is not None:
786 self.canvheight = canvheight
787 self.cv.reset(canvwidth, canvheight, bg)
788
789 def _window_size(self):
790 """ Return the width and height of the turtle window.
791 """
792 width = self.cv.winfo_width()
793 if width <= 1: # the window isn't managed by a geometry manager
794 width = self.cv['width']
795 height = self.cv.winfo_height()
796 if height <= 1: # the window isn't managed by a geometry manager
797 height = self.cv['height']
798 return width, height
799
Georg Brandleaa84ef2009-05-05 08:14:33 +0000800 def mainloop(self):
801 """Starts event loop - calling Tkinter's mainloop function.
802
803 No argument.
804
805 Must be last statement in a turtle graphics program.
806 Must NOT be used if a script is run from within IDLE in -n mode
807 (No subprocess) - for interactive use of turtle graphics.
808
809 Example (for a TurtleScreen instance named screen):
810 >>> screen.mainloop()
811
812 """
813 TK.mainloop()
814
815 def textinput(self, title, prompt):
816 """Pop up a dialog window for input of a string.
817
818 Arguments: title is the title of the dialog window,
819 prompt is a text mostly describing what information to input.
820
821 Return the string input
822 If the dialog is canceled, return None.
823
824 Example (for a TurtleScreen instance named screen):
825 >>> screen.textinput("NIM", "Name of first player:")
826
827 """
828 return simpledialog.askstring(title, prompt)
829
830 def numinput(self, title, prompt, default=None, minval=None, maxval=None):
831 """Pop up a dialog window for input of a number.
832
833 Arguments: title is the title of the dialog window,
834 prompt is a text mostly describing what numerical information to input.
835 default: default value
836 minval: minimum value for imput
837 maxval: maximum value for input
838
839 The number input must be in the range minval .. maxval if these are
840 given. If not, a hint is issued and the dialog remains open for
841 correction. Return the number input.
842 If the dialog is canceled, return None.
843
844 Example (for a TurtleScreen instance named screen):
845 >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000)
846
847 """
848 return simpledialog.askfloat(title, prompt, initialvalue=default,
849 minvalue=minval, maxvalue=maxval)
850
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000851
852##############################################################################
853### End of Tkinter - interface ###
854##############################################################################
855
856
857class Terminator (Exception):
858 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
859
Terry Jan Reedyc30b7b12013-03-11 17:57:08 -0400860 This stops execution of a turtle graphics script.
861 Main purpose: use in the Demo-Viewer turtle.Demo.py.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000862 """
863 pass
864
865
866class TurtleGraphicsError(Exception):
867 """Some TurtleGraphics Error
868 """
869
870
871class Shape(object):
872 """Data structure modeling shapes.
873
874 attribute _type is one of "polygon", "image", "compound"
875 attribute _data is - depending on _type a poygon-tuple,
876 an image or a list constructed using the addcomponent method.
877 """
878 def __init__(self, type_, data=None):
879 self._type = type_
880 if type_ == "polygon":
881 if isinstance(data, list):
882 data = tuple(data)
883 elif type_ == "image":
884 if isinstance(data, str):
885 if data.lower().endswith(".gif") and isfile(data):
886 data = TurtleScreen._image(data)
887 # else data assumed to be Photoimage
888 elif type_ == "compound":
889 data = []
890 else:
891 raise TurtleGraphicsError("There is no shape type %s" % type_)
892 self._data = data
893
894 def addcomponent(self, poly, fill, outline=None):
895 """Add component to a shape of type compound.
896
897 Arguments: poly is a polygon, i. e. a tuple of number pairs.
898 fill is the fillcolor of the component,
899 outline is the outline color of the component.
900
901 call (for a Shapeobject namend s):
902 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000903
904 Example:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000905 >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
906 >>> s = Shape("compound")
907 >>> s.addcomponent(poly, "red", "blue")
Petri Lehtinen9aa20af2011-12-02 21:24:14 +0200908 >>> # .. add more components and then use register_shape()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000909 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000910 if self._type != "compound":
911 raise TurtleGraphicsError("Cannot add component to %s Shape"
912 % self._type)
913 if outline is None:
914 outline = fill
915 self._data.append([poly, fill, outline])
Guido van Rossumb241b671998-12-04 16:42:46 +0000916
Thomas Wouters477c8d52006-05-27 19:21:47 +0000917
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000918class Tbuffer(object):
919 """Ring buffer used as undobuffer for RawTurtle objects."""
920 def __init__(self, bufsize=10):
921 self.bufsize = bufsize
922 self.buffer = [[None]] * bufsize
923 self.ptr = -1
924 self.cumulate = False
925 def reset(self, bufsize=None):
926 if bufsize is None:
927 for i in range(self.bufsize):
928 self.buffer[i] = [None]
929 else:
930 self.bufsize = bufsize
931 self.buffer = [[None]] * bufsize
932 self.ptr = -1
933 def push(self, item):
934 if self.bufsize > 0:
935 if not self.cumulate:
936 self.ptr = (self.ptr + 1) % self.bufsize
937 self.buffer[self.ptr] = item
938 else:
939 self.buffer[self.ptr].append(item)
940 def pop(self):
941 if self.bufsize > 0:
942 item = self.buffer[self.ptr]
943 if item is None:
944 return None
945 else:
946 self.buffer[self.ptr] = [None]
947 self.ptr = (self.ptr - 1) % self.bufsize
948 return (item)
949 def nr_of_items(self):
950 return self.bufsize - self.buffer.count([None])
951 def __repr__(self):
952 return str(self.buffer) + " " + str(self.ptr)
953
954
955
956class TurtleScreen(TurtleScreenBase):
957 """Provides screen oriented methods like setbg etc.
958
959 Only relies upon the methods of TurtleScreenBase and NOT
960 upon components of the underlying graphics toolkit -
961 which is Tkinter in this case.
962 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000963 _RUNNING = True
964
965 def __init__(self, cv, mode=_CFG["mode"],
966 colormode=_CFG["colormode"], delay=_CFG["delay"]):
967 self._shapes = {
968 "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
969 "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
970 (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
971 (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
972 (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
973 (2,14))),
974 "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
975 (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
976 (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
977 (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
978 (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
979 (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
980 "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
981 (-10,-10))),
982 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
983 (-10,-5.77))),
984 "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
985 "blank" : Shape("image", self._blankimage())
986 }
987
988 self._bgpics = {"nopic" : ""}
989
990 TurtleScreenBase.__init__(self, cv)
991 self._mode = mode
992 self._delayvalue = delay
993 self._colormode = _CFG["colormode"]
994 self._keys = []
995 self.clear()
Ned Deily09ae5442014-04-19 19:11:14 -0700996 if sys.platform == 'darwin':
997 # Force Turtle window to the front on OS X. This is needed because
998 # the Turtle window will show behind the Terminal window when you
999 # start the demo from the command line.
Ned Deily152dfd12014-09-13 23:39:16 -07001000 rootwindow = cv.winfo_toplevel()
1001 rootwindow.call('wm', 'attributes', '.', '-topmost', '1')
1002 rootwindow.call('wm', 'attributes', '.', '-topmost', '0')
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001003
1004 def clear(self):
1005 """Delete all drawings and all turtles from the TurtleScreen.
1006
Georg Brandleaa84ef2009-05-05 08:14:33 +00001007 No argument.
1008
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001009 Reset empty TurtleScreen to its initial state: white background,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001010 no backgroundimage, no eventbindings and tracing on.
1011
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001012 Example (for a TurtleScreen instance named screen):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001013 >>> screen.clear()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001014
1015 Note: this method is not available as function.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001016 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001017 self._delayvalue = _CFG["delay"]
1018 self._colormode = _CFG["colormode"]
1019 self._delete("all")
1020 self._bgpic = self._createimage("")
1021 self._bgpicname = "nopic"
1022 self._tracing = 1
1023 self._updatecounter = 0
1024 self._turtles = []
1025 self.bgcolor("white")
1026 for btn in 1, 2, 3:
1027 self.onclick(None, btn)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001028 self.onkeypress(None)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001029 for key in self._keys[:]:
1030 self.onkey(None, key)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001031 self.onkeypress(None, key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001032 Turtle._pen = None
Guido van Rossumb241b671998-12-04 16:42:46 +00001033
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001034 def mode(self, mode=None):
1035 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001036
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001037 Optional argument:
Martin Panter99e843b2016-09-10 10:38:28 +00001038 mode -- one of the strings 'standard', 'logo' or 'world'
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001039
1040 Mode 'standard' is compatible with turtle.py.
1041 Mode 'logo' is compatible with most Logo-Turtle-Graphics.
1042 Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
1043 this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
1044 If mode is not given, return the current mode.
1045
1046 Mode Initial turtle heading positive angles
1047 ------------|-------------------------|-------------------
1048 'standard' to the right (east) counterclockwise
1049 'logo' upward (north) clockwise
1050
1051 Examples:
1052 >>> mode('logo') # resets turtle heading to north
1053 >>> mode()
1054 'logo'
Thomas Wouters477c8d52006-05-27 19:21:47 +00001055 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001056 if mode is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001057 return self._mode
1058 mode = mode.lower()
1059 if mode not in ["standard", "logo", "world"]:
1060 raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1061 self._mode = mode
1062 if mode in ["standard", "logo"]:
1063 self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1064 self.canvwidth//2, self.canvheight//2)
1065 self.xscale = self.yscale = 1.0
1066 self.reset()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001067
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001068 def setworldcoordinates(self, llx, lly, urx, ury):
1069 """Set up a user defined coordinate-system.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001070
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001071 Arguments:
1072 llx -- a number, x-coordinate of lower left corner of canvas
1073 lly -- a number, y-coordinate of lower left corner of canvas
1074 urx -- a number, x-coordinate of upper right corner of canvas
1075 ury -- a number, y-coordinate of upper right corner of canvas
1076
1077 Set up user coodinat-system and switch to mode 'world' if necessary.
1078 This performs a screen.reset. If mode 'world' is already active,
1079 all drawings are redrawn according to the new coordinates.
1080
1081 But ATTENTION: in user-defined coordinatesystems angles may appear
1082 distorted. (see Screen.mode())
1083
1084 Example (for a TurtleScreen instance named screen):
1085 >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1086 >>> for _ in range(36):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001087 ... left(10)
1088 ... forward(0.5)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001089 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001090 if self.mode() != "world":
1091 self.mode("world")
1092 xspan = float(urx - llx)
1093 yspan = float(ury - lly)
1094 wx, wy = self._window_size()
1095 self.screensize(wx-20, wy-20)
1096 oldxscale, oldyscale = self.xscale, self.yscale
1097 self.xscale = self.canvwidth / xspan
1098 self.yscale = self.canvheight / yspan
1099 srx1 = llx * self.xscale
1100 sry1 = -ury * self.yscale
1101 srx2 = self.canvwidth + srx1
1102 sry2 = self.canvheight + sry1
1103 self._setscrollregion(srx1, sry1, srx2, sry2)
1104 self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1105 self.update()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001106
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001107 def register_shape(self, name, shape=None):
1108 """Adds a turtle shape to TurtleScreen's shapelist.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001109
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001110 Arguments:
1111 (1) name is the name of a gif-file and shape is None.
1112 Installs the corresponding image shape.
1113 !! Image-shapes DO NOT rotate when turning the turtle,
1114 !! so they do not display the heading of the turtle!
1115 (2) name is an arbitrary string and shape is a tuple
1116 of pairs of coordinates. Installs the corresponding
1117 polygon shape
1118 (3) name is an arbitrary string and shape is a
1119 (compound) Shape object. Installs the corresponding
1120 compound shape.
1121 To use a shape, you have to issue the command shape(shapename).
Thomas Wouters477c8d52006-05-27 19:21:47 +00001122
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001123 call: register_shape("turtle.gif")
1124 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1125
1126 Example (for a TurtleScreen instance named screen):
1127 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1128
Thomas Wouters477c8d52006-05-27 19:21:47 +00001129 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001130 if shape is None:
1131 # image
1132 if name.lower().endswith(".gif"):
1133 shape = Shape("image", self._image(name))
1134 else:
1135 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1136 + "Use help(register_shape)" )
1137 elif isinstance(shape, tuple):
1138 shape = Shape("polygon", shape)
1139 ## else shape assumed to be Shape-instance
1140 self._shapes[name] = shape
Guido van Rossumbffa52f2002-09-29 00:25:51 +00001141
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001142 def _colorstr(self, color):
1143 """Return color string corresponding to args.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001144
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001145 Argument may be a string or a tuple of three
1146 numbers corresponding to actual colormode,
1147 i.e. in the range 0<=n<=colormode.
1148
1149 If the argument doesn't represent a color,
1150 an error is raised.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001151 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001152 if len(color) == 1:
1153 color = color[0]
1154 if isinstance(color, str):
1155 if self._iscolorstring(color) or color == "":
1156 return color
1157 else:
1158 raise TurtleGraphicsError("bad color string: %s" % str(color))
1159 try:
1160 r, g, b = color
Serhiy Storchakacefa9172016-06-14 22:52:04 +03001161 except (TypeError, ValueError):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001162 raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1163 if self._colormode == 1.0:
1164 r, g, b = [round(255.0*x) for x in (r, g, b)]
1165 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1166 raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1167 return "#%02x%02x%02x" % (r, g, b)
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001168
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001169 def _color(self, cstr):
1170 if not cstr.startswith("#"):
1171 return cstr
1172 if len(cstr) == 7:
1173 cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1174 elif len(cstr) == 4:
1175 cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1176 else:
1177 raise TurtleGraphicsError("bad colorstring: %s" % cstr)
Jon Dufresne39726282017-05-18 07:35:54 -07001178 return tuple(c * self._colormode/255 for c in cl)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001179
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001180 def colormode(self, cmode=None):
1181 """Return the colormode or set it to 1.0 or 255.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001182
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001183 Optional argument:
1184 cmode -- one of the values 1.0 or 255
Thomas Wouters477c8d52006-05-27 19:21:47 +00001185
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001186 r, g, b values of colortriples have to be in range 0..cmode.
1187
1188 Example (for a TurtleScreen instance named screen):
1189 >>> screen.colormode()
1190 1.0
1191 >>> screen.colormode(255)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001192 >>> pencolor(240,160,80)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001193 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001194 if cmode is None:
1195 return self._colormode
1196 if cmode == 1.0:
1197 self._colormode = float(cmode)
1198 elif cmode == 255:
1199 self._colormode = int(cmode)
1200
1201 def reset(self):
1202 """Reset all Turtles on the Screen to their initial state.
1203
1204 No argument.
1205
1206 Example (for a TurtleScreen instance named screen):
1207 >>> screen.reset()
1208 """
1209 for turtle in self._turtles:
1210 turtle._setmode(self._mode)
1211 turtle.reset()
1212
1213 def turtles(self):
1214 """Return the list of turtles on the screen.
1215
1216 Example (for a TurtleScreen instance named screen):
1217 >>> screen.turtles()
1218 [<turtle.Turtle object at 0x00E11FB0>]
1219 """
1220 return self._turtles
1221
1222 def bgcolor(self, *args):
1223 """Set or return backgroundcolor of the TurtleScreen.
1224
1225 Arguments (if given): a color string or three numbers
1226 in the range 0..colormode or a 3-tuple of such numbers.
1227
1228 Example (for a TurtleScreen instance named screen):
1229 >>> screen.bgcolor("orange")
1230 >>> screen.bgcolor()
1231 'orange'
1232 >>> screen.bgcolor(0.5,0,0.5)
1233 >>> screen.bgcolor()
1234 '#800080'
1235 """
1236 if args:
1237 color = self._colorstr(args)
1238 else:
1239 color = None
1240 color = self._bgcolor(color)
1241 if color is not None:
1242 color = self._color(color)
1243 return color
1244
1245 def tracer(self, n=None, delay=None):
1246 """Turns turtle animation on/off and set delay for update drawings.
1247
1248 Optional arguments:
1249 n -- nonnegative integer
1250 delay -- nonnegative integer
1251
1252 If n is given, only each n-th regular screen update is really performed.
1253 (Can be used to accelerate the drawing of complex graphics.)
1254 Second arguments sets delay value (see RawTurtle.delay())
1255
1256 Example (for a TurtleScreen instance named screen):
1257 >>> screen.tracer(8, 25)
1258 >>> dist = 2
1259 >>> for i in range(200):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001260 ... fd(dist)
1261 ... rt(90)
1262 ... dist += 2
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001263 """
1264 if n is None:
1265 return self._tracing
1266 self._tracing = int(n)
1267 self._updatecounter = 0
1268 if delay is not None:
1269 self._delayvalue = int(delay)
1270 if self._tracing:
1271 self.update()
1272
1273 def delay(self, delay=None):
1274 """ Return or set the drawing delay in milliseconds.
1275
1276 Optional argument:
1277 delay -- positive integer
1278
1279 Example (for a TurtleScreen instance named screen):
1280 >>> screen.delay(15)
1281 >>> screen.delay()
1282 15
1283 """
1284 if delay is None:
1285 return self._delayvalue
1286 self._delayvalue = int(delay)
1287
1288 def _incrementudc(self):
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03001289 """Increment update counter."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001290 if not TurtleScreen._RUNNING:
Serhiy Storchaka80a18032015-02-22 17:25:33 +02001291 TurtleScreen._RUNNING = True
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001292 raise Terminator
1293 if self._tracing > 0:
1294 self._updatecounter += 1
1295 self._updatecounter %= self._tracing
1296
1297 def update(self):
1298 """Perform a TurtleScreen update.
1299 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00001300 tracing = self._tracing
1301 self._tracing = True
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001302 for t in self.turtles():
1303 t._update_data()
1304 t._drawturtle()
Georg Brandleaa84ef2009-05-05 08:14:33 +00001305 self._tracing = tracing
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001306 self._update()
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001307
1308 def window_width(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001309 """ Return the width of the turtle window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001310
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001311 Example (for a TurtleScreen instance named screen):
1312 >>> screen.window_width()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001313 640
1314 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001315 return self._window_size()[0]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001316
1317 def window_height(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001318 """ Return the height of the turtle window.
1319
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001320 Example (for a TurtleScreen instance named screen):
1321 >>> screen.window_height()
1322 480
Thomas Wouters477c8d52006-05-27 19:21:47 +00001323 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001324 return self._window_size()[1]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001325
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001326 def getcanvas(self):
1327 """Return the Canvas of this TurtleScreen.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001328
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001329 No argument.
1330
1331 Example (for a Screen instance named screen):
1332 >>> cv = screen.getcanvas()
1333 >>> cv
1334 <turtle.ScrolledCanvas instance at 0x010742D8>
Thomas Wouters477c8d52006-05-27 19:21:47 +00001335 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001336 return self.cv
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001337
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001338 def getshapes(self):
1339 """Return a list of names of all currently available turtle shapes.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001340
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001341 No argument.
1342
1343 Example (for a TurtleScreen instance named screen):
1344 >>> screen.getshapes()
1345 ['arrow', 'blank', 'circle', ... , 'turtle']
1346 """
1347 return sorted(self._shapes.keys())
1348
1349 def onclick(self, fun, btn=1, add=None):
1350 """Bind fun to mouse-click event on canvas.
1351
1352 Arguments:
1353 fun -- a function with two arguments, the coordinates of the
1354 clicked point on the canvas.
1355 num -- the number of the mouse-button, defaults to 1
1356
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001357 Example (for a TurtleScreen instance named screen)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001358
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001359 >>> screen.onclick(goto)
1360 >>> # Subsequently clicking into the TurtleScreen will
1361 >>> # make the turtle move to the clicked point.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001362 >>> screen.onclick(None)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001363 """
1364 self._onscreenclick(fun, btn, add)
1365
1366 def onkey(self, fun, key):
1367 """Bind fun to key-release event of key.
1368
1369 Arguments:
1370 fun -- a function with no arguments
1371 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1372
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001373 In order to be able to register key-events, TurtleScreen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001374 must have focus. (See method listen.)
1375
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001376 Example (for a TurtleScreen instance named screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001377
1378 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001379 ... fd(50)
1380 ... lt(60)
1381 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001382 >>> screen.onkey(f, "Up")
1383 >>> screen.listen()
1384
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001385 Subsequently the turtle can be moved by repeatedly pressing
1386 the up-arrow key, consequently drawing a hexagon
1387
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001388 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001389 if fun is None:
Georg Brandleaa84ef2009-05-05 08:14:33 +00001390 if key in self._keys:
1391 self._keys.remove(key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001392 elif key not in self._keys:
1393 self._keys.append(key)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001394 self._onkeyrelease(fun, key)
1395
1396 def onkeypress(self, fun, key=None):
1397 """Bind fun to key-press event of key if key is given,
1398 or to any key-press-event if no key is given.
1399
1400 Arguments:
1401 fun -- a function with no arguments
1402 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1403
1404 In order to be able to register key-events, TurtleScreen
1405 must have focus. (See method listen.)
1406
1407 Example (for a TurtleScreen instance named screen
1408 and a Turtle instance named turtle):
1409
1410 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001411 ... fd(50)
1412 ... lt(60)
1413 ...
1414 >>> screen.onkeypress(f, "Up")
Georg Brandleaa84ef2009-05-05 08:14:33 +00001415 >>> screen.listen()
1416
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001417 Subsequently the turtle can be moved by repeatedly pressing
1418 the up-arrow key, or by keeping pressed the up-arrow key.
1419 consequently drawing a hexagon.
Georg Brandleaa84ef2009-05-05 08:14:33 +00001420 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001421 if fun is None:
Georg Brandleaa84ef2009-05-05 08:14:33 +00001422 if key in self._keys:
1423 self._keys.remove(key)
1424 elif key is not None and key not in self._keys:
1425 self._keys.append(key)
1426 self._onkeypress(fun, key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001427
1428 def listen(self, xdummy=None, ydummy=None):
1429 """Set focus on TurtleScreen (in order to collect key-events)
1430
1431 No arguments.
1432 Dummy arguments are provided in order
1433 to be able to pass listen to the onclick method.
1434
1435 Example (for a TurtleScreen instance named screen):
1436 >>> screen.listen()
1437 """
1438 self._listen()
1439
1440 def ontimer(self, fun, t=0):
1441 """Install a timer, which calls fun after t milliseconds.
1442
1443 Arguments:
1444 fun -- a function with no arguments.
1445 t -- a number >= 0
1446
1447 Example (for a TurtleScreen instance named screen):
1448
1449 >>> running = True
1450 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001451 ... if running:
1452 ... fd(50)
1453 ... lt(60)
1454 ... screen.ontimer(f, 250)
1455 ...
1456 >>> f() # makes the turtle marching around
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001457 >>> running = False
1458 """
1459 self._ontimer(fun, t)
1460
1461 def bgpic(self, picname=None):
1462 """Set background image or return name of current backgroundimage.
1463
1464 Optional argument:
1465 picname -- a string, name of a gif-file or "nopic".
1466
Ezio Melotti42da6632011-03-15 05:18:48 +02001467 If picname is a filename, set the corresponding image as background.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001468 If picname is "nopic", delete backgroundimage, if present.
1469 If picname is None, return the filename of the current backgroundimage.
1470
1471 Example (for a TurtleScreen instance named screen):
1472 >>> screen.bgpic()
1473 'nopic'
1474 >>> screen.bgpic("landscape.gif")
1475 >>> screen.bgpic()
1476 'landscape.gif'
1477 """
1478 if picname is None:
1479 return self._bgpicname
1480 if picname not in self._bgpics:
1481 self._bgpics[picname] = self._image(picname)
1482 self._setbgpic(self._bgpic, self._bgpics[picname])
1483 self._bgpicname = picname
1484
1485 def screensize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001486 """Resize the canvas the turtles are drawing on.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001487
1488 Optional arguments:
1489 canvwidth -- positive integer, new width of canvas in pixels
1490 canvheight -- positive integer, new height of canvas in pixels
Ezio Melotti13925002011-03-16 11:05:33 +02001491 bg -- colorstring or color-tuple, new backgroundcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001492 If no arguments are given, return current (canvaswidth, canvasheight)
1493
1494 Do not alter the drawing window. To observe hidden parts of
1495 the canvas use the scrollbars. (Can make visible those parts
1496 of a drawing, which were outside the canvas before!)
1497
1498 Example (for a Turtle instance named turtle):
1499 >>> turtle.screensize(2000,1500)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001500 >>> # e.g. to search for an erroneously escaped turtle ;-)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001501 """
1502 return self._resize(canvwidth, canvheight, bg)
1503
1504 onscreenclick = onclick
1505 resetscreen = reset
1506 clearscreen = clear
1507 addshape = register_shape
Georg Brandleaa84ef2009-05-05 08:14:33 +00001508 onkeyrelease = onkey
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001509
1510class TNavigator(object):
1511 """Navigation part of the RawTurtle.
1512 Implements methods for turtle movement.
1513 """
1514 START_ORIENTATION = {
1515 "standard": Vec2D(1.0, 0.0),
1516 "world" : Vec2D(1.0, 0.0),
1517 "logo" : Vec2D(0.0, 1.0) }
1518 DEFAULT_MODE = "standard"
1519 DEFAULT_ANGLEOFFSET = 0
1520 DEFAULT_ANGLEORIENT = 1
1521
1522 def __init__(self, mode=DEFAULT_MODE):
1523 self._angleOffset = self.DEFAULT_ANGLEOFFSET
1524 self._angleOrient = self.DEFAULT_ANGLEORIENT
1525 self._mode = mode
1526 self.undobuffer = None
1527 self.degrees()
1528 self._mode = None
1529 self._setmode(mode)
1530 TNavigator.reset(self)
1531
1532 def reset(self):
1533 """reset turtle to its initial values
1534
1535 Will be overwritten by parent class
1536 """
1537 self._position = Vec2D(0.0, 0.0)
1538 self._orient = TNavigator.START_ORIENTATION[self._mode]
1539
1540 def _setmode(self, mode=None):
1541 """Set turtle-mode to 'standard', 'world' or 'logo'.
1542 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001543 if mode is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001544 return self._mode
1545 if mode not in ["standard", "logo", "world"]:
1546 return
1547 self._mode = mode
1548 if mode in ["standard", "world"]:
1549 self._angleOffset = 0
1550 self._angleOrient = 1
1551 else: # mode == "logo":
1552 self._angleOffset = self._fullcircle/4.
1553 self._angleOrient = -1
1554
1555 def _setDegreesPerAU(self, fullcircle):
1556 """Helper function for degrees() and radians()"""
1557 self._fullcircle = fullcircle
1558 self._degreesPerAU = 360/fullcircle
1559 if self._mode == "standard":
1560 self._angleOffset = 0
1561 else:
1562 self._angleOffset = fullcircle/4.
1563
1564 def degrees(self, fullcircle=360.0):
1565 """ Set angle measurement units to degrees.
1566
1567 Optional argument:
1568 fullcircle - a number
1569
1570 Set angle measurement units, i. e. set number
1571 of 'degrees' for a full circle. Dafault value is
1572 360 degrees.
1573
1574 Example (for a Turtle instance named turtle):
1575 >>> turtle.left(90)
1576 >>> turtle.heading()
1577 90
Alexander Belopolsky3cdfb122010-10-29 17:16:49 +00001578
1579 Change angle measurement unit to grad (also known as gon,
1580 grade, or gradian and equals 1/100-th of the right angle.)
1581 >>> turtle.degrees(400.0)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001582 >>> turtle.heading()
1583 100
1584
1585 """
1586 self._setDegreesPerAU(fullcircle)
1587
1588 def radians(self):
1589 """ Set the angle measurement units to radians.
1590
1591 No arguments.
1592
1593 Example (for a Turtle instance named turtle):
1594 >>> turtle.heading()
1595 90
1596 >>> turtle.radians()
1597 >>> turtle.heading()
1598 1.5707963267948966
1599 """
1600 self._setDegreesPerAU(2*math.pi)
1601
1602 def _go(self, distance):
1603 """move turtle forward by specified distance"""
1604 ende = self._position + self._orient * distance
1605 self._goto(ende)
1606
1607 def _rotate(self, angle):
1608 """Turn turtle counterclockwise by specified angle if angle > 0."""
1609 angle *= self._degreesPerAU
1610 self._orient = self._orient.rotate(angle)
1611
1612 def _goto(self, end):
1613 """move turtle to position end."""
1614 self._position = end
1615
1616 def forward(self, distance):
1617 """Move the turtle forward by the specified distance.
1618
1619 Aliases: forward | fd
1620
1621 Argument:
1622 distance -- a number (integer or float)
1623
1624 Move the turtle forward by the specified distance, in the direction
1625 the turtle is headed.
1626
1627 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001628 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001629 (0.00, 0.00)
1630 >>> turtle.forward(25)
1631 >>> turtle.position()
1632 (25.00,0.00)
1633 >>> turtle.forward(-75)
1634 >>> turtle.position()
1635 (-50.00,0.00)
1636 """
1637 self._go(distance)
1638
1639 def back(self, distance):
1640 """Move the turtle backward by distance.
1641
1642 Aliases: back | backward | bk
1643
1644 Argument:
1645 distance -- a number
1646
1647 Move the turtle backward by distance ,opposite to the direction the
1648 turtle is headed. Do not change the turtle's heading.
1649
1650 Example (for a Turtle instance named turtle):
1651 >>> turtle.position()
1652 (0.00, 0.00)
1653 >>> turtle.backward(30)
1654 >>> turtle.position()
1655 (-30.00, 0.00)
1656 """
1657 self._go(-distance)
1658
1659 def right(self, angle):
1660 """Turn turtle right by angle units.
1661
1662 Aliases: right | rt
1663
1664 Argument:
1665 angle -- a number (integer or float)
1666
1667 Turn turtle right by angle units. (Units are by default degrees,
1668 but can be set via the degrees() and radians() functions.)
1669 Angle orientation depends on mode. (See this.)
1670
1671 Example (for a Turtle instance named turtle):
1672 >>> turtle.heading()
1673 22.0
1674 >>> turtle.right(45)
1675 >>> turtle.heading()
1676 337.0
1677 """
1678 self._rotate(-angle)
1679
1680 def left(self, angle):
1681 """Turn turtle left by angle units.
1682
1683 Aliases: left | lt
1684
1685 Argument:
1686 angle -- a number (integer or float)
1687
1688 Turn turtle left by angle units. (Units are by default degrees,
1689 but can be set via the degrees() and radians() functions.)
1690 Angle orientation depends on mode. (See this.)
1691
1692 Example (for a Turtle instance named turtle):
1693 >>> turtle.heading()
1694 22.0
1695 >>> turtle.left(45)
1696 >>> turtle.heading()
1697 67.0
1698 """
1699 self._rotate(angle)
1700
1701 def pos(self):
1702 """Return the turtle's current location (x,y), as a Vec2D-vector.
1703
1704 Aliases: pos | position
1705
1706 No arguments.
1707
1708 Example (for a Turtle instance named turtle):
1709 >>> turtle.pos()
1710 (0.00, 240.00)
1711 """
1712 return self._position
1713
1714 def xcor(self):
1715 """ Return the turtle's x coordinate.
1716
1717 No arguments.
1718
1719 Example (for a Turtle instance named turtle):
1720 >>> reset()
1721 >>> turtle.left(60)
1722 >>> turtle.forward(100)
1723 >>> print turtle.xcor()
1724 50.0
1725 """
1726 return self._position[0]
1727
1728 def ycor(self):
1729 """ Return the turtle's y coordinate
1730 ---
1731 No arguments.
1732
1733 Example (for a Turtle instance named turtle):
1734 >>> reset()
1735 >>> turtle.left(60)
1736 >>> turtle.forward(100)
1737 >>> print turtle.ycor()
1738 86.6025403784
1739 """
1740 return self._position[1]
1741
1742
1743 def goto(self, x, y=None):
1744 """Move turtle to an absolute position.
1745
1746 Aliases: setpos | setposition | goto:
1747
1748 Arguments:
1749 x -- a number or a pair/vector of numbers
1750 y -- a number None
1751
1752 call: goto(x, y) # two coordinates
1753 --or: goto((x, y)) # a pair (tuple) of coordinates
1754 --or: goto(vec) # e.g. as returned by pos()
1755
1756 Move turtle to an absolute position. If the pen is down,
1757 a line will be drawn. The turtle's orientation does not change.
1758
1759 Example (for a Turtle instance named turtle):
1760 >>> tp = turtle.pos()
1761 >>> tp
1762 (0.00, 0.00)
1763 >>> turtle.setpos(60,30)
1764 >>> turtle.pos()
1765 (60.00,30.00)
1766 >>> turtle.setpos((20,80))
1767 >>> turtle.pos()
1768 (20.00,80.00)
1769 >>> turtle.setpos(tp)
1770 >>> turtle.pos()
1771 (0.00,0.00)
1772 """
1773 if y is None:
1774 self._goto(Vec2D(*x))
1775 else:
1776 self._goto(Vec2D(x, y))
1777
1778 def home(self):
1779 """Move turtle to the origin - coordinates (0,0).
1780
1781 No arguments.
1782
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001783 Move turtle to the origin - coordinates (0,0) and set its
1784 heading to its start-orientation (which depends on mode).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001785
1786 Example (for a Turtle instance named turtle):
1787 >>> turtle.home()
1788 """
1789 self.goto(0, 0)
1790 self.setheading(0)
1791
1792 def setx(self, x):
1793 """Set the turtle's first coordinate to x
1794
1795 Argument:
1796 x -- a number (integer or float)
1797
1798 Set the turtle's first coordinate to x, leave second coordinate
1799 unchanged.
1800
1801 Example (for a Turtle instance named turtle):
1802 >>> turtle.position()
1803 (0.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001804 >>> turtle.setx(10)
1805 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001806 (10.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001807 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001808 self._goto(Vec2D(x, self._position[1]))
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001809
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001810 def sety(self, y):
1811 """Set the turtle's second coordinate to y
Thomas Wouters477c8d52006-05-27 19:21:47 +00001812
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001813 Argument:
1814 y -- a number (integer or float)
1815
1816 Set the turtle's first coordinate to x, second coordinate remains
1817 unchanged.
1818
1819 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001820 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001821 (0.00, 40.00)
1822 >>> turtle.sety(-10)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001823 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001824 (0.00, -10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001825 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001826 self._goto(Vec2D(self._position[0], y))
Guido van Rossumb241b671998-12-04 16:42:46 +00001827
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001828 def distance(self, x, y=None):
1829 """Return the distance from the turtle to (x,y) in turtle step units.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001830
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001831 Arguments:
1832 x -- a number or a pair/vector of numbers or a turtle instance
1833 y -- a number None None
Thomas Wouters477c8d52006-05-27 19:21:47 +00001834
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001835 call: distance(x, y) # two coordinates
1836 --or: distance((x, y)) # a pair (tuple) of coordinates
1837 --or: distance(vec) # e.g. as returned by pos()
1838 --or: distance(mypen) # where mypen is another turtle
1839
1840 Example (for a Turtle instance named turtle):
1841 >>> turtle.pos()
1842 (0.00, 0.00)
1843 >>> turtle.distance(30,40)
1844 50.0
1845 >>> pen = Turtle()
1846 >>> pen.forward(77)
1847 >>> turtle.distance(pen)
1848 77.0
1849 """
1850 if y is not None:
1851 pos = Vec2D(x, y)
1852 if isinstance(x, Vec2D):
1853 pos = x
1854 elif isinstance(x, tuple):
1855 pos = Vec2D(*x)
1856 elif isinstance(x, TNavigator):
1857 pos = x._position
1858 return abs(pos - self._position)
1859
1860 def towards(self, x, y=None):
1861 """Return the angle of the line from the turtle's position to (x, y).
1862
1863 Arguments:
1864 x -- a number or a pair/vector of numbers or a turtle instance
1865 y -- a number None None
1866
1867 call: distance(x, y) # two coordinates
1868 --or: distance((x, y)) # a pair (tuple) of coordinates
1869 --or: distance(vec) # e.g. as returned by pos()
1870 --or: distance(mypen) # where mypen is another turtle
1871
1872 Return the angle, between the line from turtle-position to position
1873 specified by x, y and the turtle's start orientation. (Depends on
1874 modes - "standard" or "logo")
1875
1876 Example (for a Turtle instance named turtle):
1877 >>> turtle.pos()
1878 (10.00, 10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001879 >>> turtle.towards(0,0)
1880 225.0
1881 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001882 if y is not None:
1883 pos = Vec2D(x, y)
1884 if isinstance(x, Vec2D):
1885 pos = x
1886 elif isinstance(x, tuple):
1887 pos = Vec2D(*x)
1888 elif isinstance(x, TNavigator):
1889 pos = x._position
1890 x, y = pos - self._position
1891 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1892 result /= self._degreesPerAU
1893 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1894
1895 def heading(self):
1896 """ Return the turtle's current heading.
1897
1898 No arguments.
1899
1900 Example (for a Turtle instance named turtle):
1901 >>> turtle.left(67)
1902 >>> turtle.heading()
1903 67.0
1904 """
1905 x, y = self._orient
1906 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1907 result /= self._degreesPerAU
1908 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1909
1910 def setheading(self, to_angle):
1911 """Set the orientation of the turtle to to_angle.
1912
1913 Aliases: setheading | seth
1914
1915 Argument:
1916 to_angle -- a number (integer or float)
1917
1918 Set the orientation of the turtle to to_angle.
1919 Here are some common directions in degrees:
1920
1921 standard - mode: logo-mode:
1922 -------------------|--------------------
1923 0 - east 0 - north
1924 90 - north 90 - east
1925 180 - west 180 - south
1926 270 - south 270 - west
1927
1928 Example (for a Turtle instance named turtle):
1929 >>> turtle.setheading(90)
1930 >>> turtle.heading()
1931 90
1932 """
1933 angle = (to_angle - self.heading())*self._angleOrient
1934 full = self._fullcircle
1935 angle = (angle+full/2.)%full - full/2.
1936 self._rotate(angle)
1937
1938 def circle(self, radius, extent = None, steps = None):
1939 """ Draw a circle with given radius.
1940
1941 Arguments:
1942 radius -- a number
1943 extent (optional) -- a number
1944 steps (optional) -- an integer
1945
1946 Draw a circle with given radius. The center is radius units left
1947 of the turtle; extent - an angle - determines which part of the
1948 circle is drawn. If extent is not given, draw the entire circle.
1949 If extent is not a full circle, one endpoint of the arc is the
1950 current pen position. Draw the arc in counterclockwise direction
1951 if radius is positive, otherwise in clockwise direction. Finally
1952 the direction of the turtle is changed by the amount of extent.
1953
1954 As the circle is approximated by an inscribed regular polygon,
1955 steps determines the number of steps to use. If not given,
1956 it will be calculated automatically. Maybe used to draw regular
1957 polygons.
1958
1959 call: circle(radius) # full circle
1960 --or: circle(radius, extent) # arc
1961 --or: circle(radius, extent, steps)
1962 --or: circle(radius, steps=6) # 6-sided polygon
1963
1964 Example (for a Turtle instance named turtle):
1965 >>> turtle.circle(50)
1966 >>> turtle.circle(120, 180) # semicircle
1967 """
1968 if self.undobuffer:
1969 self.undobuffer.push(["seq"])
1970 self.undobuffer.cumulate = True
1971 speed = self.speed()
1972 if extent is None:
1973 extent = self._fullcircle
1974 if steps is None:
1975 frac = abs(extent)/self._fullcircle
1976 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1977 w = 1.0 * extent / steps
1978 w2 = 0.5 * w
1979 l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1980 if radius < 0:
1981 l, w, w2 = -l, -w, -w2
1982 tr = self._tracer()
1983 dl = self._delay()
1984 if speed == 0:
1985 self._tracer(0, 0)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001986 else:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001987 self.speed(0)
1988 self._rotate(w2)
1989 for i in range(steps):
1990 self.speed(speed)
1991 self._go(l)
1992 self.speed(0)
1993 self._rotate(w)
1994 self._rotate(-w2)
1995 if speed == 0:
1996 self._tracer(tr, dl)
1997 self.speed(speed)
1998 if self.undobuffer:
1999 self.undobuffer.cumulate = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00002000
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002001## three dummy methods to be implemented by child class:
Thomas Wouters477c8d52006-05-27 19:21:47 +00002002
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002003 def speed(self, s=0):
2004 """dummy method - to be overwritten by child class"""
2005 def _tracer(self, a=None, b=None):
2006 """dummy method - to be overwritten by child class"""
2007 def _delay(self, n=None):
2008 """dummy method - to be overwritten by child class"""
Thomas Wouters477c8d52006-05-27 19:21:47 +00002009
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002010 fd = forward
2011 bk = back
2012 backward = back
2013 rt = right
2014 lt = left
2015 position = pos
2016 setpos = goto
2017 setposition = goto
2018 seth = setheading
Thomas Wouters477c8d52006-05-27 19:21:47 +00002019
Thomas Wouters477c8d52006-05-27 19:21:47 +00002020
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002021class TPen(object):
2022 """Drawing part of the RawTurtle.
2023 Implements drawing properties.
2024 """
2025 def __init__(self, resizemode=_CFG["resizemode"]):
2026 self._resizemode = resizemode # or "user" or "noresize"
2027 self.undobuffer = None
2028 TPen._reset(self)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002029
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002030 def _reset(self, pencolor=_CFG["pencolor"],
2031 fillcolor=_CFG["fillcolor"]):
2032 self._pensize = 1
2033 self._shown = True
2034 self._pencolor = pencolor
2035 self._fillcolor = fillcolor
2036 self._drawing = True
2037 self._speed = 3
Georg Brandleaa84ef2009-05-05 08:14:33 +00002038 self._stretchfactor = (1., 1.)
2039 self._shearfactor = 0.
2040 self._tilt = 0.
2041 self._shapetrafo = (1., 0., 0., 1.)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002042 self._outlinewidth = 1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002043
2044 def resizemode(self, rmode=None):
2045 """Set resizemode to one of the values: "auto", "user", "noresize".
2046
2047 (Optional) Argument:
2048 rmode -- one of the strings "auto", "user", "noresize"
2049
2050 Different resizemodes have the following effects:
2051 - "auto" adapts the appearance of the turtle
2052 corresponding to the value of pensize.
2053 - "user" adapts the appearance of the turtle according to the
2054 values of stretchfactor and outlinewidth (outline),
2055 which are set by shapesize()
2056 - "noresize" no adaption of the turtle's appearance takes place.
2057 If no argument is given, return current resizemode.
2058 resizemode("user") is called by a call of shapesize with arguments.
2059
2060
2061 Examples (for a Turtle instance named turtle):
2062 >>> turtle.resizemode("noresize")
2063 >>> turtle.resizemode()
2064 'noresize'
Thomas Wouters477c8d52006-05-27 19:21:47 +00002065 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002066 if rmode is None:
2067 return self._resizemode
2068 rmode = rmode.lower()
2069 if rmode in ["auto", "user", "noresize"]:
2070 self.pen(resizemode=rmode)
Guido van Rossumb241b671998-12-04 16:42:46 +00002071
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002072 def pensize(self, width=None):
2073 """Set or return the line thickness.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002074
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002075 Aliases: pensize | width
Thomas Wouters477c8d52006-05-27 19:21:47 +00002076
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002077 Argument:
2078 width -- positive number
Thomas Wouters477c8d52006-05-27 19:21:47 +00002079
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002080 Set the line thickness to width or return it. If resizemode is set
2081 to "auto" and turtleshape is a polygon, that polygon is drawn with
2082 the same line thickness. If no argument is given, current pensize
2083 is returned.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002084
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002085 Example (for a Turtle instance named turtle):
2086 >>> turtle.pensize()
2087 1
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002088 >>> turtle.pensize(10) # from here on lines of width 10 are drawn
Thomas Wouters477c8d52006-05-27 19:21:47 +00002089 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002090 if width is None:
2091 return self._pensize
2092 self.pen(pensize=width)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002093
2094
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002095 def penup(self):
2096 """Pull the pen up -- no drawing when moving.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002097
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002098 Aliases: penup | pu | up
Thomas Wouters477c8d52006-05-27 19:21:47 +00002099
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002100 No argument
2101
2102 Example (for a Turtle instance named turtle):
2103 >>> turtle.penup()
Thomas Wouters477c8d52006-05-27 19:21:47 +00002104 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002105 if not self._drawing:
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002106 return
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002107 self.pen(pendown=False)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002108
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002109 def pendown(self):
2110 """Pull the pen down -- drawing when moving.
2111
2112 Aliases: pendown | pd | down
2113
2114 No argument.
2115
2116 Example (for a Turtle instance named turtle):
2117 >>> turtle.pendown()
2118 """
2119 if self._drawing:
2120 return
2121 self.pen(pendown=True)
2122
2123 def isdown(self):
2124 """Return True if pen is down, False if it's up.
2125
2126 No argument.
2127
2128 Example (for a Turtle instance named turtle):
2129 >>> turtle.penup()
2130 >>> turtle.isdown()
2131 False
2132 >>> turtle.pendown()
2133 >>> turtle.isdown()
2134 True
2135 """
2136 return self._drawing
2137
2138 def speed(self, speed=None):
2139 """ Return or set the turtle's speed.
2140
2141 Optional argument:
2142 speed -- an integer in the range 0..10 or a speedstring (see below)
2143
2144 Set the turtle's speed to an integer value in the range 0 .. 10.
2145 If no argument is given: return current speed.
2146
2147 If input is a number greater than 10 or smaller than 0.5,
2148 speed is set to 0.
2149 Speedstrings are mapped to speedvalues in the following way:
2150 'fastest' : 0
2151 'fast' : 10
2152 'normal' : 6
2153 'slow' : 3
2154 'slowest' : 1
2155 speeds from 1 to 10 enforce increasingly faster animation of
2156 line drawing and turtle turning.
2157
2158 Attention:
2159 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2160 and likewise left/right make the turtle turn instantly.
2161
2162 Example (for a Turtle instance named turtle):
2163 >>> turtle.speed(3)
2164 """
2165 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2166 if speed is None:
2167 return self._speed
2168 if speed in speeds:
2169 speed = speeds[speed]
2170 elif 0.5 < speed < 10.5:
2171 speed = int(round(speed))
2172 else:
2173 speed = 0
2174 self.pen(speed=speed)
2175
2176 def color(self, *args):
2177 """Return or set the pencolor and fillcolor.
2178
2179 Arguments:
2180 Several input formats are allowed.
2181 They use 0, 1, 2, or 3 arguments as follows:
2182
2183 color()
2184 Return the current pencolor and the current fillcolor
2185 as a pair of color specification strings as are returned
2186 by pencolor and fillcolor.
2187 color(colorstring), color((r,g,b)), color(r,g,b)
2188 inputs as in pencolor, set both, fillcolor and pencolor,
2189 to the given value.
2190 color(colorstring1, colorstring2),
2191 color((r1,g1,b1), (r2,g2,b2))
2192 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2193 and analogously, if the other input format is used.
2194
2195 If turtleshape is a polygon, outline and interior of that polygon
2196 is drawn with the newly set colors.
luzpaza5293b42017-11-05 07:37:50 -06002197 For more info see: pencolor, fillcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002198
2199 Example (for a Turtle instance named turtle):
2200 >>> turtle.color('red', 'green')
2201 >>> turtle.color()
2202 ('red', 'green')
2203 >>> colormode(255)
2204 >>> color((40, 80, 120), (160, 200, 240))
2205 >>> color()
2206 ('#285078', '#a0c8f0')
2207 """
2208 if args:
2209 l = len(args)
2210 if l == 1:
2211 pcolor = fcolor = args[0]
2212 elif l == 2:
2213 pcolor, fcolor = args
2214 elif l == 3:
2215 pcolor = fcolor = args
2216 pcolor = self._colorstr(pcolor)
2217 fcolor = self._colorstr(fcolor)
2218 self.pen(pencolor=pcolor, fillcolor=fcolor)
2219 else:
2220 return self._color(self._pencolor), self._color(self._fillcolor)
2221
2222 def pencolor(self, *args):
2223 """ Return or set the pencolor.
2224
2225 Arguments:
2226 Four input formats are allowed:
2227 - pencolor()
2228 Return the current pencolor as color specification string,
2229 possibly in hex-number format (see example).
2230 May be used as input to another color/pencolor/fillcolor call.
2231 - pencolor(colorstring)
2232 s is a Tk color specification string, such as "red" or "yellow"
2233 - pencolor((r, g, b))
2234 *a tuple* of r, g, and b, which represent, an RGB color,
2235 and each of r, g, and b are in the range 0..colormode,
2236 where colormode is either 1.0 or 255
2237 - pencolor(r, g, b)
2238 r, g, and b represent an RGB color, and each of r, g, and b
2239 are in the range 0..colormode
2240
2241 If turtleshape is a polygon, the outline of that polygon is drawn
2242 with the newly set pencolor.
2243
2244 Example (for a Turtle instance named turtle):
2245 >>> turtle.pencolor('brown')
2246 >>> tup = (0.2, 0.8, 0.55)
2247 >>> turtle.pencolor(tup)
2248 >>> turtle.pencolor()
2249 '#33cc8c'
2250 """
2251 if args:
2252 color = self._colorstr(args)
2253 if color == self._pencolor:
2254 return
2255 self.pen(pencolor=color)
2256 else:
2257 return self._color(self._pencolor)
2258
2259 def fillcolor(self, *args):
2260 """ Return or set the fillcolor.
2261
2262 Arguments:
2263 Four input formats are allowed:
2264 - fillcolor()
2265 Return the current fillcolor as color specification string,
2266 possibly in hex-number format (see example).
2267 May be used as input to another color/pencolor/fillcolor call.
2268 - fillcolor(colorstring)
2269 s is a Tk color specification string, such as "red" or "yellow"
2270 - fillcolor((r, g, b))
2271 *a tuple* of r, g, and b, which represent, an RGB color,
2272 and each of r, g, and b are in the range 0..colormode,
2273 where colormode is either 1.0 or 255
2274 - fillcolor(r, g, b)
2275 r, g, and b represent an RGB color, and each of r, g, and b
2276 are in the range 0..colormode
2277
2278 If turtleshape is a polygon, the interior of that polygon is drawn
2279 with the newly set fillcolor.
2280
2281 Example (for a Turtle instance named turtle):
2282 >>> turtle.fillcolor('violet')
2283 >>> col = turtle.pencolor()
2284 >>> turtle.fillcolor(col)
2285 >>> turtle.fillcolor(0, .5, 0)
2286 """
2287 if args:
2288 color = self._colorstr(args)
2289 if color == self._fillcolor:
2290 return
2291 self.pen(fillcolor=color)
2292 else:
2293 return self._color(self._fillcolor)
2294
2295 def showturtle(self):
2296 """Makes the turtle visible.
2297
2298 Aliases: showturtle | st
2299
2300 No argument.
2301
2302 Example (for a Turtle instance named turtle):
2303 >>> turtle.hideturtle()
2304 >>> turtle.showturtle()
2305 """
2306 self.pen(shown=True)
2307
2308 def hideturtle(self):
2309 """Makes the turtle invisible.
2310
2311 Aliases: hideturtle | ht
2312
2313 No argument.
2314
2315 It's a good idea to do this while you're in the
2316 middle of a complicated drawing, because hiding
2317 the turtle speeds up the drawing observably.
2318
2319 Example (for a Turtle instance named turtle):
2320 >>> turtle.hideturtle()
2321 """
2322 self.pen(shown=False)
2323
2324 def isvisible(self):
2325 """Return True if the Turtle is shown, False if it's hidden.
2326
2327 No argument.
2328
2329 Example (for a Turtle instance named turtle):
2330 >>> turtle.hideturtle()
2331 >>> print turtle.isvisible():
2332 False
2333 """
2334 return self._shown
2335
2336 def pen(self, pen=None, **pendict):
2337 """Return or set the pen's attributes.
2338
2339 Arguments:
2340 pen -- a dictionary with some or all of the below listed keys.
2341 **pendict -- one or more keyword-arguments with the below
2342 listed keys as keywords.
2343
2344 Return or set the pen's attributes in a 'pen-dictionary'
2345 with the following key/value pairs:
2346 "shown" : True/False
2347 "pendown" : True/False
2348 "pencolor" : color-string or color-tuple
2349 "fillcolor" : color-string or color-tuple
2350 "pensize" : positive number
2351 "speed" : number in range 0..10
2352 "resizemode" : "auto" or "user" or "noresize"
2353 "stretchfactor": (positive number, positive number)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002354 "shearfactor": number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002355 "outline" : positive number
2356 "tilt" : number
2357
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002358 This dictionary can be used as argument for a subsequent
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002359 pen()-call to restore the former pen-state. Moreover one
2360 or more of these attributes can be provided as keyword-arguments.
2361 This can be used to set several pen attributes in one statement.
Guido van Rossumb241b671998-12-04 16:42:46 +00002362
2363
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002364 Examples (for a Turtle instance named turtle):
2365 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2366 >>> turtle.pen()
2367 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2368 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002369 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002370 >>> penstate=turtle.pen()
2371 >>> turtle.color("yellow","")
2372 >>> turtle.penup()
2373 >>> turtle.pen()
2374 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2375 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002376 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002377 >>> p.pen(penstate, fillcolor="green")
2378 >>> p.pen()
2379 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2380 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002381 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002382 """
2383 _pd = {"shown" : self._shown,
2384 "pendown" : self._drawing,
2385 "pencolor" : self._pencolor,
2386 "fillcolor" : self._fillcolor,
2387 "pensize" : self._pensize,
2388 "speed" : self._speed,
2389 "resizemode" : self._resizemode,
2390 "stretchfactor" : self._stretchfactor,
Georg Brandleaa84ef2009-05-05 08:14:33 +00002391 "shearfactor" : self._shearfactor,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002392 "outline" : self._outlinewidth,
2393 "tilt" : self._tilt
2394 }
Guido van Rossumb241b671998-12-04 16:42:46 +00002395
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002396 if not (pen or pendict):
2397 return _pd
2398
2399 if isinstance(pen, dict):
2400 p = pen
2401 else:
2402 p = {}
2403 p.update(pendict)
2404
2405 _p_buf = {}
2406 for key in p:
2407 _p_buf[key] = _pd[key]
2408
2409 if self.undobuffer:
2410 self.undobuffer.push(("pen", _p_buf))
2411
2412 newLine = False
2413 if "pendown" in p:
2414 if self._drawing != p["pendown"]:
2415 newLine = True
2416 if "pencolor" in p:
2417 if isinstance(p["pencolor"], tuple):
2418 p["pencolor"] = self._colorstr((p["pencolor"],))
2419 if self._pencolor != p["pencolor"]:
2420 newLine = True
2421 if "pensize" in p:
2422 if self._pensize != p["pensize"]:
2423 newLine = True
2424 if newLine:
2425 self._newLine()
2426 if "pendown" in p:
2427 self._drawing = p["pendown"]
2428 if "pencolor" in p:
2429 self._pencolor = p["pencolor"]
2430 if "pensize" in p:
2431 self._pensize = p["pensize"]
2432 if "fillcolor" in p:
2433 if isinstance(p["fillcolor"], tuple):
2434 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2435 self._fillcolor = p["fillcolor"]
2436 if "speed" in p:
2437 self._speed = p["speed"]
2438 if "resizemode" in p:
2439 self._resizemode = p["resizemode"]
2440 if "stretchfactor" in p:
2441 sf = p["stretchfactor"]
2442 if isinstance(sf, (int, float)):
2443 sf = (sf, sf)
2444 self._stretchfactor = sf
Georg Brandleaa84ef2009-05-05 08:14:33 +00002445 if "shearfactor" in p:
2446 self._shearfactor = p["shearfactor"]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002447 if "outline" in p:
2448 self._outlinewidth = p["outline"]
2449 if "shown" in p:
2450 self._shown = p["shown"]
2451 if "tilt" in p:
2452 self._tilt = p["tilt"]
Georg Brandleaa84ef2009-05-05 08:14:33 +00002453 if "stretchfactor" in p or "tilt" in p or "shearfactor" in p:
2454 scx, scy = self._stretchfactor
2455 shf = self._shearfactor
2456 sa, ca = math.sin(self._tilt), math.cos(self._tilt)
2457 self._shapetrafo = ( scx*ca, scy*(shf*ca + sa),
2458 -scx*sa, scy*(ca - shf*sa))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002459 self._update()
2460
2461## three dummy methods to be implemented by child class:
2462
2463 def _newLine(self, usePos = True):
2464 """dummy method - to be overwritten by child class"""
2465 def _update(self, count=True, forced=False):
2466 """dummy method - to be overwritten by child class"""
2467 def _color(self, args):
2468 """dummy method - to be overwritten by child class"""
2469 def _colorstr(self, args):
2470 """dummy method - to be overwritten by child class"""
2471
2472 width = pensize
2473 up = penup
2474 pu = penup
2475 pd = pendown
2476 down = pendown
2477 st = showturtle
2478 ht = hideturtle
2479
2480
2481class _TurtleImage(object):
2482 """Helper class: Datatype to store Turtle attributes
2483 """
2484
2485 def __init__(self, screen, shapeIndex):
2486 self.screen = screen
2487 self._type = None
2488 self._setshape(shapeIndex)
2489
2490 def _setshape(self, shapeIndex):
Georg Brandleaa84ef2009-05-05 08:14:33 +00002491 screen = self.screen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002492 self.shapeIndex = shapeIndex
2493 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2494 return
2495 if self._type == "image" == screen._shapes[shapeIndex]._type:
2496 return
2497 if self._type in ["image", "polygon"]:
2498 screen._delete(self._item)
2499 elif self._type == "compound":
2500 for item in self._item:
2501 screen._delete(item)
2502 self._type = screen._shapes[shapeIndex]._type
2503 if self._type == "polygon":
2504 self._item = screen._createpoly()
2505 elif self._type == "image":
2506 self._item = screen._createimage(screen._shapes["blank"]._data)
2507 elif self._type == "compound":
2508 self._item = [screen._createpoly() for item in
2509 screen._shapes[shapeIndex]._data]
2510
2511
2512class RawTurtle(TPen, TNavigator):
2513 """Animation part of the RawTurtle.
2514 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002515 its animation.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002516 """
2517 screens = []
2518
2519 def __init__(self, canvas=None,
2520 shape=_CFG["shape"],
2521 undobuffersize=_CFG["undobuffersize"],
2522 visible=_CFG["visible"]):
Martin v. Löwis601149b2008-09-29 22:19:08 +00002523 if isinstance(canvas, _Screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002524 self.screen = canvas
2525 elif isinstance(canvas, TurtleScreen):
2526 if canvas not in RawTurtle.screens:
2527 RawTurtle.screens.append(canvas)
2528 self.screen = canvas
2529 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2530 for screen in RawTurtle.screens:
2531 if screen.cv == canvas:
2532 self.screen = screen
2533 break
2534 else:
2535 self.screen = TurtleScreen(canvas)
2536 RawTurtle.screens.append(self.screen)
2537 else:
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03002538 raise TurtleGraphicsError("bad canvas argument %s" % canvas)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002539
2540 screen = self.screen
2541 TNavigator.__init__(self, screen.mode())
2542 TPen.__init__(self)
2543 screen._turtles.append(self)
2544 self.drawingLineItem = screen._createline()
2545 self.turtle = _TurtleImage(screen, shape)
2546 self._poly = None
2547 self._creatingPoly = False
2548 self._fillitem = self._fillpath = None
2549 self._shown = visible
2550 self._hidden_from_screen = False
2551 self.currentLineItem = screen._createline()
2552 self.currentLine = [self._position]
2553 self.items = [self.currentLineItem]
2554 self.stampItems = []
2555 self._undobuffersize = undobuffersize
2556 self.undobuffer = Tbuffer(undobuffersize)
2557 self._update()
2558
2559 def reset(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002560 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002561
2562 No argument.
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002563
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002564 Delete the turtle's drawings from the screen, re-center the turtle
2565 and set variables to the default values.
2566
2567 Example (for a Turtle instance named turtle):
2568 >>> turtle.position()
2569 (0.00,-22.00)
2570 >>> turtle.heading()
2571 100.0
2572 >>> turtle.reset()
2573 >>> turtle.position()
2574 (0.00,0.00)
2575 >>> turtle.heading()
2576 0.0
2577 """
2578 TNavigator.reset(self)
2579 TPen._reset(self)
2580 self._clear()
2581 self._drawturtle()
2582 self._update()
2583
2584 def setundobuffer(self, size):
2585 """Set or disable undobuffer.
2586
2587 Argument:
2588 size -- an integer or None
2589
2590 If size is an integer an empty undobuffer of given size is installed.
2591 Size gives the maximum number of turtle-actions that can be undone
2592 by the undo() function.
2593 If size is None, no undobuffer is present.
2594
2595 Example (for a Turtle instance named turtle):
2596 >>> turtle.setundobuffer(42)
2597 """
Raymond Hettinger854e76e2014-07-20 21:30:32 -07002598 if size is None or size <= 0:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002599 self.undobuffer = None
2600 else:
2601 self.undobuffer = Tbuffer(size)
2602
2603 def undobufferentries(self):
2604 """Return count of entries in the undobuffer.
2605
2606 No argument.
2607
2608 Example (for a Turtle instance named turtle):
2609 >>> while undobufferentries():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002610 ... undo()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002611 """
2612 if self.undobuffer is None:
2613 return 0
2614 return self.undobuffer.nr_of_items()
2615
2616 def _clear(self):
2617 """Delete all of pen's drawings"""
2618 self._fillitem = self._fillpath = None
2619 for item in self.items:
2620 self.screen._delete(item)
2621 self.currentLineItem = self.screen._createline()
2622 self.currentLine = []
2623 if self._drawing:
2624 self.currentLine.append(self._position)
2625 self.items = [self.currentLineItem]
2626 self.clearstamps()
2627 self.setundobuffer(self._undobuffersize)
2628
2629
2630 def clear(self):
2631 """Delete the turtle's drawings from the screen. Do not move turtle.
2632
2633 No arguments.
2634
2635 Delete the turtle's drawings from the screen. Do not move turtle.
2636 State and position of the turtle as well as drawings of other
2637 turtles are not affected.
2638
2639 Examples (for a Turtle instance named turtle):
2640 >>> turtle.clear()
2641 """
2642 self._clear()
2643 self._update()
2644
2645 def _update_data(self):
2646 self.screen._incrementudc()
2647 if self.screen._updatecounter != 0:
2648 return
2649 if len(self.currentLine)>1:
2650 self.screen._drawline(self.currentLineItem, self.currentLine,
2651 self._pencolor, self._pensize)
2652
2653 def _update(self):
2654 """Perform a Turtle-data update.
2655 """
2656 screen = self.screen
2657 if screen._tracing == 0:
2658 return
2659 elif screen._tracing == 1:
2660 self._update_data()
2661 self._drawturtle()
2662 screen._update() # TurtleScreenBase
2663 screen._delay(screen._delayvalue) # TurtleScreenBase
2664 else:
2665 self._update_data()
2666 if screen._updatecounter == 0:
2667 for t in screen.turtles():
2668 t._drawturtle()
2669 screen._update()
2670
2671 def _tracer(self, flag=None, delay=None):
2672 """Turns turtle animation on/off and set delay for update drawings.
2673
2674 Optional arguments:
2675 n -- nonnegative integer
2676 delay -- nonnegative integer
2677
2678 If n is given, only each n-th regular screen update is really performed.
2679 (Can be used to accelerate the drawing of complex graphics.)
2680 Second arguments sets delay value (see RawTurtle.delay())
2681
2682 Example (for a Turtle instance named turtle):
2683 >>> turtle.tracer(8, 25)
2684 >>> dist = 2
2685 >>> for i in range(200):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002686 ... turtle.fd(dist)
2687 ... turtle.rt(90)
2688 ... dist += 2
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002689 """
2690 return self.screen.tracer(flag, delay)
2691
2692 def _color(self, args):
2693 return self.screen._color(args)
2694
2695 def _colorstr(self, args):
2696 return self.screen._colorstr(args)
2697
2698 def _cc(self, args):
2699 """Convert colortriples to hexstrings.
2700 """
2701 if isinstance(args, str):
2702 return args
2703 try:
2704 r, g, b = args
Serhiy Storchakacefa9172016-06-14 22:52:04 +03002705 except (TypeError, ValueError):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002706 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2707 if self.screen._colormode == 1.0:
2708 r, g, b = [round(255.0*x) for x in (r, g, b)]
2709 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2710 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2711 return "#%02x%02x%02x" % (r, g, b)
2712
2713 def clone(self):
2714 """Create and return a clone of the turtle.
2715
2716 No argument.
2717
2718 Create and return a clone of the turtle with same position, heading
2719 and turtle properties.
2720
2721 Example (for a Turtle instance named mick):
2722 mick = Turtle()
2723 joe = mick.clone()
2724 """
2725 screen = self.screen
2726 self._newLine(self._drawing)
2727
2728 turtle = self.turtle
2729 self.screen = None
2730 self.turtle = None # too make self deepcopy-able
2731
2732 q = deepcopy(self)
2733
2734 self.screen = screen
2735 self.turtle = turtle
2736
2737 q.screen = screen
2738 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2739
2740 screen._turtles.append(q)
2741 ttype = screen._shapes[self.turtle.shapeIndex]._type
2742 if ttype == "polygon":
2743 q.turtle._item = screen._createpoly()
2744 elif ttype == "image":
2745 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2746 elif ttype == "compound":
2747 q.turtle._item = [screen._createpoly() for item in
2748 screen._shapes[self.turtle.shapeIndex]._data]
2749 q.currentLineItem = screen._createline()
2750 q._update()
2751 return q
2752
2753 def shape(self, name=None):
2754 """Set turtle shape to shape with given name / return current shapename.
2755
2756 Optional argument:
2757 name -- a string, which is a valid shapename
2758
2759 Set turtle shape to shape with given name or, if name is not given,
2760 return name of current shape.
2761 Shape with name must exist in the TurtleScreen's shape dictionary.
2762 Initially there are the following polygon shapes:
2763 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2764 To learn about how to deal with shapes see Screen-method register_shape.
2765
2766 Example (for a Turtle instance named turtle):
2767 >>> turtle.shape()
2768 'arrow'
2769 >>> turtle.shape("turtle")
2770 >>> turtle.shape()
2771 'turtle'
2772 """
2773 if name is None:
2774 return self.turtle.shapeIndex
2775 if not name in self.screen.getshapes():
2776 raise TurtleGraphicsError("There is no shape named %s" % name)
2777 self.turtle._setshape(name)
2778 self._update()
2779
2780 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2781 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2782
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03002783 Optional arguments:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002784 stretch_wid : positive number
2785 stretch_len : positive number
2786 outline : positive number
2787
2788 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2789 Set resizemode to "user".
2790 If and only if resizemode is set to "user", the turtle will be displayed
2791 stretched according to its stretchfactors:
2792 stretch_wid is stretchfactor perpendicular to orientation
2793 stretch_len is stretchfactor in direction of turtles orientation.
2794 outline determines the width of the shapes's outline.
2795
2796 Examples (for a Turtle instance named turtle):
2797 >>> turtle.resizemode("user")
2798 >>> turtle.shapesize(5, 5, 12)
2799 >>> turtle.shapesize(outline=8)
2800 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00002801 if stretch_wid is stretch_len is outline is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002802 stretch_wid, stretch_len = self._stretchfactor
2803 return stretch_wid, stretch_len, self._outlinewidth
Georg Brandleaa84ef2009-05-05 08:14:33 +00002804 if stretch_wid == 0 or stretch_len == 0:
2805 raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero")
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002806 if stretch_wid is not None:
2807 if stretch_len is None:
2808 stretchfactor = stretch_wid, stretch_wid
2809 else:
2810 stretchfactor = stretch_wid, stretch_len
2811 elif stretch_len is not None:
2812 stretchfactor = self._stretchfactor[0], stretch_len
2813 else:
2814 stretchfactor = self._stretchfactor
2815 if outline is None:
2816 outline = self._outlinewidth
2817 self.pen(resizemode="user",
2818 stretchfactor=stretchfactor, outline=outline)
2819
Georg Brandleaa84ef2009-05-05 08:14:33 +00002820 def shearfactor(self, shear=None):
2821 """Set or return the current shearfactor.
2822
2823 Optional argument: shear -- number, tangent of the shear angle
2824
2825 Shear the turtleshape according to the given shearfactor shear,
2826 which is the tangent of the shear angle. DO NOT change the
2827 turtle's heading (direction of movement).
2828 If shear is not given: return the current shearfactor, i. e. the
2829 tangent of the shear angle, by which lines parallel to the
2830 heading of the turtle are sheared.
2831
2832 Examples (for a Turtle instance named turtle):
2833 >>> turtle.shape("circle")
2834 >>> turtle.shapesize(5,2)
2835 >>> turtle.shearfactor(0.5)
2836 >>> turtle.shearfactor()
2837 >>> 0.5
2838 """
2839 if shear is None:
2840 return self._shearfactor
2841 self.pen(resizemode="user", shearfactor=shear)
2842
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002843 def settiltangle(self, angle):
2844 """Rotate the turtleshape to point in the specified direction
2845
Georg Brandleaa84ef2009-05-05 08:14:33 +00002846 Argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002847
2848 Rotate the turtleshape to point in the direction specified by angle,
2849 regardless of its current tilt-angle. DO NOT change the turtle's
2850 heading (direction of movement).
2851
2852
2853 Examples (for a Turtle instance named turtle):
2854 >>> turtle.shape("circle")
2855 >>> turtle.shapesize(5,2)
2856 >>> turtle.settiltangle(45)
2857 >>> stamp()
2858 >>> turtle.fd(50)
2859 >>> turtle.settiltangle(-45)
2860 >>> stamp()
2861 >>> turtle.fd(50)
2862 """
2863 tilt = -angle * self._degreesPerAU * self._angleOrient
2864 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2865 self.pen(resizemode="user", tilt=tilt)
2866
Georg Brandleaa84ef2009-05-05 08:14:33 +00002867 def tiltangle(self, angle=None):
2868 """Set or return the current tilt-angle.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002869
Georg Brandleaa84ef2009-05-05 08:14:33 +00002870 Optional argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002871
Georg Brandleaa84ef2009-05-05 08:14:33 +00002872 Rotate the turtleshape to point in the direction specified by angle,
2873 regardless of its current tilt-angle. DO NOT change the turtle's
2874 heading (direction of movement).
2875 If angle is not given: return the current tilt-angle, i. e. the angle
2876 between the orientation of the turtleshape and the heading of the
2877 turtle (its direction of movement).
2878
2879 Deprecated since Python 3.1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002880
2881 Examples (for a Turtle instance named turtle):
2882 >>> turtle.shape("circle")
2883 >>> turtle.shapesize(5,2)
2884 >>> turtle.tilt(45)
2885 >>> turtle.tiltangle()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002886 """
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()
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002930 (4.0, -1.0, -0.0, 2.0)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002931 """
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
Raymond Hettinger6dec4ea2014-06-22 01:21:51 -07002949 self.pen(resizemode="user")
Georg Brandleaa84ef2009-05-05 08:14:33 +00002950
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
Jon Dufresne39726282017-05-18 07:35:54 -07002992 return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002993
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):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003128 ... turtle.stamp(); turtle.fd(30)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003129 ...
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
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03003146 if pen is down. All other methods for turtle movement depend
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003147 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():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003304 ... turtle.pensize(5)
3305 ... else:
3306 ... turtle.pensize(3)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003307 """
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):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003536 ... left(360)
3537 ...
3538 >>> onclick(turn) # Now clicking into the turtle will turn it.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003539 >>> 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):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003554 ... def glow(self,x,y):
3555 ... self.fillcolor("red")
3556 ... def unglow(self,x,y):
3557 ... self.fillcolor("")
3558 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003559 >>> joe = MyTurtle()
3560 >>> joe.onclick(joe.glow)
3561 >>> joe.onrelease(joe.unglow)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003562
3563 Clicking on joe turns fillcolor red, unclicking turns it to
3564 transparent.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003565 """
3566 self.screen._onrelease(self.turtle._item, fun, btn, add)
3567 self._update()
3568
3569 def ondrag(self, fun, btn=1, add=None):
3570 """Bind fun to mouse-move event on this turtle on canvas.
3571
3572 Arguments:
3573 fun -- a function with two arguments, to which will be assigned
3574 the coordinates of the clicked point on the canvas.
3575 num -- number of the mouse-button defaults to 1 (left mouse button).
3576
3577 Every sequence of mouse-move-events on a turtle is preceded by a
3578 mouse-click event on that turtle.
3579
3580 Example (for a Turtle instance named turtle):
3581 >>> turtle.ondrag(turtle.goto)
3582
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003583 Subsequently clicking and dragging a Turtle will move it
3584 across the screen thereby producing handdrawings (if pen is
3585 down).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003586 """
3587 self.screen._ondrag(self.turtle._item, fun, btn, add)
3588
3589
3590 def _undo(self, action, data):
3591 """Does the main part of the work for undo()
3592 """
3593 if self.undobuffer is None:
3594 return
3595 if action == "rot":
3596 angle, degPAU = data
3597 self._rotate(-angle*degPAU/self._degreesPerAU)
3598 dummy = self.undobuffer.pop()
3599 elif action == "stamp":
3600 stitem = data[0]
3601 self.clearstamp(stitem)
3602 elif action == "go":
3603 self._undogoto(data)
3604 elif action in ["wri", "dot"]:
3605 item = data[0]
3606 self.screen._delete(item)
3607 self.items.remove(item)
3608 elif action == "dofill":
3609 item = data[0]
3610 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3611 fill="", outline="")
3612 elif action == "beginfill":
3613 item = data[0]
3614 self._fillitem = self._fillpath = None
3615 if item in self.items:
3616 self.screen._delete(item)
3617 self.items.remove(item)
3618 elif action == "pen":
3619 TPen.pen(self, data[0])
3620 self.undobuffer.pop()
3621
3622 def undo(self):
3623 """undo (repeatedly) the last turtle action.
3624
3625 No argument.
3626
3627 undo (repeatedly) the last turtle action.
3628 Number of available undo actions is determined by the size of
3629 the undobuffer.
3630
3631 Example (for a Turtle instance named turtle):
3632 >>> for i in range(4):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003633 ... turtle.fd(50); turtle.lt(80)
3634 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003635 >>> for i in range(8):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003636 ... turtle.undo()
3637 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003638 """
3639 if self.undobuffer is None:
3640 return
3641 item = self.undobuffer.pop()
3642 action = item[0]
3643 data = item[1:]
3644 if action == "seq":
3645 while data:
3646 item = data.pop()
3647 self._undo(item[0], item[1:])
3648 else:
3649 self._undo(action, data)
3650
3651 turtlesize = shapesize
3652
3653RawPen = RawTurtle
3654
Martin v. Löwis601149b2008-09-29 22:19:08 +00003655### Screen - Singleton ########################
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003656
Martin v. Löwis601149b2008-09-29 22:19:08 +00003657def Screen():
3658 """Return the singleton screen object.
3659 If none exists at the moment, create a new one and return it,
3660 else return the existing one."""
3661 if Turtle._screen is None:
3662 Turtle._screen = _Screen()
3663 return Turtle._screen
3664
3665class _Screen(TurtleScreen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003666
3667 _root = None
3668 _canvas = None
3669 _title = _CFG["title"]
3670
Guido van Rossumb241b671998-12-04 16:42:46 +00003671 def __init__(self):
Martin v. Löwis601149b2008-09-29 22:19:08 +00003672 # XXX there is no need for this code to be conditional,
3673 # as there will be only a single _Screen instance, anyway
3674 # XXX actually, the turtle demo is injecting root window,
3675 # so perhaps the conditional creation of a root should be
3676 # preserved (perhaps by passing it as an optional parameter)
3677 if _Screen._root is None:
3678 _Screen._root = self._root = _Root()
3679 self._root.title(_Screen._title)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003680 self._root.ondestroy(self._destroy)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003681 if _Screen._canvas is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003682 width = _CFG["width"]
3683 height = _CFG["height"]
3684 canvwidth = _CFG["canvwidth"]
3685 canvheight = _CFG["canvheight"]
3686 leftright = _CFG["leftright"]
3687 topbottom = _CFG["topbottom"]
3688 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003689 _Screen._canvas = self._root._getcanvas()
Martin v. Löwis601149b2008-09-29 22:19:08 +00003690 TurtleScreen.__init__(self, _Screen._canvas)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003691 self.setup(width, height, leftright, topbottom)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003692
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003693 def setup(self, width=_CFG["width"], height=_CFG["height"],
3694 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3695 """ Set the size and position of the main window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003696
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003697 Arguments:
3698 width: as integer a size in pixels, as float a fraction of the screen.
3699 Default is 50% of screen.
3700 height: as integer the height in pixels, as float a fraction of the
3701 screen. Default is 75% of screen.
3702 startx: if positive, starting position in pixels from the left
3703 edge of the screen, if negative from the right edge
3704 Default, startx=None is to center window horizontally.
3705 starty: if positive, starting position in pixels from the top
3706 edge of the screen, if negative from the bottom edge
3707 Default, starty=None is to center window vertically.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003708
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003709 Examples (for a Screen instance named screen):
3710 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3711
3712 sets window to 200x200 pixels, in upper left of screen
3713
3714 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3715
3716 sets window to 75% of screen by 50% of screen and centers
3717 """
3718 if not hasattr(self._root, "set_geometry"):
3719 return
3720 sw = self._root.win_width()
3721 sh = self._root.win_height()
3722 if isinstance(width, float) and 0 <= width <= 1:
3723 width = sw*width
3724 if startx is None:
3725 startx = (sw - width) / 2
3726 if isinstance(height, float) and 0 <= height <= 1:
3727 height = sh*height
3728 if starty is None:
3729 starty = (sh - height) / 2
3730 self._root.set_geometry(width, height, startx, starty)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003731 self.update()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003732
3733 def title(self, titlestring):
3734 """Set title of turtle-window
3735
3736 Argument:
3737 titlestring -- a string, to appear in the titlebar of the
3738 turtle graphics window.
3739
3740 This is a method of Screen-class. Not available for TurtleScreen-
3741 objects.
3742
3743 Example (for a Screen instance named screen):
3744 >>> screen.title("Welcome to the turtle-zoo!")
3745 """
Martin v. Löwis601149b2008-09-29 22:19:08 +00003746 if _Screen._root is not None:
3747 _Screen._root.title(titlestring)
3748 _Screen._title = titlestring
Guido van Rossumb241b671998-12-04 16:42:46 +00003749
3750 def _destroy(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003751 root = self._root
Martin v. Löwis601149b2008-09-29 22:19:08 +00003752 if root is _Screen._root:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003753 Turtle._pen = None
3754 Turtle._screen = None
Martin v. Löwis601149b2008-09-29 22:19:08 +00003755 _Screen._root = None
3756 _Screen._canvas = None
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003757 TurtleScreen._RUNNING = False
Guido van Rossumb241b671998-12-04 16:42:46 +00003758 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +00003759
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003760 def bye(self):
3761 """Shut the turtlegraphics window.
3762
3763 Example (for a TurtleScreen instance named screen):
3764 >>> screen.bye()
3765 """
3766 self._destroy()
3767
3768 def exitonclick(self):
3769 """Go into mainloop until the mouse is clicked.
3770
3771 No arguments.
3772
3773 Bind bye() method to mouseclick on TurtleScreen.
3774 If "using_IDLE" - value in configuration dictionary is False
3775 (default value), enter mainloop.
3776 If IDLE with -n switch (no subprocess) is used, this value should be
3777 set to True in turtle.cfg. In this case IDLE's mainloop
3778 is active also for the client script.
3779
3780 This is a method of the Screen-class and not available for
3781 TurtleScreen instances.
3782
3783 Example (for a Screen instance named screen):
3784 >>> screen.exitonclick()
3785
3786 """
3787 def exitGracefully(x, y):
3788 """Screen.bye() with two dummy-parameters"""
3789 self.bye()
3790 self.onclick(exitGracefully)
3791 if _CFG["using_IDLE"]:
3792 return
3793 try:
3794 mainloop()
3795 except AttributeError:
3796 exit(0)
3797
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003798class Turtle(RawTurtle):
Ezio Melotti13925002011-03-16 11:05:33 +02003799 """RawTurtle auto-creating (scrolled) canvas.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003800
3801 When a Turtle object is created or a function derived from some
3802 Turtle method is called a TurtleScreen object is automatically created.
3803 """
3804 _pen = None
3805 _screen = None
3806
3807 def __init__(self,
3808 shape=_CFG["shape"],
3809 undobuffersize=_CFG["undobuffersize"],
3810 visible=_CFG["visible"]):
3811 if Turtle._screen is None:
3812 Turtle._screen = Screen()
3813 RawTurtle.__init__(self, Turtle._screen,
3814 shape=shape,
3815 undobuffersize=undobuffersize,
3816 visible=visible)
3817
3818Pen = Turtle
3819
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003820def write_docstringdict(filename="turtle_docstringdict"):
3821 """Create and write docstring-dictionary to file.
Guido van Rossumb241b671998-12-04 16:42:46 +00003822
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003823 Optional argument:
3824 filename -- a string, used as filename
3825 default value is turtle_docstringdict
Thomas Wouters477c8d52006-05-27 19:21:47 +00003826
Ezio Melotti13925002011-03-16 11:05:33 +02003827 Has to be called explicitly, (not used by the turtle-graphics classes)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003828 The docstring dictionary will be written to the Python script <filname>.py
3829 It is intended to serve as a template for translation of the docstrings
3830 into different languages.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003831 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003832 docsdict = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +00003833
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003834 for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003835 key = "_Screen."+methodname
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003836 docsdict[key] = eval(key).__doc__
3837 for methodname in _tg_turtle_functions:
3838 key = "Turtle."+methodname
3839 docsdict[key] = eval(key).__doc__
Thomas Wouters477c8d52006-05-27 19:21:47 +00003840
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003841 with open("%s.py" % filename,"w") as f:
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +02003842 keys = sorted(x for x in docsdict
Jon Dufresne39726282017-05-18 07:35:54 -07003843 if x.split('.')[1] not in _alias_list)
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003844 f.write('docsdict = {\n\n')
3845 for key in keys[:-1]:
3846 f.write('%s :\n' % repr(key))
3847 f.write(' """%s\n""",\n\n' % docsdict[key])
3848 key = keys[-1]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003849 f.write('%s :\n' % repr(key))
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003850 f.write(' """%s\n"""\n\n' % docsdict[key])
3851 f.write("}\n")
3852 f.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00003853
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003854def read_docstrings(lang):
3855 """Read in docstrings from lang-specific docstring dictionary.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003856
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003857 Transfer docstrings, translated to lang, from a dictionary-file
3858 to the methods of classes Screen and Turtle and - in revised form -
3859 to the corresponding functions.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003860 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003861 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3862 module = __import__(modname)
3863 docsdict = module.docsdict
3864 for key in docsdict:
3865 try:
3866# eval(key).im_func.__doc__ = docsdict[key]
3867 eval(key).__doc__ = docsdict[key]
Serhiy Storchakacefa9172016-06-14 22:52:04 +03003868 except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003869 print("Bad docstring-entry: %s" % key)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003870
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003871_LANGUAGE = _CFG["language"]
Guido van Rossumb241b671998-12-04 16:42:46 +00003872
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003873try:
3874 if _LANGUAGE != "english":
3875 read_docstrings(_LANGUAGE)
3876except ImportError:
3877 print("Cannot find docsdict for", _LANGUAGE)
Serhiy Storchakacefa9172016-06-14 22:52:04 +03003878except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003879 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3880 _LANGUAGE)
3881
3882
3883def getmethparlist(ob):
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003884 """Get strings describing the arguments for the given object
3885
3886 Returns a pair of strings representing function parameter lists
3887 including parenthesis. The first string is suitable for use in
3888 function definition and the second is suitable for use in function
3889 call. The "self" parameter is not included.
3890 """
3891 defText = callText = ""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003892 # bit of a hack for methods - turn it into a function
3893 # but we drop the "self" param.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003894 # Try and build one for Python defined functions
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003895 args, varargs, varkw = inspect.getargs(ob.__code__)
3896 items2 = args[1:]
3897 realArgs = args[1:]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003898 defaults = ob.__defaults__ or []
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003899 defaults = ["=%r" % (value,) for value in defaults]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003900 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003901 items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3902 if varargs is not None:
3903 items1.append("*" + varargs)
3904 items2.append("*" + varargs)
3905 if varkw is not None:
3906 items1.append("**" + varkw)
3907 items2.append("**" + varkw)
3908 defText = ", ".join(items1)
3909 defText = "(%s)" % defText
3910 callText = ", ".join(items2)
3911 callText = "(%s)" % callText
3912 return defText, callText
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003913
3914def _turtle_docrevise(docstr):
3915 """To reduce docstrings from RawTurtle class for functions
3916 """
3917 import re
3918 if docstr is None:
3919 return None
3920 turtlename = _CFG["exampleturtle"]
3921 newdocstr = docstr.replace("%s." % turtlename,"")
3922 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3923 newdocstr = parexp.sub(":", newdocstr)
3924 return newdocstr
3925
3926def _screen_docrevise(docstr):
3927 """To reduce docstrings from TurtleScreen class for functions
3928 """
3929 import re
3930 if docstr is None:
3931 return None
3932 screenname = _CFG["examplescreen"]
3933 newdocstr = docstr.replace("%s." % screenname,"")
3934 parexp = re.compile(r' \(.+ %s\):' % screenname)
3935 newdocstr = parexp.sub(":", newdocstr)
3936 return newdocstr
3937
3938## The following mechanism makes all methods of RawTurtle and Turtle available
3939## as functions. So we can enhance, change, add, delete methods to these
3940## classes and do not need to change anything here.
3941
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003942__func_body = """\
3943def {name}{paramslist}:
3944 if {obj} is None:
3945 if not TurtleScreen._RUNNING:
3946 TurtleScreen._RUNNING = True
3947 raise Terminator
3948 {obj} = {init}
3949 try:
3950 return {obj}.{name}{argslist}
3951 except TK.TclError:
3952 if not TurtleScreen._RUNNING:
3953 TurtleScreen._RUNNING = True
3954 raise Terminator
3955 raise
3956"""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003957
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003958def _make_global_funcs(functions, cls, obj, init, docrevise):
3959 for methodname in functions:
3960 method = getattr(cls, methodname)
3961 pl1, pl2 = getmethparlist(method)
3962 if pl1 == "":
3963 print(">>>>>>", pl1, pl2)
3964 continue
3965 defstr = __func_body.format(obj=obj, init=init, name=methodname,
3966 paramslist=pl1, argslist=pl2)
3967 exec(defstr, globals())
3968 globals()[methodname].__doc__ = docrevise(method.__doc__)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003969
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003970_make_global_funcs(_tg_screen_functions, _Screen,
3971 'Turtle._screen', 'Screen()', _screen_docrevise)
3972_make_global_funcs(_tg_turtle_functions, Turtle,
3973 'Turtle._pen', 'Turtle()', _turtle_docrevise)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003974
3975
Georg Brandleaa84ef2009-05-05 08:14:33 +00003976done = mainloop
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003977
3978if __name__ == "__main__":
3979 def switchpen():
3980 if isdown():
3981 pu()
3982 else:
3983 pd()
3984
3985 def demo1():
3986 """Demo of old turtle.py - module"""
3987 reset()
3988 tracer(True)
3989 up()
3990 backward(100)
3991 down()
3992 # draw 3 squares; the last filled
3993 width(3)
3994 for i in range(3):
3995 if i == 2:
3996 begin_fill()
3997 for _ in range(4):
3998 forward(20)
3999 left(90)
4000 if i == 2:
4001 color("maroon")
4002 end_fill()
4003 up()
4004 forward(30)
4005 down()
4006 width(1)
4007 color("black")
4008 # move out of the way
4009 tracer(False)
4010 up()
4011 right(90)
4012 forward(100)
4013 right(90)
4014 forward(100)
4015 right(180)
4016 down()
4017 # some text
4018 write("startstart", 1)
4019 write("start", 1)
4020 color("red")
4021 # staircase
4022 for i in range(5):
Guido van Rossumb241b671998-12-04 16:42:46 +00004023 forward(20)
4024 left(90)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004025 forward(20)
4026 right(90)
4027 # filled staircase
4028 tracer(True)
4029 begin_fill()
4030 for i in range(5):
4031 forward(20)
4032 left(90)
4033 forward(20)
4034 right(90)
4035 end_fill()
4036 # more text
Thomas Wouters477c8d52006-05-27 19:21:47 +00004037
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004038 def demo2():
4039 """Demo of some new features."""
4040 speed(1)
4041 st()
4042 pensize(3)
4043 setheading(towards(0, 0))
4044 radius = distance(0, 0)/2.0
4045 rt(90)
4046 for _ in range(18):
4047 switchpen()
4048 circle(radius, 10)
4049 write("wait a moment...")
4050 while undobufferentries():
4051 undo()
4052 reset()
4053 lt(90)
4054 colormode(255)
4055 laenge = 10
4056 pencolor("green")
4057 pensize(3)
4058 lt(180)
4059 for i in range(-2, 16):
4060 if i > 0:
4061 begin_fill()
4062 fillcolor(255-15*i, 0, 15*i)
4063 for _ in range(3):
4064 fd(laenge)
4065 lt(120)
4066 end_fill()
4067 laenge += 10
4068 lt(15)
4069 speed((speed()+1)%12)
4070 #end_fill()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004071
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004072 lt(120)
4073 pu()
4074 fd(70)
4075 rt(30)
4076 pd()
4077 color("red","yellow")
4078 speed(0)
4079 begin_fill()
4080 for _ in range(4):
4081 circle(50, 90)
4082 rt(90)
4083 fd(30)
4084 rt(90)
4085 end_fill()
4086 lt(90)
4087 pu()
4088 fd(30)
4089 pd()
4090 shape("turtle")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004091
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004092 tri = getturtle()
4093 tri.resizemode("auto")
4094 turtle = Turtle()
4095 turtle.resizemode("auto")
4096 turtle.shape("turtle")
4097 turtle.reset()
4098 turtle.left(90)
4099 turtle.speed(0)
4100 turtle.up()
4101 turtle.goto(280, 40)
4102 turtle.lt(30)
4103 turtle.down()
4104 turtle.speed(6)
4105 turtle.color("blue","orange")
4106 turtle.pensize(2)
4107 tri.speed(6)
Thomas Wouters477c8d52006-05-27 19:21:47 +00004108 setheading(towards(turtle))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004109 count = 1
4110 while tri.distance(turtle) > 4:
4111 turtle.fd(3.5)
4112 turtle.lt(0.6)
4113 tri.setheading(tri.towards(turtle))
4114 tri.fd(4)
4115 if count % 20 == 0:
4116 turtle.stamp()
4117 tri.stamp()
4118 switchpen()
4119 count += 1
4120 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4121 tri.pencolor("black")
4122 tri.pencolor("red")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004123
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004124 def baba(xdummy, ydummy):
4125 clearscreen()
4126 bye()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004127
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004128 time.sleep(2)
Guido van Rossumb241b671998-12-04 16:42:46 +00004129
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004130 while undobufferentries():
4131 tri.undo()
4132 turtle.undo()
4133 tri.fd(50)
4134 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4135 tri.onclick(baba, 1)
4136
4137 demo1()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004138 demo2()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004139 exitonclick()