blob: 81cfcfe8a701441df86842415ef0f604bce3bd9a [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)
Serhiy Storchakafd4cafd2020-09-07 18:55:22 +0300261 return NotImplemented
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000262 def __sub__(self, other):
263 return Vec2D(self[0]-other[0], self[1]-other[1])
264 def __neg__(self):
265 return Vec2D(-self[0], -self[1])
266 def __abs__(self):
Marek Madejski6844b562020-09-01 18:42:41 +0200267 return math.hypot(*self)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000268 def rotate(self, angle):
269 """rotate self counterclockwise by angle
270 """
271 perp = Vec2D(-self[1], self[0])
Marek Madejski6844b562020-09-01 18:42:41 +0200272 angle = math.radians(angle)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000273 c, s = math.cos(angle), math.sin(angle)
274 return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
275 def __getnewargs__(self):
276 return (self[0], self[1])
277 def __repr__(self):
278 return "(%.2f,%.2f)" % self
279
280
281##############################################################################
282### From here up to line : Tkinter - Interface for turtle.py ###
Mark Dickinsonf8798f52009-02-20 20:53:56 +0000283### May be replaced by an interface to some different graphics toolkit ###
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000284##############################################################################
285
286## helper functions for Scrolled Canvas, to forward Canvas-methods
287## to ScrolledCanvas class
288
289def __methodDict(cls, _dict):
290 """helper function for Scrolled Canvas"""
291 baseList = list(cls.__bases__)
292 baseList.reverse()
293 for _super in baseList:
294 __methodDict(_super, _dict)
295 for key, value in cls.__dict__.items():
296 if type(value) == types.FunctionType:
297 _dict[key] = value
298
299def __methods(cls):
300 """helper function for Scrolled Canvas"""
301 _dict = {}
302 __methodDict(cls, _dict)
303 return _dict.keys()
304
305__stringBody = (
306 'def %(method)s(self, *args, **kw): return ' +
307 'self.%(attribute)s.%(method)s(*args, **kw)')
308
309def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
310 ### MANY CHANGES ###
311 _dict_1 = {}
312 __methodDict(toClass, _dict_1)
313 _dict = {}
314 mfc = __methods(fromClass)
315 for ex in _dict_1.keys():
316 if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
317 pass
318 else:
319 _dict[ex] = _dict_1[ex]
320
321 for method, func in _dict.items():
322 d = {'method': method, 'func': func}
323 if isinstance(toPart, str):
324 execString = \
325 __stringBody % {'method' : method, 'attribute' : toPart}
326 exec(execString, d)
327 setattr(fromClass, method, d[method]) ### NEWU!
328
329
330class ScrolledCanvas(TK.Frame):
331 """Modeled after the scrolled canvas class from Grayons's Tkinter book.
332
333 Used as the default canvas, which pops up automatically when
334 using turtle graphics functions or the Turtle class.
335 """
336 def __init__(self, master, width=500, height=350,
337 canvwidth=600, canvheight=500):
338 TK.Frame.__init__(self, master, width=width, height=height)
Martin v. Löwis22d297b2008-11-19 09:14:30 +0000339 self._rootwindow = self.winfo_toplevel()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000340 self.width, self.height = width, height
341 self.canvwidth, self.canvheight = canvwidth, canvheight
342 self.bg = "white"
343 self._canvas = TK.Canvas(master, width=width, height=height,
344 bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
345 self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
346 orient=TK.HORIZONTAL)
347 self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
348 self._canvas.configure(xscrollcommand=self.hscroll.set,
349 yscrollcommand=self.vscroll.set)
350 self.rowconfigure(0, weight=1, minsize=0)
351 self.columnconfigure(0, weight=1, minsize=0)
352 self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
353 column=0, rowspan=1, columnspan=1, sticky='news')
354 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
355 column=1, rowspan=1, columnspan=1, sticky='news')
356 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
357 column=0, rowspan=1, columnspan=1, sticky='news')
358 self.reset()
Martin v. Löwis22d297b2008-11-19 09:14:30 +0000359 self._rootwindow.bind('<Configure>', self.onResize)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000360
361 def reset(self, canvwidth=None, canvheight=None, bg = None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +0000362 """Adjust canvas and scrollbars according to given canvas size."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000363 if canvwidth:
364 self.canvwidth = canvwidth
365 if canvheight:
366 self.canvheight = canvheight
367 if bg:
368 self.bg = bg
369 self._canvas.config(bg=bg,
370 scrollregion=(-self.canvwidth//2, -self.canvheight//2,
371 self.canvwidth//2, self.canvheight//2))
372 self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
373 self.canvwidth)
374 self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
375 self.canvheight)
376 self.adjustScrolls()
377
378
379 def adjustScrolls(self):
380 """ Adjust scrollbars according to window- and canvas-size.
381 """
382 cwidth = self._canvas.winfo_width()
383 cheight = self._canvas.winfo_height()
384 self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
385 self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
386 if cwidth < self.canvwidth or cheight < self.canvheight:
387 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
388 column=0, rowspan=1, columnspan=1, sticky='news')
389 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
390 column=1, rowspan=1, columnspan=1, sticky='news')
391 else:
392 self.hscroll.grid_forget()
393 self.vscroll.grid_forget()
394
395 def onResize(self, event):
396 """self-explanatory"""
397 self.adjustScrolls()
398
399 def bbox(self, *args):
400 """ 'forward' method, which canvas itself has inherited...
401 """
402 return self._canvas.bbox(*args)
403
404 def cget(self, *args, **kwargs):
405 """ 'forward' method, which canvas itself has inherited...
406 """
407 return self._canvas.cget(*args, **kwargs)
408
409 def config(self, *args, **kwargs):
410 """ 'forward' method, which canvas itself has inherited...
411 """
412 self._canvas.config(*args, **kwargs)
413
414 def bind(self, *args, **kwargs):
415 """ 'forward' method, which canvas itself has inherited...
416 """
417 self._canvas.bind(*args, **kwargs)
418
419 def unbind(self, *args, **kwargs):
420 """ 'forward' method, which canvas itself has inherited...
421 """
422 self._canvas.unbind(*args, **kwargs)
423
424 def focus_force(self):
425 """ 'forward' method, which canvas itself has inherited...
426 """
427 self._canvas.focus_force()
428
429__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
430
431
432class _Root(TK.Tk):
433 """Root class for Screen based on Tkinter."""
434 def __init__(self):
435 TK.Tk.__init__(self)
436
437 def setupcanvas(self, width, height, cwidth, cheight):
438 self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
439 self._canvas.pack(expand=1, fill="both")
440
441 def _getcanvas(self):
442 return self._canvas
443
444 def set_geometry(self, width, height, startx, starty):
445 self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
446
447 def ondestroy(self, destroy):
448 self.wm_protocol("WM_DELETE_WINDOW", destroy)
449
450 def win_width(self):
451 return self.winfo_screenwidth()
452
453 def win_height(self):
454 return self.winfo_screenheight()
455
456Canvas = TK.Canvas
457
458
459class TurtleScreenBase(object):
460 """Provide the basic graphics functionality.
461 Interface between Tkinter and turtle.py.
462
463 To port turtle.py to some different graphics toolkit
464 a corresponding TurtleScreenBase class has to be implemented.
465 """
466
467 @staticmethod
468 def _blankimage():
469 """return a blank image object
470 """
471 img = TK.PhotoImage(width=1, height=1)
472 img.blank()
473 return img
474
475 @staticmethod
476 def _image(filename):
477 """return an image object containing the
478 imagedata from a gif-file named filename.
479 """
480 return TK.PhotoImage(file=filename)
481
482 def __init__(self, cv):
483 self.cv = cv
484 if isinstance(cv, ScrolledCanvas):
485 w = self.cv.canvwidth
486 h = self.cv.canvheight
487 else: # expected: ordinary TK.Canvas
488 w = int(self.cv.cget("width"))
489 h = int(self.cv.cget("height"))
490 self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
491 self.canvwidth = w
492 self.canvheight = h
493 self.xscale = self.yscale = 1.0
494
495 def _createpoly(self):
496 """Create an invisible polygon item on canvas self.cv)
497 """
498 return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
499
500 def _drawpoly(self, polyitem, coordlist, fill=None,
501 outline=None, width=None, top=False):
502 """Configure polygonitem polyitem according to provided
503 arguments:
504 coordlist is sequence of coordinates
505 fill is filling color
506 outline is outline color
507 top is a boolean value, which specifies if polyitem
508 will be put on top of the canvas' displaylist so it
509 will not be covered by other items.
510 """
511 cl = []
512 for x, y in coordlist:
513 cl.append(x * self.xscale)
514 cl.append(-y * self.yscale)
515 self.cv.coords(polyitem, *cl)
516 if fill is not None:
517 self.cv.itemconfigure(polyitem, fill=fill)
518 if outline is not None:
519 self.cv.itemconfigure(polyitem, outline=outline)
520 if width is not None:
521 self.cv.itemconfigure(polyitem, width=width)
522 if top:
523 self.cv.tag_raise(polyitem)
524
525 def _createline(self):
526 """Create an invisible line item on canvas self.cv)
527 """
528 return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
529 capstyle = TK.ROUND)
530
531 def _drawline(self, lineitem, coordlist=None,
532 fill=None, width=None, top=False):
533 """Configure lineitem according to provided arguments:
534 coordlist is sequence of coordinates
535 fill is drawing color
536 width is width of drawn line.
537 top is a boolean value, which specifies if polyitem
538 will be put on top of the canvas' displaylist so it
539 will not be covered by other items.
540 """
541 if coordlist is not None:
542 cl = []
543 for x, y in coordlist:
544 cl.append(x * self.xscale)
545 cl.append(-y * self.yscale)
546 self.cv.coords(lineitem, *cl)
547 if fill is not None:
548 self.cv.itemconfigure(lineitem, fill=fill)
549 if width is not None:
550 self.cv.itemconfigure(lineitem, width=width)
551 if top:
552 self.cv.tag_raise(lineitem)
553
554 def _delete(self, item):
555 """Delete graphics item from canvas.
556 If item is"all" delete all graphics items.
557 """
558 self.cv.delete(item)
559
560 def _update(self):
561 """Redraw graphics items on canvas
562 """
563 self.cv.update()
564
565 def _delay(self, delay):
566 """Delay subsequent canvas actions for delay ms."""
567 self.cv.after(delay)
568
569 def _iscolorstring(self, color):
570 """Check if the string color is a legal Tkinter color string.
571 """
572 try:
573 rgb = self.cv.winfo_rgb(color)
574 ok = True
575 except TK.TclError:
576 ok = False
577 return ok
578
579 def _bgcolor(self, color=None):
580 """Set canvas' backgroundcolor if color is not None,
581 else return backgroundcolor."""
582 if color is not None:
583 self.cv.config(bg = color)
584 self._update()
585 else:
586 return self.cv.cget("bg")
587
588 def _write(self, pos, txt, align, font, pencolor):
589 """Write txt at pos in canvas with specified font
590 and color.
591 Return text item and x-coord of right bottom corner
592 of text's bounding box."""
593 x, y = pos
594 x = x * self.xscale
595 y = y * self.yscale
596 anchor = {"left":"sw", "center":"s", "right":"se" }
597 item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
598 fill = pencolor, font = font)
599 x0, y0, x1, y1 = self.cv.bbox(item)
600 self.cv.update()
601 return item, x1-1
602
603## def _dot(self, pos, size, color):
604## """may be implemented for some other graphics toolkit"""
605
606 def _onclick(self, item, fun, num=1, add=None):
607 """Bind fun to mouse-click event on turtle.
608 fun must be a function with two arguments, the coordinates
609 of the clicked point on the canvas.
610 num, the number of the mouse-button defaults to 1
611 """
612 if fun is None:
613 self.cv.tag_unbind(item, "<Button-%s>" % num)
614 else:
615 def eventfun(event):
616 x, y = (self.cv.canvasx(event.x)/self.xscale,
617 -self.cv.canvasy(event.y)/self.yscale)
618 fun(x, y)
619 self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
620
621 def _onrelease(self, item, fun, num=1, add=None):
622 """Bind fun to mouse-button-release event on turtle.
623 fun must be a function with two arguments, the coordinates
624 of the point on the canvas where mouse button is released.
625 num, the number of the mouse-button defaults to 1
626
627 If a turtle is clicked, first _onclick-event will be performed,
628 then _onscreensclick-event.
629 """
630 if fun is None:
631 self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
632 else:
633 def eventfun(event):
634 x, y = (self.cv.canvasx(event.x)/self.xscale,
635 -self.cv.canvasy(event.y)/self.yscale)
636 fun(x, y)
637 self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
638 eventfun, add)
639
640 def _ondrag(self, item, fun, num=1, add=None):
641 """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
642 fun must be a function with two arguments, the coordinates of the
643 actual mouse position on the canvas.
644 num, the number of the mouse-button defaults to 1
645
646 Every sequence of mouse-move-events on a turtle is preceded by a
647 mouse-click event on that turtle.
648 """
649 if fun is None:
650 self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
651 else:
652 def eventfun(event):
653 try:
654 x, y = (self.cv.canvasx(event.x)/self.xscale,
655 -self.cv.canvasy(event.y)/self.yscale)
656 fun(x, y)
Serhiy Storchakacefa9172016-06-14 22:52:04 +0300657 except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000658 pass
659 self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
660
661 def _onscreenclick(self, fun, num=1, add=None):
662 """Bind fun to mouse-click event on canvas.
663 fun must be a function with two arguments, the coordinates
664 of the clicked point on the canvas.
665 num, the number of the mouse-button defaults to 1
666
667 If a turtle is clicked, first _onclick-event will be performed,
668 then _onscreensclick-event.
669 """
670 if fun is None:
671 self.cv.unbind("<Button-%s>" % num)
672 else:
673 def eventfun(event):
674 x, y = (self.cv.canvasx(event.x)/self.xscale,
675 -self.cv.canvasy(event.y)/self.yscale)
676 fun(x, y)
677 self.cv.bind("<Button-%s>" % num, eventfun, add)
678
Georg Brandleaa84ef2009-05-05 08:14:33 +0000679 def _onkeyrelease(self, fun, key):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000680 """Bind fun to key-release event of key.
681 Canvas must have focus. See method listen
682 """
683 if fun is None:
684 self.cv.unbind("<KeyRelease-%s>" % key, None)
685 else:
686 def eventfun(event):
687 fun()
688 self.cv.bind("<KeyRelease-%s>" % key, eventfun)
689
Georg Brandleaa84ef2009-05-05 08:14:33 +0000690 def _onkeypress(self, fun, key=None):
691 """If key is given, bind fun to key-press event of key.
692 Otherwise bind fun to any key-press.
693 Canvas must have focus. See method listen.
694 """
695 if fun is None:
696 if key is None:
697 self.cv.unbind("<KeyPress>", None)
698 else:
699 self.cv.unbind("<KeyPress-%s>" % key, None)
700 else:
701 def eventfun(event):
702 fun()
703 if key is None:
704 self.cv.bind("<KeyPress>", eventfun)
705 else:
706 self.cv.bind("<KeyPress-%s>" % key, eventfun)
707
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000708 def _listen(self):
709 """Set focus on canvas (in order to collect key-events)
710 """
711 self.cv.focus_force()
712
713 def _ontimer(self, fun, t):
714 """Install a timer, which calls fun after t milliseconds.
715 """
716 if t == 0:
717 self.cv.after_idle(fun)
718 else:
719 self.cv.after(t, fun)
720
721 def _createimage(self, image):
722 """Create and return image item on canvas.
723 """
724 return self.cv.create_image(0, 0, image=image)
725
726 def _drawimage(self, item, pos, image):
727 """Configure image item as to draw image object
728 at position (x,y) on canvas)
729 """
730 x, y = pos
731 self.cv.coords(item, (x * self.xscale, -y * self.yscale))
732 self.cv.itemconfig(item, image=image)
733
734 def _setbgpic(self, item, image):
735 """Configure image item as to draw image object
736 at center of canvas. Set item to the first item
737 in the displaylist, so it will be drawn below
738 any other item ."""
739 self.cv.itemconfig(item, image=image)
740 self.cv.tag_lower(item)
741
742 def _type(self, item):
743 """Return 'line' or 'polygon' or 'image' depending on
744 type of item.
745 """
746 return self.cv.type(item)
747
748 def _pointlist(self, item):
749 """returns list of coordinate-pairs of points of item
750 Example (for insiders):
751 >>> from turtle import *
752 >>> getscreen()._pointlist(getturtle().turtle._item)
753 [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
754 (9.9999999999999982, 0.0)]
755 >>> """
Alexander Belopolsky022f0492010-11-22 19:40:51 +0000756 cl = self.cv.coords(item)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000757 pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
758 return pl
759
760 def _setscrollregion(self, srx1, sry1, srx2, sry2):
761 self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
762
763 def _rescale(self, xscalefactor, yscalefactor):
764 items = self.cv.find_all()
765 for item in items:
766 coordinates = list(self.cv.coords(item))
767 newcoordlist = []
768 while coordinates:
769 x, y = coordinates[:2]
770 newcoordlist.append(x * xscalefactor)
771 newcoordlist.append(y * yscalefactor)
772 coordinates = coordinates[2:]
773 self.cv.coords(item, *newcoordlist)
774
775 def _resize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +0000776 """Resize the canvas the turtles are drawing on. Does
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000777 not alter the drawing window.
778 """
779 # needs amendment
780 if not isinstance(self.cv, ScrolledCanvas):
781 return self.canvwidth, self.canvheight
Florent Xiclunafd1b0932010-03-28 00:25:02 +0000782 if canvwidth is canvheight is bg is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000783 return self.cv.canvwidth, self.cv.canvheight
784 if canvwidth is not None:
785 self.canvwidth = canvwidth
786 if canvheight is not None:
787 self.canvheight = canvheight
788 self.cv.reset(canvwidth, canvheight, bg)
789
790 def _window_size(self):
791 """ Return the width and height of the turtle window.
792 """
793 width = self.cv.winfo_width()
794 if width <= 1: # the window isn't managed by a geometry manager
795 width = self.cv['width']
796 height = self.cv.winfo_height()
797 if height <= 1: # the window isn't managed by a geometry manager
798 height = self.cv['height']
799 return width, height
800
Georg Brandleaa84ef2009-05-05 08:14:33 +0000801 def mainloop(self):
802 """Starts event loop - calling Tkinter's mainloop function.
803
804 No argument.
805
806 Must be last statement in a turtle graphics program.
807 Must NOT be used if a script is run from within IDLE in -n mode
808 (No subprocess) - for interactive use of turtle graphics.
809
810 Example (for a TurtleScreen instance named screen):
811 >>> screen.mainloop()
812
813 """
814 TK.mainloop()
815
816 def textinput(self, title, prompt):
817 """Pop up a dialog window for input of a string.
818
819 Arguments: title is the title of the dialog window,
820 prompt is a text mostly describing what information to input.
821
822 Return the string input
823 If the dialog is canceled, return None.
824
825 Example (for a TurtleScreen instance named screen):
826 >>> screen.textinput("NIM", "Name of first player:")
827
828 """
829 return simpledialog.askstring(title, prompt)
830
831 def numinput(self, title, prompt, default=None, minval=None, maxval=None):
832 """Pop up a dialog window for input of a number.
833
834 Arguments: title is the title of the dialog window,
835 prompt is a text mostly describing what numerical information to input.
836 default: default value
Xtreak0d702272019-06-03 04:42:33 +0530837 minval: minimum value for input
Georg Brandleaa84ef2009-05-05 08:14:33 +0000838 maxval: maximum value for input
839
840 The number input must be in the range minval .. maxval if these are
841 given. If not, a hint is issued and the dialog remains open for
842 correction. Return the number input.
843 If the dialog is canceled, return None.
844
845 Example (for a TurtleScreen instance named screen):
846 >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000)
847
848 """
849 return simpledialog.askfloat(title, prompt, initialvalue=default,
850 minvalue=minval, maxvalue=maxval)
851
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000852
853##############################################################################
854### End of Tkinter - interface ###
855##############################################################################
856
857
858class Terminator (Exception):
859 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
860
Terry Jan Reedyc30b7b12013-03-11 17:57:08 -0400861 This stops execution of a turtle graphics script.
862 Main purpose: use in the Demo-Viewer turtle.Demo.py.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000863 """
864 pass
865
866
867class TurtleGraphicsError(Exception):
868 """Some TurtleGraphics Error
869 """
870
871
872class Shape(object):
873 """Data structure modeling shapes.
874
875 attribute _type is one of "polygon", "image", "compound"
876 attribute _data is - depending on _type a poygon-tuple,
877 an image or a list constructed using the addcomponent method.
878 """
879 def __init__(self, type_, data=None):
880 self._type = type_
881 if type_ == "polygon":
882 if isinstance(data, list):
883 data = tuple(data)
884 elif type_ == "image":
885 if isinstance(data, str):
886 if data.lower().endswith(".gif") and isfile(data):
887 data = TurtleScreen._image(data)
888 # else data assumed to be Photoimage
889 elif type_ == "compound":
890 data = []
891 else:
892 raise TurtleGraphicsError("There is no shape type %s" % type_)
893 self._data = data
894
895 def addcomponent(self, poly, fill, outline=None):
896 """Add component to a shape of type compound.
897
898 Arguments: poly is a polygon, i. e. a tuple of number pairs.
899 fill is the fillcolor of the component,
900 outline is the outline color of the component.
901
902 call (for a Shapeobject namend s):
903 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000904
905 Example:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000906 >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
907 >>> s = Shape("compound")
908 >>> s.addcomponent(poly, "red", "blue")
Petri Lehtinen9aa20af2011-12-02 21:24:14 +0200909 >>> # .. add more components and then use register_shape()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000910 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000911 if self._type != "compound":
912 raise TurtleGraphicsError("Cannot add component to %s Shape"
913 % self._type)
914 if outline is None:
915 outline = fill
916 self._data.append([poly, fill, outline])
Guido van Rossumb241b671998-12-04 16:42:46 +0000917
Thomas Wouters477c8d52006-05-27 19:21:47 +0000918
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000919class Tbuffer(object):
920 """Ring buffer used as undobuffer for RawTurtle objects."""
921 def __init__(self, bufsize=10):
922 self.bufsize = bufsize
923 self.buffer = [[None]] * bufsize
924 self.ptr = -1
925 self.cumulate = False
926 def reset(self, bufsize=None):
927 if bufsize is None:
928 for i in range(self.bufsize):
929 self.buffer[i] = [None]
930 else:
931 self.bufsize = bufsize
932 self.buffer = [[None]] * bufsize
933 self.ptr = -1
934 def push(self, item):
935 if self.bufsize > 0:
936 if not self.cumulate:
937 self.ptr = (self.ptr + 1) % self.bufsize
938 self.buffer[self.ptr] = item
939 else:
940 self.buffer[self.ptr].append(item)
941 def pop(self):
942 if self.bufsize > 0:
943 item = self.buffer[self.ptr]
944 if item is None:
945 return None
946 else:
947 self.buffer[self.ptr] = [None]
948 self.ptr = (self.ptr - 1) % self.bufsize
949 return (item)
950 def nr_of_items(self):
951 return self.bufsize - self.buffer.count([None])
952 def __repr__(self):
953 return str(self.buffer) + " " + str(self.ptr)
954
955
956
957class TurtleScreen(TurtleScreenBase):
958 """Provides screen oriented methods like setbg etc.
959
960 Only relies upon the methods of TurtleScreenBase and NOT
961 upon components of the underlying graphics toolkit -
962 which is Tkinter in this case.
963 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000964 _RUNNING = True
965
966 def __init__(self, cv, mode=_CFG["mode"],
967 colormode=_CFG["colormode"], delay=_CFG["delay"]):
968 self._shapes = {
969 "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
970 "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
971 (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
972 (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
973 (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
974 (2,14))),
975 "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
976 (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
977 (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
978 (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
979 (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
980 (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
981 "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
982 (-10,-10))),
983 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
984 (-10,-5.77))),
985 "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
986 "blank" : Shape("image", self._blankimage())
987 }
988
989 self._bgpics = {"nopic" : ""}
990
991 TurtleScreenBase.__init__(self, cv)
992 self._mode = mode
993 self._delayvalue = delay
994 self._colormode = _CFG["colormode"]
995 self._keys = []
996 self.clear()
Ned Deily09ae5442014-04-19 19:11:14 -0700997 if sys.platform == 'darwin':
998 # Force Turtle window to the front on OS X. This is needed because
999 # the Turtle window will show behind the Terminal window when you
1000 # start the demo from the command line.
Ned Deily152dfd12014-09-13 23:39:16 -07001001 rootwindow = cv.winfo_toplevel()
1002 rootwindow.call('wm', 'attributes', '.', '-topmost', '1')
1003 rootwindow.call('wm', 'attributes', '.', '-topmost', '0')
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001004
1005 def clear(self):
1006 """Delete all drawings and all turtles from the TurtleScreen.
1007
Georg Brandleaa84ef2009-05-05 08:14:33 +00001008 No argument.
1009
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001010 Reset empty TurtleScreen to its initial state: white background,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001011 no backgroundimage, no eventbindings and tracing on.
1012
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001013 Example (for a TurtleScreen instance named screen):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001014 >>> screen.clear()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001015
1016 Note: this method is not available as function.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001017 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001018 self._delayvalue = _CFG["delay"]
1019 self._colormode = _CFG["colormode"]
1020 self._delete("all")
1021 self._bgpic = self._createimage("")
1022 self._bgpicname = "nopic"
1023 self._tracing = 1
1024 self._updatecounter = 0
1025 self._turtles = []
1026 self.bgcolor("white")
1027 for btn in 1, 2, 3:
1028 self.onclick(None, btn)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001029 self.onkeypress(None)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001030 for key in self._keys[:]:
1031 self.onkey(None, key)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001032 self.onkeypress(None, key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001033 Turtle._pen = None
Guido van Rossumb241b671998-12-04 16:42:46 +00001034
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001035 def mode(self, mode=None):
1036 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001037
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001038 Optional argument:
Martin Panter99e843b2016-09-10 10:38:28 +00001039 mode -- one of the strings 'standard', 'logo' or 'world'
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001040
1041 Mode 'standard' is compatible with turtle.py.
1042 Mode 'logo' is compatible with most Logo-Turtle-Graphics.
1043 Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
1044 this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
1045 If mode is not given, return the current mode.
1046
1047 Mode Initial turtle heading positive angles
1048 ------------|-------------------------|-------------------
1049 'standard' to the right (east) counterclockwise
1050 'logo' upward (north) clockwise
1051
1052 Examples:
1053 >>> mode('logo') # resets turtle heading to north
1054 >>> mode()
1055 'logo'
Thomas Wouters477c8d52006-05-27 19:21:47 +00001056 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001057 if mode is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001058 return self._mode
1059 mode = mode.lower()
1060 if mode not in ["standard", "logo", "world"]:
1061 raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1062 self._mode = mode
1063 if mode in ["standard", "logo"]:
1064 self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1065 self.canvwidth//2, self.canvheight//2)
1066 self.xscale = self.yscale = 1.0
1067 self.reset()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001068
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001069 def setworldcoordinates(self, llx, lly, urx, ury):
1070 """Set up a user defined coordinate-system.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001071
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001072 Arguments:
1073 llx -- a number, x-coordinate of lower left corner of canvas
1074 lly -- a number, y-coordinate of lower left corner of canvas
1075 urx -- a number, x-coordinate of upper right corner of canvas
1076 ury -- a number, y-coordinate of upper right corner of canvas
1077
1078 Set up user coodinat-system and switch to mode 'world' if necessary.
1079 This performs a screen.reset. If mode 'world' is already active,
1080 all drawings are redrawn according to the new coordinates.
1081
1082 But ATTENTION: in user-defined coordinatesystems angles may appear
1083 distorted. (see Screen.mode())
1084
1085 Example (for a TurtleScreen instance named screen):
1086 >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1087 >>> for _ in range(36):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001088 ... left(10)
1089 ... forward(0.5)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001090 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001091 if self.mode() != "world":
1092 self.mode("world")
1093 xspan = float(urx - llx)
1094 yspan = float(ury - lly)
1095 wx, wy = self._window_size()
1096 self.screensize(wx-20, wy-20)
1097 oldxscale, oldyscale = self.xscale, self.yscale
1098 self.xscale = self.canvwidth / xspan
1099 self.yscale = self.canvheight / yspan
1100 srx1 = llx * self.xscale
1101 sry1 = -ury * self.yscale
1102 srx2 = self.canvwidth + srx1
1103 sry2 = self.canvheight + sry1
1104 self._setscrollregion(srx1, sry1, srx2, sry2)
1105 self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1106 self.update()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001107
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001108 def register_shape(self, name, shape=None):
1109 """Adds a turtle shape to TurtleScreen's shapelist.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001110
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001111 Arguments:
1112 (1) name is the name of a gif-file and shape is None.
1113 Installs the corresponding image shape.
1114 !! Image-shapes DO NOT rotate when turning the turtle,
1115 !! so they do not display the heading of the turtle!
1116 (2) name is an arbitrary string and shape is a tuple
1117 of pairs of coordinates. Installs the corresponding
1118 polygon shape
1119 (3) name is an arbitrary string and shape is a
1120 (compound) Shape object. Installs the corresponding
1121 compound shape.
1122 To use a shape, you have to issue the command shape(shapename).
Thomas Wouters477c8d52006-05-27 19:21:47 +00001123
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001124 call: register_shape("turtle.gif")
1125 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1126
1127 Example (for a TurtleScreen instance named screen):
1128 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1129
Thomas Wouters477c8d52006-05-27 19:21:47 +00001130 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001131 if shape is None:
1132 # image
1133 if name.lower().endswith(".gif"):
1134 shape = Shape("image", self._image(name))
1135 else:
1136 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1137 + "Use help(register_shape)" )
1138 elif isinstance(shape, tuple):
1139 shape = Shape("polygon", shape)
1140 ## else shape assumed to be Shape-instance
1141 self._shapes[name] = shape
Guido van Rossumbffa52f2002-09-29 00:25:51 +00001142
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001143 def _colorstr(self, color):
1144 """Return color string corresponding to args.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001145
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001146 Argument may be a string or a tuple of three
1147 numbers corresponding to actual colormode,
1148 i.e. in the range 0<=n<=colormode.
1149
1150 If the argument doesn't represent a color,
1151 an error is raised.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001152 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001153 if len(color) == 1:
1154 color = color[0]
1155 if isinstance(color, str):
1156 if self._iscolorstring(color) or color == "":
1157 return color
1158 else:
1159 raise TurtleGraphicsError("bad color string: %s" % str(color))
1160 try:
1161 r, g, b = color
Serhiy Storchakacefa9172016-06-14 22:52:04 +03001162 except (TypeError, ValueError):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001163 raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1164 if self._colormode == 1.0:
1165 r, g, b = [round(255.0*x) for x in (r, g, b)]
1166 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1167 raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1168 return "#%02x%02x%02x" % (r, g, b)
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001169
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001170 def _color(self, cstr):
1171 if not cstr.startswith("#"):
1172 return cstr
1173 if len(cstr) == 7:
1174 cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1175 elif len(cstr) == 4:
1176 cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1177 else:
1178 raise TurtleGraphicsError("bad colorstring: %s" % cstr)
Jon Dufresne39726282017-05-18 07:35:54 -07001179 return tuple(c * self._colormode/255 for c in cl)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001180
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001181 def colormode(self, cmode=None):
1182 """Return the colormode or set it to 1.0 or 255.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001183
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001184 Optional argument:
1185 cmode -- one of the values 1.0 or 255
Thomas Wouters477c8d52006-05-27 19:21:47 +00001186
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001187 r, g, b values of colortriples have to be in range 0..cmode.
1188
1189 Example (for a TurtleScreen instance named screen):
1190 >>> screen.colormode()
1191 1.0
1192 >>> screen.colormode(255)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001193 >>> pencolor(240,160,80)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001194 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001195 if cmode is None:
1196 return self._colormode
1197 if cmode == 1.0:
1198 self._colormode = float(cmode)
1199 elif cmode == 255:
1200 self._colormode = int(cmode)
1201
1202 def reset(self):
1203 """Reset all Turtles on the Screen to their initial state.
1204
1205 No argument.
1206
1207 Example (for a TurtleScreen instance named screen):
1208 >>> screen.reset()
1209 """
1210 for turtle in self._turtles:
1211 turtle._setmode(self._mode)
1212 turtle.reset()
1213
1214 def turtles(self):
1215 """Return the list of turtles on the screen.
1216
1217 Example (for a TurtleScreen instance named screen):
1218 >>> screen.turtles()
1219 [<turtle.Turtle object at 0x00E11FB0>]
1220 """
1221 return self._turtles
1222
1223 def bgcolor(self, *args):
1224 """Set or return backgroundcolor of the TurtleScreen.
1225
1226 Arguments (if given): a color string or three numbers
1227 in the range 0..colormode or a 3-tuple of such numbers.
1228
1229 Example (for a TurtleScreen instance named screen):
1230 >>> screen.bgcolor("orange")
1231 >>> screen.bgcolor()
1232 'orange'
1233 >>> screen.bgcolor(0.5,0,0.5)
1234 >>> screen.bgcolor()
1235 '#800080'
1236 """
1237 if args:
1238 color = self._colorstr(args)
1239 else:
1240 color = None
1241 color = self._bgcolor(color)
1242 if color is not None:
1243 color = self._color(color)
1244 return color
1245
1246 def tracer(self, n=None, delay=None):
1247 """Turns turtle animation on/off and set delay for update drawings.
1248
1249 Optional arguments:
1250 n -- nonnegative integer
1251 delay -- nonnegative integer
1252
1253 If n is given, only each n-th regular screen update is really performed.
1254 (Can be used to accelerate the drawing of complex graphics.)
1255 Second arguments sets delay value (see RawTurtle.delay())
1256
1257 Example (for a TurtleScreen instance named screen):
1258 >>> screen.tracer(8, 25)
1259 >>> dist = 2
1260 >>> for i in range(200):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001261 ... fd(dist)
1262 ... rt(90)
1263 ... dist += 2
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001264 """
1265 if n is None:
1266 return self._tracing
1267 self._tracing = int(n)
1268 self._updatecounter = 0
1269 if delay is not None:
1270 self._delayvalue = int(delay)
1271 if self._tracing:
1272 self.update()
1273
1274 def delay(self, delay=None):
1275 """ Return or set the drawing delay in milliseconds.
1276
1277 Optional argument:
1278 delay -- positive integer
1279
1280 Example (for a TurtleScreen instance named screen):
1281 >>> screen.delay(15)
1282 >>> screen.delay()
1283 15
1284 """
1285 if delay is None:
1286 return self._delayvalue
1287 self._delayvalue = int(delay)
1288
1289 def _incrementudc(self):
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03001290 """Increment update counter."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001291 if not TurtleScreen._RUNNING:
Serhiy Storchaka80a18032015-02-22 17:25:33 +02001292 TurtleScreen._RUNNING = True
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001293 raise Terminator
1294 if self._tracing > 0:
1295 self._updatecounter += 1
1296 self._updatecounter %= self._tracing
1297
1298 def update(self):
1299 """Perform a TurtleScreen update.
1300 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00001301 tracing = self._tracing
1302 self._tracing = True
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001303 for t in self.turtles():
1304 t._update_data()
1305 t._drawturtle()
Georg Brandleaa84ef2009-05-05 08:14:33 +00001306 self._tracing = tracing
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001307 self._update()
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001308
1309 def window_width(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001310 """ Return the width of the turtle window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001311
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001312 Example (for a TurtleScreen instance named screen):
1313 >>> screen.window_width()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001314 640
1315 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001316 return self._window_size()[0]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001317
1318 def window_height(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001319 """ Return the height of the turtle window.
1320
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001321 Example (for a TurtleScreen instance named screen):
1322 >>> screen.window_height()
1323 480
Thomas Wouters477c8d52006-05-27 19:21:47 +00001324 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001325 return self._window_size()[1]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001326
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001327 def getcanvas(self):
1328 """Return the Canvas of this TurtleScreen.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001329
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001330 No argument.
1331
1332 Example (for a Screen instance named screen):
1333 >>> cv = screen.getcanvas()
1334 >>> cv
1335 <turtle.ScrolledCanvas instance at 0x010742D8>
Thomas Wouters477c8d52006-05-27 19:21:47 +00001336 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001337 return self.cv
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001338
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001339 def getshapes(self):
1340 """Return a list of names of all currently available turtle shapes.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001341
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001342 No argument.
1343
1344 Example (for a TurtleScreen instance named screen):
1345 >>> screen.getshapes()
1346 ['arrow', 'blank', 'circle', ... , 'turtle']
1347 """
1348 return sorted(self._shapes.keys())
1349
1350 def onclick(self, fun, btn=1, add=None):
1351 """Bind fun to mouse-click event on canvas.
1352
1353 Arguments:
1354 fun -- a function with two arguments, the coordinates of the
1355 clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05301356 btn -- the number of the mouse-button, defaults to 1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001357
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001358 Example (for a TurtleScreen instance named screen)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001359
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001360 >>> screen.onclick(goto)
1361 >>> # Subsequently clicking into the TurtleScreen will
1362 >>> # make the turtle move to the clicked point.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001363 >>> screen.onclick(None)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001364 """
1365 self._onscreenclick(fun, btn, add)
1366
1367 def onkey(self, fun, key):
1368 """Bind fun to key-release event of key.
1369
1370 Arguments:
1371 fun -- a function with no arguments
1372 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1373
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001374 In order to be able to register key-events, TurtleScreen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001375 must have focus. (See method listen.)
1376
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001377 Example (for a TurtleScreen instance named screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001378
1379 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001380 ... fd(50)
1381 ... lt(60)
1382 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001383 >>> screen.onkey(f, "Up")
1384 >>> screen.listen()
1385
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001386 Subsequently the turtle can be moved by repeatedly pressing
1387 the up-arrow key, consequently drawing a hexagon
1388
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001389 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001390 if fun is None:
Georg Brandleaa84ef2009-05-05 08:14:33 +00001391 if key in self._keys:
1392 self._keys.remove(key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001393 elif key not in self._keys:
1394 self._keys.append(key)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001395 self._onkeyrelease(fun, key)
1396
1397 def onkeypress(self, fun, key=None):
1398 """Bind fun to key-press event of key if key is given,
1399 or to any key-press-event if no key is given.
1400
1401 Arguments:
1402 fun -- a function with no arguments
1403 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1404
1405 In order to be able to register key-events, TurtleScreen
1406 must have focus. (See method listen.)
1407
1408 Example (for a TurtleScreen instance named screen
1409 and a Turtle instance named turtle):
1410
1411 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001412 ... fd(50)
1413 ... lt(60)
1414 ...
1415 >>> screen.onkeypress(f, "Up")
Georg Brandleaa84ef2009-05-05 08:14:33 +00001416 >>> screen.listen()
1417
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001418 Subsequently the turtle can be moved by repeatedly pressing
1419 the up-arrow key, or by keeping pressed the up-arrow key.
1420 consequently drawing a hexagon.
Georg Brandleaa84ef2009-05-05 08:14:33 +00001421 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001422 if fun is None:
Georg Brandleaa84ef2009-05-05 08:14:33 +00001423 if key in self._keys:
1424 self._keys.remove(key)
1425 elif key is not None and key not in self._keys:
1426 self._keys.append(key)
1427 self._onkeypress(fun, key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001428
1429 def listen(self, xdummy=None, ydummy=None):
1430 """Set focus on TurtleScreen (in order to collect key-events)
1431
1432 No arguments.
1433 Dummy arguments are provided in order
1434 to be able to pass listen to the onclick method.
1435
1436 Example (for a TurtleScreen instance named screen):
1437 >>> screen.listen()
1438 """
1439 self._listen()
1440
1441 def ontimer(self, fun, t=0):
1442 """Install a timer, which calls fun after t milliseconds.
1443
1444 Arguments:
1445 fun -- a function with no arguments.
1446 t -- a number >= 0
1447
1448 Example (for a TurtleScreen instance named screen):
1449
1450 >>> running = True
1451 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001452 ... if running:
1453 ... fd(50)
1454 ... lt(60)
1455 ... screen.ontimer(f, 250)
1456 ...
1457 >>> f() # makes the turtle marching around
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001458 >>> running = False
1459 """
1460 self._ontimer(fun, t)
1461
1462 def bgpic(self, picname=None):
1463 """Set background image or return name of current backgroundimage.
1464
1465 Optional argument:
1466 picname -- a string, name of a gif-file or "nopic".
1467
Ezio Melotti42da6632011-03-15 05:18:48 +02001468 If picname is a filename, set the corresponding image as background.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001469 If picname is "nopic", delete backgroundimage, if present.
1470 If picname is None, return the filename of the current backgroundimage.
1471
1472 Example (for a TurtleScreen instance named screen):
1473 >>> screen.bgpic()
1474 'nopic'
1475 >>> screen.bgpic("landscape.gif")
1476 >>> screen.bgpic()
1477 'landscape.gif'
1478 """
1479 if picname is None:
1480 return self._bgpicname
1481 if picname not in self._bgpics:
1482 self._bgpics[picname] = self._image(picname)
1483 self._setbgpic(self._bgpic, self._bgpics[picname])
1484 self._bgpicname = picname
1485
1486 def screensize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001487 """Resize the canvas the turtles are drawing on.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001488
1489 Optional arguments:
1490 canvwidth -- positive integer, new width of canvas in pixels
1491 canvheight -- positive integer, new height of canvas in pixels
Ezio Melotti13925002011-03-16 11:05:33 +02001492 bg -- colorstring or color-tuple, new backgroundcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001493 If no arguments are given, return current (canvaswidth, canvasheight)
1494
1495 Do not alter the drawing window. To observe hidden parts of
1496 the canvas use the scrollbars. (Can make visible those parts
1497 of a drawing, which were outside the canvas before!)
1498
1499 Example (for a Turtle instance named turtle):
1500 >>> turtle.screensize(2000,1500)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001501 >>> # e.g. to search for an erroneously escaped turtle ;-)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001502 """
1503 return self._resize(canvwidth, canvheight, bg)
1504
1505 onscreenclick = onclick
1506 resetscreen = reset
1507 clearscreen = clear
1508 addshape = register_shape
Georg Brandleaa84ef2009-05-05 08:14:33 +00001509 onkeyrelease = onkey
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001510
1511class TNavigator(object):
1512 """Navigation part of the RawTurtle.
1513 Implements methods for turtle movement.
1514 """
1515 START_ORIENTATION = {
1516 "standard": Vec2D(1.0, 0.0),
1517 "world" : Vec2D(1.0, 0.0),
1518 "logo" : Vec2D(0.0, 1.0) }
1519 DEFAULT_MODE = "standard"
1520 DEFAULT_ANGLEOFFSET = 0
1521 DEFAULT_ANGLEORIENT = 1
1522
1523 def __init__(self, mode=DEFAULT_MODE):
1524 self._angleOffset = self.DEFAULT_ANGLEOFFSET
1525 self._angleOrient = self.DEFAULT_ANGLEORIENT
1526 self._mode = mode
1527 self.undobuffer = None
1528 self.degrees()
1529 self._mode = None
1530 self._setmode(mode)
1531 TNavigator.reset(self)
1532
1533 def reset(self):
1534 """reset turtle to its initial values
1535
1536 Will be overwritten by parent class
1537 """
1538 self._position = Vec2D(0.0, 0.0)
1539 self._orient = TNavigator.START_ORIENTATION[self._mode]
1540
1541 def _setmode(self, mode=None):
1542 """Set turtle-mode to 'standard', 'world' or 'logo'.
1543 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001544 if mode is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001545 return self._mode
1546 if mode not in ["standard", "logo", "world"]:
1547 return
1548 self._mode = mode
1549 if mode in ["standard", "world"]:
1550 self._angleOffset = 0
1551 self._angleOrient = 1
1552 else: # mode == "logo":
1553 self._angleOffset = self._fullcircle/4.
1554 self._angleOrient = -1
1555
1556 def _setDegreesPerAU(self, fullcircle):
1557 """Helper function for degrees() and radians()"""
1558 self._fullcircle = fullcircle
1559 self._degreesPerAU = 360/fullcircle
1560 if self._mode == "standard":
1561 self._angleOffset = 0
1562 else:
1563 self._angleOffset = fullcircle/4.
1564
1565 def degrees(self, fullcircle=360.0):
1566 """ Set angle measurement units to degrees.
1567
1568 Optional argument:
1569 fullcircle - a number
1570
1571 Set angle measurement units, i. e. set number
penguindustin96466302019-05-06 14:57:17 -04001572 of 'degrees' for a full circle. Default value is
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001573 360 degrees.
1574
1575 Example (for a Turtle instance named turtle):
1576 >>> turtle.left(90)
1577 >>> turtle.heading()
1578 90
Alexander Belopolsky3cdfb122010-10-29 17:16:49 +00001579
1580 Change angle measurement unit to grad (also known as gon,
1581 grade, or gradian and equals 1/100-th of the right angle.)
1582 >>> turtle.degrees(400.0)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001583 >>> turtle.heading()
1584 100
1585
1586 """
1587 self._setDegreesPerAU(fullcircle)
1588
1589 def radians(self):
1590 """ Set the angle measurement units to radians.
1591
1592 No arguments.
1593
1594 Example (for a Turtle instance named turtle):
1595 >>> turtle.heading()
1596 90
1597 >>> turtle.radians()
1598 >>> turtle.heading()
1599 1.5707963267948966
1600 """
Marek Madejski6844b562020-09-01 18:42:41 +02001601 self._setDegreesPerAU(math.tau)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001602
1603 def _go(self, distance):
1604 """move turtle forward by specified distance"""
1605 ende = self._position + self._orient * distance
1606 self._goto(ende)
1607
1608 def _rotate(self, angle):
1609 """Turn turtle counterclockwise by specified angle if angle > 0."""
1610 angle *= self._degreesPerAU
1611 self._orient = self._orient.rotate(angle)
1612
1613 def _goto(self, end):
1614 """move turtle to position end."""
1615 self._position = end
1616
1617 def forward(self, distance):
1618 """Move the turtle forward by the specified distance.
1619
1620 Aliases: forward | fd
1621
1622 Argument:
1623 distance -- a number (integer or float)
1624
1625 Move the turtle forward by the specified distance, in the direction
1626 the turtle is headed.
1627
1628 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001629 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001630 (0.00, 0.00)
1631 >>> turtle.forward(25)
1632 >>> turtle.position()
1633 (25.00,0.00)
1634 >>> turtle.forward(-75)
1635 >>> turtle.position()
1636 (-50.00,0.00)
1637 """
1638 self._go(distance)
1639
1640 def back(self, distance):
1641 """Move the turtle backward by distance.
1642
1643 Aliases: back | backward | bk
1644
1645 Argument:
1646 distance -- a number
1647
1648 Move the turtle backward by distance ,opposite to the direction the
1649 turtle is headed. Do not change the turtle's heading.
1650
1651 Example (for a Turtle instance named turtle):
1652 >>> turtle.position()
1653 (0.00, 0.00)
1654 >>> turtle.backward(30)
1655 >>> turtle.position()
1656 (-30.00, 0.00)
1657 """
1658 self._go(-distance)
1659
1660 def right(self, angle):
1661 """Turn turtle right by angle units.
1662
1663 Aliases: right | rt
1664
1665 Argument:
1666 angle -- a number (integer or float)
1667
1668 Turn turtle right by angle units. (Units are by default degrees,
1669 but can be set via the degrees() and radians() functions.)
1670 Angle orientation depends on mode. (See this.)
1671
1672 Example (for a Turtle instance named turtle):
1673 >>> turtle.heading()
1674 22.0
1675 >>> turtle.right(45)
1676 >>> turtle.heading()
1677 337.0
1678 """
1679 self._rotate(-angle)
1680
1681 def left(self, angle):
1682 """Turn turtle left by angle units.
1683
1684 Aliases: left | lt
1685
1686 Argument:
1687 angle -- a number (integer or float)
1688
1689 Turn turtle left by angle units. (Units are by default degrees,
1690 but can be set via the degrees() and radians() functions.)
1691 Angle orientation depends on mode. (See this.)
1692
1693 Example (for a Turtle instance named turtle):
1694 >>> turtle.heading()
1695 22.0
1696 >>> turtle.left(45)
1697 >>> turtle.heading()
1698 67.0
1699 """
1700 self._rotate(angle)
1701
1702 def pos(self):
1703 """Return the turtle's current location (x,y), as a Vec2D-vector.
1704
1705 Aliases: pos | position
1706
1707 No arguments.
1708
1709 Example (for a Turtle instance named turtle):
1710 >>> turtle.pos()
1711 (0.00, 240.00)
1712 """
1713 return self._position
1714
1715 def xcor(self):
1716 """ Return the turtle's x coordinate.
1717
1718 No arguments.
1719
1720 Example (for a Turtle instance named turtle):
1721 >>> reset()
1722 >>> turtle.left(60)
1723 >>> turtle.forward(100)
1724 >>> print turtle.xcor()
1725 50.0
1726 """
1727 return self._position[0]
1728
1729 def ycor(self):
1730 """ Return the turtle's y coordinate
1731 ---
1732 No arguments.
1733
1734 Example (for a Turtle instance named turtle):
1735 >>> reset()
1736 >>> turtle.left(60)
1737 >>> turtle.forward(100)
1738 >>> print turtle.ycor()
1739 86.6025403784
1740 """
1741 return self._position[1]
1742
1743
1744 def goto(self, x, y=None):
1745 """Move turtle to an absolute position.
1746
1747 Aliases: setpos | setposition | goto:
1748
1749 Arguments:
1750 x -- a number or a pair/vector of numbers
1751 y -- a number None
1752
1753 call: goto(x, y) # two coordinates
1754 --or: goto((x, y)) # a pair (tuple) of coordinates
1755 --or: goto(vec) # e.g. as returned by pos()
1756
1757 Move turtle to an absolute position. If the pen is down,
1758 a line will be drawn. The turtle's orientation does not change.
1759
1760 Example (for a Turtle instance named turtle):
1761 >>> tp = turtle.pos()
1762 >>> tp
1763 (0.00, 0.00)
1764 >>> turtle.setpos(60,30)
1765 >>> turtle.pos()
1766 (60.00,30.00)
1767 >>> turtle.setpos((20,80))
1768 >>> turtle.pos()
1769 (20.00,80.00)
1770 >>> turtle.setpos(tp)
1771 >>> turtle.pos()
1772 (0.00,0.00)
1773 """
1774 if y is None:
1775 self._goto(Vec2D(*x))
1776 else:
1777 self._goto(Vec2D(x, y))
1778
1779 def home(self):
1780 """Move turtle to the origin - coordinates (0,0).
1781
1782 No arguments.
1783
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001784 Move turtle to the origin - coordinates (0,0) and set its
1785 heading to its start-orientation (which depends on mode).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001786
1787 Example (for a Turtle instance named turtle):
1788 >>> turtle.home()
1789 """
1790 self.goto(0, 0)
1791 self.setheading(0)
1792
1793 def setx(self, x):
1794 """Set the turtle's first coordinate to x
1795
1796 Argument:
1797 x -- a number (integer or float)
1798
1799 Set the turtle's first coordinate to x, leave second coordinate
1800 unchanged.
1801
1802 Example (for a Turtle instance named turtle):
1803 >>> turtle.position()
1804 (0.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001805 >>> turtle.setx(10)
1806 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001807 (10.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001808 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001809 self._goto(Vec2D(x, self._position[1]))
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001810
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001811 def sety(self, y):
1812 """Set the turtle's second coordinate to y
Thomas Wouters477c8d52006-05-27 19:21:47 +00001813
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001814 Argument:
1815 y -- a number (integer or float)
1816
1817 Set the turtle's first coordinate to x, second coordinate remains
1818 unchanged.
1819
1820 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001821 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001822 (0.00, 40.00)
1823 >>> turtle.sety(-10)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001824 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001825 (0.00, -10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001826 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001827 self._goto(Vec2D(self._position[0], y))
Guido van Rossumb241b671998-12-04 16:42:46 +00001828
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001829 def distance(self, x, y=None):
1830 """Return the distance from the turtle to (x,y) in turtle step units.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001831
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001832 Arguments:
1833 x -- a number or a pair/vector of numbers or a turtle instance
1834 y -- a number None None
Thomas Wouters477c8d52006-05-27 19:21:47 +00001835
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001836 call: distance(x, y) # two coordinates
1837 --or: distance((x, y)) # a pair (tuple) of coordinates
1838 --or: distance(vec) # e.g. as returned by pos()
1839 --or: distance(mypen) # where mypen is another turtle
1840
1841 Example (for a Turtle instance named turtle):
1842 >>> turtle.pos()
1843 (0.00, 0.00)
1844 >>> turtle.distance(30,40)
1845 50.0
1846 >>> pen = Turtle()
1847 >>> pen.forward(77)
1848 >>> turtle.distance(pen)
1849 77.0
1850 """
1851 if y is not None:
1852 pos = Vec2D(x, y)
1853 if isinstance(x, Vec2D):
1854 pos = x
1855 elif isinstance(x, tuple):
1856 pos = Vec2D(*x)
1857 elif isinstance(x, TNavigator):
1858 pos = x._position
1859 return abs(pos - self._position)
1860
1861 def towards(self, x, y=None):
1862 """Return the angle of the line from the turtle's position to (x, y).
1863
1864 Arguments:
1865 x -- a number or a pair/vector of numbers or a turtle instance
1866 y -- a number None None
1867
1868 call: distance(x, y) # two coordinates
1869 --or: distance((x, y)) # a pair (tuple) of coordinates
1870 --or: distance(vec) # e.g. as returned by pos()
1871 --or: distance(mypen) # where mypen is another turtle
1872
1873 Return the angle, between the line from turtle-position to position
1874 specified by x, y and the turtle's start orientation. (Depends on
1875 modes - "standard" or "logo")
1876
1877 Example (for a Turtle instance named turtle):
1878 >>> turtle.pos()
1879 (10.00, 10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001880 >>> turtle.towards(0,0)
1881 225.0
1882 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001883 if y is not None:
1884 pos = Vec2D(x, y)
1885 if isinstance(x, Vec2D):
1886 pos = x
1887 elif isinstance(x, tuple):
1888 pos = Vec2D(*x)
1889 elif isinstance(x, TNavigator):
1890 pos = x._position
1891 x, y = pos - self._position
Marek Madejski6844b562020-09-01 18:42:41 +02001892 result = round(math.degrees(math.atan2(y, x)), 10) % 360.0
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001893 result /= self._degreesPerAU
1894 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1895
1896 def heading(self):
1897 """ Return the turtle's current heading.
1898
1899 No arguments.
1900
1901 Example (for a Turtle instance named turtle):
1902 >>> turtle.left(67)
1903 >>> turtle.heading()
1904 67.0
1905 """
1906 x, y = self._orient
Marek Madejski6844b562020-09-01 18:42:41 +02001907 result = round(math.degrees(math.atan2(y, x)), 10) % 360.0
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001908 result /= self._degreesPerAU
1909 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1910
1911 def setheading(self, to_angle):
1912 """Set the orientation of the turtle to to_angle.
1913
1914 Aliases: setheading | seth
1915
1916 Argument:
1917 to_angle -- a number (integer or float)
1918
1919 Set the orientation of the turtle to to_angle.
1920 Here are some common directions in degrees:
1921
1922 standard - mode: logo-mode:
1923 -------------------|--------------------
1924 0 - east 0 - north
1925 90 - north 90 - east
1926 180 - west 180 - south
1927 270 - south 270 - west
1928
1929 Example (for a Turtle instance named turtle):
1930 >>> turtle.setheading(90)
1931 >>> turtle.heading()
1932 90
1933 """
1934 angle = (to_angle - self.heading())*self._angleOrient
1935 full = self._fullcircle
1936 angle = (angle+full/2.)%full - full/2.
1937 self._rotate(angle)
1938
1939 def circle(self, radius, extent = None, steps = None):
1940 """ Draw a circle with given radius.
1941
1942 Arguments:
1943 radius -- a number
1944 extent (optional) -- a number
1945 steps (optional) -- an integer
1946
1947 Draw a circle with given radius. The center is radius units left
1948 of the turtle; extent - an angle - determines which part of the
1949 circle is drawn. If extent is not given, draw the entire circle.
1950 If extent is not a full circle, one endpoint of the arc is the
1951 current pen position. Draw the arc in counterclockwise direction
1952 if radius is positive, otherwise in clockwise direction. Finally
1953 the direction of the turtle is changed by the amount of extent.
1954
1955 As the circle is approximated by an inscribed regular polygon,
1956 steps determines the number of steps to use. If not given,
1957 it will be calculated automatically. Maybe used to draw regular
1958 polygons.
1959
1960 call: circle(radius) # full circle
1961 --or: circle(radius, extent) # arc
1962 --or: circle(radius, extent, steps)
1963 --or: circle(radius, steps=6) # 6-sided polygon
1964
1965 Example (for a Turtle instance named turtle):
1966 >>> turtle.circle(50)
1967 >>> turtle.circle(120, 180) # semicircle
1968 """
1969 if self.undobuffer:
1970 self.undobuffer.push(["seq"])
1971 self.undobuffer.cumulate = True
1972 speed = self.speed()
1973 if extent is None:
1974 extent = self._fullcircle
1975 if steps is None:
1976 frac = abs(extent)/self._fullcircle
1977 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1978 w = 1.0 * extent / steps
1979 w2 = 0.5 * w
Marek Madejski6844b562020-09-01 18:42:41 +02001980 l = 2.0 * radius * math.sin(math.radians(w2)*self._degreesPerAU)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001981 if radius < 0:
1982 l, w, w2 = -l, -w, -w2
1983 tr = self._tracer()
1984 dl = self._delay()
1985 if speed == 0:
1986 self._tracer(0, 0)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001987 else:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001988 self.speed(0)
1989 self._rotate(w2)
1990 for i in range(steps):
1991 self.speed(speed)
1992 self._go(l)
1993 self.speed(0)
1994 self._rotate(w)
1995 self._rotate(-w2)
1996 if speed == 0:
1997 self._tracer(tr, dl)
1998 self.speed(speed)
1999 if self.undobuffer:
2000 self.undobuffer.cumulate = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00002001
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002002## three dummy methods to be implemented by child class:
Thomas Wouters477c8d52006-05-27 19:21:47 +00002003
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002004 def speed(self, s=0):
2005 """dummy method - to be overwritten by child class"""
2006 def _tracer(self, a=None, b=None):
2007 """dummy method - to be overwritten by child class"""
2008 def _delay(self, n=None):
2009 """dummy method - to be overwritten by child class"""
Thomas Wouters477c8d52006-05-27 19:21:47 +00002010
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002011 fd = forward
2012 bk = back
2013 backward = back
2014 rt = right
2015 lt = left
2016 position = pos
2017 setpos = goto
2018 setposition = goto
2019 seth = setheading
Thomas Wouters477c8d52006-05-27 19:21:47 +00002020
Thomas Wouters477c8d52006-05-27 19:21:47 +00002021
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002022class TPen(object):
2023 """Drawing part of the RawTurtle.
2024 Implements drawing properties.
2025 """
2026 def __init__(self, resizemode=_CFG["resizemode"]):
2027 self._resizemode = resizemode # or "user" or "noresize"
2028 self.undobuffer = None
2029 TPen._reset(self)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002030
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002031 def _reset(self, pencolor=_CFG["pencolor"],
2032 fillcolor=_CFG["fillcolor"]):
2033 self._pensize = 1
2034 self._shown = True
2035 self._pencolor = pencolor
2036 self._fillcolor = fillcolor
2037 self._drawing = True
2038 self._speed = 3
Georg Brandleaa84ef2009-05-05 08:14:33 +00002039 self._stretchfactor = (1., 1.)
2040 self._shearfactor = 0.
2041 self._tilt = 0.
2042 self._shapetrafo = (1., 0., 0., 1.)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002043 self._outlinewidth = 1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002044
2045 def resizemode(self, rmode=None):
2046 """Set resizemode to one of the values: "auto", "user", "noresize".
2047
2048 (Optional) Argument:
2049 rmode -- one of the strings "auto", "user", "noresize"
2050
2051 Different resizemodes have the following effects:
2052 - "auto" adapts the appearance of the turtle
2053 corresponding to the value of pensize.
2054 - "user" adapts the appearance of the turtle according to the
2055 values of stretchfactor and outlinewidth (outline),
2056 which are set by shapesize()
2057 - "noresize" no adaption of the turtle's appearance takes place.
2058 If no argument is given, return current resizemode.
2059 resizemode("user") is called by a call of shapesize with arguments.
2060
2061
2062 Examples (for a Turtle instance named turtle):
2063 >>> turtle.resizemode("noresize")
2064 >>> turtle.resizemode()
2065 'noresize'
Thomas Wouters477c8d52006-05-27 19:21:47 +00002066 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002067 if rmode is None:
2068 return self._resizemode
2069 rmode = rmode.lower()
2070 if rmode in ["auto", "user", "noresize"]:
2071 self.pen(resizemode=rmode)
Guido van Rossumb241b671998-12-04 16:42:46 +00002072
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002073 def pensize(self, width=None):
2074 """Set or return the line thickness.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002075
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002076 Aliases: pensize | width
Thomas Wouters477c8d52006-05-27 19:21:47 +00002077
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002078 Argument:
2079 width -- positive number
Thomas Wouters477c8d52006-05-27 19:21:47 +00002080
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002081 Set the line thickness to width or return it. If resizemode is set
2082 to "auto" and turtleshape is a polygon, that polygon is drawn with
2083 the same line thickness. If no argument is given, current pensize
2084 is returned.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002085
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002086 Example (for a Turtle instance named turtle):
2087 >>> turtle.pensize()
2088 1
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002089 >>> turtle.pensize(10) # from here on lines of width 10 are drawn
Thomas Wouters477c8d52006-05-27 19:21:47 +00002090 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002091 if width is None:
2092 return self._pensize
2093 self.pen(pensize=width)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002094
2095
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002096 def penup(self):
2097 """Pull the pen up -- no drawing when moving.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002098
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002099 Aliases: penup | pu | up
Thomas Wouters477c8d52006-05-27 19:21:47 +00002100
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002101 No argument
2102
2103 Example (for a Turtle instance named turtle):
2104 >>> turtle.penup()
Thomas Wouters477c8d52006-05-27 19:21:47 +00002105 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002106 if not self._drawing:
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002107 return
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002108 self.pen(pendown=False)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002109
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002110 def pendown(self):
2111 """Pull the pen down -- drawing when moving.
2112
2113 Aliases: pendown | pd | down
2114
2115 No argument.
2116
2117 Example (for a Turtle instance named turtle):
2118 >>> turtle.pendown()
2119 """
2120 if self._drawing:
2121 return
2122 self.pen(pendown=True)
2123
2124 def isdown(self):
2125 """Return True if pen is down, False if it's up.
2126
2127 No argument.
2128
2129 Example (for a Turtle instance named turtle):
2130 >>> turtle.penup()
2131 >>> turtle.isdown()
2132 False
2133 >>> turtle.pendown()
2134 >>> turtle.isdown()
2135 True
2136 """
2137 return self._drawing
2138
2139 def speed(self, speed=None):
2140 """ Return or set the turtle's speed.
2141
2142 Optional argument:
2143 speed -- an integer in the range 0..10 or a speedstring (see below)
2144
2145 Set the turtle's speed to an integer value in the range 0 .. 10.
2146 If no argument is given: return current speed.
2147
2148 If input is a number greater than 10 or smaller than 0.5,
2149 speed is set to 0.
2150 Speedstrings are mapped to speedvalues in the following way:
2151 'fastest' : 0
2152 'fast' : 10
2153 'normal' : 6
2154 'slow' : 3
2155 'slowest' : 1
2156 speeds from 1 to 10 enforce increasingly faster animation of
2157 line drawing and turtle turning.
2158
2159 Attention:
2160 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2161 and likewise left/right make the turtle turn instantly.
2162
2163 Example (for a Turtle instance named turtle):
2164 >>> turtle.speed(3)
2165 """
2166 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2167 if speed is None:
2168 return self._speed
2169 if speed in speeds:
2170 speed = speeds[speed]
2171 elif 0.5 < speed < 10.5:
2172 speed = int(round(speed))
2173 else:
2174 speed = 0
2175 self.pen(speed=speed)
2176
2177 def color(self, *args):
2178 """Return or set the pencolor and fillcolor.
2179
2180 Arguments:
2181 Several input formats are allowed.
2182 They use 0, 1, 2, or 3 arguments as follows:
2183
2184 color()
2185 Return the current pencolor and the current fillcolor
2186 as a pair of color specification strings as are returned
2187 by pencolor and fillcolor.
2188 color(colorstring), color((r,g,b)), color(r,g,b)
2189 inputs as in pencolor, set both, fillcolor and pencolor,
2190 to the given value.
2191 color(colorstring1, colorstring2),
2192 color((r1,g1,b1), (r2,g2,b2))
2193 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2194 and analogously, if the other input format is used.
2195
2196 If turtleshape is a polygon, outline and interior of that polygon
2197 is drawn with the newly set colors.
luzpaza5293b42017-11-05 07:37:50 -06002198 For more info see: pencolor, fillcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002199
2200 Example (for a Turtle instance named turtle):
2201 >>> turtle.color('red', 'green')
2202 >>> turtle.color()
2203 ('red', 'green')
2204 >>> colormode(255)
2205 >>> color((40, 80, 120), (160, 200, 240))
2206 >>> color()
2207 ('#285078', '#a0c8f0')
2208 """
2209 if args:
2210 l = len(args)
2211 if l == 1:
2212 pcolor = fcolor = args[0]
2213 elif l == 2:
2214 pcolor, fcolor = args
2215 elif l == 3:
2216 pcolor = fcolor = args
2217 pcolor = self._colorstr(pcolor)
2218 fcolor = self._colorstr(fcolor)
2219 self.pen(pencolor=pcolor, fillcolor=fcolor)
2220 else:
2221 return self._color(self._pencolor), self._color(self._fillcolor)
2222
2223 def pencolor(self, *args):
2224 """ Return or set the pencolor.
2225
2226 Arguments:
2227 Four input formats are allowed:
2228 - pencolor()
2229 Return the current pencolor as color specification string,
2230 possibly in hex-number format (see example).
2231 May be used as input to another color/pencolor/fillcolor call.
2232 - pencolor(colorstring)
2233 s is a Tk color specification string, such as "red" or "yellow"
2234 - pencolor((r, g, b))
2235 *a tuple* of r, g, and b, which represent, an RGB color,
2236 and each of r, g, and b are in the range 0..colormode,
2237 where colormode is either 1.0 or 255
2238 - pencolor(r, g, b)
2239 r, g, and b represent an RGB color, and each of r, g, and b
2240 are in the range 0..colormode
2241
2242 If turtleshape is a polygon, the outline of that polygon is drawn
2243 with the newly set pencolor.
2244
2245 Example (for a Turtle instance named turtle):
2246 >>> turtle.pencolor('brown')
2247 >>> tup = (0.2, 0.8, 0.55)
2248 >>> turtle.pencolor(tup)
2249 >>> turtle.pencolor()
2250 '#33cc8c'
2251 """
2252 if args:
2253 color = self._colorstr(args)
2254 if color == self._pencolor:
2255 return
2256 self.pen(pencolor=color)
2257 else:
2258 return self._color(self._pencolor)
2259
2260 def fillcolor(self, *args):
2261 """ Return or set the fillcolor.
2262
2263 Arguments:
2264 Four input formats are allowed:
2265 - fillcolor()
2266 Return the current fillcolor as color specification string,
2267 possibly in hex-number format (see example).
2268 May be used as input to another color/pencolor/fillcolor call.
2269 - fillcolor(colorstring)
2270 s is a Tk color specification string, such as "red" or "yellow"
2271 - fillcolor((r, g, b))
2272 *a tuple* of r, g, and b, which represent, an RGB color,
2273 and each of r, g, and b are in the range 0..colormode,
2274 where colormode is either 1.0 or 255
2275 - fillcolor(r, g, b)
2276 r, g, and b represent an RGB color, and each of r, g, and b
2277 are in the range 0..colormode
2278
2279 If turtleshape is a polygon, the interior of that polygon is drawn
2280 with the newly set fillcolor.
2281
2282 Example (for a Turtle instance named turtle):
2283 >>> turtle.fillcolor('violet')
2284 >>> col = turtle.pencolor()
2285 >>> turtle.fillcolor(col)
2286 >>> turtle.fillcolor(0, .5, 0)
2287 """
2288 if args:
2289 color = self._colorstr(args)
2290 if color == self._fillcolor:
2291 return
2292 self.pen(fillcolor=color)
2293 else:
2294 return self._color(self._fillcolor)
2295
2296 def showturtle(self):
2297 """Makes the turtle visible.
2298
2299 Aliases: showturtle | st
2300
2301 No argument.
2302
2303 Example (for a Turtle instance named turtle):
2304 >>> turtle.hideturtle()
2305 >>> turtle.showturtle()
2306 """
2307 self.pen(shown=True)
2308
2309 def hideturtle(self):
2310 """Makes the turtle invisible.
2311
2312 Aliases: hideturtle | ht
2313
2314 No argument.
2315
2316 It's a good idea to do this while you're in the
2317 middle of a complicated drawing, because hiding
2318 the turtle speeds up the drawing observably.
2319
2320 Example (for a Turtle instance named turtle):
2321 >>> turtle.hideturtle()
2322 """
2323 self.pen(shown=False)
2324
2325 def isvisible(self):
2326 """Return True if the Turtle is shown, False if it's hidden.
2327
2328 No argument.
2329
2330 Example (for a Turtle instance named turtle):
2331 >>> turtle.hideturtle()
2332 >>> print turtle.isvisible():
2333 False
2334 """
2335 return self._shown
2336
2337 def pen(self, pen=None, **pendict):
2338 """Return or set the pen's attributes.
2339
2340 Arguments:
2341 pen -- a dictionary with some or all of the below listed keys.
2342 **pendict -- one or more keyword-arguments with the below
2343 listed keys as keywords.
2344
2345 Return or set the pen's attributes in a 'pen-dictionary'
2346 with the following key/value pairs:
2347 "shown" : True/False
2348 "pendown" : True/False
2349 "pencolor" : color-string or color-tuple
2350 "fillcolor" : color-string or color-tuple
2351 "pensize" : positive number
2352 "speed" : number in range 0..10
2353 "resizemode" : "auto" or "user" or "noresize"
2354 "stretchfactor": (positive number, positive number)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002355 "shearfactor": number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002356 "outline" : positive number
2357 "tilt" : number
2358
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002359 This dictionary can be used as argument for a subsequent
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002360 pen()-call to restore the former pen-state. Moreover one
2361 or more of these attributes can be provided as keyword-arguments.
2362 This can be used to set several pen attributes in one statement.
Guido van Rossumb241b671998-12-04 16:42:46 +00002363
2364
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002365 Examples (for a Turtle instance named turtle):
2366 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2367 >>> turtle.pen()
2368 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2369 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002370 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002371 >>> penstate=turtle.pen()
2372 >>> turtle.color("yellow","")
2373 >>> turtle.penup()
2374 >>> turtle.pen()
2375 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2376 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002377 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002378 >>> p.pen(penstate, fillcolor="green")
2379 >>> p.pen()
2380 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2381 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002382 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002383 """
2384 _pd = {"shown" : self._shown,
2385 "pendown" : self._drawing,
2386 "pencolor" : self._pencolor,
2387 "fillcolor" : self._fillcolor,
2388 "pensize" : self._pensize,
2389 "speed" : self._speed,
2390 "resizemode" : self._resizemode,
2391 "stretchfactor" : self._stretchfactor,
Georg Brandleaa84ef2009-05-05 08:14:33 +00002392 "shearfactor" : self._shearfactor,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002393 "outline" : self._outlinewidth,
2394 "tilt" : self._tilt
2395 }
Guido van Rossumb241b671998-12-04 16:42:46 +00002396
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002397 if not (pen or pendict):
2398 return _pd
2399
2400 if isinstance(pen, dict):
2401 p = pen
2402 else:
2403 p = {}
2404 p.update(pendict)
2405
2406 _p_buf = {}
2407 for key in p:
2408 _p_buf[key] = _pd[key]
2409
2410 if self.undobuffer:
2411 self.undobuffer.push(("pen", _p_buf))
2412
2413 newLine = False
2414 if "pendown" in p:
2415 if self._drawing != p["pendown"]:
2416 newLine = True
2417 if "pencolor" in p:
2418 if isinstance(p["pencolor"], tuple):
2419 p["pencolor"] = self._colorstr((p["pencolor"],))
2420 if self._pencolor != p["pencolor"]:
2421 newLine = True
2422 if "pensize" in p:
2423 if self._pensize != p["pensize"]:
2424 newLine = True
2425 if newLine:
2426 self._newLine()
2427 if "pendown" in p:
2428 self._drawing = p["pendown"]
2429 if "pencolor" in p:
2430 self._pencolor = p["pencolor"]
2431 if "pensize" in p:
2432 self._pensize = p["pensize"]
2433 if "fillcolor" in p:
2434 if isinstance(p["fillcolor"], tuple):
2435 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2436 self._fillcolor = p["fillcolor"]
2437 if "speed" in p:
2438 self._speed = p["speed"]
2439 if "resizemode" in p:
2440 self._resizemode = p["resizemode"]
2441 if "stretchfactor" in p:
2442 sf = p["stretchfactor"]
2443 if isinstance(sf, (int, float)):
2444 sf = (sf, sf)
2445 self._stretchfactor = sf
Georg Brandleaa84ef2009-05-05 08:14:33 +00002446 if "shearfactor" in p:
2447 self._shearfactor = p["shearfactor"]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002448 if "outline" in p:
2449 self._outlinewidth = p["outline"]
2450 if "shown" in p:
2451 self._shown = p["shown"]
2452 if "tilt" in p:
2453 self._tilt = p["tilt"]
Georg Brandleaa84ef2009-05-05 08:14:33 +00002454 if "stretchfactor" in p or "tilt" in p or "shearfactor" in p:
2455 scx, scy = self._stretchfactor
2456 shf = self._shearfactor
2457 sa, ca = math.sin(self._tilt), math.cos(self._tilt)
2458 self._shapetrafo = ( scx*ca, scy*(shf*ca + sa),
2459 -scx*sa, scy*(ca - shf*sa))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002460 self._update()
2461
2462## three dummy methods to be implemented by child class:
2463
2464 def _newLine(self, usePos = True):
2465 """dummy method - to be overwritten by child class"""
2466 def _update(self, count=True, forced=False):
2467 """dummy method - to be overwritten by child class"""
2468 def _color(self, args):
2469 """dummy method - to be overwritten by child class"""
2470 def _colorstr(self, args):
2471 """dummy method - to be overwritten by child class"""
2472
2473 width = pensize
2474 up = penup
2475 pu = penup
2476 pd = pendown
2477 down = pendown
2478 st = showturtle
2479 ht = hideturtle
2480
2481
2482class _TurtleImage(object):
2483 """Helper class: Datatype to store Turtle attributes
2484 """
2485
2486 def __init__(self, screen, shapeIndex):
2487 self.screen = screen
2488 self._type = None
2489 self._setshape(shapeIndex)
2490
2491 def _setshape(self, shapeIndex):
Georg Brandleaa84ef2009-05-05 08:14:33 +00002492 screen = self.screen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002493 self.shapeIndex = shapeIndex
2494 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2495 return
2496 if self._type == "image" == screen._shapes[shapeIndex]._type:
2497 return
2498 if self._type in ["image", "polygon"]:
2499 screen._delete(self._item)
2500 elif self._type == "compound":
2501 for item in self._item:
2502 screen._delete(item)
2503 self._type = screen._shapes[shapeIndex]._type
2504 if self._type == "polygon":
2505 self._item = screen._createpoly()
2506 elif self._type == "image":
2507 self._item = screen._createimage(screen._shapes["blank"]._data)
2508 elif self._type == "compound":
2509 self._item = [screen._createpoly() for item in
2510 screen._shapes[shapeIndex]._data]
2511
2512
2513class RawTurtle(TPen, TNavigator):
2514 """Animation part of the RawTurtle.
2515 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002516 its animation.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002517 """
2518 screens = []
2519
2520 def __init__(self, canvas=None,
2521 shape=_CFG["shape"],
2522 undobuffersize=_CFG["undobuffersize"],
2523 visible=_CFG["visible"]):
Martin v. Löwis601149b2008-09-29 22:19:08 +00002524 if isinstance(canvas, _Screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002525 self.screen = canvas
2526 elif isinstance(canvas, TurtleScreen):
2527 if canvas not in RawTurtle.screens:
2528 RawTurtle.screens.append(canvas)
2529 self.screen = canvas
2530 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2531 for screen in RawTurtle.screens:
2532 if screen.cv == canvas:
2533 self.screen = screen
2534 break
2535 else:
2536 self.screen = TurtleScreen(canvas)
2537 RawTurtle.screens.append(self.screen)
2538 else:
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03002539 raise TurtleGraphicsError("bad canvas argument %s" % canvas)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002540
2541 screen = self.screen
2542 TNavigator.__init__(self, screen.mode())
2543 TPen.__init__(self)
2544 screen._turtles.append(self)
2545 self.drawingLineItem = screen._createline()
2546 self.turtle = _TurtleImage(screen, shape)
2547 self._poly = None
2548 self._creatingPoly = False
2549 self._fillitem = self._fillpath = None
2550 self._shown = visible
2551 self._hidden_from_screen = False
2552 self.currentLineItem = screen._createline()
2553 self.currentLine = [self._position]
2554 self.items = [self.currentLineItem]
2555 self.stampItems = []
2556 self._undobuffersize = undobuffersize
2557 self.undobuffer = Tbuffer(undobuffersize)
2558 self._update()
2559
2560 def reset(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002561 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002562
2563 No argument.
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002564
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002565 Delete the turtle's drawings from the screen, re-center the turtle
2566 and set variables to the default values.
2567
2568 Example (for a Turtle instance named turtle):
2569 >>> turtle.position()
2570 (0.00,-22.00)
2571 >>> turtle.heading()
2572 100.0
2573 >>> turtle.reset()
2574 >>> turtle.position()
2575 (0.00,0.00)
2576 >>> turtle.heading()
2577 0.0
2578 """
2579 TNavigator.reset(self)
2580 TPen._reset(self)
2581 self._clear()
2582 self._drawturtle()
2583 self._update()
2584
2585 def setundobuffer(self, size):
2586 """Set or disable undobuffer.
2587
2588 Argument:
2589 size -- an integer or None
2590
2591 If size is an integer an empty undobuffer of given size is installed.
2592 Size gives the maximum number of turtle-actions that can be undone
2593 by the undo() function.
2594 If size is None, no undobuffer is present.
2595
2596 Example (for a Turtle instance named turtle):
2597 >>> turtle.setundobuffer(42)
2598 """
Raymond Hettinger854e76e2014-07-20 21:30:32 -07002599 if size is None or size <= 0:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002600 self.undobuffer = None
2601 else:
2602 self.undobuffer = Tbuffer(size)
2603
2604 def undobufferentries(self):
2605 """Return count of entries in the undobuffer.
2606
2607 No argument.
2608
2609 Example (for a Turtle instance named turtle):
2610 >>> while undobufferentries():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002611 ... undo()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002612 """
2613 if self.undobuffer is None:
2614 return 0
2615 return self.undobuffer.nr_of_items()
2616
2617 def _clear(self):
2618 """Delete all of pen's drawings"""
2619 self._fillitem = self._fillpath = None
2620 for item in self.items:
2621 self.screen._delete(item)
2622 self.currentLineItem = self.screen._createline()
2623 self.currentLine = []
2624 if self._drawing:
2625 self.currentLine.append(self._position)
2626 self.items = [self.currentLineItem]
2627 self.clearstamps()
2628 self.setundobuffer(self._undobuffersize)
2629
2630
2631 def clear(self):
2632 """Delete the turtle's drawings from the screen. Do not move turtle.
2633
2634 No arguments.
2635
2636 Delete the turtle's drawings from the screen. Do not move turtle.
2637 State and position of the turtle as well as drawings of other
2638 turtles are not affected.
2639
2640 Examples (for a Turtle instance named turtle):
2641 >>> turtle.clear()
2642 """
2643 self._clear()
2644 self._update()
2645
2646 def _update_data(self):
2647 self.screen._incrementudc()
2648 if self.screen._updatecounter != 0:
2649 return
2650 if len(self.currentLine)>1:
2651 self.screen._drawline(self.currentLineItem, self.currentLine,
2652 self._pencolor, self._pensize)
2653
2654 def _update(self):
2655 """Perform a Turtle-data update.
2656 """
2657 screen = self.screen
2658 if screen._tracing == 0:
2659 return
2660 elif screen._tracing == 1:
2661 self._update_data()
2662 self._drawturtle()
2663 screen._update() # TurtleScreenBase
2664 screen._delay(screen._delayvalue) # TurtleScreenBase
2665 else:
2666 self._update_data()
2667 if screen._updatecounter == 0:
2668 for t in screen.turtles():
2669 t._drawturtle()
2670 screen._update()
2671
2672 def _tracer(self, flag=None, delay=None):
2673 """Turns turtle animation on/off and set delay for update drawings.
2674
2675 Optional arguments:
2676 n -- nonnegative integer
2677 delay -- nonnegative integer
2678
2679 If n is given, only each n-th regular screen update is really performed.
2680 (Can be used to accelerate the drawing of complex graphics.)
2681 Second arguments sets delay value (see RawTurtle.delay())
2682
2683 Example (for a Turtle instance named turtle):
2684 >>> turtle.tracer(8, 25)
2685 >>> dist = 2
2686 >>> for i in range(200):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002687 ... turtle.fd(dist)
2688 ... turtle.rt(90)
2689 ... dist += 2
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002690 """
2691 return self.screen.tracer(flag, delay)
2692
2693 def _color(self, args):
2694 return self.screen._color(args)
2695
2696 def _colorstr(self, args):
2697 return self.screen._colorstr(args)
2698
2699 def _cc(self, args):
2700 """Convert colortriples to hexstrings.
2701 """
2702 if isinstance(args, str):
2703 return args
2704 try:
2705 r, g, b = args
Serhiy Storchakacefa9172016-06-14 22:52:04 +03002706 except (TypeError, ValueError):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002707 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2708 if self.screen._colormode == 1.0:
2709 r, g, b = [round(255.0*x) for x in (r, g, b)]
2710 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2711 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2712 return "#%02x%02x%02x" % (r, g, b)
2713
2714 def clone(self):
2715 """Create and return a clone of the turtle.
2716
2717 No argument.
2718
2719 Create and return a clone of the turtle with same position, heading
2720 and turtle properties.
2721
2722 Example (for a Turtle instance named mick):
2723 mick = Turtle()
2724 joe = mick.clone()
2725 """
2726 screen = self.screen
2727 self._newLine(self._drawing)
2728
2729 turtle = self.turtle
2730 self.screen = None
2731 self.turtle = None # too make self deepcopy-able
2732
2733 q = deepcopy(self)
2734
2735 self.screen = screen
2736 self.turtle = turtle
2737
2738 q.screen = screen
2739 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2740
2741 screen._turtles.append(q)
2742 ttype = screen._shapes[self.turtle.shapeIndex]._type
2743 if ttype == "polygon":
2744 q.turtle._item = screen._createpoly()
2745 elif ttype == "image":
2746 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2747 elif ttype == "compound":
2748 q.turtle._item = [screen._createpoly() for item in
2749 screen._shapes[self.turtle.shapeIndex]._data]
2750 q.currentLineItem = screen._createline()
2751 q._update()
2752 return q
2753
2754 def shape(self, name=None):
2755 """Set turtle shape to shape with given name / return current shapename.
2756
2757 Optional argument:
2758 name -- a string, which is a valid shapename
2759
2760 Set turtle shape to shape with given name or, if name is not given,
2761 return name of current shape.
2762 Shape with name must exist in the TurtleScreen's shape dictionary.
2763 Initially there are the following polygon shapes:
2764 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2765 To learn about how to deal with shapes see Screen-method register_shape.
2766
2767 Example (for a Turtle instance named turtle):
2768 >>> turtle.shape()
2769 'arrow'
2770 >>> turtle.shape("turtle")
2771 >>> turtle.shape()
2772 'turtle'
2773 """
2774 if name is None:
2775 return self.turtle.shapeIndex
2776 if not name in self.screen.getshapes():
2777 raise TurtleGraphicsError("There is no shape named %s" % name)
2778 self.turtle._setshape(name)
2779 self._update()
2780
2781 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2782 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2783
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03002784 Optional arguments:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002785 stretch_wid : positive number
2786 stretch_len : positive number
2787 outline : positive number
2788
2789 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2790 Set resizemode to "user".
2791 If and only if resizemode is set to "user", the turtle will be displayed
2792 stretched according to its stretchfactors:
2793 stretch_wid is stretchfactor perpendicular to orientation
2794 stretch_len is stretchfactor in direction of turtles orientation.
2795 outline determines the width of the shapes's outline.
2796
2797 Examples (for a Turtle instance named turtle):
2798 >>> turtle.resizemode("user")
2799 >>> turtle.shapesize(5, 5, 12)
2800 >>> turtle.shapesize(outline=8)
2801 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00002802 if stretch_wid is stretch_len is outline is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002803 stretch_wid, stretch_len = self._stretchfactor
2804 return stretch_wid, stretch_len, self._outlinewidth
Georg Brandleaa84ef2009-05-05 08:14:33 +00002805 if stretch_wid == 0 or stretch_len == 0:
2806 raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero")
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002807 if stretch_wid is not None:
2808 if stretch_len is None:
2809 stretchfactor = stretch_wid, stretch_wid
2810 else:
2811 stretchfactor = stretch_wid, stretch_len
2812 elif stretch_len is not None:
2813 stretchfactor = self._stretchfactor[0], stretch_len
2814 else:
2815 stretchfactor = self._stretchfactor
2816 if outline is None:
2817 outline = self._outlinewidth
2818 self.pen(resizemode="user",
2819 stretchfactor=stretchfactor, outline=outline)
2820
Georg Brandleaa84ef2009-05-05 08:14:33 +00002821 def shearfactor(self, shear=None):
2822 """Set or return the current shearfactor.
2823
2824 Optional argument: shear -- number, tangent of the shear angle
2825
2826 Shear the turtleshape according to the given shearfactor shear,
2827 which is the tangent of the shear angle. DO NOT change the
2828 turtle's heading (direction of movement).
2829 If shear is not given: return the current shearfactor, i. e. the
2830 tangent of the shear angle, by which lines parallel to the
2831 heading of the turtle are sheared.
2832
2833 Examples (for a Turtle instance named turtle):
2834 >>> turtle.shape("circle")
2835 >>> turtle.shapesize(5,2)
2836 >>> turtle.shearfactor(0.5)
2837 >>> turtle.shearfactor()
2838 >>> 0.5
2839 """
2840 if shear is None:
2841 return self._shearfactor
2842 self.pen(resizemode="user", shearfactor=shear)
2843
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002844 def settiltangle(self, angle):
2845 """Rotate the turtleshape to point in the specified direction
2846
Georg Brandleaa84ef2009-05-05 08:14:33 +00002847 Argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002848
2849 Rotate the turtleshape to point in the direction specified by angle,
2850 regardless of its current tilt-angle. DO NOT change the turtle's
2851 heading (direction of movement).
2852
2853
2854 Examples (for a Turtle instance named turtle):
2855 >>> turtle.shape("circle")
2856 >>> turtle.shapesize(5,2)
2857 >>> turtle.settiltangle(45)
2858 >>> stamp()
2859 >>> turtle.fd(50)
2860 >>> turtle.settiltangle(-45)
2861 >>> stamp()
2862 >>> turtle.fd(50)
2863 """
2864 tilt = -angle * self._degreesPerAU * self._angleOrient
Marek Madejski6844b562020-09-01 18:42:41 +02002865 tilt = math.radians(tilt) % math.tau
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002866 self.pen(resizemode="user", tilt=tilt)
2867
Georg Brandleaa84ef2009-05-05 08:14:33 +00002868 def tiltangle(self, angle=None):
2869 """Set or return the current tilt-angle.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002870
Georg Brandleaa84ef2009-05-05 08:14:33 +00002871 Optional argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002872
Georg Brandleaa84ef2009-05-05 08:14:33 +00002873 Rotate the turtleshape to point in the direction specified by angle,
2874 regardless of its current tilt-angle. DO NOT change the turtle's
2875 heading (direction of movement).
2876 If angle is not given: return the current tilt-angle, i. e. the angle
2877 between the orientation of the turtleshape and the heading of the
2878 turtle (its direction of movement).
2879
2880 Deprecated since Python 3.1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002881
2882 Examples (for a Turtle instance named turtle):
2883 >>> turtle.shape("circle")
2884 >>> turtle.shapesize(5,2)
2885 >>> turtle.tilt(45)
2886 >>> turtle.tiltangle()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002887 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00002888 if angle is None:
Marek Madejski6844b562020-09-01 18:42:41 +02002889 tilt = -math.degrees(self._tilt) * self._angleOrient
Georg Brandleaa84ef2009-05-05 08:14:33 +00002890 return (tilt / self._degreesPerAU) % self._fullcircle
2891 else:
2892 self.settiltangle(angle)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002893
2894 def tilt(self, angle):
2895 """Rotate the turtleshape by angle.
2896
2897 Argument:
2898 angle - a number
2899
2900 Rotate the turtleshape by angle from its current tilt-angle,
2901 but do NOT change the turtle's heading (direction of movement).
2902
2903 Examples (for a Turtle instance named turtle):
2904 >>> turtle.shape("circle")
2905 >>> turtle.shapesize(5,2)
2906 >>> turtle.tilt(30)
2907 >>> turtle.fd(50)
2908 >>> turtle.tilt(30)
2909 >>> turtle.fd(50)
2910 """
2911 self.settiltangle(angle + self.tiltangle())
2912
Georg Brandleaa84ef2009-05-05 08:14:33 +00002913 def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2914 """Set or return the current transformation matrix of the turtle shape.
2915
2916 Optional arguments: t11, t12, t21, t22 -- numbers.
2917
2918 If none of the matrix elements are given, return the transformation
2919 matrix.
2920 Otherwise set the given elements and transform the turtleshape
2921 according to the matrix consisting of first row t11, t12 and
2922 second row t21, 22.
2923 Modify stretchfactor, shearfactor and tiltangle according to the
2924 given matrix.
2925
2926 Examples (for a Turtle instance named turtle):
2927 >>> turtle.shape("square")
2928 >>> turtle.shapesize(4,2)
2929 >>> turtle.shearfactor(-0.5)
2930 >>> turtle.shapetransform()
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002931 (4.0, -1.0, -0.0, 2.0)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002932 """
2933 if t11 is t12 is t21 is t22 is None:
2934 return self._shapetrafo
2935 m11, m12, m21, m22 = self._shapetrafo
2936 if t11 is not None: m11 = t11
2937 if t12 is not None: m12 = t12
2938 if t21 is not None: m21 = t21
2939 if t22 is not None: m22 = t22
2940 if t11 * t22 - t12 * t21 == 0:
2941 raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2942 self._shapetrafo = (m11, m12, m21, m22)
Marek Madejski6844b562020-09-01 18:42:41 +02002943 alfa = math.atan2(-m21, m11) % math.tau
Georg Brandleaa84ef2009-05-05 08:14:33 +00002944 sa, ca = math.sin(alfa), math.cos(alfa)
2945 a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2946 sa*m11 + ca*m21, sa*m12 + ca*m22)
2947 self._stretchfactor = a11, a22
2948 self._shearfactor = a12/a22
2949 self._tilt = alfa
Raymond Hettinger6dec4ea2014-06-22 01:21:51 -07002950 self.pen(resizemode="user")
Georg Brandleaa84ef2009-05-05 08:14:33 +00002951
2952
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002953 def _polytrafo(self, poly):
2954 """Computes transformed polygon shapes from a shape
2955 according to current position and heading.
2956 """
2957 screen = self.screen
2958 p0, p1 = self._position
2959 e0, e1 = self._orient
2960 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2961 e0, e1 = (1.0 / abs(e)) * e
2962 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2963 for (x, y) in poly]
2964
Georg Brandleaa84ef2009-05-05 08:14:33 +00002965 def get_shapepoly(self):
2966 """Return the current shape polygon as tuple of coordinate pairs.
2967
2968 No argument.
2969
2970 Examples (for a Turtle instance named turtle):
2971 >>> turtle.shape("square")
2972 >>> turtle.shapetransform(4, -1, 0, 2)
2973 >>> turtle.get_shapepoly()
2974 ((50, -20), (30, 20), (-50, 20), (-30, -20))
2975
2976 """
2977 shape = self.screen._shapes[self.turtle.shapeIndex]
2978 if shape._type == "polygon":
2979 return self._getshapepoly(shape._data, shape._type == "compound")
2980 # else return None
2981
2982 def _getshapepoly(self, polygon, compound=False):
2983 """Calculate transformed shape polygon according to resizemode
2984 and shapetransform.
2985 """
2986 if self._resizemode == "user" or compound:
2987 t11, t12, t21, t22 = self._shapetrafo
2988 elif self._resizemode == "auto":
2989 l = max(1, self._pensize/5.0)
2990 t11, t12, t21, t22 = l, 0, 0, l
2991 elif self._resizemode == "noresize":
2992 return polygon
Jon Dufresne39726282017-05-18 07:35:54 -07002993 return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002994
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002995 def _drawturtle(self):
2996 """Manages the correct rendering of the turtle with respect to
Mark Dickinson934896d2009-02-21 20:59:32 +00002997 its shape, resizemode, stretch and tilt etc."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002998 screen = self.screen
2999 shape = screen._shapes[self.turtle.shapeIndex]
3000 ttype = shape._type
3001 titem = self.turtle._item
3002 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3003 self._hidden_from_screen = False
3004 tshape = shape._data
3005 if ttype == "polygon":
Georg Brandleaa84ef2009-05-05 08:14:33 +00003006 if self._resizemode == "noresize": w = 1
3007 elif self._resizemode == "auto": w = self._pensize
3008 else: w =self._outlinewidth
3009 shape = self._polytrafo(self._getshapepoly(tshape))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003010 fc, oc = self._fillcolor, self._pencolor
3011 screen._drawpoly(titem, shape, fill=fc, outline=oc,
3012 width=w, top=True)
3013 elif ttype == "image":
3014 screen._drawimage(titem, self._position, tshape)
3015 elif ttype == "compound":
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003016 for item, (poly, fc, oc) in zip(titem, tshape):
Georg Brandleaa84ef2009-05-05 08:14:33 +00003017 poly = self._polytrafo(self._getshapepoly(poly, True))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003018 screen._drawpoly(item, poly, fill=self._cc(fc),
Georg Brandleaa84ef2009-05-05 08:14:33 +00003019 outline=self._cc(oc), width=self._outlinewidth, top=True)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003020 else:
3021 if self._hidden_from_screen:
3022 return
3023 if ttype == "polygon":
3024 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3025 elif ttype == "image":
3026 screen._drawimage(titem, self._position,
3027 screen._shapes["blank"]._data)
3028 elif ttype == "compound":
3029 for item in titem:
3030 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3031 self._hidden_from_screen = True
3032
3033############################## stamp stuff ###############################
3034
3035 def stamp(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00003036 """Stamp a copy of the turtleshape onto the canvas and return its id.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003037
3038 No argument.
3039
3040 Stamp a copy of the turtle shape onto the canvas at the current
3041 turtle position. Return a stamp_id for that stamp, which can be
3042 used to delete it by calling clearstamp(stamp_id).
3043
3044 Example (for a Turtle instance named turtle):
3045 >>> turtle.color("blue")
3046 >>> turtle.stamp()
3047 13
3048 >>> turtle.fd(50)
3049 """
3050 screen = self.screen
3051 shape = screen._shapes[self.turtle.shapeIndex]
3052 ttype = shape._type
3053 tshape = shape._data
3054 if ttype == "polygon":
3055 stitem = screen._createpoly()
Georg Brandleaa84ef2009-05-05 08:14:33 +00003056 if self._resizemode == "noresize": w = 1
3057 elif self._resizemode == "auto": w = self._pensize
3058 else: w =self._outlinewidth
3059 shape = self._polytrafo(self._getshapepoly(tshape))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003060 fc, oc = self._fillcolor, self._pencolor
3061 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3062 width=w, top=True)
3063 elif ttype == "image":
3064 stitem = screen._createimage("")
3065 screen._drawimage(stitem, self._position, tshape)
3066 elif ttype == "compound":
3067 stitem = []
3068 for element in tshape:
3069 item = screen._createpoly()
3070 stitem.append(item)
3071 stitem = tuple(stitem)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003072 for item, (poly, fc, oc) in zip(stitem, tshape):
Georg Brandleaa84ef2009-05-05 08:14:33 +00003073 poly = self._polytrafo(self._getshapepoly(poly, True))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003074 screen._drawpoly(item, poly, fill=self._cc(fc),
Georg Brandleaa84ef2009-05-05 08:14:33 +00003075 outline=self._cc(oc), width=self._outlinewidth, top=True)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003076 self.stampItems.append(stitem)
3077 self.undobuffer.push(("stamp", stitem))
3078 return stitem
3079
3080 def _clearstamp(self, stampid):
3081 """does the work for clearstamp() and clearstamps()
3082 """
3083 if stampid in self.stampItems:
3084 if isinstance(stampid, tuple):
3085 for subitem in stampid:
3086 self.screen._delete(subitem)
3087 else:
3088 self.screen._delete(stampid)
3089 self.stampItems.remove(stampid)
3090 # Delete stampitem from undobuffer if necessary
3091 # if clearstamp is called directly.
3092 item = ("stamp", stampid)
3093 buf = self.undobuffer
3094 if item not in buf.buffer:
3095 return
3096 index = buf.buffer.index(item)
3097 buf.buffer.remove(item)
3098 if index <= buf.ptr:
3099 buf.ptr = (buf.ptr - 1) % buf.bufsize
3100 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3101
3102 def clearstamp(self, stampid):
3103 """Delete stamp with given stampid
3104
3105 Argument:
3106 stampid - an integer, must be return value of previous stamp() call.
3107
3108 Example (for a Turtle instance named turtle):
3109 >>> turtle.color("blue")
3110 >>> astamp = turtle.stamp()
3111 >>> turtle.fd(50)
3112 >>> turtle.clearstamp(astamp)
3113 """
3114 self._clearstamp(stampid)
3115 self._update()
3116
3117 def clearstamps(self, n=None):
3118 """Delete all or first/last n of turtle's stamps.
3119
3120 Optional argument:
3121 n -- an integer
3122
3123 If n is None, delete all of pen's stamps,
3124 else if n > 0 delete first n stamps
3125 else if n < 0 delete last n stamps.
3126
3127 Example (for a Turtle instance named turtle):
3128 >>> for i in range(8):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003129 ... turtle.stamp(); turtle.fd(30)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003130 ...
3131 >>> turtle.clearstamps(2)
3132 >>> turtle.clearstamps(-2)
3133 >>> turtle.clearstamps()
3134 """
3135 if n is None:
3136 toDelete = self.stampItems[:]
3137 elif n >= 0:
3138 toDelete = self.stampItems[:n]
3139 else:
3140 toDelete = self.stampItems[n:]
3141 for item in toDelete:
3142 self._clearstamp(item)
3143 self._update()
3144
3145 def _goto(self, end):
3146 """Move the pen to the point end, thereby drawing a line
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03003147 if pen is down. All other methods for turtle movement depend
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003148 on this one.
3149 """
Alexander Belopolsky1842d0c2010-10-28 20:13:52 +00003150 ## Version with undo-stuff
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003151 go_modes = ( self._drawing,
3152 self._pencolor,
3153 self._pensize,
3154 isinstance(self._fillpath, list))
3155 screen = self.screen
3156 undo_entry = ("go", self._position, end, go_modes,
3157 (self.currentLineItem,
3158 self.currentLine[:],
3159 screen._pointlist(self.currentLineItem),
3160 self.items[:])
3161 )
3162 if self.undobuffer:
3163 self.undobuffer.push(undo_entry)
3164 start = self._position
3165 if self._speed and screen._tracing == 1:
3166 diff = (end-start)
3167 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3168 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3169 delta = diff * (1.0/nhops)
3170 for n in range(1, nhops):
3171 if n == 1:
3172 top = True
3173 else:
3174 top = False
3175 self._position = start + delta * n
3176 if self._drawing:
3177 screen._drawline(self.drawingLineItem,
3178 (start, self._position),
3179 self._pencolor, self._pensize, top)
3180 self._update()
3181 if self._drawing:
3182 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3183 fill="", width=self._pensize)
3184 # Turtle now at end,
3185 if self._drawing: # now update currentLine
3186 self.currentLine.append(end)
3187 if isinstance(self._fillpath, list):
3188 self._fillpath.append(end)
3189 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3190 self._position = end
3191 if self._creatingPoly:
3192 self._poly.append(end)
3193 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3194 # of life, the universe and everything
3195 self._newLine()
3196 self._update() #count=True)
3197
3198 def _undogoto(self, entry):
3199 """Reverse a _goto. Used for undo()
3200 """
3201 old, new, go_modes, coodata = entry
3202 drawing, pc, ps, filling = go_modes
3203 cLI, cL, pl, items = coodata
3204 screen = self.screen
3205 if abs(self._position - new) > 0.5:
3206 print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3207 # restore former situation
3208 self.currentLineItem = cLI
3209 self.currentLine = cL
3210
3211 if pl == [(0, 0), (0, 0)]:
3212 usepc = ""
3213 else:
3214 usepc = pc
3215 screen._drawline(cLI, pl, fill=usepc, width=ps)
3216
3217 todelete = [i for i in self.items if (i not in items) and
3218 (screen._type(i) == "line")]
3219 for i in todelete:
3220 screen._delete(i)
3221 self.items.remove(i)
3222
3223 start = old
3224 if self._speed and screen._tracing == 1:
3225 diff = old - new
3226 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3227 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3228 delta = diff * (1.0/nhops)
3229 for n in range(1, nhops):
3230 if n == 1:
3231 top = True
3232 else:
3233 top = False
3234 self._position = new + delta * n
3235 if drawing:
3236 screen._drawline(self.drawingLineItem,
3237 (start, self._position),
3238 pc, ps, top)
3239 self._update()
3240 if drawing:
3241 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3242 fill="", width=ps)
3243 # Turtle now at position old,
3244 self._position = old
Ezio Melotti13925002011-03-16 11:05:33 +02003245 ## if undo is done during creating a polygon, the last vertex
3246 ## will be deleted. if the polygon is entirely deleted,
3247 ## creatingPoly will be set to False.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003248 ## Polygons created before the last one will not be affected by undo()
3249 if self._creatingPoly:
3250 if len(self._poly) > 0:
3251 self._poly.pop()
3252 if self._poly == []:
3253 self._creatingPoly = False
3254 self._poly = None
3255 if filling:
3256 if self._fillpath == []:
3257 self._fillpath = None
3258 print("Unwahrscheinlich in _undogoto!")
3259 elif self._fillpath is not None:
3260 self._fillpath.pop()
3261 self._update() #count=True)
3262
3263 def _rotate(self, angle):
3264 """Turns pen clockwise by angle.
3265 """
3266 if self.undobuffer:
3267 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3268 angle *= self._degreesPerAU
3269 neworient = self._orient.rotate(angle)
3270 tracing = self.screen._tracing
3271 if tracing == 1 and self._speed > 0:
3272 anglevel = 3.0 * self._speed
3273 steps = 1 + int(abs(angle)/anglevel)
3274 delta = 1.0*angle/steps
3275 for _ in range(steps):
3276 self._orient = self._orient.rotate(delta)
3277 self._update()
3278 self._orient = neworient
3279 self._update()
3280
3281 def _newLine(self, usePos=True):
3282 """Closes current line item and starts a new one.
3283 Remark: if current line became too long, animation
3284 performance (via _drawline) slowed down considerably.
3285 """
3286 if len(self.currentLine) > 1:
3287 self.screen._drawline(self.currentLineItem, self.currentLine,
3288 self._pencolor, self._pensize)
3289 self.currentLineItem = self.screen._createline()
3290 self.items.append(self.currentLineItem)
3291 else:
3292 self.screen._drawline(self.currentLineItem, top=True)
3293 self.currentLine = []
3294 if usePos:
3295 self.currentLine = [self._position]
3296
3297 def filling(self):
3298 """Return fillstate (True if filling, False else).
3299
3300 No argument.
3301
3302 Example (for a Turtle instance named turtle):
3303 >>> turtle.begin_fill()
3304 >>> if turtle.filling():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003305 ... turtle.pensize(5)
3306 ... else:
3307 ... turtle.pensize(3)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003308 """
3309 return isinstance(self._fillpath, list)
3310
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003311 def begin_fill(self):
3312 """Called just before drawing a shape to be filled.
3313
3314 No argument.
3315
3316 Example (for a Turtle instance named turtle):
3317 >>> turtle.color("black", "red")
3318 >>> turtle.begin_fill()
3319 >>> turtle.circle(60)
3320 >>> turtle.end_fill()
3321 """
3322 if not self.filling():
3323 self._fillitem = self.screen._createpoly()
3324 self.items.append(self._fillitem)
3325 self._fillpath = [self._position]
3326 self._newLine()
3327 if self.undobuffer:
3328 self.undobuffer.push(("beginfill", self._fillitem))
3329 self._update()
3330
3331
3332 def end_fill(self):
3333 """Fill the shape drawn after the call begin_fill().
3334
3335 No argument.
3336
3337 Example (for a Turtle instance named turtle):
3338 >>> turtle.color("black", "red")
3339 >>> turtle.begin_fill()
3340 >>> turtle.circle(60)
3341 >>> turtle.end_fill()
3342 """
3343 if self.filling():
3344 if len(self._fillpath) > 2:
3345 self.screen._drawpoly(self._fillitem, self._fillpath,
3346 fill=self._fillcolor)
3347 if self.undobuffer:
3348 self.undobuffer.push(("dofill", self._fillitem))
3349 self._fillitem = self._fillpath = None
3350 self._update()
3351
3352 def dot(self, size=None, *color):
3353 """Draw a dot with diameter size, using color.
3354
Ezio Melotti42da6632011-03-15 05:18:48 +02003355 Optional arguments:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003356 size -- an integer >= 1 (if given)
3357 color -- a colorstring or a numeric color tuple
3358
3359 Draw a circular dot with diameter size, using color.
3360 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3361
3362 Example (for a Turtle instance named turtle):
3363 >>> turtle.dot()
3364 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3365 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003366 if not color:
3367 if isinstance(size, (str, tuple)):
3368 color = self._colorstr(size)
3369 size = self._pensize + max(self._pensize, 4)
3370 else:
3371 color = self._pencolor
3372 if not size:
3373 size = self._pensize + max(self._pensize, 4)
3374 else:
3375 if size is None:
3376 size = self._pensize + max(self._pensize, 4)
3377 color = self._colorstr(color)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003378 if hasattr(self.screen, "_dot"):
3379 item = self.screen._dot(self._position, size, color)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003380 self.items.append(item)
3381 if self.undobuffer:
3382 self.undobuffer.push(("dot", item))
3383 else:
3384 pen = self.pen()
3385 if self.undobuffer:
3386 self.undobuffer.push(["seq"])
3387 self.undobuffer.cumulate = True
3388 try:
3389 if self.resizemode() == 'auto':
3390 self.ht()
3391 self.pendown()
3392 self.pensize(size)
3393 self.pencolor(color)
3394 self.forward(0)
3395 finally:
3396 self.pen(pen)
3397 if self.undobuffer:
3398 self.undobuffer.cumulate = False
3399
3400 def _write(self, txt, align, font):
3401 """Performs the writing for write()
3402 """
3403 item, end = self.screen._write(self._position, txt, align, font,
3404 self._pencolor)
3405 self.items.append(item)
3406 if self.undobuffer:
3407 self.undobuffer.push(("wri", item))
3408 return end
3409
3410 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3411 """Write text at the current turtle position.
3412
3413 Arguments:
3414 arg -- info, which is to be written to the TurtleScreen
3415 move (optional) -- True/False
3416 align (optional) -- one of the strings "left", "center" or right"
3417 font (optional) -- a triple (fontname, fontsize, fonttype)
3418
3419 Write text - the string representation of arg - at the current
3420 turtle position according to align ("left", "center" or right")
3421 and with the given font.
3422 If move is True, the pen is moved to the bottom-right corner
3423 of the text. By default, move is False.
3424
3425 Example (for a Turtle instance named turtle):
3426 >>> turtle.write('Home = ', True, align="center")
3427 >>> turtle.write((0,0), True)
3428 """
3429 if self.undobuffer:
3430 self.undobuffer.push(["seq"])
3431 self.undobuffer.cumulate = True
3432 end = self._write(str(arg), align.lower(), font)
3433 if move:
3434 x, y = self.pos()
3435 self.setpos(end, y)
3436 if self.undobuffer:
3437 self.undobuffer.cumulate = False
3438
3439 def begin_poly(self):
3440 """Start recording the vertices of a polygon.
3441
3442 No argument.
3443
3444 Start recording the vertices of a polygon. Current turtle position
3445 is first point of polygon.
3446
3447 Example (for a Turtle instance named turtle):
3448 >>> turtle.begin_poly()
3449 """
3450 self._poly = [self._position]
3451 self._creatingPoly = True
3452
3453 def end_poly(self):
3454 """Stop recording the vertices of a polygon.
3455
3456 No argument.
3457
3458 Stop recording the vertices of a polygon. Current turtle position is
3459 last point of polygon. This will be connected with the first point.
3460
3461 Example (for a Turtle instance named turtle):
3462 >>> turtle.end_poly()
3463 """
3464 self._creatingPoly = False
3465
3466 def get_poly(self):
3467 """Return the lastly recorded polygon.
3468
3469 No argument.
3470
3471 Example (for a Turtle instance named turtle):
3472 >>> p = turtle.get_poly()
3473 >>> turtle.register_shape("myFavouriteShape", p)
3474 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00003475 ## check if there is any poly?
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003476 if self._poly is not None:
3477 return tuple(self._poly)
3478
3479 def getscreen(self):
3480 """Return the TurtleScreen object, the turtle is drawing on.
3481
3482 No argument.
3483
3484 Return the TurtleScreen object, the turtle is drawing on.
3485 So TurtleScreen-methods can be called for that object.
3486
3487 Example (for a Turtle instance named turtle):
3488 >>> ts = turtle.getscreen()
3489 >>> ts
3490 <turtle.TurtleScreen object at 0x0106B770>
3491 >>> ts.bgcolor("pink")
3492 """
3493 return self.screen
3494
3495 def getturtle(self):
3496 """Return the Turtleobject itself.
3497
3498 No argument.
3499
3500 Only reasonable use: as a function to return the 'anonymous turtle':
3501
3502 Example:
3503 >>> pet = getturtle()
3504 >>> pet.fd(50)
3505 >>> pet
3506 <turtle.Turtle object at 0x0187D810>
3507 >>> turtles()
3508 [<turtle.Turtle object at 0x0187D810>]
3509 """
3510 return self
3511
3512 getpen = getturtle
3513
3514
3515 ################################################################
3516 ### screen oriented methods recurring to methods of TurtleScreen
3517 ################################################################
3518
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003519 def _delay(self, delay=None):
3520 """Set delay value which determines speed of turtle animation.
3521 """
3522 return self.screen.delay(delay)
3523
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003524 def onclick(self, fun, btn=1, add=None):
3525 """Bind fun to mouse-click event on this turtle on canvas.
3526
3527 Arguments:
3528 fun -- a function with two arguments, to which will be assigned
3529 the coordinates of the clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05303530 btn -- number of the mouse-button defaults to 1 (left mouse button).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003531 add -- True or False. If True, new binding will be added, otherwise
3532 it will replace a former binding.
3533
3534 Example for the anonymous turtle, i. e. the procedural way:
3535
3536 >>> def turn(x, y):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003537 ... left(360)
3538 ...
3539 >>> onclick(turn) # Now clicking into the turtle will turn it.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003540 >>> onclick(None) # event-binding will be removed
3541 """
3542 self.screen._onclick(self.turtle._item, fun, btn, add)
3543 self._update()
3544
3545 def onrelease(self, fun, btn=1, add=None):
3546 """Bind fun to mouse-button-release event on this turtle on canvas.
3547
3548 Arguments:
3549 fun -- a function with two arguments, to which will be assigned
3550 the coordinates of the clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05303551 btn -- number of the mouse-button defaults to 1 (left mouse button).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003552
3553 Example (for a MyTurtle instance named joe):
3554 >>> class MyTurtle(Turtle):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003555 ... def glow(self,x,y):
3556 ... self.fillcolor("red")
3557 ... def unglow(self,x,y):
3558 ... self.fillcolor("")
3559 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003560 >>> joe = MyTurtle()
3561 >>> joe.onclick(joe.glow)
3562 >>> joe.onrelease(joe.unglow)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003563
3564 Clicking on joe turns fillcolor red, unclicking turns it to
3565 transparent.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003566 """
3567 self.screen._onrelease(self.turtle._item, fun, btn, add)
3568 self._update()
3569
3570 def ondrag(self, fun, btn=1, add=None):
3571 """Bind fun to mouse-move event on this turtle on canvas.
3572
3573 Arguments:
3574 fun -- a function with two arguments, to which will be assigned
3575 the coordinates of the clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05303576 btn -- number of the mouse-button defaults to 1 (left mouse button).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003577
3578 Every sequence of mouse-move-events on a turtle is preceded by a
3579 mouse-click event on that turtle.
3580
3581 Example (for a Turtle instance named turtle):
3582 >>> turtle.ondrag(turtle.goto)
3583
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003584 Subsequently clicking and dragging a Turtle will move it
3585 across the screen thereby producing handdrawings (if pen is
3586 down).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003587 """
3588 self.screen._ondrag(self.turtle._item, fun, btn, add)
3589
3590
3591 def _undo(self, action, data):
3592 """Does the main part of the work for undo()
3593 """
3594 if self.undobuffer is None:
3595 return
3596 if action == "rot":
3597 angle, degPAU = data
3598 self._rotate(-angle*degPAU/self._degreesPerAU)
3599 dummy = self.undobuffer.pop()
3600 elif action == "stamp":
3601 stitem = data[0]
3602 self.clearstamp(stitem)
3603 elif action == "go":
3604 self._undogoto(data)
3605 elif action in ["wri", "dot"]:
3606 item = data[0]
3607 self.screen._delete(item)
3608 self.items.remove(item)
3609 elif action == "dofill":
3610 item = data[0]
3611 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3612 fill="", outline="")
3613 elif action == "beginfill":
3614 item = data[0]
3615 self._fillitem = self._fillpath = None
3616 if item in self.items:
3617 self.screen._delete(item)
3618 self.items.remove(item)
3619 elif action == "pen":
3620 TPen.pen(self, data[0])
3621 self.undobuffer.pop()
3622
3623 def undo(self):
3624 """undo (repeatedly) the last turtle action.
3625
3626 No argument.
3627
3628 undo (repeatedly) the last turtle action.
3629 Number of available undo actions is determined by the size of
3630 the undobuffer.
3631
3632 Example (for a Turtle instance named turtle):
3633 >>> for i in range(4):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003634 ... turtle.fd(50); turtle.lt(80)
3635 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003636 >>> for i in range(8):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003637 ... turtle.undo()
3638 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003639 """
3640 if self.undobuffer is None:
3641 return
3642 item = self.undobuffer.pop()
3643 action = item[0]
3644 data = item[1:]
3645 if action == "seq":
3646 while data:
3647 item = data.pop()
3648 self._undo(item[0], item[1:])
3649 else:
3650 self._undo(action, data)
3651
3652 turtlesize = shapesize
3653
3654RawPen = RawTurtle
3655
Martin v. Löwis601149b2008-09-29 22:19:08 +00003656### Screen - Singleton ########################
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003657
Martin v. Löwis601149b2008-09-29 22:19:08 +00003658def Screen():
3659 """Return the singleton screen object.
3660 If none exists at the moment, create a new one and return it,
3661 else return the existing one."""
3662 if Turtle._screen is None:
3663 Turtle._screen = _Screen()
3664 return Turtle._screen
3665
3666class _Screen(TurtleScreen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003667
3668 _root = None
3669 _canvas = None
3670 _title = _CFG["title"]
3671
Guido van Rossumb241b671998-12-04 16:42:46 +00003672 def __init__(self):
Martin v. Löwis601149b2008-09-29 22:19:08 +00003673 # XXX there is no need for this code to be conditional,
3674 # as there will be only a single _Screen instance, anyway
3675 # XXX actually, the turtle demo is injecting root window,
3676 # so perhaps the conditional creation of a root should be
3677 # preserved (perhaps by passing it as an optional parameter)
3678 if _Screen._root is None:
3679 _Screen._root = self._root = _Root()
3680 self._root.title(_Screen._title)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003681 self._root.ondestroy(self._destroy)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003682 if _Screen._canvas is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003683 width = _CFG["width"]
3684 height = _CFG["height"]
3685 canvwidth = _CFG["canvwidth"]
3686 canvheight = _CFG["canvheight"]
3687 leftright = _CFG["leftright"]
3688 topbottom = _CFG["topbottom"]
3689 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003690 _Screen._canvas = self._root._getcanvas()
Martin v. Löwis601149b2008-09-29 22:19:08 +00003691 TurtleScreen.__init__(self, _Screen._canvas)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003692 self.setup(width, height, leftright, topbottom)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003693
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003694 def setup(self, width=_CFG["width"], height=_CFG["height"],
3695 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3696 """ Set the size and position of the main window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003697
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003698 Arguments:
3699 width: as integer a size in pixels, as float a fraction of the screen.
3700 Default is 50% of screen.
3701 height: as integer the height in pixels, as float a fraction of the
3702 screen. Default is 75% of screen.
3703 startx: if positive, starting position in pixels from the left
3704 edge of the screen, if negative from the right edge
3705 Default, startx=None is to center window horizontally.
3706 starty: if positive, starting position in pixels from the top
3707 edge of the screen, if negative from the bottom edge
3708 Default, starty=None is to center window vertically.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003709
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003710 Examples (for a Screen instance named screen):
3711 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3712
3713 sets window to 200x200 pixels, in upper left of screen
3714
3715 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3716
3717 sets window to 75% of screen by 50% of screen and centers
3718 """
3719 if not hasattr(self._root, "set_geometry"):
3720 return
3721 sw = self._root.win_width()
3722 sh = self._root.win_height()
3723 if isinstance(width, float) and 0 <= width <= 1:
3724 width = sw*width
3725 if startx is None:
3726 startx = (sw - width) / 2
3727 if isinstance(height, float) and 0 <= height <= 1:
3728 height = sh*height
3729 if starty is None:
3730 starty = (sh - height) / 2
3731 self._root.set_geometry(width, height, startx, starty)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003732 self.update()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003733
3734 def title(self, titlestring):
3735 """Set title of turtle-window
3736
3737 Argument:
3738 titlestring -- a string, to appear in the titlebar of the
3739 turtle graphics window.
3740
3741 This is a method of Screen-class. Not available for TurtleScreen-
3742 objects.
3743
3744 Example (for a Screen instance named screen):
3745 >>> screen.title("Welcome to the turtle-zoo!")
3746 """
Martin v. Löwis601149b2008-09-29 22:19:08 +00003747 if _Screen._root is not None:
3748 _Screen._root.title(titlestring)
3749 _Screen._title = titlestring
Guido van Rossumb241b671998-12-04 16:42:46 +00003750
3751 def _destroy(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003752 root = self._root
Martin v. Löwis601149b2008-09-29 22:19:08 +00003753 if root is _Screen._root:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003754 Turtle._pen = None
3755 Turtle._screen = None
Martin v. Löwis601149b2008-09-29 22:19:08 +00003756 _Screen._root = None
3757 _Screen._canvas = None
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003758 TurtleScreen._RUNNING = False
Guido van Rossumb241b671998-12-04 16:42:46 +00003759 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +00003760
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003761 def bye(self):
3762 """Shut the turtlegraphics window.
3763
3764 Example (for a TurtleScreen instance named screen):
3765 >>> screen.bye()
3766 """
3767 self._destroy()
3768
3769 def exitonclick(self):
3770 """Go into mainloop until the mouse is clicked.
3771
3772 No arguments.
3773
3774 Bind bye() method to mouseclick on TurtleScreen.
3775 If "using_IDLE" - value in configuration dictionary is False
3776 (default value), enter mainloop.
3777 If IDLE with -n switch (no subprocess) is used, this value should be
3778 set to True in turtle.cfg. In this case IDLE's mainloop
3779 is active also for the client script.
3780
3781 This is a method of the Screen-class and not available for
3782 TurtleScreen instances.
3783
3784 Example (for a Screen instance named screen):
3785 >>> screen.exitonclick()
3786
3787 """
3788 def exitGracefully(x, y):
3789 """Screen.bye() with two dummy-parameters"""
3790 self.bye()
3791 self.onclick(exitGracefully)
3792 if _CFG["using_IDLE"]:
3793 return
3794 try:
3795 mainloop()
3796 except AttributeError:
3797 exit(0)
3798
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003799class Turtle(RawTurtle):
Ezio Melotti13925002011-03-16 11:05:33 +02003800 """RawTurtle auto-creating (scrolled) canvas.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003801
3802 When a Turtle object is created or a function derived from some
3803 Turtle method is called a TurtleScreen object is automatically created.
3804 """
3805 _pen = None
3806 _screen = None
3807
3808 def __init__(self,
3809 shape=_CFG["shape"],
3810 undobuffersize=_CFG["undobuffersize"],
3811 visible=_CFG["visible"]):
3812 if Turtle._screen is None:
3813 Turtle._screen = Screen()
3814 RawTurtle.__init__(self, Turtle._screen,
3815 shape=shape,
3816 undobuffersize=undobuffersize,
3817 visible=visible)
3818
3819Pen = Turtle
3820
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003821def write_docstringdict(filename="turtle_docstringdict"):
3822 """Create and write docstring-dictionary to file.
Guido van Rossumb241b671998-12-04 16:42:46 +00003823
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003824 Optional argument:
3825 filename -- a string, used as filename
3826 default value is turtle_docstringdict
Thomas Wouters477c8d52006-05-27 19:21:47 +00003827
Ezio Melotti13925002011-03-16 11:05:33 +02003828 Has to be called explicitly, (not used by the turtle-graphics classes)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003829 The docstring dictionary will be written to the Python script <filname>.py
3830 It is intended to serve as a template for translation of the docstrings
3831 into different languages.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003832 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003833 docsdict = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +00003834
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003835 for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003836 key = "_Screen."+methodname
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003837 docsdict[key] = eval(key).__doc__
3838 for methodname in _tg_turtle_functions:
3839 key = "Turtle."+methodname
3840 docsdict[key] = eval(key).__doc__
Thomas Wouters477c8d52006-05-27 19:21:47 +00003841
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003842 with open("%s.py" % filename,"w") as f:
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +02003843 keys = sorted(x for x in docsdict
Jon Dufresne39726282017-05-18 07:35:54 -07003844 if x.split('.')[1] not in _alias_list)
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003845 f.write('docsdict = {\n\n')
3846 for key in keys[:-1]:
3847 f.write('%s :\n' % repr(key))
3848 f.write(' """%s\n""",\n\n' % docsdict[key])
3849 key = keys[-1]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003850 f.write('%s :\n' % repr(key))
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003851 f.write(' """%s\n"""\n\n' % docsdict[key])
3852 f.write("}\n")
3853 f.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00003854
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003855def read_docstrings(lang):
3856 """Read in docstrings from lang-specific docstring dictionary.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003857
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003858 Transfer docstrings, translated to lang, from a dictionary-file
3859 to the methods of classes Screen and Turtle and - in revised form -
3860 to the corresponding functions.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003861 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003862 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3863 module = __import__(modname)
3864 docsdict = module.docsdict
3865 for key in docsdict:
3866 try:
3867# eval(key).im_func.__doc__ = docsdict[key]
3868 eval(key).__doc__ = docsdict[key]
Serhiy Storchakacefa9172016-06-14 22:52:04 +03003869 except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003870 print("Bad docstring-entry: %s" % key)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003871
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003872_LANGUAGE = _CFG["language"]
Guido van Rossumb241b671998-12-04 16:42:46 +00003873
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003874try:
3875 if _LANGUAGE != "english":
3876 read_docstrings(_LANGUAGE)
3877except ImportError:
3878 print("Cannot find docsdict for", _LANGUAGE)
Serhiy Storchakacefa9172016-06-14 22:52:04 +03003879except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003880 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3881 _LANGUAGE)
3882
3883
3884def getmethparlist(ob):
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003885 """Get strings describing the arguments for the given object
3886
3887 Returns a pair of strings representing function parameter lists
3888 including parenthesis. The first string is suitable for use in
3889 function definition and the second is suitable for use in function
3890 call. The "self" parameter is not included.
3891 """
3892 defText = callText = ""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003893 # bit of a hack for methods - turn it into a function
3894 # but we drop the "self" param.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003895 # Try and build one for Python defined functions
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003896 args, varargs, varkw = inspect.getargs(ob.__code__)
3897 items2 = args[1:]
3898 realArgs = args[1:]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003899 defaults = ob.__defaults__ or []
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003900 defaults = ["=%r" % (value,) for value in defaults]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003901 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003902 items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3903 if varargs is not None:
3904 items1.append("*" + varargs)
3905 items2.append("*" + varargs)
3906 if varkw is not None:
3907 items1.append("**" + varkw)
3908 items2.append("**" + varkw)
3909 defText = ", ".join(items1)
3910 defText = "(%s)" % defText
3911 callText = ", ".join(items2)
3912 callText = "(%s)" % callText
3913 return defText, callText
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003914
3915def _turtle_docrevise(docstr):
3916 """To reduce docstrings from RawTurtle class for functions
3917 """
3918 import re
3919 if docstr is None:
3920 return None
3921 turtlename = _CFG["exampleturtle"]
3922 newdocstr = docstr.replace("%s." % turtlename,"")
3923 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3924 newdocstr = parexp.sub(":", newdocstr)
3925 return newdocstr
3926
3927def _screen_docrevise(docstr):
3928 """To reduce docstrings from TurtleScreen class for functions
3929 """
3930 import re
3931 if docstr is None:
3932 return None
3933 screenname = _CFG["examplescreen"]
3934 newdocstr = docstr.replace("%s." % screenname,"")
3935 parexp = re.compile(r' \(.+ %s\):' % screenname)
3936 newdocstr = parexp.sub(":", newdocstr)
3937 return newdocstr
3938
3939## The following mechanism makes all methods of RawTurtle and Turtle available
3940## as functions. So we can enhance, change, add, delete methods to these
3941## classes and do not need to change anything here.
3942
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003943__func_body = """\
3944def {name}{paramslist}:
3945 if {obj} is None:
3946 if not TurtleScreen._RUNNING:
3947 TurtleScreen._RUNNING = True
3948 raise Terminator
3949 {obj} = {init}
3950 try:
3951 return {obj}.{name}{argslist}
3952 except TK.TclError:
3953 if not TurtleScreen._RUNNING:
3954 TurtleScreen._RUNNING = True
3955 raise Terminator
3956 raise
3957"""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003958
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003959def _make_global_funcs(functions, cls, obj, init, docrevise):
3960 for methodname in functions:
3961 method = getattr(cls, methodname)
3962 pl1, pl2 = getmethparlist(method)
3963 if pl1 == "":
3964 print(">>>>>>", pl1, pl2)
3965 continue
3966 defstr = __func_body.format(obj=obj, init=init, name=methodname,
3967 paramslist=pl1, argslist=pl2)
3968 exec(defstr, globals())
3969 globals()[methodname].__doc__ = docrevise(method.__doc__)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003970
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003971_make_global_funcs(_tg_screen_functions, _Screen,
3972 'Turtle._screen', 'Screen()', _screen_docrevise)
3973_make_global_funcs(_tg_turtle_functions, Turtle,
3974 'Turtle._pen', 'Turtle()', _turtle_docrevise)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003975
3976
Georg Brandleaa84ef2009-05-05 08:14:33 +00003977done = mainloop
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003978
3979if __name__ == "__main__":
3980 def switchpen():
3981 if isdown():
3982 pu()
3983 else:
3984 pd()
3985
3986 def demo1():
3987 """Demo of old turtle.py - module"""
3988 reset()
3989 tracer(True)
3990 up()
3991 backward(100)
3992 down()
3993 # draw 3 squares; the last filled
3994 width(3)
3995 for i in range(3):
3996 if i == 2:
3997 begin_fill()
3998 for _ in range(4):
3999 forward(20)
4000 left(90)
4001 if i == 2:
4002 color("maroon")
4003 end_fill()
4004 up()
4005 forward(30)
4006 down()
4007 width(1)
4008 color("black")
4009 # move out of the way
4010 tracer(False)
4011 up()
4012 right(90)
4013 forward(100)
4014 right(90)
4015 forward(100)
4016 right(180)
4017 down()
4018 # some text
4019 write("startstart", 1)
4020 write("start", 1)
4021 color("red")
4022 # staircase
4023 for i in range(5):
Guido van Rossumb241b671998-12-04 16:42:46 +00004024 forward(20)
4025 left(90)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004026 forward(20)
4027 right(90)
4028 # filled staircase
4029 tracer(True)
4030 begin_fill()
4031 for i in range(5):
4032 forward(20)
4033 left(90)
4034 forward(20)
4035 right(90)
4036 end_fill()
4037 # more text
Thomas Wouters477c8d52006-05-27 19:21:47 +00004038
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004039 def demo2():
4040 """Demo of some new features."""
4041 speed(1)
4042 st()
4043 pensize(3)
4044 setheading(towards(0, 0))
4045 radius = distance(0, 0)/2.0
4046 rt(90)
4047 for _ in range(18):
4048 switchpen()
4049 circle(radius, 10)
4050 write("wait a moment...")
4051 while undobufferentries():
4052 undo()
4053 reset()
4054 lt(90)
4055 colormode(255)
4056 laenge = 10
4057 pencolor("green")
4058 pensize(3)
4059 lt(180)
4060 for i in range(-2, 16):
4061 if i > 0:
4062 begin_fill()
4063 fillcolor(255-15*i, 0, 15*i)
4064 for _ in range(3):
4065 fd(laenge)
4066 lt(120)
4067 end_fill()
4068 laenge += 10
4069 lt(15)
4070 speed((speed()+1)%12)
4071 #end_fill()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004072
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004073 lt(120)
4074 pu()
4075 fd(70)
4076 rt(30)
4077 pd()
4078 color("red","yellow")
4079 speed(0)
4080 begin_fill()
4081 for _ in range(4):
4082 circle(50, 90)
4083 rt(90)
4084 fd(30)
4085 rt(90)
4086 end_fill()
4087 lt(90)
4088 pu()
4089 fd(30)
4090 pd()
4091 shape("turtle")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004092
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004093 tri = getturtle()
4094 tri.resizemode("auto")
4095 turtle = Turtle()
4096 turtle.resizemode("auto")
4097 turtle.shape("turtle")
4098 turtle.reset()
4099 turtle.left(90)
4100 turtle.speed(0)
4101 turtle.up()
4102 turtle.goto(280, 40)
4103 turtle.lt(30)
4104 turtle.down()
4105 turtle.speed(6)
4106 turtle.color("blue","orange")
4107 turtle.pensize(2)
4108 tri.speed(6)
Thomas Wouters477c8d52006-05-27 19:21:47 +00004109 setheading(towards(turtle))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004110 count = 1
4111 while tri.distance(turtle) > 4:
4112 turtle.fd(3.5)
4113 turtle.lt(0.6)
4114 tri.setheading(tri.towards(turtle))
4115 tri.fd(4)
4116 if count % 20 == 0:
4117 turtle.stamp()
4118 tri.stamp()
4119 switchpen()
4120 count += 1
4121 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4122 tri.pencolor("black")
4123 tri.pencolor("red")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004124
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004125 def baba(xdummy, ydummy):
4126 clearscreen()
4127 bye()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004128
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004129 time.sleep(2)
Guido van Rossumb241b671998-12-04 16:42:46 +00004130
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004131 while undobufferentries():
4132 tri.undo()
4133 turtle.undo()
4134 tri.fd(50)
4135 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4136 tri.onclick(baba, 1)
4137
4138 demo1()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004139 demo2()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004140 exitonclick()