blob: 11429a62d9477caa51ff60d5a2be5448cb64b224 [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 """
Serhiy Storchakab5adc8a2021-04-25 13:16:49 +0300829 return simpledialog.askstring(title, prompt, parent=self.cv)
Georg Brandleaa84ef2009-05-05 08:14:33 +0000830
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,
Serhiy Storchakab5adc8a2021-04-25 13:16:49 +0300850 minvalue=minval, maxvalue=maxval,
851 parent=self.cv)
Georg Brandleaa84ef2009-05-05 08:14:33 +0000852
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000853
854##############################################################################
855### End of Tkinter - interface ###
856##############################################################################
857
858
859class Terminator (Exception):
860 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
861
Terry Jan Reedyc30b7b12013-03-11 17:57:08 -0400862 This stops execution of a turtle graphics script.
863 Main purpose: use in the Demo-Viewer turtle.Demo.py.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000864 """
865 pass
866
867
868class TurtleGraphicsError(Exception):
869 """Some TurtleGraphics Error
870 """
871
872
873class Shape(object):
874 """Data structure modeling shapes.
875
876 attribute _type is one of "polygon", "image", "compound"
877 attribute _data is - depending on _type a poygon-tuple,
878 an image or a list constructed using the addcomponent method.
879 """
880 def __init__(self, type_, data=None):
881 self._type = type_
882 if type_ == "polygon":
883 if isinstance(data, list):
884 data = tuple(data)
885 elif type_ == "image":
886 if isinstance(data, str):
887 if data.lower().endswith(".gif") and isfile(data):
888 data = TurtleScreen._image(data)
889 # else data assumed to be Photoimage
890 elif type_ == "compound":
891 data = []
892 else:
893 raise TurtleGraphicsError("There is no shape type %s" % type_)
894 self._data = data
895
896 def addcomponent(self, poly, fill, outline=None):
897 """Add component to a shape of type compound.
898
899 Arguments: poly is a polygon, i. e. a tuple of number pairs.
900 fill is the fillcolor of the component,
901 outline is the outline color of the component.
902
903 call (for a Shapeobject namend s):
904 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000905
906 Example:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000907 >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
908 >>> s = Shape("compound")
909 >>> s.addcomponent(poly, "red", "blue")
Petri Lehtinen9aa20af2011-12-02 21:24:14 +0200910 >>> # .. add more components and then use register_shape()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000911 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000912 if self._type != "compound":
913 raise TurtleGraphicsError("Cannot add component to %s Shape"
914 % self._type)
915 if outline is None:
916 outline = fill
917 self._data.append([poly, fill, outline])
Guido van Rossumb241b671998-12-04 16:42:46 +0000918
Thomas Wouters477c8d52006-05-27 19:21:47 +0000919
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000920class Tbuffer(object):
921 """Ring buffer used as undobuffer for RawTurtle objects."""
922 def __init__(self, bufsize=10):
923 self.bufsize = bufsize
924 self.buffer = [[None]] * bufsize
925 self.ptr = -1
926 self.cumulate = False
927 def reset(self, bufsize=None):
928 if bufsize is None:
929 for i in range(self.bufsize):
930 self.buffer[i] = [None]
931 else:
932 self.bufsize = bufsize
933 self.buffer = [[None]] * bufsize
934 self.ptr = -1
935 def push(self, item):
936 if self.bufsize > 0:
937 if not self.cumulate:
938 self.ptr = (self.ptr + 1) % self.bufsize
939 self.buffer[self.ptr] = item
940 else:
941 self.buffer[self.ptr].append(item)
942 def pop(self):
943 if self.bufsize > 0:
944 item = self.buffer[self.ptr]
945 if item is None:
946 return None
947 else:
948 self.buffer[self.ptr] = [None]
949 self.ptr = (self.ptr - 1) % self.bufsize
950 return (item)
951 def nr_of_items(self):
952 return self.bufsize - self.buffer.count([None])
953 def __repr__(self):
954 return str(self.buffer) + " " + str(self.ptr)
955
956
957
958class TurtleScreen(TurtleScreenBase):
959 """Provides screen oriented methods like setbg etc.
960
961 Only relies upon the methods of TurtleScreenBase and NOT
962 upon components of the underlying graphics toolkit -
963 which is Tkinter in this case.
964 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +0000965 _RUNNING = True
966
967 def __init__(self, cv, mode=_CFG["mode"],
968 colormode=_CFG["colormode"], delay=_CFG["delay"]):
969 self._shapes = {
970 "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
971 "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
972 (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
973 (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
974 (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
975 (2,14))),
976 "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
977 (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
978 (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
979 (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
980 (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
981 (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
982 "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
983 (-10,-10))),
984 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
985 (-10,-5.77))),
986 "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
987 "blank" : Shape("image", self._blankimage())
988 }
989
990 self._bgpics = {"nopic" : ""}
991
992 TurtleScreenBase.__init__(self, cv)
993 self._mode = mode
994 self._delayvalue = delay
995 self._colormode = _CFG["colormode"]
996 self._keys = []
997 self.clear()
Ned Deily09ae5442014-04-19 19:11:14 -0700998 if sys.platform == 'darwin':
999 # Force Turtle window to the front on OS X. This is needed because
1000 # the Turtle window will show behind the Terminal window when you
1001 # start the demo from the command line.
Ned Deily152dfd12014-09-13 23:39:16 -07001002 rootwindow = cv.winfo_toplevel()
1003 rootwindow.call('wm', 'attributes', '.', '-topmost', '1')
1004 rootwindow.call('wm', 'attributes', '.', '-topmost', '0')
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001005
1006 def clear(self):
1007 """Delete all drawings and all turtles from the TurtleScreen.
1008
Georg Brandleaa84ef2009-05-05 08:14:33 +00001009 No argument.
1010
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001011 Reset empty TurtleScreen to its initial state: white background,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001012 no backgroundimage, no eventbindings and tracing on.
1013
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001014 Example (for a TurtleScreen instance named screen):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001015 >>> screen.clear()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001016
1017 Note: this method is not available as function.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001018 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001019 self._delayvalue = _CFG["delay"]
1020 self._colormode = _CFG["colormode"]
1021 self._delete("all")
1022 self._bgpic = self._createimage("")
1023 self._bgpicname = "nopic"
1024 self._tracing = 1
1025 self._updatecounter = 0
1026 self._turtles = []
1027 self.bgcolor("white")
1028 for btn in 1, 2, 3:
1029 self.onclick(None, btn)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001030 self.onkeypress(None)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001031 for key in self._keys[:]:
1032 self.onkey(None, key)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001033 self.onkeypress(None, key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001034 Turtle._pen = None
Guido van Rossumb241b671998-12-04 16:42:46 +00001035
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001036 def mode(self, mode=None):
1037 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001038
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001039 Optional argument:
Martin Panter99e843b2016-09-10 10:38:28 +00001040 mode -- one of the strings 'standard', 'logo' or 'world'
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001041
1042 Mode 'standard' is compatible with turtle.py.
1043 Mode 'logo' is compatible with most Logo-Turtle-Graphics.
1044 Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
1045 this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
1046 If mode is not given, return the current mode.
1047
1048 Mode Initial turtle heading positive angles
1049 ------------|-------------------------|-------------------
1050 'standard' to the right (east) counterclockwise
1051 'logo' upward (north) clockwise
1052
1053 Examples:
1054 >>> mode('logo') # resets turtle heading to north
1055 >>> mode()
1056 'logo'
Thomas Wouters477c8d52006-05-27 19:21:47 +00001057 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001058 if mode is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001059 return self._mode
1060 mode = mode.lower()
1061 if mode not in ["standard", "logo", "world"]:
1062 raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1063 self._mode = mode
1064 if mode in ["standard", "logo"]:
1065 self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1066 self.canvwidth//2, self.canvheight//2)
1067 self.xscale = self.yscale = 1.0
1068 self.reset()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001069
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001070 def setworldcoordinates(self, llx, lly, urx, ury):
1071 """Set up a user defined coordinate-system.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001072
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001073 Arguments:
1074 llx -- a number, x-coordinate of lower left corner of canvas
1075 lly -- a number, y-coordinate of lower left corner of canvas
1076 urx -- a number, x-coordinate of upper right corner of canvas
1077 ury -- a number, y-coordinate of upper right corner of canvas
1078
1079 Set up user coodinat-system and switch to mode 'world' if necessary.
1080 This performs a screen.reset. If mode 'world' is already active,
1081 all drawings are redrawn according to the new coordinates.
1082
1083 But ATTENTION: in user-defined coordinatesystems angles may appear
1084 distorted. (see Screen.mode())
1085
1086 Example (for a TurtleScreen instance named screen):
1087 >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1088 >>> for _ in range(36):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001089 ... left(10)
1090 ... forward(0.5)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001091 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001092 if self.mode() != "world":
1093 self.mode("world")
1094 xspan = float(urx - llx)
1095 yspan = float(ury - lly)
1096 wx, wy = self._window_size()
1097 self.screensize(wx-20, wy-20)
1098 oldxscale, oldyscale = self.xscale, self.yscale
1099 self.xscale = self.canvwidth / xspan
1100 self.yscale = self.canvheight / yspan
1101 srx1 = llx * self.xscale
1102 sry1 = -ury * self.yscale
1103 srx2 = self.canvwidth + srx1
1104 sry2 = self.canvheight + sry1
1105 self._setscrollregion(srx1, sry1, srx2, sry2)
1106 self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1107 self.update()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001108
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001109 def register_shape(self, name, shape=None):
1110 """Adds a turtle shape to TurtleScreen's shapelist.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001111
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001112 Arguments:
1113 (1) name is the name of a gif-file and shape is None.
1114 Installs the corresponding image shape.
1115 !! Image-shapes DO NOT rotate when turning the turtle,
1116 !! so they do not display the heading of the turtle!
1117 (2) name is an arbitrary string and shape is a tuple
1118 of pairs of coordinates. Installs the corresponding
1119 polygon shape
1120 (3) name is an arbitrary string and shape is a
1121 (compound) Shape object. Installs the corresponding
1122 compound shape.
1123 To use a shape, you have to issue the command shape(shapename).
Thomas Wouters477c8d52006-05-27 19:21:47 +00001124
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001125 call: register_shape("turtle.gif")
1126 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1127
1128 Example (for a TurtleScreen instance named screen):
1129 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1130
Thomas Wouters477c8d52006-05-27 19:21:47 +00001131 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001132 if shape is None:
1133 # image
1134 if name.lower().endswith(".gif"):
1135 shape = Shape("image", self._image(name))
1136 else:
1137 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1138 + "Use help(register_shape)" )
1139 elif isinstance(shape, tuple):
1140 shape = Shape("polygon", shape)
1141 ## else shape assumed to be Shape-instance
1142 self._shapes[name] = shape
Guido van Rossumbffa52f2002-09-29 00:25:51 +00001143
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001144 def _colorstr(self, color):
1145 """Return color string corresponding to args.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001146
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001147 Argument may be a string or a tuple of three
1148 numbers corresponding to actual colormode,
1149 i.e. in the range 0<=n<=colormode.
1150
1151 If the argument doesn't represent a color,
1152 an error is raised.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001153 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001154 if len(color) == 1:
1155 color = color[0]
1156 if isinstance(color, str):
1157 if self._iscolorstring(color) or color == "":
1158 return color
1159 else:
1160 raise TurtleGraphicsError("bad color string: %s" % str(color))
1161 try:
1162 r, g, b = color
Serhiy Storchakacefa9172016-06-14 22:52:04 +03001163 except (TypeError, ValueError):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001164 raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1165 if self._colormode == 1.0:
1166 r, g, b = [round(255.0*x) for x in (r, g, b)]
1167 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1168 raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1169 return "#%02x%02x%02x" % (r, g, b)
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001170
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001171 def _color(self, cstr):
1172 if not cstr.startswith("#"):
1173 return cstr
1174 if len(cstr) == 7:
1175 cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1176 elif len(cstr) == 4:
1177 cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1178 else:
1179 raise TurtleGraphicsError("bad colorstring: %s" % cstr)
Jon Dufresne39726282017-05-18 07:35:54 -07001180 return tuple(c * self._colormode/255 for c in cl)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001181
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001182 def colormode(self, cmode=None):
1183 """Return the colormode or set it to 1.0 or 255.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001184
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001185 Optional argument:
1186 cmode -- one of the values 1.0 or 255
Thomas Wouters477c8d52006-05-27 19:21:47 +00001187
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001188 r, g, b values of colortriples have to be in range 0..cmode.
1189
1190 Example (for a TurtleScreen instance named screen):
1191 >>> screen.colormode()
1192 1.0
1193 >>> screen.colormode(255)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001194 >>> pencolor(240,160,80)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001195 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001196 if cmode is None:
1197 return self._colormode
1198 if cmode == 1.0:
1199 self._colormode = float(cmode)
1200 elif cmode == 255:
1201 self._colormode = int(cmode)
1202
1203 def reset(self):
1204 """Reset all Turtles on the Screen to their initial state.
1205
1206 No argument.
1207
1208 Example (for a TurtleScreen instance named screen):
1209 >>> screen.reset()
1210 """
1211 for turtle in self._turtles:
1212 turtle._setmode(self._mode)
1213 turtle.reset()
1214
1215 def turtles(self):
1216 """Return the list of turtles on the screen.
1217
1218 Example (for a TurtleScreen instance named screen):
1219 >>> screen.turtles()
1220 [<turtle.Turtle object at 0x00E11FB0>]
1221 """
1222 return self._turtles
1223
1224 def bgcolor(self, *args):
1225 """Set or return backgroundcolor of the TurtleScreen.
1226
1227 Arguments (if given): a color string or three numbers
1228 in the range 0..colormode or a 3-tuple of such numbers.
1229
1230 Example (for a TurtleScreen instance named screen):
1231 >>> screen.bgcolor("orange")
1232 >>> screen.bgcolor()
1233 'orange'
1234 >>> screen.bgcolor(0.5,0,0.5)
1235 >>> screen.bgcolor()
1236 '#800080'
1237 """
1238 if args:
1239 color = self._colorstr(args)
1240 else:
1241 color = None
1242 color = self._bgcolor(color)
1243 if color is not None:
1244 color = self._color(color)
1245 return color
1246
1247 def tracer(self, n=None, delay=None):
1248 """Turns turtle animation on/off and set delay for update drawings.
1249
1250 Optional arguments:
1251 n -- nonnegative integer
1252 delay -- nonnegative integer
1253
1254 If n is given, only each n-th regular screen update is really performed.
1255 (Can be used to accelerate the drawing of complex graphics.)
1256 Second arguments sets delay value (see RawTurtle.delay())
1257
1258 Example (for a TurtleScreen instance named screen):
1259 >>> screen.tracer(8, 25)
1260 >>> dist = 2
1261 >>> for i in range(200):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001262 ... fd(dist)
1263 ... rt(90)
1264 ... dist += 2
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001265 """
1266 if n is None:
1267 return self._tracing
1268 self._tracing = int(n)
1269 self._updatecounter = 0
1270 if delay is not None:
1271 self._delayvalue = int(delay)
1272 if self._tracing:
1273 self.update()
1274
1275 def delay(self, delay=None):
1276 """ Return or set the drawing delay in milliseconds.
1277
1278 Optional argument:
1279 delay -- positive integer
1280
1281 Example (for a TurtleScreen instance named screen):
1282 >>> screen.delay(15)
1283 >>> screen.delay()
1284 15
1285 """
1286 if delay is None:
1287 return self._delayvalue
1288 self._delayvalue = int(delay)
1289
1290 def _incrementudc(self):
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03001291 """Increment update counter."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001292 if not TurtleScreen._RUNNING:
Serhiy Storchaka80a18032015-02-22 17:25:33 +02001293 TurtleScreen._RUNNING = True
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001294 raise Terminator
1295 if self._tracing > 0:
1296 self._updatecounter += 1
1297 self._updatecounter %= self._tracing
1298
1299 def update(self):
1300 """Perform a TurtleScreen update.
1301 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00001302 tracing = self._tracing
1303 self._tracing = True
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001304 for t in self.turtles():
1305 t._update_data()
1306 t._drawturtle()
Georg Brandleaa84ef2009-05-05 08:14:33 +00001307 self._tracing = tracing
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001308 self._update()
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001309
1310 def window_width(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001311 """ Return the width of the turtle window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001312
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001313 Example (for a TurtleScreen instance named screen):
1314 >>> screen.window_width()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001315 640
1316 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001317 return self._window_size()[0]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001318
1319 def window_height(self):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001320 """ Return the height of the turtle window.
1321
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001322 Example (for a TurtleScreen instance named screen):
1323 >>> screen.window_height()
1324 480
Thomas Wouters477c8d52006-05-27 19:21:47 +00001325 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001326 return self._window_size()[1]
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001327
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001328 def getcanvas(self):
1329 """Return the Canvas of this TurtleScreen.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001330
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001331 No argument.
1332
1333 Example (for a Screen instance named screen):
1334 >>> cv = screen.getcanvas()
1335 >>> cv
1336 <turtle.ScrolledCanvas instance at 0x010742D8>
Thomas Wouters477c8d52006-05-27 19:21:47 +00001337 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001338 return self.cv
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001339
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001340 def getshapes(self):
1341 """Return a list of names of all currently available turtle shapes.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001342
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001343 No argument.
1344
1345 Example (for a TurtleScreen instance named screen):
1346 >>> screen.getshapes()
1347 ['arrow', 'blank', 'circle', ... , 'turtle']
1348 """
1349 return sorted(self._shapes.keys())
1350
1351 def onclick(self, fun, btn=1, add=None):
1352 """Bind fun to mouse-click event on canvas.
1353
1354 Arguments:
1355 fun -- a function with two arguments, the coordinates of the
1356 clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05301357 btn -- the number of the mouse-button, defaults to 1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001358
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001359 Example (for a TurtleScreen instance named screen)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001360
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001361 >>> screen.onclick(goto)
1362 >>> # Subsequently clicking into the TurtleScreen will
1363 >>> # make the turtle move to the clicked point.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001364 >>> screen.onclick(None)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001365 """
1366 self._onscreenclick(fun, btn, add)
1367
1368 def onkey(self, fun, key):
1369 """Bind fun to key-release event of key.
1370
1371 Arguments:
1372 fun -- a function with no arguments
1373 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1374
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001375 In order to be able to register key-events, TurtleScreen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001376 must have focus. (See method listen.)
1377
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001378 Example (for a TurtleScreen instance named screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001379
1380 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001381 ... fd(50)
1382 ... lt(60)
1383 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001384 >>> screen.onkey(f, "Up")
1385 >>> screen.listen()
1386
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001387 Subsequently the turtle can be moved by repeatedly pressing
1388 the up-arrow key, consequently drawing a hexagon
1389
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001390 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001391 if fun is None:
Georg Brandleaa84ef2009-05-05 08:14:33 +00001392 if key in self._keys:
1393 self._keys.remove(key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001394 elif key not in self._keys:
1395 self._keys.append(key)
Georg Brandleaa84ef2009-05-05 08:14:33 +00001396 self._onkeyrelease(fun, key)
1397
1398 def onkeypress(self, fun, key=None):
1399 """Bind fun to key-press event of key if key is given,
1400 or to any key-press-event if no key is given.
1401
1402 Arguments:
1403 fun -- a function with no arguments
1404 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1405
1406 In order to be able to register key-events, TurtleScreen
1407 must have focus. (See method listen.)
1408
1409 Example (for a TurtleScreen instance named screen
1410 and a Turtle instance named turtle):
1411
1412 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001413 ... fd(50)
1414 ... lt(60)
1415 ...
1416 >>> screen.onkeypress(f, "Up")
Georg Brandleaa84ef2009-05-05 08:14:33 +00001417 >>> screen.listen()
1418
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001419 Subsequently the turtle can be moved by repeatedly pressing
1420 the up-arrow key, or by keeping pressed the up-arrow key.
1421 consequently drawing a hexagon.
Georg Brandleaa84ef2009-05-05 08:14:33 +00001422 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001423 if fun is None:
Georg Brandleaa84ef2009-05-05 08:14:33 +00001424 if key in self._keys:
1425 self._keys.remove(key)
1426 elif key is not None and key not in self._keys:
1427 self._keys.append(key)
1428 self._onkeypress(fun, key)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001429
1430 def listen(self, xdummy=None, ydummy=None):
1431 """Set focus on TurtleScreen (in order to collect key-events)
1432
1433 No arguments.
1434 Dummy arguments are provided in order
1435 to be able to pass listen to the onclick method.
1436
1437 Example (for a TurtleScreen instance named screen):
1438 >>> screen.listen()
1439 """
1440 self._listen()
1441
1442 def ontimer(self, fun, t=0):
1443 """Install a timer, which calls fun after t milliseconds.
1444
1445 Arguments:
1446 fun -- a function with no arguments.
1447 t -- a number >= 0
1448
1449 Example (for a TurtleScreen instance named screen):
1450
1451 >>> running = True
1452 >>> def f():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001453 ... if running:
1454 ... fd(50)
1455 ... lt(60)
1456 ... screen.ontimer(f, 250)
1457 ...
1458 >>> f() # makes the turtle marching around
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001459 >>> running = False
1460 """
1461 self._ontimer(fun, t)
1462
1463 def bgpic(self, picname=None):
1464 """Set background image or return name of current backgroundimage.
1465
1466 Optional argument:
1467 picname -- a string, name of a gif-file or "nopic".
1468
Ezio Melotti42da6632011-03-15 05:18:48 +02001469 If picname is a filename, set the corresponding image as background.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001470 If picname is "nopic", delete backgroundimage, if present.
1471 If picname is None, return the filename of the current backgroundimage.
1472
1473 Example (for a TurtleScreen instance named screen):
1474 >>> screen.bgpic()
1475 'nopic'
1476 >>> screen.bgpic("landscape.gif")
1477 >>> screen.bgpic()
1478 'landscape.gif'
1479 """
1480 if picname is None:
1481 return self._bgpicname
1482 if picname not in self._bgpics:
1483 self._bgpics[picname] = self._image(picname)
1484 self._setbgpic(self._bgpic, self._bgpics[picname])
1485 self._bgpicname = picname
1486
1487 def screensize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001488 """Resize the canvas the turtles are drawing on.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001489
1490 Optional arguments:
1491 canvwidth -- positive integer, new width of canvas in pixels
1492 canvheight -- positive integer, new height of canvas in pixels
Ezio Melotti13925002011-03-16 11:05:33 +02001493 bg -- colorstring or color-tuple, new backgroundcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001494 If no arguments are given, return current (canvaswidth, canvasheight)
1495
1496 Do not alter the drawing window. To observe hidden parts of
1497 the canvas use the scrollbars. (Can make visible those parts
1498 of a drawing, which were outside the canvas before!)
1499
1500 Example (for a Turtle instance named turtle):
1501 >>> turtle.screensize(2000,1500)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02001502 >>> # e.g. to search for an erroneously escaped turtle ;-)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001503 """
1504 return self._resize(canvwidth, canvheight, bg)
1505
1506 onscreenclick = onclick
1507 resetscreen = reset
1508 clearscreen = clear
1509 addshape = register_shape
Georg Brandleaa84ef2009-05-05 08:14:33 +00001510 onkeyrelease = onkey
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001511
1512class TNavigator(object):
1513 """Navigation part of the RawTurtle.
1514 Implements methods for turtle movement.
1515 """
1516 START_ORIENTATION = {
1517 "standard": Vec2D(1.0, 0.0),
1518 "world" : Vec2D(1.0, 0.0),
1519 "logo" : Vec2D(0.0, 1.0) }
1520 DEFAULT_MODE = "standard"
1521 DEFAULT_ANGLEOFFSET = 0
1522 DEFAULT_ANGLEORIENT = 1
1523
1524 def __init__(self, mode=DEFAULT_MODE):
1525 self._angleOffset = self.DEFAULT_ANGLEOFFSET
1526 self._angleOrient = self.DEFAULT_ANGLEORIENT
1527 self._mode = mode
1528 self.undobuffer = None
1529 self.degrees()
1530 self._mode = None
1531 self._setmode(mode)
1532 TNavigator.reset(self)
1533
1534 def reset(self):
1535 """reset turtle to its initial values
1536
1537 Will be overwritten by parent class
1538 """
1539 self._position = Vec2D(0.0, 0.0)
1540 self._orient = TNavigator.START_ORIENTATION[self._mode]
1541
1542 def _setmode(self, mode=None):
1543 """Set turtle-mode to 'standard', 'world' or 'logo'.
1544 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00001545 if mode is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001546 return self._mode
1547 if mode not in ["standard", "logo", "world"]:
1548 return
1549 self._mode = mode
1550 if mode in ["standard", "world"]:
1551 self._angleOffset = 0
1552 self._angleOrient = 1
1553 else: # mode == "logo":
1554 self._angleOffset = self._fullcircle/4.
1555 self._angleOrient = -1
1556
1557 def _setDegreesPerAU(self, fullcircle):
1558 """Helper function for degrees() and radians()"""
1559 self._fullcircle = fullcircle
1560 self._degreesPerAU = 360/fullcircle
1561 if self._mode == "standard":
1562 self._angleOffset = 0
1563 else:
1564 self._angleOffset = fullcircle/4.
1565
1566 def degrees(self, fullcircle=360.0):
1567 """ Set angle measurement units to degrees.
1568
1569 Optional argument:
1570 fullcircle - a number
1571
1572 Set angle measurement units, i. e. set number
penguindustin96466302019-05-06 14:57:17 -04001573 of 'degrees' for a full circle. Default value is
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001574 360 degrees.
1575
1576 Example (for a Turtle instance named turtle):
1577 >>> turtle.left(90)
1578 >>> turtle.heading()
1579 90
Alexander Belopolsky3cdfb122010-10-29 17:16:49 +00001580
1581 Change angle measurement unit to grad (also known as gon,
1582 grade, or gradian and equals 1/100-th of the right angle.)
1583 >>> turtle.degrees(400.0)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001584 >>> turtle.heading()
1585 100
1586
1587 """
1588 self._setDegreesPerAU(fullcircle)
1589
1590 def radians(self):
1591 """ Set the angle measurement units to radians.
1592
1593 No arguments.
1594
1595 Example (for a Turtle instance named turtle):
1596 >>> turtle.heading()
1597 90
1598 >>> turtle.radians()
1599 >>> turtle.heading()
1600 1.5707963267948966
1601 """
Marek Madejski6844b562020-09-01 18:42:41 +02001602 self._setDegreesPerAU(math.tau)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001603
1604 def _go(self, distance):
1605 """move turtle forward by specified distance"""
1606 ende = self._position + self._orient * distance
1607 self._goto(ende)
1608
1609 def _rotate(self, angle):
1610 """Turn turtle counterclockwise by specified angle if angle > 0."""
1611 angle *= self._degreesPerAU
1612 self._orient = self._orient.rotate(angle)
1613
1614 def _goto(self, end):
1615 """move turtle to position end."""
1616 self._position = end
1617
1618 def forward(self, distance):
1619 """Move the turtle forward by the specified distance.
1620
1621 Aliases: forward | fd
1622
1623 Argument:
1624 distance -- a number (integer or float)
1625
1626 Move the turtle forward by the specified distance, in the direction
1627 the turtle is headed.
1628
1629 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001630 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001631 (0.00, 0.00)
1632 >>> turtle.forward(25)
1633 >>> turtle.position()
1634 (25.00,0.00)
1635 >>> turtle.forward(-75)
1636 >>> turtle.position()
1637 (-50.00,0.00)
1638 """
1639 self._go(distance)
1640
1641 def back(self, distance):
1642 """Move the turtle backward by distance.
1643
1644 Aliases: back | backward | bk
1645
1646 Argument:
1647 distance -- a number
1648
1649 Move the turtle backward by distance ,opposite to the direction the
1650 turtle is headed. Do not change the turtle's heading.
1651
1652 Example (for a Turtle instance named turtle):
1653 >>> turtle.position()
1654 (0.00, 0.00)
1655 >>> turtle.backward(30)
1656 >>> turtle.position()
1657 (-30.00, 0.00)
1658 """
1659 self._go(-distance)
1660
1661 def right(self, angle):
1662 """Turn turtle right by angle units.
1663
1664 Aliases: right | rt
1665
1666 Argument:
1667 angle -- a number (integer or float)
1668
1669 Turn turtle right by angle units. (Units are by default degrees,
1670 but can be set via the degrees() and radians() functions.)
1671 Angle orientation depends on mode. (See this.)
1672
1673 Example (for a Turtle instance named turtle):
1674 >>> turtle.heading()
1675 22.0
1676 >>> turtle.right(45)
1677 >>> turtle.heading()
1678 337.0
1679 """
1680 self._rotate(-angle)
1681
1682 def left(self, angle):
1683 """Turn turtle left by angle units.
1684
1685 Aliases: left | lt
1686
1687 Argument:
1688 angle -- a number (integer or float)
1689
1690 Turn turtle left by angle units. (Units are by default degrees,
1691 but can be set via the degrees() and radians() functions.)
1692 Angle orientation depends on mode. (See this.)
1693
1694 Example (for a Turtle instance named turtle):
1695 >>> turtle.heading()
1696 22.0
1697 >>> turtle.left(45)
1698 >>> turtle.heading()
1699 67.0
1700 """
1701 self._rotate(angle)
1702
1703 def pos(self):
1704 """Return the turtle's current location (x,y), as a Vec2D-vector.
1705
1706 Aliases: pos | position
1707
1708 No arguments.
1709
1710 Example (for a Turtle instance named turtle):
1711 >>> turtle.pos()
1712 (0.00, 240.00)
1713 """
1714 return self._position
1715
1716 def xcor(self):
1717 """ Return the turtle's x coordinate.
1718
1719 No arguments.
1720
1721 Example (for a Turtle instance named turtle):
1722 >>> reset()
1723 >>> turtle.left(60)
1724 >>> turtle.forward(100)
1725 >>> print turtle.xcor()
1726 50.0
1727 """
1728 return self._position[0]
1729
1730 def ycor(self):
1731 """ Return the turtle's y coordinate
1732 ---
1733 No arguments.
1734
1735 Example (for a Turtle instance named turtle):
1736 >>> reset()
1737 >>> turtle.left(60)
1738 >>> turtle.forward(100)
1739 >>> print turtle.ycor()
1740 86.6025403784
1741 """
1742 return self._position[1]
1743
1744
1745 def goto(self, x, y=None):
1746 """Move turtle to an absolute position.
1747
1748 Aliases: setpos | setposition | goto:
1749
1750 Arguments:
1751 x -- a number or a pair/vector of numbers
1752 y -- a number None
1753
1754 call: goto(x, y) # two coordinates
1755 --or: goto((x, y)) # a pair (tuple) of coordinates
1756 --or: goto(vec) # e.g. as returned by pos()
1757
1758 Move turtle to an absolute position. If the pen is down,
1759 a line will be drawn. The turtle's orientation does not change.
1760
1761 Example (for a Turtle instance named turtle):
1762 >>> tp = turtle.pos()
1763 >>> tp
1764 (0.00, 0.00)
1765 >>> turtle.setpos(60,30)
1766 >>> turtle.pos()
1767 (60.00,30.00)
1768 >>> turtle.setpos((20,80))
1769 >>> turtle.pos()
1770 (20.00,80.00)
1771 >>> turtle.setpos(tp)
1772 >>> turtle.pos()
1773 (0.00,0.00)
1774 """
1775 if y is None:
1776 self._goto(Vec2D(*x))
1777 else:
1778 self._goto(Vec2D(x, y))
1779
1780 def home(self):
1781 """Move turtle to the origin - coordinates (0,0).
1782
1783 No arguments.
1784
Mark Dickinsonf8798f52009-02-20 20:53:56 +00001785 Move turtle to the origin - coordinates (0,0) and set its
1786 heading to its start-orientation (which depends on mode).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001787
1788 Example (for a Turtle instance named turtle):
1789 >>> turtle.home()
1790 """
1791 self.goto(0, 0)
1792 self.setheading(0)
1793
1794 def setx(self, x):
1795 """Set the turtle's first coordinate to x
1796
1797 Argument:
1798 x -- a number (integer or float)
1799
1800 Set the turtle's first coordinate to x, leave second coordinate
1801 unchanged.
1802
1803 Example (for a Turtle instance named turtle):
1804 >>> turtle.position()
1805 (0.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001806 >>> turtle.setx(10)
1807 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001808 (10.00, 240.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001809 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001810 self._goto(Vec2D(x, self._position[1]))
Guido van Rossumfd2ede22002-09-23 16:55:05 +00001811
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001812 def sety(self, y):
1813 """Set the turtle's second coordinate to y
Thomas Wouters477c8d52006-05-27 19:21:47 +00001814
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001815 Argument:
1816 y -- a number (integer or float)
1817
1818 Set the turtle's first coordinate to x, second coordinate remains
1819 unchanged.
1820
1821 Example (for a Turtle instance named turtle):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001822 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001823 (0.00, 40.00)
1824 >>> turtle.sety(-10)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001825 >>> turtle.position()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001826 (0.00, -10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001827 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001828 self._goto(Vec2D(self._position[0], y))
Guido van Rossumb241b671998-12-04 16:42:46 +00001829
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001830 def distance(self, x, y=None):
1831 """Return the distance from the turtle to (x,y) in turtle step units.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001832
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001833 Arguments:
1834 x -- a number or a pair/vector of numbers or a turtle instance
1835 y -- a number None None
Thomas Wouters477c8d52006-05-27 19:21:47 +00001836
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001837 call: distance(x, y) # two coordinates
1838 --or: distance((x, y)) # a pair (tuple) of coordinates
1839 --or: distance(vec) # e.g. as returned by pos()
1840 --or: distance(mypen) # where mypen is another turtle
1841
1842 Example (for a Turtle instance named turtle):
1843 >>> turtle.pos()
1844 (0.00, 0.00)
1845 >>> turtle.distance(30,40)
1846 50.0
1847 >>> pen = Turtle()
1848 >>> pen.forward(77)
1849 >>> turtle.distance(pen)
1850 77.0
1851 """
1852 if y is not None:
1853 pos = Vec2D(x, y)
1854 if isinstance(x, Vec2D):
1855 pos = x
1856 elif isinstance(x, tuple):
1857 pos = Vec2D(*x)
1858 elif isinstance(x, TNavigator):
1859 pos = x._position
1860 return abs(pos - self._position)
1861
1862 def towards(self, x, y=None):
1863 """Return the angle of the line from the turtle's position to (x, y).
1864
1865 Arguments:
1866 x -- a number or a pair/vector of numbers or a turtle instance
1867 y -- a number None None
1868
1869 call: distance(x, y) # two coordinates
1870 --or: distance((x, y)) # a pair (tuple) of coordinates
1871 --or: distance(vec) # e.g. as returned by pos()
1872 --or: distance(mypen) # where mypen is another turtle
1873
1874 Return the angle, between the line from turtle-position to position
1875 specified by x, y and the turtle's start orientation. (Depends on
1876 modes - "standard" or "logo")
1877
1878 Example (for a Turtle instance named turtle):
1879 >>> turtle.pos()
1880 (10.00, 10.00)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001881 >>> turtle.towards(0,0)
1882 225.0
1883 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001884 if y is not None:
1885 pos = Vec2D(x, y)
1886 if isinstance(x, Vec2D):
1887 pos = x
1888 elif isinstance(x, tuple):
1889 pos = Vec2D(*x)
1890 elif isinstance(x, TNavigator):
1891 pos = x._position
1892 x, y = pos - self._position
Marek Madejski6844b562020-09-01 18:42:41 +02001893 result = round(math.degrees(math.atan2(y, x)), 10) % 360.0
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001894 result /= self._degreesPerAU
1895 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1896
1897 def heading(self):
1898 """ Return the turtle's current heading.
1899
1900 No arguments.
1901
1902 Example (for a Turtle instance named turtle):
1903 >>> turtle.left(67)
1904 >>> turtle.heading()
1905 67.0
1906 """
1907 x, y = self._orient
Marek Madejski6844b562020-09-01 18:42:41 +02001908 result = round(math.degrees(math.atan2(y, x)), 10) % 360.0
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001909 result /= self._degreesPerAU
1910 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1911
1912 def setheading(self, to_angle):
1913 """Set the orientation of the turtle to to_angle.
1914
1915 Aliases: setheading | seth
1916
1917 Argument:
1918 to_angle -- a number (integer or float)
1919
1920 Set the orientation of the turtle to to_angle.
1921 Here are some common directions in degrees:
1922
1923 standard - mode: logo-mode:
1924 -------------------|--------------------
1925 0 - east 0 - north
1926 90 - north 90 - east
1927 180 - west 180 - south
1928 270 - south 270 - west
1929
1930 Example (for a Turtle instance named turtle):
1931 >>> turtle.setheading(90)
1932 >>> turtle.heading()
1933 90
1934 """
1935 angle = (to_angle - self.heading())*self._angleOrient
1936 full = self._fullcircle
1937 angle = (angle+full/2.)%full - full/2.
1938 self._rotate(angle)
1939
1940 def circle(self, radius, extent = None, steps = None):
1941 """ Draw a circle with given radius.
1942
1943 Arguments:
1944 radius -- a number
1945 extent (optional) -- a number
1946 steps (optional) -- an integer
1947
1948 Draw a circle with given radius. The center is radius units left
1949 of the turtle; extent - an angle - determines which part of the
1950 circle is drawn. If extent is not given, draw the entire circle.
1951 If extent is not a full circle, one endpoint of the arc is the
1952 current pen position. Draw the arc in counterclockwise direction
1953 if radius is positive, otherwise in clockwise direction. Finally
1954 the direction of the turtle is changed by the amount of extent.
1955
1956 As the circle is approximated by an inscribed regular polygon,
1957 steps determines the number of steps to use. If not given,
1958 it will be calculated automatically. Maybe used to draw regular
1959 polygons.
1960
1961 call: circle(radius) # full circle
1962 --or: circle(radius, extent) # arc
1963 --or: circle(radius, extent, steps)
1964 --or: circle(radius, steps=6) # 6-sided polygon
1965
1966 Example (for a Turtle instance named turtle):
1967 >>> turtle.circle(50)
1968 >>> turtle.circle(120, 180) # semicircle
1969 """
1970 if self.undobuffer:
1971 self.undobuffer.push(["seq"])
1972 self.undobuffer.cumulate = True
1973 speed = self.speed()
1974 if extent is None:
1975 extent = self._fullcircle
1976 if steps is None:
1977 frac = abs(extent)/self._fullcircle
1978 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1979 w = 1.0 * extent / steps
1980 w2 = 0.5 * w
Marek Madejski6844b562020-09-01 18:42:41 +02001981 l = 2.0 * radius * math.sin(math.radians(w2)*self._degreesPerAU)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001982 if radius < 0:
1983 l, w, w2 = -l, -w, -w2
1984 tr = self._tracer()
1985 dl = self._delay()
1986 if speed == 0:
1987 self._tracer(0, 0)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001988 else:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00001989 self.speed(0)
1990 self._rotate(w2)
1991 for i in range(steps):
1992 self.speed(speed)
1993 self._go(l)
1994 self.speed(0)
1995 self._rotate(w)
1996 self._rotate(-w2)
1997 if speed == 0:
1998 self._tracer(tr, dl)
1999 self.speed(speed)
2000 if self.undobuffer:
2001 self.undobuffer.cumulate = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00002002
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002003## three dummy methods to be implemented by child class:
Thomas Wouters477c8d52006-05-27 19:21:47 +00002004
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002005 def speed(self, s=0):
2006 """dummy method - to be overwritten by child class"""
2007 def _tracer(self, a=None, b=None):
2008 """dummy method - to be overwritten by child class"""
2009 def _delay(self, n=None):
2010 """dummy method - to be overwritten by child class"""
Thomas Wouters477c8d52006-05-27 19:21:47 +00002011
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002012 fd = forward
2013 bk = back
2014 backward = back
2015 rt = right
2016 lt = left
2017 position = pos
2018 setpos = goto
2019 setposition = goto
2020 seth = setheading
Thomas Wouters477c8d52006-05-27 19:21:47 +00002021
Thomas Wouters477c8d52006-05-27 19:21:47 +00002022
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002023class TPen(object):
2024 """Drawing part of the RawTurtle.
2025 Implements drawing properties.
2026 """
2027 def __init__(self, resizemode=_CFG["resizemode"]):
2028 self._resizemode = resizemode # or "user" or "noresize"
2029 self.undobuffer = None
2030 TPen._reset(self)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002031
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002032 def _reset(self, pencolor=_CFG["pencolor"],
2033 fillcolor=_CFG["fillcolor"]):
2034 self._pensize = 1
2035 self._shown = True
2036 self._pencolor = pencolor
2037 self._fillcolor = fillcolor
2038 self._drawing = True
2039 self._speed = 3
Georg Brandleaa84ef2009-05-05 08:14:33 +00002040 self._stretchfactor = (1., 1.)
2041 self._shearfactor = 0.
2042 self._tilt = 0.
2043 self._shapetrafo = (1., 0., 0., 1.)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002044 self._outlinewidth = 1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002045
2046 def resizemode(self, rmode=None):
2047 """Set resizemode to one of the values: "auto", "user", "noresize".
2048
2049 (Optional) Argument:
2050 rmode -- one of the strings "auto", "user", "noresize"
2051
2052 Different resizemodes have the following effects:
2053 - "auto" adapts the appearance of the turtle
2054 corresponding to the value of pensize.
2055 - "user" adapts the appearance of the turtle according to the
2056 values of stretchfactor and outlinewidth (outline),
2057 which are set by shapesize()
2058 - "noresize" no adaption of the turtle's appearance takes place.
2059 If no argument is given, return current resizemode.
2060 resizemode("user") is called by a call of shapesize with arguments.
2061
2062
2063 Examples (for a Turtle instance named turtle):
2064 >>> turtle.resizemode("noresize")
2065 >>> turtle.resizemode()
2066 'noresize'
Thomas Wouters477c8d52006-05-27 19:21:47 +00002067 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002068 if rmode is None:
2069 return self._resizemode
2070 rmode = rmode.lower()
2071 if rmode in ["auto", "user", "noresize"]:
2072 self.pen(resizemode=rmode)
Guido van Rossumb241b671998-12-04 16:42:46 +00002073
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002074 def pensize(self, width=None):
2075 """Set or return the line thickness.
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002076
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002077 Aliases: pensize | width
Thomas Wouters477c8d52006-05-27 19:21:47 +00002078
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002079 Argument:
2080 width -- positive number
Thomas Wouters477c8d52006-05-27 19:21:47 +00002081
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002082 Set the line thickness to width or return it. If resizemode is set
2083 to "auto" and turtleshape is a polygon, that polygon is drawn with
2084 the same line thickness. If no argument is given, current pensize
2085 is returned.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002086
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002087 Example (for a Turtle instance named turtle):
2088 >>> turtle.pensize()
2089 1
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002090 >>> turtle.pensize(10) # from here on lines of width 10 are drawn
Thomas Wouters477c8d52006-05-27 19:21:47 +00002091 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002092 if width is None:
2093 return self._pensize
2094 self.pen(pensize=width)
Thomas Wouters477c8d52006-05-27 19:21:47 +00002095
2096
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002097 def penup(self):
2098 """Pull the pen up -- no drawing when moving.
Thomas Wouters477c8d52006-05-27 19:21:47 +00002099
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002100 Aliases: penup | pu | up
Thomas Wouters477c8d52006-05-27 19:21:47 +00002101
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002102 No argument
2103
2104 Example (for a Turtle instance named turtle):
2105 >>> turtle.penup()
Thomas Wouters477c8d52006-05-27 19:21:47 +00002106 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002107 if not self._drawing:
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002108 return
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002109 self.pen(pendown=False)
Guido van Rossum3c7a25a2001-08-09 16:42:07 +00002110
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002111 def pendown(self):
2112 """Pull the pen down -- drawing when moving.
2113
2114 Aliases: pendown | pd | down
2115
2116 No argument.
2117
2118 Example (for a Turtle instance named turtle):
2119 >>> turtle.pendown()
2120 """
2121 if self._drawing:
2122 return
2123 self.pen(pendown=True)
2124
2125 def isdown(self):
2126 """Return True if pen is down, False if it's up.
2127
2128 No argument.
2129
2130 Example (for a Turtle instance named turtle):
2131 >>> turtle.penup()
2132 >>> turtle.isdown()
2133 False
2134 >>> turtle.pendown()
2135 >>> turtle.isdown()
2136 True
2137 """
2138 return self._drawing
2139
2140 def speed(self, speed=None):
2141 """ Return or set the turtle's speed.
2142
2143 Optional argument:
2144 speed -- an integer in the range 0..10 or a speedstring (see below)
2145
2146 Set the turtle's speed to an integer value in the range 0 .. 10.
2147 If no argument is given: return current speed.
2148
2149 If input is a number greater than 10 or smaller than 0.5,
2150 speed is set to 0.
2151 Speedstrings are mapped to speedvalues in the following way:
2152 'fastest' : 0
2153 'fast' : 10
2154 'normal' : 6
2155 'slow' : 3
2156 'slowest' : 1
2157 speeds from 1 to 10 enforce increasingly faster animation of
2158 line drawing and turtle turning.
2159
2160 Attention:
2161 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2162 and likewise left/right make the turtle turn instantly.
2163
2164 Example (for a Turtle instance named turtle):
2165 >>> turtle.speed(3)
2166 """
2167 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2168 if speed is None:
2169 return self._speed
2170 if speed in speeds:
2171 speed = speeds[speed]
2172 elif 0.5 < speed < 10.5:
2173 speed = int(round(speed))
2174 else:
2175 speed = 0
2176 self.pen(speed=speed)
2177
2178 def color(self, *args):
2179 """Return or set the pencolor and fillcolor.
2180
2181 Arguments:
2182 Several input formats are allowed.
2183 They use 0, 1, 2, or 3 arguments as follows:
2184
2185 color()
2186 Return the current pencolor and the current fillcolor
2187 as a pair of color specification strings as are returned
2188 by pencolor and fillcolor.
2189 color(colorstring), color((r,g,b)), color(r,g,b)
2190 inputs as in pencolor, set both, fillcolor and pencolor,
2191 to the given value.
2192 color(colorstring1, colorstring2),
2193 color((r1,g1,b1), (r2,g2,b2))
2194 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2195 and analogously, if the other input format is used.
2196
2197 If turtleshape is a polygon, outline and interior of that polygon
2198 is drawn with the newly set colors.
luzpaza5293b42017-11-05 07:37:50 -06002199 For more info see: pencolor, fillcolor
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002200
2201 Example (for a Turtle instance named turtle):
2202 >>> turtle.color('red', 'green')
2203 >>> turtle.color()
2204 ('red', 'green')
2205 >>> colormode(255)
2206 >>> color((40, 80, 120), (160, 200, 240))
2207 >>> color()
2208 ('#285078', '#a0c8f0')
2209 """
2210 if args:
2211 l = len(args)
2212 if l == 1:
2213 pcolor = fcolor = args[0]
2214 elif l == 2:
2215 pcolor, fcolor = args
2216 elif l == 3:
2217 pcolor = fcolor = args
2218 pcolor = self._colorstr(pcolor)
2219 fcolor = self._colorstr(fcolor)
2220 self.pen(pencolor=pcolor, fillcolor=fcolor)
2221 else:
2222 return self._color(self._pencolor), self._color(self._fillcolor)
2223
2224 def pencolor(self, *args):
2225 """ Return or set the pencolor.
2226
2227 Arguments:
2228 Four input formats are allowed:
2229 - pencolor()
2230 Return the current pencolor as color specification string,
2231 possibly in hex-number format (see example).
2232 May be used as input to another color/pencolor/fillcolor call.
2233 - pencolor(colorstring)
2234 s is a Tk color specification string, such as "red" or "yellow"
2235 - pencolor((r, g, b))
2236 *a tuple* of r, g, and b, which represent, an RGB color,
2237 and each of r, g, and b are in the range 0..colormode,
2238 where colormode is either 1.0 or 255
2239 - pencolor(r, g, b)
2240 r, g, and b represent an RGB color, and each of r, g, and b
2241 are in the range 0..colormode
2242
2243 If turtleshape is a polygon, the outline of that polygon is drawn
2244 with the newly set pencolor.
2245
2246 Example (for a Turtle instance named turtle):
2247 >>> turtle.pencolor('brown')
2248 >>> tup = (0.2, 0.8, 0.55)
2249 >>> turtle.pencolor(tup)
2250 >>> turtle.pencolor()
2251 '#33cc8c'
2252 """
2253 if args:
2254 color = self._colorstr(args)
2255 if color == self._pencolor:
2256 return
2257 self.pen(pencolor=color)
2258 else:
2259 return self._color(self._pencolor)
2260
2261 def fillcolor(self, *args):
2262 """ Return or set the fillcolor.
2263
2264 Arguments:
2265 Four input formats are allowed:
2266 - fillcolor()
2267 Return the current fillcolor as color specification string,
2268 possibly in hex-number format (see example).
2269 May be used as input to another color/pencolor/fillcolor call.
2270 - fillcolor(colorstring)
2271 s is a Tk color specification string, such as "red" or "yellow"
2272 - fillcolor((r, g, b))
2273 *a tuple* of r, g, and b, which represent, an RGB color,
2274 and each of r, g, and b are in the range 0..colormode,
2275 where colormode is either 1.0 or 255
2276 - fillcolor(r, g, b)
2277 r, g, and b represent an RGB color, and each of r, g, and b
2278 are in the range 0..colormode
2279
2280 If turtleshape is a polygon, the interior of that polygon is drawn
2281 with the newly set fillcolor.
2282
2283 Example (for a Turtle instance named turtle):
2284 >>> turtle.fillcolor('violet')
2285 >>> col = turtle.pencolor()
2286 >>> turtle.fillcolor(col)
2287 >>> turtle.fillcolor(0, .5, 0)
2288 """
2289 if args:
2290 color = self._colorstr(args)
2291 if color == self._fillcolor:
2292 return
2293 self.pen(fillcolor=color)
2294 else:
2295 return self._color(self._fillcolor)
2296
2297 def showturtle(self):
2298 """Makes the turtle visible.
2299
2300 Aliases: showturtle | st
2301
2302 No argument.
2303
2304 Example (for a Turtle instance named turtle):
2305 >>> turtle.hideturtle()
2306 >>> turtle.showturtle()
2307 """
2308 self.pen(shown=True)
2309
2310 def hideturtle(self):
2311 """Makes the turtle invisible.
2312
2313 Aliases: hideturtle | ht
2314
2315 No argument.
2316
2317 It's a good idea to do this while you're in the
2318 middle of a complicated drawing, because hiding
2319 the turtle speeds up the drawing observably.
2320
2321 Example (for a Turtle instance named turtle):
2322 >>> turtle.hideturtle()
2323 """
2324 self.pen(shown=False)
2325
2326 def isvisible(self):
2327 """Return True if the Turtle is shown, False if it's hidden.
2328
2329 No argument.
2330
2331 Example (for a Turtle instance named turtle):
2332 >>> turtle.hideturtle()
2333 >>> print turtle.isvisible():
2334 False
2335 """
2336 return self._shown
2337
2338 def pen(self, pen=None, **pendict):
2339 """Return or set the pen's attributes.
2340
2341 Arguments:
2342 pen -- a dictionary with some or all of the below listed keys.
2343 **pendict -- one or more keyword-arguments with the below
2344 listed keys as keywords.
2345
2346 Return or set the pen's attributes in a 'pen-dictionary'
2347 with the following key/value pairs:
2348 "shown" : True/False
2349 "pendown" : True/False
2350 "pencolor" : color-string or color-tuple
2351 "fillcolor" : color-string or color-tuple
2352 "pensize" : positive number
2353 "speed" : number in range 0..10
2354 "resizemode" : "auto" or "user" or "noresize"
2355 "stretchfactor": (positive number, positive number)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002356 "shearfactor": number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002357 "outline" : positive number
2358 "tilt" : number
2359
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002360 This dictionary can be used as argument for a subsequent
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002361 pen()-call to restore the former pen-state. Moreover one
2362 or more of these attributes can be provided as keyword-arguments.
2363 This can be used to set several pen attributes in one statement.
Guido van Rossumb241b671998-12-04 16:42:46 +00002364
2365
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002366 Examples (for a Turtle instance named turtle):
2367 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2368 >>> turtle.pen()
2369 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2370 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002371 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002372 >>> penstate=turtle.pen()
2373 >>> turtle.color("yellow","")
2374 >>> turtle.penup()
2375 >>> turtle.pen()
2376 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2377 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002378 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002379 >>> p.pen(penstate, fillcolor="green")
2380 >>> p.pen()
2381 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2382 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
Georg Brandleaa84ef2009-05-05 08:14:33 +00002383 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0}
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002384 """
2385 _pd = {"shown" : self._shown,
2386 "pendown" : self._drawing,
2387 "pencolor" : self._pencolor,
2388 "fillcolor" : self._fillcolor,
2389 "pensize" : self._pensize,
2390 "speed" : self._speed,
2391 "resizemode" : self._resizemode,
2392 "stretchfactor" : self._stretchfactor,
Georg Brandleaa84ef2009-05-05 08:14:33 +00002393 "shearfactor" : self._shearfactor,
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002394 "outline" : self._outlinewidth,
2395 "tilt" : self._tilt
2396 }
Guido van Rossumb241b671998-12-04 16:42:46 +00002397
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002398 if not (pen or pendict):
2399 return _pd
2400
2401 if isinstance(pen, dict):
2402 p = pen
2403 else:
2404 p = {}
2405 p.update(pendict)
2406
2407 _p_buf = {}
2408 for key in p:
2409 _p_buf[key] = _pd[key]
2410
2411 if self.undobuffer:
2412 self.undobuffer.push(("pen", _p_buf))
2413
2414 newLine = False
2415 if "pendown" in p:
2416 if self._drawing != p["pendown"]:
2417 newLine = True
2418 if "pencolor" in p:
2419 if isinstance(p["pencolor"], tuple):
2420 p["pencolor"] = self._colorstr((p["pencolor"],))
2421 if self._pencolor != p["pencolor"]:
2422 newLine = True
2423 if "pensize" in p:
2424 if self._pensize != p["pensize"]:
2425 newLine = True
2426 if newLine:
2427 self._newLine()
2428 if "pendown" in p:
2429 self._drawing = p["pendown"]
2430 if "pencolor" in p:
2431 self._pencolor = p["pencolor"]
2432 if "pensize" in p:
2433 self._pensize = p["pensize"]
2434 if "fillcolor" in p:
2435 if isinstance(p["fillcolor"], tuple):
2436 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2437 self._fillcolor = p["fillcolor"]
2438 if "speed" in p:
2439 self._speed = p["speed"]
2440 if "resizemode" in p:
2441 self._resizemode = p["resizemode"]
2442 if "stretchfactor" in p:
2443 sf = p["stretchfactor"]
2444 if isinstance(sf, (int, float)):
2445 sf = (sf, sf)
2446 self._stretchfactor = sf
Georg Brandleaa84ef2009-05-05 08:14:33 +00002447 if "shearfactor" in p:
2448 self._shearfactor = p["shearfactor"]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002449 if "outline" in p:
2450 self._outlinewidth = p["outline"]
2451 if "shown" in p:
2452 self._shown = p["shown"]
2453 if "tilt" in p:
2454 self._tilt = p["tilt"]
Georg Brandleaa84ef2009-05-05 08:14:33 +00002455 if "stretchfactor" in p or "tilt" in p or "shearfactor" in p:
2456 scx, scy = self._stretchfactor
2457 shf = self._shearfactor
2458 sa, ca = math.sin(self._tilt), math.cos(self._tilt)
2459 self._shapetrafo = ( scx*ca, scy*(shf*ca + sa),
2460 -scx*sa, scy*(ca - shf*sa))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002461 self._update()
2462
2463## three dummy methods to be implemented by child class:
2464
2465 def _newLine(self, usePos = True):
2466 """dummy method - to be overwritten by child class"""
2467 def _update(self, count=True, forced=False):
2468 """dummy method - to be overwritten by child class"""
2469 def _color(self, args):
2470 """dummy method - to be overwritten by child class"""
2471 def _colorstr(self, args):
2472 """dummy method - to be overwritten by child class"""
2473
2474 width = pensize
2475 up = penup
2476 pu = penup
2477 pd = pendown
2478 down = pendown
2479 st = showturtle
2480 ht = hideturtle
2481
2482
2483class _TurtleImage(object):
2484 """Helper class: Datatype to store Turtle attributes
2485 """
2486
2487 def __init__(self, screen, shapeIndex):
2488 self.screen = screen
2489 self._type = None
2490 self._setshape(shapeIndex)
2491
2492 def _setshape(self, shapeIndex):
Georg Brandleaa84ef2009-05-05 08:14:33 +00002493 screen = self.screen
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002494 self.shapeIndex = shapeIndex
2495 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2496 return
2497 if self._type == "image" == screen._shapes[shapeIndex]._type:
2498 return
2499 if self._type in ["image", "polygon"]:
2500 screen._delete(self._item)
2501 elif self._type == "compound":
2502 for item in self._item:
2503 screen._delete(item)
2504 self._type = screen._shapes[shapeIndex]._type
2505 if self._type == "polygon":
2506 self._item = screen._createpoly()
2507 elif self._type == "image":
2508 self._item = screen._createimage(screen._shapes["blank"]._data)
2509 elif self._type == "compound":
2510 self._item = [screen._createpoly() for item in
2511 screen._shapes[shapeIndex]._data]
2512
2513
2514class RawTurtle(TPen, TNavigator):
2515 """Animation part of the RawTurtle.
2516 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002517 its animation.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002518 """
2519 screens = []
2520
2521 def __init__(self, canvas=None,
2522 shape=_CFG["shape"],
2523 undobuffersize=_CFG["undobuffersize"],
2524 visible=_CFG["visible"]):
Martin v. Löwis601149b2008-09-29 22:19:08 +00002525 if isinstance(canvas, _Screen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002526 self.screen = canvas
2527 elif isinstance(canvas, TurtleScreen):
2528 if canvas not in RawTurtle.screens:
2529 RawTurtle.screens.append(canvas)
2530 self.screen = canvas
2531 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2532 for screen in RawTurtle.screens:
2533 if screen.cv == canvas:
2534 self.screen = screen
2535 break
2536 else:
2537 self.screen = TurtleScreen(canvas)
2538 RawTurtle.screens.append(self.screen)
2539 else:
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03002540 raise TurtleGraphicsError("bad canvas argument %s" % canvas)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002541
2542 screen = self.screen
2543 TNavigator.__init__(self, screen.mode())
2544 TPen.__init__(self)
2545 screen._turtles.append(self)
2546 self.drawingLineItem = screen._createline()
2547 self.turtle = _TurtleImage(screen, shape)
2548 self._poly = None
2549 self._creatingPoly = False
2550 self._fillitem = self._fillpath = None
2551 self._shown = visible
2552 self._hidden_from_screen = False
2553 self.currentLineItem = screen._createline()
2554 self.currentLine = [self._position]
2555 self.items = [self.currentLineItem]
2556 self.stampItems = []
2557 self._undobuffersize = undobuffersize
2558 self.undobuffer = Tbuffer(undobuffersize)
2559 self._update()
2560
2561 def reset(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00002562 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002563
2564 No argument.
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002565
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002566 Delete the turtle's drawings from the screen, re-center the turtle
2567 and set variables to the default values.
2568
2569 Example (for a Turtle instance named turtle):
2570 >>> turtle.position()
2571 (0.00,-22.00)
2572 >>> turtle.heading()
2573 100.0
2574 >>> turtle.reset()
2575 >>> turtle.position()
2576 (0.00,0.00)
2577 >>> turtle.heading()
2578 0.0
2579 """
2580 TNavigator.reset(self)
2581 TPen._reset(self)
2582 self._clear()
2583 self._drawturtle()
2584 self._update()
2585
2586 def setundobuffer(self, size):
2587 """Set or disable undobuffer.
2588
2589 Argument:
2590 size -- an integer or None
2591
2592 If size is an integer an empty undobuffer of given size is installed.
2593 Size gives the maximum number of turtle-actions that can be undone
2594 by the undo() function.
2595 If size is None, no undobuffer is present.
2596
2597 Example (for a Turtle instance named turtle):
2598 >>> turtle.setundobuffer(42)
2599 """
Raymond Hettinger854e76e2014-07-20 21:30:32 -07002600 if size is None or size <= 0:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002601 self.undobuffer = None
2602 else:
2603 self.undobuffer = Tbuffer(size)
2604
2605 def undobufferentries(self):
2606 """Return count of entries in the undobuffer.
2607
2608 No argument.
2609
2610 Example (for a Turtle instance named turtle):
2611 >>> while undobufferentries():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002612 ... undo()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002613 """
2614 if self.undobuffer is None:
2615 return 0
2616 return self.undobuffer.nr_of_items()
2617
2618 def _clear(self):
2619 """Delete all of pen's drawings"""
2620 self._fillitem = self._fillpath = None
2621 for item in self.items:
2622 self.screen._delete(item)
2623 self.currentLineItem = self.screen._createline()
2624 self.currentLine = []
2625 if self._drawing:
2626 self.currentLine.append(self._position)
2627 self.items = [self.currentLineItem]
2628 self.clearstamps()
2629 self.setundobuffer(self._undobuffersize)
2630
2631
2632 def clear(self):
2633 """Delete the turtle's drawings from the screen. Do not move turtle.
2634
2635 No arguments.
2636
2637 Delete the turtle's drawings from the screen. Do not move turtle.
2638 State and position of the turtle as well as drawings of other
2639 turtles are not affected.
2640
2641 Examples (for a Turtle instance named turtle):
2642 >>> turtle.clear()
2643 """
2644 self._clear()
2645 self._update()
2646
2647 def _update_data(self):
2648 self.screen._incrementudc()
2649 if self.screen._updatecounter != 0:
2650 return
2651 if len(self.currentLine)>1:
2652 self.screen._drawline(self.currentLineItem, self.currentLine,
2653 self._pencolor, self._pensize)
2654
2655 def _update(self):
2656 """Perform a Turtle-data update.
2657 """
2658 screen = self.screen
2659 if screen._tracing == 0:
2660 return
2661 elif screen._tracing == 1:
2662 self._update_data()
2663 self._drawturtle()
2664 screen._update() # TurtleScreenBase
2665 screen._delay(screen._delayvalue) # TurtleScreenBase
2666 else:
2667 self._update_data()
2668 if screen._updatecounter == 0:
2669 for t in screen.turtles():
2670 t._drawturtle()
2671 screen._update()
2672
2673 def _tracer(self, flag=None, delay=None):
2674 """Turns turtle animation on/off and set delay for update drawings.
2675
2676 Optional arguments:
2677 n -- nonnegative integer
2678 delay -- nonnegative integer
2679
2680 If n is given, only each n-th regular screen update is really performed.
2681 (Can be used to accelerate the drawing of complex graphics.)
2682 Second arguments sets delay value (see RawTurtle.delay())
2683
2684 Example (for a Turtle instance named turtle):
2685 >>> turtle.tracer(8, 25)
2686 >>> dist = 2
2687 >>> for i in range(200):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002688 ... turtle.fd(dist)
2689 ... turtle.rt(90)
2690 ... dist += 2
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002691 """
2692 return self.screen.tracer(flag, delay)
2693
2694 def _color(self, args):
2695 return self.screen._color(args)
2696
2697 def _colorstr(self, args):
2698 return self.screen._colorstr(args)
2699
2700 def _cc(self, args):
2701 """Convert colortriples to hexstrings.
2702 """
2703 if isinstance(args, str):
2704 return args
2705 try:
2706 r, g, b = args
Serhiy Storchakacefa9172016-06-14 22:52:04 +03002707 except (TypeError, ValueError):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002708 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2709 if self.screen._colormode == 1.0:
2710 r, g, b = [round(255.0*x) for x in (r, g, b)]
2711 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2712 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2713 return "#%02x%02x%02x" % (r, g, b)
2714
2715 def clone(self):
2716 """Create and return a clone of the turtle.
2717
2718 No argument.
2719
2720 Create and return a clone of the turtle with same position, heading
2721 and turtle properties.
2722
2723 Example (for a Turtle instance named mick):
2724 mick = Turtle()
2725 joe = mick.clone()
2726 """
2727 screen = self.screen
2728 self._newLine(self._drawing)
2729
2730 turtle = self.turtle
2731 self.screen = None
2732 self.turtle = None # too make self deepcopy-able
2733
2734 q = deepcopy(self)
2735
2736 self.screen = screen
2737 self.turtle = turtle
2738
2739 q.screen = screen
2740 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2741
2742 screen._turtles.append(q)
2743 ttype = screen._shapes[self.turtle.shapeIndex]._type
2744 if ttype == "polygon":
2745 q.turtle._item = screen._createpoly()
2746 elif ttype == "image":
2747 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2748 elif ttype == "compound":
2749 q.turtle._item = [screen._createpoly() for item in
2750 screen._shapes[self.turtle.shapeIndex]._data]
2751 q.currentLineItem = screen._createline()
2752 q._update()
2753 return q
2754
2755 def shape(self, name=None):
2756 """Set turtle shape to shape with given name / return current shapename.
2757
2758 Optional argument:
2759 name -- a string, which is a valid shapename
2760
2761 Set turtle shape to shape with given name or, if name is not given,
2762 return name of current shape.
2763 Shape with name must exist in the TurtleScreen's shape dictionary.
2764 Initially there are the following polygon shapes:
2765 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2766 To learn about how to deal with shapes see Screen-method register_shape.
2767
2768 Example (for a Turtle instance named turtle):
2769 >>> turtle.shape()
2770 'arrow'
2771 >>> turtle.shape("turtle")
2772 >>> turtle.shape()
2773 'turtle'
2774 """
2775 if name is None:
2776 return self.turtle.shapeIndex
2777 if not name in self.screen.getshapes():
2778 raise TurtleGraphicsError("There is no shape named %s" % name)
2779 self.turtle._setshape(name)
2780 self._update()
2781
2782 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2783 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2784
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03002785 Optional arguments:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002786 stretch_wid : positive number
2787 stretch_len : positive number
2788 outline : positive number
2789
2790 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2791 Set resizemode to "user".
2792 If and only if resizemode is set to "user", the turtle will be displayed
2793 stretched according to its stretchfactors:
2794 stretch_wid is stretchfactor perpendicular to orientation
2795 stretch_len is stretchfactor in direction of turtles orientation.
2796 outline determines the width of the shapes's outline.
2797
2798 Examples (for a Turtle instance named turtle):
2799 >>> turtle.resizemode("user")
2800 >>> turtle.shapesize(5, 5, 12)
2801 >>> turtle.shapesize(outline=8)
2802 """
Florent Xiclunafd1b0932010-03-28 00:25:02 +00002803 if stretch_wid is stretch_len is outline is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002804 stretch_wid, stretch_len = self._stretchfactor
2805 return stretch_wid, stretch_len, self._outlinewidth
Georg Brandleaa84ef2009-05-05 08:14:33 +00002806 if stretch_wid == 0 or stretch_len == 0:
2807 raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero")
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002808 if stretch_wid is not None:
2809 if stretch_len is None:
2810 stretchfactor = stretch_wid, stretch_wid
2811 else:
2812 stretchfactor = stretch_wid, stretch_len
2813 elif stretch_len is not None:
2814 stretchfactor = self._stretchfactor[0], stretch_len
2815 else:
2816 stretchfactor = self._stretchfactor
2817 if outline is None:
2818 outline = self._outlinewidth
2819 self.pen(resizemode="user",
2820 stretchfactor=stretchfactor, outline=outline)
2821
Georg Brandleaa84ef2009-05-05 08:14:33 +00002822 def shearfactor(self, shear=None):
2823 """Set or return the current shearfactor.
2824
2825 Optional argument: shear -- number, tangent of the shear angle
2826
2827 Shear the turtleshape according to the given shearfactor shear,
2828 which is the tangent of the shear angle. DO NOT change the
2829 turtle's heading (direction of movement).
2830 If shear is not given: return the current shearfactor, i. e. the
2831 tangent of the shear angle, by which lines parallel to the
2832 heading of the turtle are sheared.
2833
2834 Examples (for a Turtle instance named turtle):
2835 >>> turtle.shape("circle")
2836 >>> turtle.shapesize(5,2)
2837 >>> turtle.shearfactor(0.5)
2838 >>> turtle.shearfactor()
2839 >>> 0.5
2840 """
2841 if shear is None:
2842 return self._shearfactor
2843 self.pen(resizemode="user", shearfactor=shear)
2844
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002845 def settiltangle(self, angle):
2846 """Rotate the turtleshape to point in the specified direction
2847
Georg Brandleaa84ef2009-05-05 08:14:33 +00002848 Argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002849
2850 Rotate the turtleshape to point in the direction specified by angle,
2851 regardless of its current tilt-angle. DO NOT change the turtle's
2852 heading (direction of movement).
2853
2854
2855 Examples (for a Turtle instance named turtle):
2856 >>> turtle.shape("circle")
2857 >>> turtle.shapesize(5,2)
2858 >>> turtle.settiltangle(45)
2859 >>> stamp()
2860 >>> turtle.fd(50)
2861 >>> turtle.settiltangle(-45)
2862 >>> stamp()
2863 >>> turtle.fd(50)
2864 """
2865 tilt = -angle * self._degreesPerAU * self._angleOrient
Marek Madejski6844b562020-09-01 18:42:41 +02002866 tilt = math.radians(tilt) % math.tau
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002867 self.pen(resizemode="user", tilt=tilt)
2868
Georg Brandleaa84ef2009-05-05 08:14:33 +00002869 def tiltangle(self, angle=None):
2870 """Set or return the current tilt-angle.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002871
Georg Brandleaa84ef2009-05-05 08:14:33 +00002872 Optional argument: angle -- number
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002873
Georg Brandleaa84ef2009-05-05 08:14:33 +00002874 Rotate the turtleshape to point in the direction specified by angle,
2875 regardless of its current tilt-angle. DO NOT change the turtle's
2876 heading (direction of movement).
2877 If angle is not given: return the current tilt-angle, i. e. the angle
2878 between the orientation of the turtleshape and the heading of the
2879 turtle (its direction of movement).
2880
2881 Deprecated since Python 3.1
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002882
2883 Examples (for a Turtle instance named turtle):
2884 >>> turtle.shape("circle")
2885 >>> turtle.shapesize(5,2)
2886 >>> turtle.tilt(45)
2887 >>> turtle.tiltangle()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002888 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00002889 if angle is None:
Marek Madejski6844b562020-09-01 18:42:41 +02002890 tilt = -math.degrees(self._tilt) * self._angleOrient
Georg Brandleaa84ef2009-05-05 08:14:33 +00002891 return (tilt / self._degreesPerAU) % self._fullcircle
2892 else:
2893 self.settiltangle(angle)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002894
2895 def tilt(self, angle):
2896 """Rotate the turtleshape by angle.
2897
2898 Argument:
2899 angle - a number
2900
2901 Rotate the turtleshape by angle from its current tilt-angle,
2902 but do NOT change the turtle's heading (direction of movement).
2903
2904 Examples (for a Turtle instance named turtle):
2905 >>> turtle.shape("circle")
2906 >>> turtle.shapesize(5,2)
2907 >>> turtle.tilt(30)
2908 >>> turtle.fd(50)
2909 >>> turtle.tilt(30)
2910 >>> turtle.fd(50)
2911 """
2912 self.settiltangle(angle + self.tiltangle())
2913
Georg Brandleaa84ef2009-05-05 08:14:33 +00002914 def shapetransform(self, t11=None, t12=None, t21=None, t22=None):
2915 """Set or return the current transformation matrix of the turtle shape.
2916
2917 Optional arguments: t11, t12, t21, t22 -- numbers.
2918
2919 If none of the matrix elements are given, return the transformation
2920 matrix.
2921 Otherwise set the given elements and transform the turtleshape
2922 according to the matrix consisting of first row t11, t12 and
2923 second row t21, 22.
2924 Modify stretchfactor, shearfactor and tiltangle according to the
2925 given matrix.
2926
2927 Examples (for a Turtle instance named turtle):
2928 >>> turtle.shape("square")
2929 >>> turtle.shapesize(4,2)
2930 >>> turtle.shearfactor(-0.5)
2931 >>> turtle.shapetransform()
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02002932 (4.0, -1.0, -0.0, 2.0)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002933 """
2934 if t11 is t12 is t21 is t22 is None:
2935 return self._shapetrafo
2936 m11, m12, m21, m22 = self._shapetrafo
2937 if t11 is not None: m11 = t11
2938 if t12 is not None: m12 = t12
2939 if t21 is not None: m21 = t21
2940 if t22 is not None: m22 = t22
2941 if t11 * t22 - t12 * t21 == 0:
2942 raise TurtleGraphicsError("Bad shape transform matrix: must not be singular")
2943 self._shapetrafo = (m11, m12, m21, m22)
Marek Madejski6844b562020-09-01 18:42:41 +02002944 alfa = math.atan2(-m21, m11) % math.tau
Georg Brandleaa84ef2009-05-05 08:14:33 +00002945 sa, ca = math.sin(alfa), math.cos(alfa)
2946 a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22,
2947 sa*m11 + ca*m21, sa*m12 + ca*m22)
2948 self._stretchfactor = a11, a22
2949 self._shearfactor = a12/a22
2950 self._tilt = alfa
Raymond Hettinger6dec4ea2014-06-22 01:21:51 -07002951 self.pen(resizemode="user")
Georg Brandleaa84ef2009-05-05 08:14:33 +00002952
2953
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002954 def _polytrafo(self, poly):
2955 """Computes transformed polygon shapes from a shape
2956 according to current position and heading.
2957 """
2958 screen = self.screen
2959 p0, p1 = self._position
2960 e0, e1 = self._orient
2961 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2962 e0, e1 = (1.0 / abs(e)) * e
2963 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2964 for (x, y) in poly]
2965
Georg Brandleaa84ef2009-05-05 08:14:33 +00002966 def get_shapepoly(self):
2967 """Return the current shape polygon as tuple of coordinate pairs.
2968
2969 No argument.
2970
2971 Examples (for a Turtle instance named turtle):
2972 >>> turtle.shape("square")
2973 >>> turtle.shapetransform(4, -1, 0, 2)
2974 >>> turtle.get_shapepoly()
2975 ((50, -20), (30, 20), (-50, 20), (-30, -20))
2976
2977 """
2978 shape = self.screen._shapes[self.turtle.shapeIndex]
2979 if shape._type == "polygon":
2980 return self._getshapepoly(shape._data, shape._type == "compound")
2981 # else return None
2982
2983 def _getshapepoly(self, polygon, compound=False):
2984 """Calculate transformed shape polygon according to resizemode
2985 and shapetransform.
2986 """
2987 if self._resizemode == "user" or compound:
2988 t11, t12, t21, t22 = self._shapetrafo
2989 elif self._resizemode == "auto":
2990 l = max(1, self._pensize/5.0)
2991 t11, t12, t21, t22 = l, 0, 0, l
2992 elif self._resizemode == "noresize":
2993 return polygon
Jon Dufresne39726282017-05-18 07:35:54 -07002994 return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon)
Georg Brandleaa84ef2009-05-05 08:14:33 +00002995
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002996 def _drawturtle(self):
2997 """Manages the correct rendering of the turtle with respect to
Mark Dickinson934896d2009-02-21 20:59:32 +00002998 its shape, resizemode, stretch and tilt etc."""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00002999 screen = self.screen
3000 shape = screen._shapes[self.turtle.shapeIndex]
3001 ttype = shape._type
3002 titem = self.turtle._item
3003 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
3004 self._hidden_from_screen = False
3005 tshape = shape._data
3006 if ttype == "polygon":
Georg Brandleaa84ef2009-05-05 08:14:33 +00003007 if self._resizemode == "noresize": w = 1
3008 elif self._resizemode == "auto": w = self._pensize
3009 else: w =self._outlinewidth
3010 shape = self._polytrafo(self._getshapepoly(tshape))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003011 fc, oc = self._fillcolor, self._pencolor
3012 screen._drawpoly(titem, shape, fill=fc, outline=oc,
3013 width=w, top=True)
3014 elif ttype == "image":
3015 screen._drawimage(titem, self._position, tshape)
3016 elif ttype == "compound":
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003017 for item, (poly, fc, oc) in zip(titem, tshape):
Georg Brandleaa84ef2009-05-05 08:14:33 +00003018 poly = self._polytrafo(self._getshapepoly(poly, True))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003019 screen._drawpoly(item, poly, fill=self._cc(fc),
Georg Brandleaa84ef2009-05-05 08:14:33 +00003020 outline=self._cc(oc), width=self._outlinewidth, top=True)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003021 else:
3022 if self._hidden_from_screen:
3023 return
3024 if ttype == "polygon":
3025 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
3026 elif ttype == "image":
3027 screen._drawimage(titem, self._position,
3028 screen._shapes["blank"]._data)
3029 elif ttype == "compound":
3030 for item in titem:
3031 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
3032 self._hidden_from_screen = True
3033
3034############################## stamp stuff ###############################
3035
3036 def stamp(self):
Mark Dickinsonf8798f52009-02-20 20:53:56 +00003037 """Stamp a copy of the turtleshape onto the canvas and return its id.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003038
3039 No argument.
3040
3041 Stamp a copy of the turtle shape onto the canvas at the current
3042 turtle position. Return a stamp_id for that stamp, which can be
3043 used to delete it by calling clearstamp(stamp_id).
3044
3045 Example (for a Turtle instance named turtle):
3046 >>> turtle.color("blue")
3047 >>> turtle.stamp()
3048 13
3049 >>> turtle.fd(50)
3050 """
3051 screen = self.screen
3052 shape = screen._shapes[self.turtle.shapeIndex]
3053 ttype = shape._type
3054 tshape = shape._data
3055 if ttype == "polygon":
3056 stitem = screen._createpoly()
Georg Brandleaa84ef2009-05-05 08:14:33 +00003057 if self._resizemode == "noresize": w = 1
3058 elif self._resizemode == "auto": w = self._pensize
3059 else: w =self._outlinewidth
3060 shape = self._polytrafo(self._getshapepoly(tshape))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003061 fc, oc = self._fillcolor, self._pencolor
3062 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
3063 width=w, top=True)
3064 elif ttype == "image":
3065 stitem = screen._createimage("")
3066 screen._drawimage(stitem, self._position, tshape)
3067 elif ttype == "compound":
3068 stitem = []
3069 for element in tshape:
3070 item = screen._createpoly()
3071 stitem.append(item)
3072 stitem = tuple(stitem)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003073 for item, (poly, fc, oc) in zip(stitem, tshape):
Georg Brandleaa84ef2009-05-05 08:14:33 +00003074 poly = self._polytrafo(self._getshapepoly(poly, True))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003075 screen._drawpoly(item, poly, fill=self._cc(fc),
Georg Brandleaa84ef2009-05-05 08:14:33 +00003076 outline=self._cc(oc), width=self._outlinewidth, top=True)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003077 self.stampItems.append(stitem)
3078 self.undobuffer.push(("stamp", stitem))
3079 return stitem
3080
3081 def _clearstamp(self, stampid):
3082 """does the work for clearstamp() and clearstamps()
3083 """
3084 if stampid in self.stampItems:
3085 if isinstance(stampid, tuple):
3086 for subitem in stampid:
3087 self.screen._delete(subitem)
3088 else:
3089 self.screen._delete(stampid)
3090 self.stampItems.remove(stampid)
3091 # Delete stampitem from undobuffer if necessary
3092 # if clearstamp is called directly.
3093 item = ("stamp", stampid)
3094 buf = self.undobuffer
3095 if item not in buf.buffer:
3096 return
3097 index = buf.buffer.index(item)
3098 buf.buffer.remove(item)
3099 if index <= buf.ptr:
3100 buf.ptr = (buf.ptr - 1) % buf.bufsize
3101 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
3102
3103 def clearstamp(self, stampid):
3104 """Delete stamp with given stampid
3105
3106 Argument:
3107 stampid - an integer, must be return value of previous stamp() call.
3108
3109 Example (for a Turtle instance named turtle):
3110 >>> turtle.color("blue")
3111 >>> astamp = turtle.stamp()
3112 >>> turtle.fd(50)
3113 >>> turtle.clearstamp(astamp)
3114 """
3115 self._clearstamp(stampid)
3116 self._update()
3117
3118 def clearstamps(self, n=None):
3119 """Delete all or first/last n of turtle's stamps.
3120
3121 Optional argument:
3122 n -- an integer
3123
3124 If n is None, delete all of pen's stamps,
3125 else if n > 0 delete first n stamps
3126 else if n < 0 delete last n stamps.
3127
3128 Example (for a Turtle instance named turtle):
3129 >>> for i in range(8):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003130 ... turtle.stamp(); turtle.fd(30)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003131 ...
3132 >>> turtle.clearstamps(2)
3133 >>> turtle.clearstamps(-2)
3134 >>> turtle.clearstamps()
3135 """
3136 if n is None:
3137 toDelete = self.stampItems[:]
3138 elif n >= 0:
3139 toDelete = self.stampItems[:n]
3140 else:
3141 toDelete = self.stampItems[n:]
3142 for item in toDelete:
3143 self._clearstamp(item)
3144 self._update()
3145
3146 def _goto(self, end):
3147 """Move the pen to the point end, thereby drawing a line
Ezio Melotti30b9d5d2013-08-17 15:50:46 +03003148 if pen is down. All other methods for turtle movement depend
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003149 on this one.
3150 """
Alexander Belopolsky1842d0c2010-10-28 20:13:52 +00003151 ## Version with undo-stuff
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003152 go_modes = ( self._drawing,
3153 self._pencolor,
3154 self._pensize,
3155 isinstance(self._fillpath, list))
3156 screen = self.screen
3157 undo_entry = ("go", self._position, end, go_modes,
3158 (self.currentLineItem,
3159 self.currentLine[:],
3160 screen._pointlist(self.currentLineItem),
3161 self.items[:])
3162 )
3163 if self.undobuffer:
3164 self.undobuffer.push(undo_entry)
3165 start = self._position
3166 if self._speed and screen._tracing == 1:
3167 diff = (end-start)
3168 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3169 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3170 delta = diff * (1.0/nhops)
3171 for n in range(1, nhops):
3172 if n == 1:
3173 top = True
3174 else:
3175 top = False
3176 self._position = start + delta * n
3177 if self._drawing:
3178 screen._drawline(self.drawingLineItem,
3179 (start, self._position),
3180 self._pencolor, self._pensize, top)
3181 self._update()
3182 if self._drawing:
3183 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3184 fill="", width=self._pensize)
3185 # Turtle now at end,
3186 if self._drawing: # now update currentLine
3187 self.currentLine.append(end)
3188 if isinstance(self._fillpath, list):
3189 self._fillpath.append(end)
3190 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3191 self._position = end
3192 if self._creatingPoly:
3193 self._poly.append(end)
3194 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3195 # of life, the universe and everything
3196 self._newLine()
3197 self._update() #count=True)
3198
3199 def _undogoto(self, entry):
3200 """Reverse a _goto. Used for undo()
3201 """
3202 old, new, go_modes, coodata = entry
3203 drawing, pc, ps, filling = go_modes
3204 cLI, cL, pl, items = coodata
3205 screen = self.screen
3206 if abs(self._position - new) > 0.5:
3207 print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!")
3208 # restore former situation
3209 self.currentLineItem = cLI
3210 self.currentLine = cL
3211
3212 if pl == [(0, 0), (0, 0)]:
3213 usepc = ""
3214 else:
3215 usepc = pc
3216 screen._drawline(cLI, pl, fill=usepc, width=ps)
3217
3218 todelete = [i for i in self.items if (i not in items) and
3219 (screen._type(i) == "line")]
3220 for i in todelete:
3221 screen._delete(i)
3222 self.items.remove(i)
3223
3224 start = old
3225 if self._speed and screen._tracing == 1:
3226 diff = old - new
3227 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3228 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3229 delta = diff * (1.0/nhops)
3230 for n in range(1, nhops):
3231 if n == 1:
3232 top = True
3233 else:
3234 top = False
3235 self._position = new + delta * n
3236 if drawing:
3237 screen._drawline(self.drawingLineItem,
3238 (start, self._position),
3239 pc, ps, top)
3240 self._update()
3241 if drawing:
3242 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3243 fill="", width=ps)
3244 # Turtle now at position old,
3245 self._position = old
Ezio Melotti13925002011-03-16 11:05:33 +02003246 ## if undo is done during creating a polygon, the last vertex
3247 ## will be deleted. if the polygon is entirely deleted,
3248 ## creatingPoly will be set to False.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003249 ## Polygons created before the last one will not be affected by undo()
3250 if self._creatingPoly:
3251 if len(self._poly) > 0:
3252 self._poly.pop()
3253 if self._poly == []:
3254 self._creatingPoly = False
3255 self._poly = None
3256 if filling:
3257 if self._fillpath == []:
3258 self._fillpath = None
3259 print("Unwahrscheinlich in _undogoto!")
3260 elif self._fillpath is not None:
3261 self._fillpath.pop()
3262 self._update() #count=True)
3263
3264 def _rotate(self, angle):
3265 """Turns pen clockwise by angle.
3266 """
3267 if self.undobuffer:
3268 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3269 angle *= self._degreesPerAU
3270 neworient = self._orient.rotate(angle)
3271 tracing = self.screen._tracing
3272 if tracing == 1 and self._speed > 0:
3273 anglevel = 3.0 * self._speed
3274 steps = 1 + int(abs(angle)/anglevel)
3275 delta = 1.0*angle/steps
3276 for _ in range(steps):
3277 self._orient = self._orient.rotate(delta)
3278 self._update()
3279 self._orient = neworient
3280 self._update()
3281
3282 def _newLine(self, usePos=True):
3283 """Closes current line item and starts a new one.
3284 Remark: if current line became too long, animation
3285 performance (via _drawline) slowed down considerably.
3286 """
3287 if len(self.currentLine) > 1:
3288 self.screen._drawline(self.currentLineItem, self.currentLine,
3289 self._pencolor, self._pensize)
3290 self.currentLineItem = self.screen._createline()
3291 self.items.append(self.currentLineItem)
3292 else:
3293 self.screen._drawline(self.currentLineItem, top=True)
3294 self.currentLine = []
3295 if usePos:
3296 self.currentLine = [self._position]
3297
3298 def filling(self):
3299 """Return fillstate (True if filling, False else).
3300
3301 No argument.
3302
3303 Example (for a Turtle instance named turtle):
3304 >>> turtle.begin_fill()
3305 >>> if turtle.filling():
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003306 ... turtle.pensize(5)
3307 ... else:
3308 ... turtle.pensize(3)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003309 """
3310 return isinstance(self._fillpath, list)
3311
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003312 def begin_fill(self):
3313 """Called just before drawing a shape to be filled.
3314
3315 No argument.
3316
3317 Example (for a Turtle instance named turtle):
3318 >>> turtle.color("black", "red")
3319 >>> turtle.begin_fill()
3320 >>> turtle.circle(60)
3321 >>> turtle.end_fill()
3322 """
3323 if not self.filling():
3324 self._fillitem = self.screen._createpoly()
3325 self.items.append(self._fillitem)
3326 self._fillpath = [self._position]
3327 self._newLine()
3328 if self.undobuffer:
3329 self.undobuffer.push(("beginfill", self._fillitem))
3330 self._update()
3331
3332
3333 def end_fill(self):
3334 """Fill the shape drawn after the call begin_fill().
3335
3336 No argument.
3337
3338 Example (for a Turtle instance named turtle):
3339 >>> turtle.color("black", "red")
3340 >>> turtle.begin_fill()
3341 >>> turtle.circle(60)
3342 >>> turtle.end_fill()
3343 """
3344 if self.filling():
3345 if len(self._fillpath) > 2:
3346 self.screen._drawpoly(self._fillitem, self._fillpath,
3347 fill=self._fillcolor)
3348 if self.undobuffer:
3349 self.undobuffer.push(("dofill", self._fillitem))
3350 self._fillitem = self._fillpath = None
3351 self._update()
3352
3353 def dot(self, size=None, *color):
3354 """Draw a dot with diameter size, using color.
3355
Ezio Melotti42da6632011-03-15 05:18:48 +02003356 Optional arguments:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003357 size -- an integer >= 1 (if given)
3358 color -- a colorstring or a numeric color tuple
3359
3360 Draw a circular dot with diameter size, using color.
3361 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3362
3363 Example (for a Turtle instance named turtle):
3364 >>> turtle.dot()
3365 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3366 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003367 if not color:
3368 if isinstance(size, (str, tuple)):
3369 color = self._colorstr(size)
3370 size = self._pensize + max(self._pensize, 4)
3371 else:
3372 color = self._pencolor
3373 if not size:
3374 size = self._pensize + max(self._pensize, 4)
3375 else:
3376 if size is None:
3377 size = self._pensize + max(self._pensize, 4)
3378 color = self._colorstr(color)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003379 if hasattr(self.screen, "_dot"):
3380 item = self.screen._dot(self._position, size, color)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003381 self.items.append(item)
3382 if self.undobuffer:
3383 self.undobuffer.push(("dot", item))
3384 else:
3385 pen = self.pen()
3386 if self.undobuffer:
3387 self.undobuffer.push(["seq"])
3388 self.undobuffer.cumulate = True
3389 try:
3390 if self.resizemode() == 'auto':
3391 self.ht()
3392 self.pendown()
3393 self.pensize(size)
3394 self.pencolor(color)
3395 self.forward(0)
3396 finally:
3397 self.pen(pen)
3398 if self.undobuffer:
3399 self.undobuffer.cumulate = False
3400
3401 def _write(self, txt, align, font):
3402 """Performs the writing for write()
3403 """
3404 item, end = self.screen._write(self._position, txt, align, font,
3405 self._pencolor)
3406 self.items.append(item)
3407 if self.undobuffer:
3408 self.undobuffer.push(("wri", item))
3409 return end
3410
3411 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3412 """Write text at the current turtle position.
3413
3414 Arguments:
3415 arg -- info, which is to be written to the TurtleScreen
3416 move (optional) -- True/False
3417 align (optional) -- one of the strings "left", "center" or right"
3418 font (optional) -- a triple (fontname, fontsize, fonttype)
3419
3420 Write text - the string representation of arg - at the current
3421 turtle position according to align ("left", "center" or right")
3422 and with the given font.
3423 If move is True, the pen is moved to the bottom-right corner
3424 of the text. By default, move is False.
3425
3426 Example (for a Turtle instance named turtle):
3427 >>> turtle.write('Home = ', True, align="center")
3428 >>> turtle.write((0,0), True)
3429 """
3430 if self.undobuffer:
3431 self.undobuffer.push(["seq"])
3432 self.undobuffer.cumulate = True
3433 end = self._write(str(arg), align.lower(), font)
3434 if move:
3435 x, y = self.pos()
3436 self.setpos(end, y)
3437 if self.undobuffer:
3438 self.undobuffer.cumulate = False
3439
3440 def begin_poly(self):
3441 """Start recording the vertices of a polygon.
3442
3443 No argument.
3444
3445 Start recording the vertices of a polygon. Current turtle position
3446 is first point of polygon.
3447
3448 Example (for a Turtle instance named turtle):
3449 >>> turtle.begin_poly()
3450 """
3451 self._poly = [self._position]
3452 self._creatingPoly = True
3453
3454 def end_poly(self):
3455 """Stop recording the vertices of a polygon.
3456
3457 No argument.
3458
3459 Stop recording the vertices of a polygon. Current turtle position is
3460 last point of polygon. This will be connected with the first point.
3461
3462 Example (for a Turtle instance named turtle):
3463 >>> turtle.end_poly()
3464 """
3465 self._creatingPoly = False
3466
3467 def get_poly(self):
3468 """Return the lastly recorded polygon.
3469
3470 No argument.
3471
3472 Example (for a Turtle instance named turtle):
3473 >>> p = turtle.get_poly()
3474 >>> turtle.register_shape("myFavouriteShape", p)
3475 """
Georg Brandleaa84ef2009-05-05 08:14:33 +00003476 ## check if there is any poly?
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003477 if self._poly is not None:
3478 return tuple(self._poly)
3479
3480 def getscreen(self):
3481 """Return the TurtleScreen object, the turtle is drawing on.
3482
3483 No argument.
3484
3485 Return the TurtleScreen object, the turtle is drawing on.
3486 So TurtleScreen-methods can be called for that object.
3487
3488 Example (for a Turtle instance named turtle):
3489 >>> ts = turtle.getscreen()
3490 >>> ts
3491 <turtle.TurtleScreen object at 0x0106B770>
3492 >>> ts.bgcolor("pink")
3493 """
3494 return self.screen
3495
3496 def getturtle(self):
3497 """Return the Turtleobject itself.
3498
3499 No argument.
3500
3501 Only reasonable use: as a function to return the 'anonymous turtle':
3502
3503 Example:
3504 >>> pet = getturtle()
3505 >>> pet.fd(50)
3506 >>> pet
3507 <turtle.Turtle object at 0x0187D810>
3508 >>> turtles()
3509 [<turtle.Turtle object at 0x0187D810>]
3510 """
3511 return self
3512
3513 getpen = getturtle
3514
3515
3516 ################################################################
3517 ### screen oriented methods recurring to methods of TurtleScreen
3518 ################################################################
3519
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003520 def _delay(self, delay=None):
3521 """Set delay value which determines speed of turtle animation.
3522 """
3523 return self.screen.delay(delay)
3524
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003525 def onclick(self, fun, btn=1, add=None):
3526 """Bind fun to mouse-click event on this turtle on canvas.
3527
3528 Arguments:
3529 fun -- a function with two arguments, to which will be assigned
3530 the coordinates of the clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05303531 btn -- number of the mouse-button defaults to 1 (left mouse button).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003532 add -- True or False. If True, new binding will be added, otherwise
3533 it will replace a former binding.
3534
3535 Example for the anonymous turtle, i. e. the procedural way:
3536
3537 >>> def turn(x, y):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003538 ... left(360)
3539 ...
3540 >>> onclick(turn) # Now clicking into the turtle will turn it.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003541 >>> onclick(None) # event-binding will be removed
3542 """
3543 self.screen._onclick(self.turtle._item, fun, btn, add)
3544 self._update()
3545
3546 def onrelease(self, fun, btn=1, add=None):
3547 """Bind fun to mouse-button-release event on this turtle on canvas.
3548
3549 Arguments:
3550 fun -- a function with two arguments, to which will be assigned
3551 the coordinates of the clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05303552 btn -- number of the mouse-button defaults to 1 (left mouse button).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003553
3554 Example (for a MyTurtle instance named joe):
3555 >>> class MyTurtle(Turtle):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003556 ... def glow(self,x,y):
3557 ... self.fillcolor("red")
3558 ... def unglow(self,x,y):
3559 ... self.fillcolor("")
3560 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003561 >>> joe = MyTurtle()
3562 >>> joe.onclick(joe.glow)
3563 >>> joe.onrelease(joe.unglow)
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003564
3565 Clicking on joe turns fillcolor red, unclicking turns it to
3566 transparent.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003567 """
3568 self.screen._onrelease(self.turtle._item, fun, btn, add)
3569 self._update()
3570
3571 def ondrag(self, fun, btn=1, add=None):
3572 """Bind fun to mouse-move event on this turtle on canvas.
3573
3574 Arguments:
3575 fun -- a function with two arguments, to which will be assigned
3576 the coordinates of the clicked point on the canvas.
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)4edeaea2018-11-16 18:58:51 +05303577 btn -- number of the mouse-button defaults to 1 (left mouse button).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003578
3579 Every sequence of mouse-move-events on a turtle is preceded by a
3580 mouse-click event on that turtle.
3581
3582 Example (for a Turtle instance named turtle):
3583 >>> turtle.ondrag(turtle.goto)
3584
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003585 Subsequently clicking and dragging a Turtle will move it
3586 across the screen thereby producing handdrawings (if pen is
3587 down).
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003588 """
3589 self.screen._ondrag(self.turtle._item, fun, btn, add)
3590
3591
3592 def _undo(self, action, data):
3593 """Does the main part of the work for undo()
3594 """
3595 if self.undobuffer is None:
3596 return
3597 if action == "rot":
3598 angle, degPAU = data
3599 self._rotate(-angle*degPAU/self._degreesPerAU)
3600 dummy = self.undobuffer.pop()
3601 elif action == "stamp":
3602 stitem = data[0]
3603 self.clearstamp(stitem)
3604 elif action == "go":
3605 self._undogoto(data)
3606 elif action in ["wri", "dot"]:
3607 item = data[0]
3608 self.screen._delete(item)
3609 self.items.remove(item)
3610 elif action == "dofill":
3611 item = data[0]
3612 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3613 fill="", outline="")
3614 elif action == "beginfill":
3615 item = data[0]
3616 self._fillitem = self._fillpath = None
3617 if item in self.items:
3618 self.screen._delete(item)
3619 self.items.remove(item)
3620 elif action == "pen":
3621 TPen.pen(self, data[0])
3622 self.undobuffer.pop()
3623
3624 def undo(self):
3625 """undo (repeatedly) the last turtle action.
3626
3627 No argument.
3628
3629 undo (repeatedly) the last turtle action.
3630 Number of available undo actions is determined by the size of
3631 the undobuffer.
3632
3633 Example (for a Turtle instance named turtle):
3634 >>> for i in range(4):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003635 ... turtle.fd(50); turtle.lt(80)
3636 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003637 >>> for i in range(8):
Petri Lehtinen9aa20af2011-12-02 21:24:14 +02003638 ... turtle.undo()
3639 ...
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003640 """
3641 if self.undobuffer is None:
3642 return
3643 item = self.undobuffer.pop()
3644 action = item[0]
3645 data = item[1:]
3646 if action == "seq":
3647 while data:
3648 item = data.pop()
3649 self._undo(item[0], item[1:])
3650 else:
3651 self._undo(action, data)
3652
3653 turtlesize = shapesize
3654
3655RawPen = RawTurtle
3656
Martin v. Löwis601149b2008-09-29 22:19:08 +00003657### Screen - Singleton ########################
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003658
Martin v. Löwis601149b2008-09-29 22:19:08 +00003659def Screen():
3660 """Return the singleton screen object.
3661 If none exists at the moment, create a new one and return it,
3662 else return the existing one."""
3663 if Turtle._screen is None:
3664 Turtle._screen = _Screen()
3665 return Turtle._screen
3666
3667class _Screen(TurtleScreen):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003668
3669 _root = None
3670 _canvas = None
3671 _title = _CFG["title"]
3672
Guido van Rossumb241b671998-12-04 16:42:46 +00003673 def __init__(self):
Martin v. Löwis601149b2008-09-29 22:19:08 +00003674 # XXX there is no need for this code to be conditional,
3675 # as there will be only a single _Screen instance, anyway
3676 # XXX actually, the turtle demo is injecting root window,
3677 # so perhaps the conditional creation of a root should be
3678 # preserved (perhaps by passing it as an optional parameter)
3679 if _Screen._root is None:
3680 _Screen._root = self._root = _Root()
3681 self._root.title(_Screen._title)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003682 self._root.ondestroy(self._destroy)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003683 if _Screen._canvas is None:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003684 width = _CFG["width"]
3685 height = _CFG["height"]
3686 canvwidth = _CFG["canvwidth"]
3687 canvheight = _CFG["canvheight"]
3688 leftright = _CFG["leftright"]
3689 topbottom = _CFG["topbottom"]
3690 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwis601149b2008-09-29 22:19:08 +00003691 _Screen._canvas = self._root._getcanvas()
Martin v. Löwis601149b2008-09-29 22:19:08 +00003692 TurtleScreen.__init__(self, _Screen._canvas)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003693 self.setup(width, height, leftright, topbottom)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003694
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003695 def setup(self, width=_CFG["width"], height=_CFG["height"],
3696 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3697 """ Set the size and position of the main window.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003698
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003699 Arguments:
3700 width: as integer a size in pixels, as float a fraction of the screen.
3701 Default is 50% of screen.
3702 height: as integer the height in pixels, as float a fraction of the
3703 screen. Default is 75% of screen.
3704 startx: if positive, starting position in pixels from the left
3705 edge of the screen, if negative from the right edge
3706 Default, startx=None is to center window horizontally.
3707 starty: if positive, starting position in pixels from the top
3708 edge of the screen, if negative from the bottom edge
3709 Default, starty=None is to center window vertically.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003710
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003711 Examples (for a Screen instance named screen):
3712 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3713
3714 sets window to 200x200 pixels, in upper left of screen
3715
3716 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3717
3718 sets window to 75% of screen by 50% of screen and centers
3719 """
3720 if not hasattr(self._root, "set_geometry"):
3721 return
3722 sw = self._root.win_width()
3723 sh = self._root.win_height()
3724 if isinstance(width, float) and 0 <= width <= 1:
3725 width = sw*width
3726 if startx is None:
3727 startx = (sw - width) / 2
3728 if isinstance(height, float) and 0 <= height <= 1:
3729 height = sh*height
3730 if starty is None:
3731 starty = (sh - height) / 2
3732 self._root.set_geometry(width, height, startx, starty)
Georg Brandleaa84ef2009-05-05 08:14:33 +00003733 self.update()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003734
3735 def title(self, titlestring):
3736 """Set title of turtle-window
3737
3738 Argument:
3739 titlestring -- a string, to appear in the titlebar of the
3740 turtle graphics window.
3741
3742 This is a method of Screen-class. Not available for TurtleScreen-
3743 objects.
3744
3745 Example (for a Screen instance named screen):
3746 >>> screen.title("Welcome to the turtle-zoo!")
3747 """
Martin v. Löwis601149b2008-09-29 22:19:08 +00003748 if _Screen._root is not None:
3749 _Screen._root.title(titlestring)
3750 _Screen._title = titlestring
Guido van Rossumb241b671998-12-04 16:42:46 +00003751
3752 def _destroy(self):
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003753 root = self._root
Martin v. Löwis601149b2008-09-29 22:19:08 +00003754 if root is _Screen._root:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003755 Turtle._pen = None
3756 Turtle._screen = None
Martin v. Löwis601149b2008-09-29 22:19:08 +00003757 _Screen._root = None
3758 _Screen._canvas = None
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003759 TurtleScreen._RUNNING = False
Guido van Rossumb241b671998-12-04 16:42:46 +00003760 root.destroy()
Fred Draked038ca82000-10-23 18:31:14 +00003761
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003762 def bye(self):
3763 """Shut the turtlegraphics window.
3764
3765 Example (for a TurtleScreen instance named screen):
3766 >>> screen.bye()
3767 """
3768 self._destroy()
3769
3770 def exitonclick(self):
3771 """Go into mainloop until the mouse is clicked.
3772
3773 No arguments.
3774
3775 Bind bye() method to mouseclick on TurtleScreen.
3776 If "using_IDLE" - value in configuration dictionary is False
3777 (default value), enter mainloop.
3778 If IDLE with -n switch (no subprocess) is used, this value should be
3779 set to True in turtle.cfg. In this case IDLE's mainloop
3780 is active also for the client script.
3781
3782 This is a method of the Screen-class and not available for
3783 TurtleScreen instances.
3784
3785 Example (for a Screen instance named screen):
3786 >>> screen.exitonclick()
3787
3788 """
3789 def exitGracefully(x, y):
3790 """Screen.bye() with two dummy-parameters"""
3791 self.bye()
3792 self.onclick(exitGracefully)
3793 if _CFG["using_IDLE"]:
3794 return
3795 try:
3796 mainloop()
3797 except AttributeError:
3798 exit(0)
3799
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003800class Turtle(RawTurtle):
Ezio Melotti13925002011-03-16 11:05:33 +02003801 """RawTurtle auto-creating (scrolled) canvas.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003802
3803 When a Turtle object is created or a function derived from some
3804 Turtle method is called a TurtleScreen object is automatically created.
3805 """
3806 _pen = None
3807 _screen = None
3808
3809 def __init__(self,
3810 shape=_CFG["shape"],
3811 undobuffersize=_CFG["undobuffersize"],
3812 visible=_CFG["visible"]):
3813 if Turtle._screen is None:
3814 Turtle._screen = Screen()
3815 RawTurtle.__init__(self, Turtle._screen,
3816 shape=shape,
3817 undobuffersize=undobuffersize,
3818 visible=visible)
3819
3820Pen = Turtle
3821
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003822def write_docstringdict(filename="turtle_docstringdict"):
3823 """Create and write docstring-dictionary to file.
Guido van Rossumb241b671998-12-04 16:42:46 +00003824
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003825 Optional argument:
3826 filename -- a string, used as filename
3827 default value is turtle_docstringdict
Thomas Wouters477c8d52006-05-27 19:21:47 +00003828
Ezio Melotti13925002011-03-16 11:05:33 +02003829 Has to be called explicitly, (not used by the turtle-graphics classes)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003830 The docstring dictionary will be written to the Python script <filname>.py
3831 It is intended to serve as a template for translation of the docstrings
3832 into different languages.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003833 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003834 docsdict = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +00003835
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003836 for methodname in _tg_screen_functions:
Martin v. Löwis601149b2008-09-29 22:19:08 +00003837 key = "_Screen."+methodname
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003838 docsdict[key] = eval(key).__doc__
3839 for methodname in _tg_turtle_functions:
3840 key = "Turtle."+methodname
3841 docsdict[key] = eval(key).__doc__
Thomas Wouters477c8d52006-05-27 19:21:47 +00003842
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003843 with open("%s.py" % filename,"w") as f:
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +02003844 keys = sorted(x for x in docsdict
Jon Dufresne39726282017-05-18 07:35:54 -07003845 if x.split('.')[1] not in _alias_list)
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003846 f.write('docsdict = {\n\n')
3847 for key in keys[:-1]:
3848 f.write('%s :\n' % repr(key))
3849 f.write(' """%s\n""",\n\n' % docsdict[key])
3850 key = keys[-1]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003851 f.write('%s :\n' % repr(key))
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +01003852 f.write(' """%s\n"""\n\n' % docsdict[key])
3853 f.write("}\n")
3854 f.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00003855
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003856def read_docstrings(lang):
3857 """Read in docstrings from lang-specific docstring dictionary.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003858
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003859 Transfer docstrings, translated to lang, from a dictionary-file
3860 to the methods of classes Screen and Turtle and - in revised form -
3861 to the corresponding functions.
Thomas Wouters477c8d52006-05-27 19:21:47 +00003862 """
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003863 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3864 module = __import__(modname)
3865 docsdict = module.docsdict
3866 for key in docsdict:
3867 try:
3868# eval(key).im_func.__doc__ = docsdict[key]
3869 eval(key).__doc__ = docsdict[key]
Serhiy Storchakacefa9172016-06-14 22:52:04 +03003870 except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003871 print("Bad docstring-entry: %s" % key)
Thomas Wouters477c8d52006-05-27 19:21:47 +00003872
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003873_LANGUAGE = _CFG["language"]
Guido van Rossumb241b671998-12-04 16:42:46 +00003874
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003875try:
3876 if _LANGUAGE != "english":
3877 read_docstrings(_LANGUAGE)
3878except ImportError:
3879 print("Cannot find docsdict for", _LANGUAGE)
Serhiy Storchakacefa9172016-06-14 22:52:04 +03003880except Exception:
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003881 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3882 _LANGUAGE)
3883
3884
3885def getmethparlist(ob):
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003886 """Get strings describing the arguments for the given object
3887
3888 Returns a pair of strings representing function parameter lists
3889 including parenthesis. The first string is suitable for use in
3890 function definition and the second is suitable for use in function
3891 call. The "self" parameter is not included.
3892 """
3893 defText = callText = ""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003894 # bit of a hack for methods - turn it into a function
3895 # but we drop the "self" param.
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003896 # Try and build one for Python defined functions
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003897 args, varargs, varkw = inspect.getargs(ob.__code__)
3898 items2 = args[1:]
3899 realArgs = args[1:]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003900 defaults = ob.__defaults__ or []
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003901 defaults = ["=%r" % (value,) for value in defaults]
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003902 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
Alexander Belopolskyc1a68362010-10-27 13:25:45 +00003903 items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
3904 if varargs is not None:
3905 items1.append("*" + varargs)
3906 items2.append("*" + varargs)
3907 if varkw is not None:
3908 items1.append("**" + varkw)
3909 items2.append("**" + varkw)
3910 defText = ", ".join(items1)
3911 defText = "(%s)" % defText
3912 callText = ", ".join(items2)
3913 callText = "(%s)" % callText
3914 return defText, callText
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003915
3916def _turtle_docrevise(docstr):
3917 """To reduce docstrings from RawTurtle class for functions
3918 """
3919 import re
3920 if docstr is None:
3921 return None
3922 turtlename = _CFG["exampleturtle"]
3923 newdocstr = docstr.replace("%s." % turtlename,"")
3924 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3925 newdocstr = parexp.sub(":", newdocstr)
3926 return newdocstr
3927
3928def _screen_docrevise(docstr):
3929 """To reduce docstrings from TurtleScreen class for functions
3930 """
3931 import re
3932 if docstr is None:
3933 return None
3934 screenname = _CFG["examplescreen"]
3935 newdocstr = docstr.replace("%s." % screenname,"")
3936 parexp = re.compile(r' \(.+ %s\):' % screenname)
3937 newdocstr = parexp.sub(":", newdocstr)
3938 return newdocstr
3939
3940## The following mechanism makes all methods of RawTurtle and Turtle available
3941## as functions. So we can enhance, change, add, delete methods to these
3942## classes and do not need to change anything here.
3943
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003944__func_body = """\
3945def {name}{paramslist}:
3946 if {obj} is None:
3947 if not TurtleScreen._RUNNING:
3948 TurtleScreen._RUNNING = True
3949 raise Terminator
3950 {obj} = {init}
3951 try:
3952 return {obj}.{name}{argslist}
3953 except TK.TclError:
3954 if not TurtleScreen._RUNNING:
3955 TurtleScreen._RUNNING = True
3956 raise Terminator
3957 raise
3958"""
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003959
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003960def _make_global_funcs(functions, cls, obj, init, docrevise):
3961 for methodname in functions:
3962 method = getattr(cls, methodname)
3963 pl1, pl2 = getmethparlist(method)
3964 if pl1 == "":
3965 print(">>>>>>", pl1, pl2)
3966 continue
3967 defstr = __func_body.format(obj=obj, init=init, name=methodname,
3968 paramslist=pl1, argslist=pl2)
3969 exec(defstr, globals())
3970 globals()[methodname].__doc__ = docrevise(method.__doc__)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003971
Serhiy Storchaka80a18032015-02-22 17:25:33 +02003972_make_global_funcs(_tg_screen_functions, _Screen,
3973 'Turtle._screen', 'Screen()', _screen_docrevise)
3974_make_global_funcs(_tg_turtle_functions, Turtle,
3975 'Turtle._pen', 'Turtle()', _turtle_docrevise)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003976
3977
Georg Brandleaa84ef2009-05-05 08:14:33 +00003978done = mainloop
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00003979
3980if __name__ == "__main__":
3981 def switchpen():
3982 if isdown():
3983 pu()
3984 else:
3985 pd()
3986
3987 def demo1():
3988 """Demo of old turtle.py - module"""
3989 reset()
3990 tracer(True)
3991 up()
3992 backward(100)
3993 down()
3994 # draw 3 squares; the last filled
3995 width(3)
3996 for i in range(3):
3997 if i == 2:
3998 begin_fill()
3999 for _ in range(4):
4000 forward(20)
4001 left(90)
4002 if i == 2:
4003 color("maroon")
4004 end_fill()
4005 up()
4006 forward(30)
4007 down()
4008 width(1)
4009 color("black")
4010 # move out of the way
4011 tracer(False)
4012 up()
4013 right(90)
4014 forward(100)
4015 right(90)
4016 forward(100)
4017 right(180)
4018 down()
4019 # some text
4020 write("startstart", 1)
4021 write("start", 1)
4022 color("red")
4023 # staircase
4024 for i in range(5):
Guido van Rossumb241b671998-12-04 16:42:46 +00004025 forward(20)
4026 left(90)
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004027 forward(20)
4028 right(90)
4029 # filled staircase
4030 tracer(True)
4031 begin_fill()
4032 for i in range(5):
4033 forward(20)
4034 left(90)
4035 forward(20)
4036 right(90)
4037 end_fill()
4038 # more text
Thomas Wouters477c8d52006-05-27 19:21:47 +00004039
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004040 def demo2():
4041 """Demo of some new features."""
4042 speed(1)
4043 st()
4044 pensize(3)
4045 setheading(towards(0, 0))
4046 radius = distance(0, 0)/2.0
4047 rt(90)
4048 for _ in range(18):
4049 switchpen()
4050 circle(radius, 10)
4051 write("wait a moment...")
4052 while undobufferentries():
4053 undo()
4054 reset()
4055 lt(90)
4056 colormode(255)
4057 laenge = 10
4058 pencolor("green")
4059 pensize(3)
4060 lt(180)
4061 for i in range(-2, 16):
4062 if i > 0:
4063 begin_fill()
4064 fillcolor(255-15*i, 0, 15*i)
4065 for _ in range(3):
4066 fd(laenge)
4067 lt(120)
4068 end_fill()
4069 laenge += 10
4070 lt(15)
4071 speed((speed()+1)%12)
4072 #end_fill()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004073
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004074 lt(120)
4075 pu()
4076 fd(70)
4077 rt(30)
4078 pd()
4079 color("red","yellow")
4080 speed(0)
4081 begin_fill()
4082 for _ in range(4):
4083 circle(50, 90)
4084 rt(90)
4085 fd(30)
4086 rt(90)
4087 end_fill()
4088 lt(90)
4089 pu()
4090 fd(30)
4091 pd()
4092 shape("turtle")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004093
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004094 tri = getturtle()
4095 tri.resizemode("auto")
4096 turtle = Turtle()
4097 turtle.resizemode("auto")
4098 turtle.shape("turtle")
4099 turtle.reset()
4100 turtle.left(90)
4101 turtle.speed(0)
4102 turtle.up()
4103 turtle.goto(280, 40)
4104 turtle.lt(30)
4105 turtle.down()
4106 turtle.speed(6)
4107 turtle.color("blue","orange")
4108 turtle.pensize(2)
4109 tri.speed(6)
Thomas Wouters477c8d52006-05-27 19:21:47 +00004110 setheading(towards(turtle))
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004111 count = 1
4112 while tri.distance(turtle) > 4:
4113 turtle.fd(3.5)
4114 turtle.lt(0.6)
4115 tri.setheading(tri.towards(turtle))
4116 tri.fd(4)
4117 if count % 20 == 0:
4118 turtle.stamp()
4119 tri.stamp()
4120 switchpen()
4121 count += 1
4122 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
4123 tri.pencolor("black")
4124 tri.pencolor("red")
Thomas Wouters477c8d52006-05-27 19:21:47 +00004125
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004126 def baba(xdummy, ydummy):
4127 clearscreen()
4128 bye()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004129
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004130 time.sleep(2)
Guido van Rossumb241b671998-12-04 16:42:46 +00004131
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004132 while undobufferentries():
4133 tri.undo()
4134 turtle.undo()
4135 tri.fd(50)
4136 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4137 tri.onclick(baba, 1)
4138
4139 demo1()
Thomas Wouters477c8d52006-05-27 19:21:47 +00004140 demo2()
Martin v. Löwis97cf99f2008-06-10 04:44:07 +00004141 exitonclick()