blob: 264318effc3dc2d803ea9ab7fcf9c0e9cedf80f6 [file] [log] [blame]
Martin v. Löwis87184592008-06-04 06:29:55 +00001#
2# turtle.py: a Tkinter based turtle graphics module for Python
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003# Version 1.0.1 - 24. 9. 2009
Martin v. Löwis87184592008-06-04 06:29:55 +00004#
Georg Brandl8cdc9bc2010-01-01 13:07:05 +00005# Copyright (C) 2006 - 2010 Gregor Lingl
Martin v. Löwis87184592008-06-04 06:29:55 +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
Georg Brandl33cece02008-05-20 06:58:21 +000024
25"""
26Turtle graphics is a popular way for introducing programming to
27kids. It was part of the original Logo programming language developed
Martin v. Löwis87184592008-06-04 06:29:55 +000028by Wally Feurzig and Seymour Papert in 1966.
Georg Brandl33cece02008-05-20 06:58:21 +000029
Sandro Tosi1381a312011-08-07 17:09:15 +020030Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it
Georg Brandl33cece02008-05-20 06:58:21 +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 Tosi1381a312011-08-07 17:09:15 +020033command turtle.right(25), and it rotates in-place 25 degrees clockwise.
Georg Brandl33cece02008-05-20 06:58:21 +000034
35By combining together these and similar commands, intricate shapes and
36pictures can easily be drawn.
Martin v. Löwis87184592008-06-04 06:29:55 +000037
38----- turtle.py
39
40This module is an extended reimplementation of turtle.py from the
Mark Dickinson2752e9b2009-02-20 20:42:53 +000041Python standard distribution up to Python 2.5. (See: http://www.python.org)
Martin v. Löwis87184592008-06-04 06:29:55 +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 Dickinson2752e9b2009-02-20 20:42:53 +000057 (multicolored) shapes. Turtle shapes can be stretched and tilted, which
58 makes turtles very versatile geometrical objects.
Martin v. Löwis87184592008-06-04 06:29:55 +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 Dickinson2752e9b2009-02-20 20:42:53 +000067 turtle graphics.
Martin v. Löwis87184592008-06-04 06:29:55 +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 Dickinson2752e9b2009-02-20 20:42:53 +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öwis87184592008-06-04 06:29:55 +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 Melotti1e87da12011-10-19 10:39:35 +030099extensions in mind. These will be commented and documented elsewhere.
Martin v. Löwis87184592008-06-04 06:29:55 +0000100
Georg Brandl33cece02008-05-20 06:58:21 +0000101"""
102
Martin v. Löwis87184592008-06-04 06:29:55 +0000103_ver = "turtle 1.0b1 - for Python 2.6 - 30. 5. 2008, 18:08"
Georg Brandl33cece02008-05-20 06:58:21 +0000104
Martin v. Löwis87184592008-06-04 06:29:55 +0000105#print _ver
Georg Brandl33cece02008-05-20 06:58:21 +0000106
Martin v. Löwis87184592008-06-04 06:29:55 +0000107import Tkinter as TK
108import types
109import math
110import time
111import os
Georg Brandl33cece02008-05-20 06:58:21 +0000112
Martin v. Löwis87184592008-06-04 06:29:55 +0000113from os.path import isfile, split, join
114from copy import deepcopy
Georg Brandl33cece02008-05-20 06:58:21 +0000115
Martin v. Löwis87184592008-06-04 06:29:55 +0000116from math import * ## for compatibility with old turtle module
Georg Brandl33cece02008-05-20 06:58:21 +0000117
Martin v. Löwis87184592008-06-04 06:29:55 +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',
122 'getshapes', 'listen', 'mode', 'onkey', 'onscreenclick', 'ontimer',
123 'register_shape', 'resetscreen', 'screensize', 'setup',
124 'setworldcoordinates', 'title', 'tracer', 'turtles', 'update',
125 'window_height', 'window_width']
126_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
127 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
128 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
129 'fill', 'fillcolor', 'forward', 'get_poly', 'getpen', 'getscreen',
130 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
131 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
132 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
133 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
134 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
135 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'showturtle',
136 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', 'tracer',
137 'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
138 'window_height', 'window_width', 'write', 'xcor', 'ycor']
139_tg_utilities = ['write_docstringdict', 'done', 'mainloop']
140_math_functions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
141 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
142 'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
Georg Brandl33cece02008-05-20 06:58:21 +0000143
Martin v. Löwis87184592008-06-04 06:29:55 +0000144__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
Terry Jan Reedyf7f746a2014-06-30 16:09:16 -0400145 _tg_utilities + ['Terminator'] + _math_functions)
Georg Brandl33cece02008-05-20 06:58:21 +0000146
Martin v. Löwis87184592008-06-04 06:29:55 +0000147_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
148 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
149 'turtlesize', 'up', 'width']
Georg Brandl33cece02008-05-20 06:58:21 +0000150
Martin v. Löwis87184592008-06-04 06:29:55 +0000151_CFG = {"width" : 0.5, # Screen
152 "height" : 0.75,
153 "canvwidth" : 400,
154 "canvheight": 300,
155 "leftright": None,
156 "topbottom": None,
157 "mode": "standard", # TurtleScreen
158 "colormode": 1.0,
159 "delay": 10,
160 "undobuffersize": 1000, # RawTurtle
161 "shape": "classic",
162 "pencolor" : "black",
163 "fillcolor" : "black",
164 "resizemode" : "noresize",
165 "visible" : True,
166 "language": "english", # docstrings
167 "exampleturtle": "turtle",
168 "examplescreen": "screen",
169 "title": "Python Turtle Graphics",
170 "using_IDLE": False
171 }
Georg Brandl33cece02008-05-20 06:58:21 +0000172
Martin v. Löwis87184592008-06-04 06:29:55 +0000173##print "cwd:", os.getcwd()
174##print "__file__:", __file__
175##
176##def show(dictionary):
177## print "=========================="
178## for key in sorted(dictionary.keys()):
179## print key, ":", dictionary[key]
180## print "=========================="
181## print
Georg Brandl33cece02008-05-20 06:58:21 +0000182
Martin v. Löwis87184592008-06-04 06:29:55 +0000183def config_dict(filename):
184 """Convert content of config-file into dictionary."""
185 f = open(filename, "r")
186 cfglines = f.readlines()
187 f.close()
188 cfgdict = {}
189 for line in cfglines:
190 line = line.strip()
191 if not line or line.startswith("#"):
192 continue
193 try:
194 key, value = line.split("=")
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +0300195 except ValueError:
Martin v. Löwis87184592008-06-04 06:29:55 +0000196 print "Bad line in config-file %s:\n%s" % (filename,line)
197 continue
198 key = key.strip()
199 value = value.strip()
200 if value in ["True", "False", "None", "''", '""']:
201 value = eval(value)
Georg Brandl33cece02008-05-20 06:58:21 +0000202 else:
203 try:
Martin v. Löwis87184592008-06-04 06:29:55 +0000204 if "." in value:
205 value = float(value)
206 else:
207 value = int(value)
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +0300208 except ValueError:
Martin v. Löwis87184592008-06-04 06:29:55 +0000209 pass # value need not be converted
210 cfgdict[key] = value
211 return cfgdict
Georg Brandl33cece02008-05-20 06:58:21 +0000212
Martin v. Löwis87184592008-06-04 06:29:55 +0000213def readconfig(cfgdict):
214 """Read config-files, change configuration-dict accordingly.
Georg Brandl33cece02008-05-20 06:58:21 +0000215
Martin v. Löwis87184592008-06-04 06:29:55 +0000216 If there is a turtle.cfg file in the current working directory,
217 read it from there. If this contains an importconfig-value,
218 say 'myway', construct filename turtle_mayway.cfg else use
219 turtle.cfg and read it from the import-directory, where
220 turtle.py is located.
221 Update configuration dictionary first according to config-file,
222 in the import directory, then according to config-file in the
223 current working directory.
224 If no config-file is found, the default configuration is used.
225 """
226 default_cfg = "turtle.cfg"
227 cfgdict1 = {}
228 cfgdict2 = {}
229 if isfile(default_cfg):
230 cfgdict1 = config_dict(default_cfg)
231 #print "1. Loading config-file %s from: %s" % (default_cfg, os.getcwd())
232 if "importconfig" in cfgdict1:
233 default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
234 try:
235 head, tail = split(__file__)
236 cfg_file2 = join(head, default_cfg)
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +0300237 except BaseException:
Martin v. Löwis87184592008-06-04 06:29:55 +0000238 cfg_file2 = ""
239 if isfile(cfg_file2):
240 #print "2. Loading config-file %s:" % cfg_file2
241 cfgdict2 = config_dict(cfg_file2)
242## show(_CFG)
243## show(cfgdict2)
244 _CFG.update(cfgdict2)
245## show(_CFG)
246## show(cfgdict1)
247 _CFG.update(cfgdict1)
248## show(_CFG)
Georg Brandl33cece02008-05-20 06:58:21 +0000249
Martin v. Löwis87184592008-06-04 06:29:55 +0000250try:
251 readconfig(_CFG)
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +0300252except BaseException:
Martin v. Löwis87184592008-06-04 06:29:55 +0000253 print "No configfile read, reason unknown"
254
255
256class Vec2D(tuple):
257 """A 2 dimensional vector class, used as a helper class
258 for implementing turtle graphics.
259 May be useful for turtle graphics programs also.
260 Derived from tuple, so a vector is a tuple!
261
262 Provides (for a, b vectors, k number):
263 a+b vector addition
264 a-b vector subtraction
265 a*b inner product
266 k*a and a*k multiplication with scalar
267 |a| absolute value of a
268 a.rotate(angle) rotation
269 """
270 def __new__(cls, x, y):
271 return tuple.__new__(cls, (x, y))
272 def __add__(self, other):
273 return Vec2D(self[0]+other[0], self[1]+other[1])
274 def __mul__(self, other):
275 if isinstance(other, Vec2D):
276 return self[0]*other[0]+self[1]*other[1]
277 return Vec2D(self[0]*other, self[1]*other)
278 def __rmul__(self, other):
279 if isinstance(other, int) or isinstance(other, float):
280 return Vec2D(self[0]*other, self[1]*other)
281 def __sub__(self, other):
282 return Vec2D(self[0]-other[0], self[1]-other[1])
283 def __neg__(self):
284 return Vec2D(-self[0], -self[1])
285 def __abs__(self):
286 return (self[0]**2 + self[1]**2)**0.5
287 def rotate(self, angle):
288 """rotate self counterclockwise by angle
289 """
290 perp = Vec2D(-self[1], self[0])
291 angle = angle * math.pi / 180.0
292 c, s = math.cos(angle), math.sin(angle)
293 return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
294 def __getnewargs__(self):
295 return (self[0], self[1])
296 def __repr__(self):
297 return "(%.2f,%.2f)" % self
298
299
300##############################################################################
301### From here up to line : Tkinter - Interface for turtle.py ###
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000302### May be replaced by an interface to some different graphics toolkit ###
Martin v. Löwis87184592008-06-04 06:29:55 +0000303##############################################################################
304
305## helper functions for Scrolled Canvas, to forward Canvas-methods
306## to ScrolledCanvas class
307
308def __methodDict(cls, _dict):
309 """helper function for Scrolled Canvas"""
310 baseList = list(cls.__bases__)
311 baseList.reverse()
312 for _super in baseList:
313 __methodDict(_super, _dict)
314 for key, value in cls.__dict__.items():
315 if type(value) == types.FunctionType:
316 _dict[key] = value
317
318def __methods(cls):
319 """helper function for Scrolled Canvas"""
320 _dict = {}
321 __methodDict(cls, _dict)
322 return _dict.keys()
323
324__stringBody = (
325 'def %(method)s(self, *args, **kw): return ' +
326 'self.%(attribute)s.%(method)s(*args, **kw)')
327
328def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
329 """Helper functions for Scrolled Canvas, used to forward
330 ScrolledCanvas-methods to Tkinter.Canvas class.
331 """
332 _dict = {}
333 __methodDict(toClass, _dict)
334 for ex in _dict.keys():
335 if ex[:1] == '_' or ex[-1:] == '_':
336 del _dict[ex]
337 for ex in exclude:
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000338 if ex in _dict:
Martin v. Löwis87184592008-06-04 06:29:55 +0000339 del _dict[ex]
340 for ex in __methods(fromClass):
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000341 if ex in _dict:
Martin v. Löwis87184592008-06-04 06:29:55 +0000342 del _dict[ex]
343
344 for method, func in _dict.items():
345 d = {'method': method, 'func': func}
346 if type(toPart) == types.StringType:
347 execString = \
348 __stringBody % {'method' : method, 'attribute' : toPart}
349 exec execString in d
350 fromClass.__dict__[method] = d[method]
351
352
353class ScrolledCanvas(TK.Frame):
354 """Modeled after the scrolled canvas class from Grayons's Tkinter book.
355
356 Used as the default canvas, which pops up automatically when
357 using turtle graphics functions or the Turtle class.
358 """
359 def __init__(self, master, width=500, height=350,
360 canvwidth=600, canvheight=500):
361 TK.Frame.__init__(self, master, width=width, height=height)
Martin v. Löwisbb9b1f12008-11-19 09:09:41 +0000362 self._rootwindow = self.winfo_toplevel()
Martin v. Löwis87184592008-06-04 06:29:55 +0000363 self.width, self.height = width, height
364 self.canvwidth, self.canvheight = canvwidth, canvheight
365 self.bg = "white"
366 self._canvas = TK.Canvas(master, width=width, height=height,
367 bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
368 self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
369 orient=TK.HORIZONTAL)
370 self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
371 self._canvas.configure(xscrollcommand=self.hscroll.set,
372 yscrollcommand=self.vscroll.set)
373 self.rowconfigure(0, weight=1, minsize=0)
374 self.columnconfigure(0, weight=1, minsize=0)
375 self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
376 column=0, rowspan=1, columnspan=1, sticky='news')
377 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
378 column=1, rowspan=1, columnspan=1, sticky='news')
379 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
380 column=0, rowspan=1, columnspan=1, sticky='news')
381 self.reset()
Martin v. Löwisbb9b1f12008-11-19 09:09:41 +0000382 self._rootwindow.bind('<Configure>', self.onResize)
Martin v. Löwis87184592008-06-04 06:29:55 +0000383
384 def reset(self, canvwidth=None, canvheight=None, bg = None):
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000385 """Adjust canvas and scrollbars according to given canvas size."""
Martin v. Löwis87184592008-06-04 06:29:55 +0000386 if canvwidth:
387 self.canvwidth = canvwidth
388 if canvheight:
389 self.canvheight = canvheight
390 if bg:
391 self.bg = bg
392 self._canvas.config(bg=bg,
393 scrollregion=(-self.canvwidth//2, -self.canvheight//2,
394 self.canvwidth//2, self.canvheight//2))
395 self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
396 self.canvwidth)
397 self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
398 self.canvheight)
399 self.adjustScrolls()
400
401
402 def adjustScrolls(self):
403 """ Adjust scrollbars according to window- and canvas-size.
404 """
405 cwidth = self._canvas.winfo_width()
406 cheight = self._canvas.winfo_height()
407 self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
408 self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
409 if cwidth < self.canvwidth or cheight < self.canvheight:
410 self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
411 column=0, rowspan=1, columnspan=1, sticky='news')
412 self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
413 column=1, rowspan=1, columnspan=1, sticky='news')
414 else:
415 self.hscroll.grid_forget()
416 self.vscroll.grid_forget()
417
418 def onResize(self, event):
419 """self-explanatory"""
420 self.adjustScrolls()
421
422 def bbox(self, *args):
423 """ 'forward' method, which canvas itself has inherited...
424 """
425 return self._canvas.bbox(*args)
426
427 def cget(self, *args, **kwargs):
428 """ 'forward' method, which canvas itself has inherited...
429 """
430 return self._canvas.cget(*args, **kwargs)
431
432 def config(self, *args, **kwargs):
433 """ 'forward' method, which canvas itself has inherited...
434 """
435 self._canvas.config(*args, **kwargs)
436
437 def bind(self, *args, **kwargs):
438 """ 'forward' method, which canvas itself has inherited...
439 """
440 self._canvas.bind(*args, **kwargs)
441
442 def unbind(self, *args, **kwargs):
443 """ 'forward' method, which canvas itself has inherited...
444 """
445 self._canvas.unbind(*args, **kwargs)
446
447 def focus_force(self):
448 """ 'forward' method, which canvas itself has inherited...
449 """
450 self._canvas.focus_force()
451
452__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
453
454
455class _Root(TK.Tk):
456 """Root class for Screen based on Tkinter."""
457 def __init__(self):
458 TK.Tk.__init__(self)
459
460 def setupcanvas(self, width, height, cwidth, cheight):
461 self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
462 self._canvas.pack(expand=1, fill="both")
463
464 def _getcanvas(self):
465 return self._canvas
466
467 def set_geometry(self, width, height, startx, starty):
468 self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
469
470 def ondestroy(self, destroy):
471 self.wm_protocol("WM_DELETE_WINDOW", destroy)
472
473 def win_width(self):
474 return self.winfo_screenwidth()
475
476 def win_height(self):
477 return self.winfo_screenheight()
478
479Canvas = TK.Canvas
480
481
482class TurtleScreenBase(object):
483 """Provide the basic graphics functionality.
484 Interface between Tkinter and turtle.py.
485
486 To port turtle.py to some different graphics toolkit
487 a corresponding TurtleScreenBase class has to be implemented.
488 """
489
490 @staticmethod
491 def _blankimage():
492 """return a blank image object
493 """
494 img = TK.PhotoImage(width=1, height=1)
495 img.blank()
496 return img
497
498 @staticmethod
499 def _image(filename):
500 """return an image object containing the
501 imagedata from a gif-file named filename.
502 """
503 return TK.PhotoImage(file=filename)
504
505 def __init__(self, cv):
506 self.cv = cv
507 if isinstance(cv, ScrolledCanvas):
508 w = self.cv.canvwidth
509 h = self.cv.canvheight
510 else: # expected: ordinary TK.Canvas
511 w = int(self.cv.cget("width"))
512 h = int(self.cv.cget("height"))
513 self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
514 self.canvwidth = w
515 self.canvheight = h
516 self.xscale = self.yscale = 1.0
517
518 def _createpoly(self):
519 """Create an invisible polygon item on canvas self.cv)
520 """
521 return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
522
523 def _drawpoly(self, polyitem, coordlist, fill=None,
524 outline=None, width=None, top=False):
525 """Configure polygonitem polyitem according to provided
526 arguments:
527 coordlist is sequence of coordinates
528 fill is filling color
529 outline is outline color
530 top is a boolean value, which specifies if polyitem
531 will be put on top of the canvas' displaylist so it
532 will not be covered by other items.
533 """
534 cl = []
535 for x, y in coordlist:
536 cl.append(x * self.xscale)
537 cl.append(-y * self.yscale)
538 self.cv.coords(polyitem, *cl)
539 if fill is not None:
540 self.cv.itemconfigure(polyitem, fill=fill)
541 if outline is not None:
542 self.cv.itemconfigure(polyitem, outline=outline)
543 if width is not None:
544 self.cv.itemconfigure(polyitem, width=width)
545 if top:
546 self.cv.tag_raise(polyitem)
547
548 def _createline(self):
549 """Create an invisible line item on canvas self.cv)
550 """
551 return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
552 capstyle = TK.ROUND)
553
554 def _drawline(self, lineitem, coordlist=None,
555 fill=None, width=None, top=False):
556 """Configure lineitem according to provided arguments:
557 coordlist is sequence of coordinates
558 fill is drawing color
559 width is width of drawn line.
560 top is a boolean value, which specifies if polyitem
561 will be put on top of the canvas' displaylist so it
562 will not be covered by other items.
563 """
564 if coordlist is not None:
565 cl = []
566 for x, y in coordlist:
567 cl.append(x * self.xscale)
568 cl.append(-y * self.yscale)
569 self.cv.coords(lineitem, *cl)
570 if fill is not None:
571 self.cv.itemconfigure(lineitem, fill=fill)
572 if width is not None:
573 self.cv.itemconfigure(lineitem, width=width)
574 if top:
575 self.cv.tag_raise(lineitem)
576
577 def _delete(self, item):
578 """Delete graphics item from canvas.
579 If item is"all" delete all graphics items.
580 """
581 self.cv.delete(item)
582
583 def _update(self):
584 """Redraw graphics items on canvas
585 """
586 self.cv.update()
587
588 def _delay(self, delay):
589 """Delay subsequent canvas actions for delay ms."""
590 self.cv.after(delay)
591
592 def _iscolorstring(self, color):
593 """Check if the string color is a legal Tkinter color string.
594 """
595 try:
596 rgb = self.cv.winfo_rgb(color)
597 ok = True
598 except TK.TclError:
599 ok = False
600 return ok
601
602 def _bgcolor(self, color=None):
603 """Set canvas' backgroundcolor if color is not None,
604 else return backgroundcolor."""
605 if color is not None:
606 self.cv.config(bg = color)
607 self._update()
608 else:
609 return self.cv.cget("bg")
610
611 def _write(self, pos, txt, align, font, pencolor):
612 """Write txt at pos in canvas with specified font
613 and color.
614 Return text item and x-coord of right bottom corner
615 of text's bounding box."""
616 x, y = pos
617 x = x * self.xscale
618 y = y * self.yscale
619 anchor = {"left":"sw", "center":"s", "right":"se" }
620 item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
621 fill = pencolor, font = font)
622 x0, y0, x1, y1 = self.cv.bbox(item)
623 self.cv.update()
624 return item, x1-1
625
626## def _dot(self, pos, size, color):
627## """may be implemented for some other graphics toolkit"""
628
629 def _onclick(self, item, fun, num=1, add=None):
630 """Bind fun to mouse-click event on turtle.
631 fun must be a function with two arguments, the coordinates
632 of the clicked point on the canvas.
633 num, the number of the mouse-button defaults to 1
634 """
635 if fun is None:
636 self.cv.tag_unbind(item, "<Button-%s>" % num)
637 else:
638 def eventfun(event):
639 x, y = (self.cv.canvasx(event.x)/self.xscale,
640 -self.cv.canvasy(event.y)/self.yscale)
641 fun(x, y)
642 self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
643
644 def _onrelease(self, item, fun, num=1, add=None):
645 """Bind fun to mouse-button-release event on turtle.
646 fun must be a function with two arguments, the coordinates
647 of the point on the canvas where mouse button is released.
648 num, the number of the mouse-button defaults to 1
649
650 If a turtle is clicked, first _onclick-event will be performed,
651 then _onscreensclick-event.
652 """
653 if fun is None:
654 self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
655 else:
656 def eventfun(event):
657 x, y = (self.cv.canvasx(event.x)/self.xscale,
658 -self.cv.canvasy(event.y)/self.yscale)
659 fun(x, y)
660 self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
661 eventfun, add)
662
663 def _ondrag(self, item, fun, num=1, add=None):
664 """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
665 fun must be a function with two arguments, the coordinates of the
666 actual mouse position on the canvas.
667 num, the number of the mouse-button defaults to 1
668
669 Every sequence of mouse-move-events on a turtle is preceded by a
670 mouse-click event on that turtle.
671 """
672 if fun is None:
673 self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
674 else:
675 def eventfun(event):
676 try:
677 x, y = (self.cv.canvasx(event.x)/self.xscale,
678 -self.cv.canvasy(event.y)/self.yscale)
679 fun(x, y)
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +0300680 except BaseException:
Martin v. Löwis87184592008-06-04 06:29:55 +0000681 pass
682 self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
683
684 def _onscreenclick(self, fun, num=1, add=None):
685 """Bind fun to mouse-click event on canvas.
686 fun must be a function with two arguments, the coordinates
687 of the clicked point on the canvas.
688 num, the number of the mouse-button defaults to 1
689
690 If a turtle is clicked, first _onclick-event will be performed,
691 then _onscreensclick-event.
692 """
693 if fun is None:
694 self.cv.unbind("<Button-%s>" % num)
695 else:
696 def eventfun(event):
697 x, y = (self.cv.canvasx(event.x)/self.xscale,
698 -self.cv.canvasy(event.y)/self.yscale)
699 fun(x, y)
700 self.cv.bind("<Button-%s>" % num, eventfun, add)
701
702 def _onkey(self, fun, key):
703 """Bind fun to key-release event of key.
704 Canvas must have focus. See method listen
705 """
706 if fun is None:
707 self.cv.unbind("<KeyRelease-%s>" % key, None)
708 else:
709 def eventfun(event):
710 fun()
711 self.cv.bind("<KeyRelease-%s>" % key, eventfun)
712
713 def _listen(self):
714 """Set focus on canvas (in order to collect key-events)
715 """
716 self.cv.focus_force()
717
718 def _ontimer(self, fun, t):
719 """Install a timer, which calls fun after t milliseconds.
720 """
721 if t == 0:
722 self.cv.after_idle(fun)
723 else:
724 self.cv.after(t, fun)
725
726 def _createimage(self, image):
727 """Create and return image item on canvas.
728 """
729 return self.cv.create_image(0, 0, image=image)
730
Serhiy Storchakaf8cc2872016-10-25 09:51:38 +0300731 def _drawimage(self, item, pos, image):
Martin v. Löwis87184592008-06-04 06:29:55 +0000732 """Configure image item as to draw image object
733 at position (x,y) on canvas)
734 """
Serhiy Storchakaf8cc2872016-10-25 09:51:38 +0300735 x, y = pos
Benjamin Peterson52d52f12008-09-24 22:11:59 +0000736 self.cv.coords(item, (x * self.xscale, -y * self.yscale))
Martin v. Löwis87184592008-06-04 06:29:55 +0000737 self.cv.itemconfig(item, image=image)
738
739 def _setbgpic(self, item, image):
740 """Configure image item as to draw image object
741 at center of canvas. Set item to the first item
742 in the displaylist, so it will be drawn below
743 any other item ."""
744 self.cv.itemconfig(item, image=image)
745 self.cv.tag_lower(item)
746
747 def _type(self, item):
748 """Return 'line' or 'polygon' or 'image' depending on
749 type of item.
750 """
751 return self.cv.type(item)
752
753 def _pointlist(self, item):
754 """returns list of coordinate-pairs of points of item
755 Example (for insiders):
756 >>> from turtle import *
757 >>> getscreen()._pointlist(getturtle().turtle._item)
758 [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
759 (9.9999999999999982, 0.0)]
760 >>> """
761 cl = self.cv.coords(item)
762 pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
763 return pl
764
765 def _setscrollregion(self, srx1, sry1, srx2, sry2):
766 self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
767
768 def _rescale(self, xscalefactor, yscalefactor):
769 items = self.cv.find_all()
770 for item in items:
771 coordinates = self.cv.coords(item)
772 newcoordlist = []
773 while coordinates:
774 x, y = coordinates[:2]
775 newcoordlist.append(x * xscalefactor)
776 newcoordlist.append(y * yscalefactor)
777 coordinates = coordinates[2:]
778 self.cv.coords(item, *newcoordlist)
779
780 def _resize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000781 """Resize the canvas the turtles are drawing on. Does
Martin v. Löwis87184592008-06-04 06:29:55 +0000782 not alter the drawing window.
783 """
784 # needs amendment
785 if not isinstance(self.cv, ScrolledCanvas):
786 return self.canvwidth, self.canvheight
Florent Xiclunac8a730b2010-03-25 20:32:07 +0000787 if canvwidth is canvheight is bg is None:
Martin v. Löwis87184592008-06-04 06:29:55 +0000788 return self.cv.canvwidth, self.cv.canvheight
789 if canvwidth is not None:
790 self.canvwidth = canvwidth
791 if canvheight is not None:
792 self.canvheight = canvheight
793 self.cv.reset(canvwidth, canvheight, bg)
794
795 def _window_size(self):
796 """ Return the width and height of the turtle window.
797 """
798 width = self.cv.winfo_width()
799 if width <= 1: # the window isn't managed by a geometry manager
800 width = self.cv['width']
801 height = self.cv.winfo_height()
802 if height <= 1: # the window isn't managed by a geometry manager
803 height = self.cv['height']
804 return width, height
805
806
807##############################################################################
808### End of Tkinter - interface ###
809##############################################################################
810
811
812class Terminator (Exception):
813 """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
814
Terry Jan Reedya70f60a2013-03-11 17:56:17 -0400815 This stops execution of a turtle graphics script.
816 Main purpose: use in the Demo-Viewer turtle.Demo.py.
Martin v. Löwis87184592008-06-04 06:29:55 +0000817 """
818 pass
819
820
821class TurtleGraphicsError(Exception):
822 """Some TurtleGraphics Error
823 """
824
825
826class Shape(object):
827 """Data structure modeling shapes.
828
829 attribute _type is one of "polygon", "image", "compound"
830 attribute _data is - depending on _type a poygon-tuple,
831 an image or a list constructed using the addcomponent method.
832 """
833 def __init__(self, type_, data=None):
834 self._type = type_
835 if type_ == "polygon":
836 if isinstance(data, list):
837 data = tuple(data)
838 elif type_ == "image":
Terry Jan Reedy09f4f252014-03-05 23:15:57 -0500839 if isinstance(data, basestring):
Martin v. Löwis87184592008-06-04 06:29:55 +0000840 if data.lower().endswith(".gif") and isfile(data):
841 data = TurtleScreen._image(data)
842 # else data assumed to be Photoimage
843 elif type_ == "compound":
844 data = []
845 else:
846 raise TurtleGraphicsError("There is no shape type %s" % type_)
847 self._data = data
848
849 def addcomponent(self, poly, fill, outline=None):
850 """Add component to a shape of type compound.
851
852 Arguments: poly is a polygon, i. e. a tuple of number pairs.
853 fill is the fillcolor of the component,
854 outline is the outline color of the component.
855
856 call (for a Shapeobject namend s):
857 -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
Georg Brandl33cece02008-05-20 06:58:21 +0000858
859 Example:
Martin v. Löwis87184592008-06-04 06:29:55 +0000860 >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
861 >>> s = Shape("compound")
862 >>> s.addcomponent(poly, "red", "blue")
Petri Lehtinen49e49a22011-12-02 21:09:30 +0200863 >>> # .. add more components and then use register_shape()
Georg Brandl33cece02008-05-20 06:58:21 +0000864 """
Martin v. Löwis87184592008-06-04 06:29:55 +0000865 if self._type != "compound":
866 raise TurtleGraphicsError("Cannot add component to %s Shape"
867 % self._type)
868 if outline is None:
869 outline = fill
870 self._data.append([poly, fill, outline])
Georg Brandl33cece02008-05-20 06:58:21 +0000871
Georg Brandl33cece02008-05-20 06:58:21 +0000872
Martin v. Löwis87184592008-06-04 06:29:55 +0000873class Tbuffer(object):
874 """Ring buffer used as undobuffer for RawTurtle objects."""
875 def __init__(self, bufsize=10):
876 self.bufsize = bufsize
877 self.buffer = [[None]] * bufsize
878 self.ptr = -1
879 self.cumulate = False
880 def reset(self, bufsize=None):
881 if bufsize is None:
882 for i in range(self.bufsize):
883 self.buffer[i] = [None]
884 else:
885 self.bufsize = bufsize
886 self.buffer = [[None]] * bufsize
887 self.ptr = -1
888 def push(self, item):
889 if self.bufsize > 0:
890 if not self.cumulate:
891 self.ptr = (self.ptr + 1) % self.bufsize
892 self.buffer[self.ptr] = item
893 else:
894 self.buffer[self.ptr].append(item)
895 def pop(self):
896 if self.bufsize > 0:
897 item = self.buffer[self.ptr]
898 if item is None:
899 return None
900 else:
901 self.buffer[self.ptr] = [None]
902 self.ptr = (self.ptr - 1) % self.bufsize
903 return (item)
904 def nr_of_items(self):
905 return self.bufsize - self.buffer.count([None])
906 def __repr__(self):
907 return str(self.buffer) + " " + str(self.ptr)
908
909
910
911class TurtleScreen(TurtleScreenBase):
912 """Provides screen oriented methods like setbg etc.
913
914 Only relies upon the methods of TurtleScreenBase and NOT
915 upon components of the underlying graphics toolkit -
916 which is Tkinter in this case.
917 """
918# _STANDARD_DELAY = 5
919 _RUNNING = True
920
921 def __init__(self, cv, mode=_CFG["mode"],
922 colormode=_CFG["colormode"], delay=_CFG["delay"]):
923 self._shapes = {
924 "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
925 "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
926 (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
927 (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
928 (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
929 (2,14))),
930 "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
931 (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
932 (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
933 (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
934 (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
935 (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
936 "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
937 (-10,-10))),
938 "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
939 (-10,-5.77))),
940 "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
941 "blank" : Shape("image", self._blankimage())
942 }
943
944 self._bgpics = {"nopic" : ""}
945
946 TurtleScreenBase.__init__(self, cv)
947 self._mode = mode
948 self._delayvalue = delay
949 self._colormode = _CFG["colormode"]
950 self._keys = []
951 self.clear()
952
953 def clear(self):
954 """Delete all drawings and all turtles from the TurtleScreen.
955
Mark Dickinson2752e9b2009-02-20 20:42:53 +0000956 Reset empty TurtleScreen to its initial state: white background,
Martin v. Löwis87184592008-06-04 06:29:55 +0000957 no backgroundimage, no eventbindings and tracing on.
958
959 No argument.
960
961 Example (for a TurtleScreen instance named screen):
Petri Lehtinen49e49a22011-12-02 21:09:30 +0200962 >>> screen.clear()
Martin v. Löwis87184592008-06-04 06:29:55 +0000963
964 Note: this method is not available as function.
Georg Brandl33cece02008-05-20 06:58:21 +0000965 """
Martin v. Löwis87184592008-06-04 06:29:55 +0000966 self._delayvalue = _CFG["delay"]
967 self._colormode = _CFG["colormode"]
968 self._delete("all")
969 self._bgpic = self._createimage("")
970 self._bgpicname = "nopic"
971 self._tracing = 1
972 self._updatecounter = 0
973 self._turtles = []
974 self.bgcolor("white")
975 for btn in 1, 2, 3:
976 self.onclick(None, btn)
977 for key in self._keys[:]:
978 self.onkey(None, key)
979 Turtle._pen = None
Georg Brandl33cece02008-05-20 06:58:21 +0000980
Martin v. Löwis87184592008-06-04 06:29:55 +0000981 def mode(self, mode=None):
982 """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
Georg Brandl33cece02008-05-20 06:58:21 +0000983
Martin v. Löwis87184592008-06-04 06:29:55 +0000984 Optional argument:
Martin Panterfcc8a0f2016-09-10 10:38:28 +0000985 mode -- one of the strings 'standard', 'logo' or 'world'
Martin v. Löwis87184592008-06-04 06:29:55 +0000986
987 Mode 'standard' is compatible with turtle.py.
988 Mode 'logo' is compatible with most Logo-Turtle-Graphics.
989 Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
990 this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
991 If mode is not given, return the current mode.
992
993 Mode Initial turtle heading positive angles
994 ------------|-------------------------|-------------------
995 'standard' to the right (east) counterclockwise
996 'logo' upward (north) clockwise
997
998 Examples:
999 >>> mode('logo') # resets turtle heading to north
1000 >>> mode()
1001 'logo'
Georg Brandl33cece02008-05-20 06:58:21 +00001002 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00001003 if mode is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00001004 return self._mode
1005 mode = mode.lower()
1006 if mode not in ["standard", "logo", "world"]:
1007 raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1008 self._mode = mode
1009 if mode in ["standard", "logo"]:
1010 self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1011 self.canvwidth//2, self.canvheight//2)
1012 self.xscale = self.yscale = 1.0
1013 self.reset()
Georg Brandl33cece02008-05-20 06:58:21 +00001014
Martin v. Löwis87184592008-06-04 06:29:55 +00001015 def setworldcoordinates(self, llx, lly, urx, ury):
1016 """Set up a user defined coordinate-system.
Georg Brandl33cece02008-05-20 06:58:21 +00001017
Martin v. Löwis87184592008-06-04 06:29:55 +00001018 Arguments:
1019 llx -- a number, x-coordinate of lower left corner of canvas
1020 lly -- a number, y-coordinate of lower left corner of canvas
1021 urx -- a number, x-coordinate of upper right corner of canvas
1022 ury -- a number, y-coordinate of upper right corner of canvas
1023
1024 Set up user coodinat-system and switch to mode 'world' if necessary.
1025 This performs a screen.reset. If mode 'world' is already active,
1026 all drawings are redrawn according to the new coordinates.
1027
1028 But ATTENTION: in user-defined coordinatesystems angles may appear
1029 distorted. (see Screen.mode())
1030
1031 Example (for a TurtleScreen instance named screen):
1032 >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1033 >>> for _ in range(36):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001034 ... left(10)
1035 ... forward(0.5)
Georg Brandl33cece02008-05-20 06:58:21 +00001036 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001037 if self.mode() != "world":
1038 self.mode("world")
1039 xspan = float(urx - llx)
1040 yspan = float(ury - lly)
1041 wx, wy = self._window_size()
1042 self.screensize(wx-20, wy-20)
1043 oldxscale, oldyscale = self.xscale, self.yscale
1044 self.xscale = self.canvwidth / xspan
1045 self.yscale = self.canvheight / yspan
1046 srx1 = llx * self.xscale
1047 sry1 = -ury * self.yscale
1048 srx2 = self.canvwidth + srx1
1049 sry2 = self.canvheight + sry1
1050 self._setscrollregion(srx1, sry1, srx2, sry2)
1051 self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1052 self.update()
Georg Brandl33cece02008-05-20 06:58:21 +00001053
Martin v. Löwis87184592008-06-04 06:29:55 +00001054 def register_shape(self, name, shape=None):
1055 """Adds a turtle shape to TurtleScreen's shapelist.
Georg Brandl33cece02008-05-20 06:58:21 +00001056
Martin v. Löwis87184592008-06-04 06:29:55 +00001057 Arguments:
1058 (1) name is the name of a gif-file and shape is None.
1059 Installs the corresponding image shape.
1060 !! Image-shapes DO NOT rotate when turning the turtle,
1061 !! so they do not display the heading of the turtle!
1062 (2) name is an arbitrary string and shape is a tuple
1063 of pairs of coordinates. Installs the corresponding
1064 polygon shape
1065 (3) name is an arbitrary string and shape is a
1066 (compound) Shape object. Installs the corresponding
1067 compound shape.
1068 To use a shape, you have to issue the command shape(shapename).
Georg Brandl33cece02008-05-20 06:58:21 +00001069
Martin v. Löwis87184592008-06-04 06:29:55 +00001070 call: register_shape("turtle.gif")
1071 --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1072
1073 Example (for a TurtleScreen instance named screen):
1074 >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1075
Georg Brandl33cece02008-05-20 06:58:21 +00001076 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001077 if shape is None:
1078 # image
1079 if name.lower().endswith(".gif"):
1080 shape = Shape("image", self._image(name))
1081 else:
1082 raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1083 + "Use help(register_shape)" )
1084 elif isinstance(shape, tuple):
1085 shape = Shape("polygon", shape)
1086 ## else shape assumed to be Shape-instance
1087 self._shapes[name] = shape
1088 # print "shape added:" , self._shapes
Georg Brandl33cece02008-05-20 06:58:21 +00001089
Martin v. Löwis87184592008-06-04 06:29:55 +00001090 def _colorstr(self, color):
1091 """Return color string corresponding to args.
Georg Brandl33cece02008-05-20 06:58:21 +00001092
Martin v. Löwis87184592008-06-04 06:29:55 +00001093 Argument may be a string or a tuple of three
1094 numbers corresponding to actual colormode,
1095 i.e. in the range 0<=n<=colormode.
1096
1097 If the argument doesn't represent a color,
1098 an error is raised.
Georg Brandl33cece02008-05-20 06:58:21 +00001099 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001100 if len(color) == 1:
1101 color = color[0]
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05001102 if isinstance(color, basestring):
Martin v. Löwis87184592008-06-04 06:29:55 +00001103 if self._iscolorstring(color) or color == "":
1104 return color
1105 else:
1106 raise TurtleGraphicsError("bad color string: %s" % str(color))
1107 try:
1108 r, g, b = color
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +03001109 except (TypeError, ValueError):
Martin v. Löwis87184592008-06-04 06:29:55 +00001110 raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1111 if self._colormode == 1.0:
1112 r, g, b = [round(255.0*x) for x in (r, g, b)]
1113 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1114 raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1115 return "#%02x%02x%02x" % (r, g, b)
Georg Brandl33cece02008-05-20 06:58:21 +00001116
Martin v. Löwis87184592008-06-04 06:29:55 +00001117 def _color(self, cstr):
1118 if not cstr.startswith("#"):
1119 return cstr
1120 if len(cstr) == 7:
1121 cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1122 elif len(cstr) == 4:
1123 cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1124 else:
1125 raise TurtleGraphicsError("bad colorstring: %s" % cstr)
1126 return tuple([c * self._colormode/255 for c in cl])
Georg Brandl33cece02008-05-20 06:58:21 +00001127
Martin v. Löwis87184592008-06-04 06:29:55 +00001128 def colormode(self, cmode=None):
1129 """Return the colormode or set it to 1.0 or 255.
Georg Brandl33cece02008-05-20 06:58:21 +00001130
Martin v. Löwis87184592008-06-04 06:29:55 +00001131 Optional argument:
1132 cmode -- one of the values 1.0 or 255
Georg Brandl33cece02008-05-20 06:58:21 +00001133
Martin v. Löwis87184592008-06-04 06:29:55 +00001134 r, g, b values of colortriples have to be in range 0..cmode.
1135
1136 Example (for a TurtleScreen instance named screen):
1137 >>> screen.colormode()
1138 1.0
1139 >>> screen.colormode(255)
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001140 >>> pencolor(240,160,80)
Georg Brandl33cece02008-05-20 06:58:21 +00001141 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001142 if cmode is None:
1143 return self._colormode
1144 if cmode == 1.0:
1145 self._colormode = float(cmode)
1146 elif cmode == 255:
1147 self._colormode = int(cmode)
1148
1149 def reset(self):
1150 """Reset all Turtles on the Screen to their initial state.
1151
1152 No argument.
1153
1154 Example (for a TurtleScreen instance named screen):
1155 >>> screen.reset()
1156 """
1157 for turtle in self._turtles:
1158 turtle._setmode(self._mode)
1159 turtle.reset()
1160
1161 def turtles(self):
1162 """Return the list of turtles on the screen.
1163
1164 Example (for a TurtleScreen instance named screen):
1165 >>> screen.turtles()
1166 [<turtle.Turtle object at 0x00E11FB0>]
1167 """
1168 return self._turtles
1169
1170 def bgcolor(self, *args):
1171 """Set or return backgroundcolor of the TurtleScreen.
1172
1173 Arguments (if given): a color string or three numbers
1174 in the range 0..colormode or a 3-tuple of such numbers.
1175
1176 Example (for a TurtleScreen instance named screen):
1177 >>> screen.bgcolor("orange")
1178 >>> screen.bgcolor()
1179 'orange'
1180 >>> screen.bgcolor(0.5,0,0.5)
1181 >>> screen.bgcolor()
1182 '#800080'
1183 """
1184 if args:
1185 color = self._colorstr(args)
1186 else:
1187 color = None
1188 color = self._bgcolor(color)
1189 if color is not None:
1190 color = self._color(color)
1191 return color
1192
1193 def tracer(self, n=None, delay=None):
1194 """Turns turtle animation on/off and set delay for update drawings.
1195
1196 Optional arguments:
1197 n -- nonnegative integer
1198 delay -- nonnegative integer
1199
1200 If n is given, only each n-th regular screen update is really performed.
1201 (Can be used to accelerate the drawing of complex graphics.)
1202 Second arguments sets delay value (see RawTurtle.delay())
1203
1204 Example (for a TurtleScreen instance named screen):
1205 >>> screen.tracer(8, 25)
1206 >>> dist = 2
1207 >>> for i in range(200):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001208 ... fd(dist)
1209 ... rt(90)
1210 ... dist += 2
Martin v. Löwis87184592008-06-04 06:29:55 +00001211 """
1212 if n is None:
1213 return self._tracing
1214 self._tracing = int(n)
1215 self._updatecounter = 0
1216 if delay is not None:
1217 self._delayvalue = int(delay)
1218 if self._tracing:
1219 self.update()
1220
1221 def delay(self, delay=None):
1222 """ Return or set the drawing delay in milliseconds.
1223
1224 Optional argument:
1225 delay -- positive integer
1226
1227 Example (for a TurtleScreen instance named screen):
1228 >>> screen.delay(15)
1229 >>> screen.delay()
1230 15
1231 """
1232 if delay is None:
1233 return self._delayvalue
1234 self._delayvalue = int(delay)
1235
1236 def _incrementudc(self):
Ezio Melottif5469cf2013-08-17 15:43:51 +03001237 """Increment update counter."""
Martin v. Löwis87184592008-06-04 06:29:55 +00001238 if not TurtleScreen._RUNNING:
Serhiy Storchakacc49aa12015-02-22 17:22:53 +02001239 TurtleScreen._RUNNING = True
Martin v. Löwis87184592008-06-04 06:29:55 +00001240 raise Terminator
1241 if self._tracing > 0:
1242 self._updatecounter += 1
1243 self._updatecounter %= self._tracing
1244
1245 def update(self):
1246 """Perform a TurtleScreen update.
1247 """
R. David Murrayddfb6cd2009-09-28 18:29:28 +00001248 tracing = self._tracing
1249 self._tracing = True
Martin v. Löwis87184592008-06-04 06:29:55 +00001250 for t in self.turtles():
1251 t._update_data()
1252 t._drawturtle()
R. David Murrayddfb6cd2009-09-28 18:29:28 +00001253 self._tracing = tracing
Martin v. Löwis87184592008-06-04 06:29:55 +00001254 self._update()
Georg Brandl33cece02008-05-20 06:58:21 +00001255
1256 def window_width(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00001257 """ Return the width of the turtle window.
Georg Brandl33cece02008-05-20 06:58:21 +00001258
Martin v. Löwis87184592008-06-04 06:29:55 +00001259 Example (for a TurtleScreen instance named screen):
1260 >>> screen.window_width()
Georg Brandl33cece02008-05-20 06:58:21 +00001261 640
1262 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001263 return self._window_size()[0]
Georg Brandl33cece02008-05-20 06:58:21 +00001264
1265 def window_height(self):
1266 """ Return the height of the turtle window.
1267
Martin v. Löwis87184592008-06-04 06:29:55 +00001268 Example (for a TurtleScreen instance named screen):
1269 >>> screen.window_height()
1270 480
Georg Brandl33cece02008-05-20 06:58:21 +00001271 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001272 return self._window_size()[1]
Georg Brandl33cece02008-05-20 06:58:21 +00001273
Martin v. Löwis87184592008-06-04 06:29:55 +00001274 def getcanvas(self):
1275 """Return the Canvas of this TurtleScreen.
Georg Brandl33cece02008-05-20 06:58:21 +00001276
Martin v. Löwis87184592008-06-04 06:29:55 +00001277 No argument.
1278
1279 Example (for a Screen instance named screen):
1280 >>> cv = screen.getcanvas()
1281 >>> cv
1282 <turtle.ScrolledCanvas instance at 0x010742D8>
Georg Brandl33cece02008-05-20 06:58:21 +00001283 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001284 return self.cv
Georg Brandl33cece02008-05-20 06:58:21 +00001285
Martin v. Löwis87184592008-06-04 06:29:55 +00001286 def getshapes(self):
1287 """Return a list of names of all currently available turtle shapes.
Georg Brandl33cece02008-05-20 06:58:21 +00001288
Martin v. Löwis87184592008-06-04 06:29:55 +00001289 No argument.
1290
1291 Example (for a TurtleScreen instance named screen):
1292 >>> screen.getshapes()
1293 ['arrow', 'blank', 'circle', ... , 'turtle']
1294 """
1295 return sorted(self._shapes.keys())
1296
1297 def onclick(self, fun, btn=1, add=None):
1298 """Bind fun to mouse-click event on canvas.
1299
1300 Arguments:
1301 fun -- a function with two arguments, the coordinates of the
1302 clicked point on the canvas.
1303 num -- the number of the mouse-button, defaults to 1
1304
1305 Example (for a TurtleScreen instance named screen
1306 and a Turtle instance named turtle):
1307
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001308 >>> screen.onclick(goto)
1309 >>> # Subsequently clicking into the TurtleScreen will
1310 >>> # make the turtle move to the clicked point.
Martin v. Löwis87184592008-06-04 06:29:55 +00001311 >>> screen.onclick(None)
Martin v. Löwis87184592008-06-04 06:29:55 +00001312 """
1313 self._onscreenclick(fun, btn, add)
1314
1315 def onkey(self, fun, key):
1316 """Bind fun to key-release event of key.
1317
1318 Arguments:
1319 fun -- a function with no arguments
1320 key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1321
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001322 In order to be able to register key-events, TurtleScreen
Martin v. Löwis87184592008-06-04 06:29:55 +00001323 must have focus. (See method listen.)
1324
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001325 Example (for a TurtleScreen instance named screen):
Martin v. Löwis87184592008-06-04 06:29:55 +00001326
1327 >>> def f():
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001328 ... fd(50)
1329 ... lt(60)
1330 ...
Martin v. Löwis87184592008-06-04 06:29:55 +00001331 >>> screen.onkey(f, "Up")
1332 >>> screen.listen()
1333
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001334 Subsequently the turtle can be moved by repeatedly pressing
1335 the up-arrow key, consequently drawing a hexagon
1336
Martin v. Löwis87184592008-06-04 06:29:55 +00001337 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00001338 if fun is None:
R. David Murrayddfb6cd2009-09-28 18:29:28 +00001339 if key in self._keys:
1340 self._keys.remove(key)
Martin v. Löwis87184592008-06-04 06:29:55 +00001341 elif key not in self._keys:
1342 self._keys.append(key)
1343 self._onkey(fun, key)
1344
1345 def listen(self, xdummy=None, ydummy=None):
1346 """Set focus on TurtleScreen (in order to collect key-events)
1347
1348 No arguments.
1349 Dummy arguments are provided in order
1350 to be able to pass listen to the onclick method.
1351
1352 Example (for a TurtleScreen instance named screen):
1353 >>> screen.listen()
1354 """
1355 self._listen()
1356
1357 def ontimer(self, fun, t=0):
1358 """Install a timer, which calls fun after t milliseconds.
1359
1360 Arguments:
1361 fun -- a function with no arguments.
1362 t -- a number >= 0
1363
1364 Example (for a TurtleScreen instance named screen):
1365
1366 >>> running = True
1367 >>> def f():
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001368 ... if running:
1369 ... fd(50)
1370 ... lt(60)
1371 ... screen.ontimer(f, 250)
1372 ...
1373 >>> f() # makes the turtle marching around
Martin v. Löwis87184592008-06-04 06:29:55 +00001374 >>> running = False
1375 """
1376 self._ontimer(fun, t)
1377
1378 def bgpic(self, picname=None):
1379 """Set background image or return name of current backgroundimage.
1380
1381 Optional argument:
1382 picname -- a string, name of a gif-file or "nopic".
1383
Ezio Melotti24b07bc2011-03-15 18:55:01 +02001384 If picname is a filename, set the corresponding image as background.
Martin v. Löwis87184592008-06-04 06:29:55 +00001385 If picname is "nopic", delete backgroundimage, if present.
1386 If picname is None, return the filename of the current backgroundimage.
1387
1388 Example (for a TurtleScreen instance named screen):
1389 >>> screen.bgpic()
1390 'nopic'
1391 >>> screen.bgpic("landscape.gif")
1392 >>> screen.bgpic()
1393 'landscape.gif'
1394 """
1395 if picname is None:
1396 return self._bgpicname
1397 if picname not in self._bgpics:
1398 self._bgpics[picname] = self._image(picname)
1399 self._setbgpic(self._bgpic, self._bgpics[picname])
1400 self._bgpicname = picname
1401
1402 def screensize(self, canvwidth=None, canvheight=None, bg=None):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001403 """Resize the canvas the turtles are drawing on.
Martin v. Löwis87184592008-06-04 06:29:55 +00001404
1405 Optional arguments:
1406 canvwidth -- positive integer, new width of canvas in pixels
1407 canvheight -- positive integer, new height of canvas in pixels
Ezio Melottic2077b02011-03-16 12:34:31 +02001408 bg -- colorstring or color-tuple, new backgroundcolor
Martin v. Löwis87184592008-06-04 06:29:55 +00001409 If no arguments are given, return current (canvaswidth, canvasheight)
1410
1411 Do not alter the drawing window. To observe hidden parts of
1412 the canvas use the scrollbars. (Can make visible those parts
1413 of a drawing, which were outside the canvas before!)
1414
1415 Example (for a Turtle instance named turtle):
1416 >>> turtle.screensize(2000,1500)
Petri Lehtinen49e49a22011-12-02 21:09:30 +02001417 >>> # e. g. to search for an erroneously escaped turtle ;-)
Martin v. Löwis87184592008-06-04 06:29:55 +00001418 """
1419 return self._resize(canvwidth, canvheight, bg)
1420
1421 onscreenclick = onclick
1422 resetscreen = reset
1423 clearscreen = clear
1424 addshape = register_shape
1425
1426class TNavigator(object):
1427 """Navigation part of the RawTurtle.
1428 Implements methods for turtle movement.
1429 """
1430 START_ORIENTATION = {
1431 "standard": Vec2D(1.0, 0.0),
1432 "world" : Vec2D(1.0, 0.0),
1433 "logo" : Vec2D(0.0, 1.0) }
1434 DEFAULT_MODE = "standard"
1435 DEFAULT_ANGLEOFFSET = 0
1436 DEFAULT_ANGLEORIENT = 1
1437
1438 def __init__(self, mode=DEFAULT_MODE):
1439 self._angleOffset = self.DEFAULT_ANGLEOFFSET
1440 self._angleOrient = self.DEFAULT_ANGLEORIENT
1441 self._mode = mode
1442 self.undobuffer = None
1443 self.degrees()
1444 self._mode = None
1445 self._setmode(mode)
1446 TNavigator.reset(self)
1447
1448 def reset(self):
1449 """reset turtle to its initial values
1450
1451 Will be overwritten by parent class
1452 """
1453 self._position = Vec2D(0.0, 0.0)
1454 self._orient = TNavigator.START_ORIENTATION[self._mode]
1455
1456 def _setmode(self, mode=None):
1457 """Set turtle-mode to 'standard', 'world' or 'logo'.
1458 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00001459 if mode is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00001460 return self._mode
1461 if mode not in ["standard", "logo", "world"]:
1462 return
1463 self._mode = mode
1464 if mode in ["standard", "world"]:
1465 self._angleOffset = 0
1466 self._angleOrient = 1
1467 else: # mode == "logo":
1468 self._angleOffset = self._fullcircle/4.
1469 self._angleOrient = -1
1470
1471 def _setDegreesPerAU(self, fullcircle):
1472 """Helper function for degrees() and radians()"""
1473 self._fullcircle = fullcircle
1474 self._degreesPerAU = 360/fullcircle
1475 if self._mode == "standard":
1476 self._angleOffset = 0
1477 else:
1478 self._angleOffset = fullcircle/4.
1479
1480 def degrees(self, fullcircle=360.0):
1481 """ Set angle measurement units to degrees.
1482
1483 Optional argument:
1484 fullcircle - a number
1485
1486 Set angle measurement units, i. e. set number
1487 of 'degrees' for a full circle. Dafault value is
1488 360 degrees.
1489
1490 Example (for a Turtle instance named turtle):
1491 >>> turtle.left(90)
1492 >>> turtle.heading()
1493 90
Alexander Belopolskyab016d22010-11-05 01:56:24 +00001494
1495 Change angle measurement unit to grad (also known as gon,
1496 grade, or gradian and equals 1/100-th of the right angle.)
1497 >>> turtle.degrees(400.0)
Martin v. Löwis87184592008-06-04 06:29:55 +00001498 >>> turtle.heading()
1499 100
1500
1501 """
1502 self._setDegreesPerAU(fullcircle)
1503
1504 def radians(self):
1505 """ Set the angle measurement units to radians.
1506
1507 No arguments.
1508
1509 Example (for a Turtle instance named turtle):
1510 >>> turtle.heading()
1511 90
1512 >>> turtle.radians()
1513 >>> turtle.heading()
1514 1.5707963267948966
1515 """
1516 self._setDegreesPerAU(2*math.pi)
1517
1518 def _go(self, distance):
1519 """move turtle forward by specified distance"""
1520 ende = self._position + self._orient * distance
1521 self._goto(ende)
1522
1523 def _rotate(self, angle):
1524 """Turn turtle counterclockwise by specified angle if angle > 0."""
1525 angle *= self._degreesPerAU
1526 self._orient = self._orient.rotate(angle)
1527
1528 def _goto(self, end):
1529 """move turtle to position end."""
1530 self._position = end
1531
1532 def forward(self, distance):
1533 """Move the turtle forward by the specified distance.
1534
1535 Aliases: forward | fd
1536
1537 Argument:
1538 distance -- a number (integer or float)
1539
1540 Move the turtle forward by the specified distance, in the direction
1541 the turtle is headed.
1542
1543 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001544 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001545 (0.00, 0.00)
1546 >>> turtle.forward(25)
1547 >>> turtle.position()
1548 (25.00,0.00)
1549 >>> turtle.forward(-75)
1550 >>> turtle.position()
1551 (-50.00,0.00)
1552 """
1553 self._go(distance)
1554
1555 def back(self, distance):
1556 """Move the turtle backward by distance.
1557
1558 Aliases: back | backward | bk
1559
1560 Argument:
1561 distance -- a number
1562
1563 Move the turtle backward by distance ,opposite to the direction the
1564 turtle is headed. Do not change the turtle's heading.
1565
1566 Example (for a Turtle instance named turtle):
1567 >>> turtle.position()
1568 (0.00, 0.00)
1569 >>> turtle.backward(30)
1570 >>> turtle.position()
1571 (-30.00, 0.00)
1572 """
1573 self._go(-distance)
1574
1575 def right(self, angle):
1576 """Turn turtle right by angle units.
1577
1578 Aliases: right | rt
1579
1580 Argument:
1581 angle -- a number (integer or float)
1582
1583 Turn turtle right by angle units. (Units are by default degrees,
1584 but can be set via the degrees() and radians() functions.)
1585 Angle orientation depends on mode. (See this.)
1586
1587 Example (for a Turtle instance named turtle):
1588 >>> turtle.heading()
1589 22.0
1590 >>> turtle.right(45)
1591 >>> turtle.heading()
1592 337.0
1593 """
1594 self._rotate(-angle)
1595
1596 def left(self, angle):
1597 """Turn turtle left by angle units.
1598
1599 Aliases: left | lt
1600
1601 Argument:
1602 angle -- a number (integer or float)
1603
1604 Turn turtle left by angle units. (Units are by default degrees,
1605 but can be set via the degrees() and radians() functions.)
1606 Angle orientation depends on mode. (See this.)
1607
1608 Example (for a Turtle instance named turtle):
1609 >>> turtle.heading()
1610 22.0
1611 >>> turtle.left(45)
1612 >>> turtle.heading()
1613 67.0
1614 """
1615 self._rotate(angle)
1616
1617 def pos(self):
1618 """Return the turtle's current location (x,y), as a Vec2D-vector.
1619
1620 Aliases: pos | position
1621
1622 No arguments.
1623
1624 Example (for a Turtle instance named turtle):
1625 >>> turtle.pos()
1626 (0.00, 240.00)
1627 """
1628 return self._position
1629
1630 def xcor(self):
1631 """ Return the turtle's x coordinate.
1632
1633 No arguments.
1634
1635 Example (for a Turtle instance named turtle):
1636 >>> reset()
1637 >>> turtle.left(60)
1638 >>> turtle.forward(100)
1639 >>> print turtle.xcor()
1640 50.0
1641 """
1642 return self._position[0]
1643
1644 def ycor(self):
1645 """ Return the turtle's y coordinate
1646 ---
1647 No arguments.
1648
1649 Example (for a Turtle instance named turtle):
1650 >>> reset()
1651 >>> turtle.left(60)
1652 >>> turtle.forward(100)
1653 >>> print turtle.ycor()
1654 86.6025403784
1655 """
1656 return self._position[1]
1657
1658
1659 def goto(self, x, y=None):
1660 """Move turtle to an absolute position.
1661
1662 Aliases: setpos | setposition | goto:
1663
1664 Arguments:
1665 x -- a number or a pair/vector of numbers
1666 y -- a number None
1667
1668 call: goto(x, y) # two coordinates
1669 --or: goto((x, y)) # a pair (tuple) of coordinates
1670 --or: goto(vec) # e.g. as returned by pos()
1671
1672 Move turtle to an absolute position. If the pen is down,
1673 a line will be drawn. The turtle's orientation does not change.
1674
1675 Example (for a Turtle instance named turtle):
1676 >>> tp = turtle.pos()
1677 >>> tp
1678 (0.00, 0.00)
1679 >>> turtle.setpos(60,30)
1680 >>> turtle.pos()
1681 (60.00,30.00)
1682 >>> turtle.setpos((20,80))
1683 >>> turtle.pos()
1684 (20.00,80.00)
1685 >>> turtle.setpos(tp)
1686 >>> turtle.pos()
1687 (0.00,0.00)
1688 """
1689 if y is None:
1690 self._goto(Vec2D(*x))
1691 else:
1692 self._goto(Vec2D(x, y))
1693
1694 def home(self):
1695 """Move turtle to the origin - coordinates (0,0).
1696
1697 No arguments.
1698
Mark Dickinson2752e9b2009-02-20 20:42:53 +00001699 Move turtle to the origin - coordinates (0,0) and set its
1700 heading to its start-orientation (which depends on mode).
Martin v. Löwis87184592008-06-04 06:29:55 +00001701
1702 Example (for a Turtle instance named turtle):
1703 >>> turtle.home()
1704 """
1705 self.goto(0, 0)
1706 self.setheading(0)
1707
1708 def setx(self, x):
1709 """Set the turtle's first coordinate to x
1710
1711 Argument:
1712 x -- a number (integer or float)
1713
1714 Set the turtle's first coordinate to x, leave second coordinate
1715 unchanged.
1716
1717 Example (for a Turtle instance named turtle):
1718 >>> turtle.position()
1719 (0.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001720 >>> turtle.setx(10)
1721 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001722 (10.00, 240.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001723 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001724 self._goto(Vec2D(x, self._position[1]))
Georg Brandl33cece02008-05-20 06:58:21 +00001725
Martin v. Löwis87184592008-06-04 06:29:55 +00001726 def sety(self, y):
1727 """Set the turtle's second coordinate to y
Georg Brandl33cece02008-05-20 06:58:21 +00001728
Martin v. Löwis87184592008-06-04 06:29:55 +00001729 Argument:
1730 y -- a number (integer or float)
1731
1732 Set the turtle's first coordinate to x, second coordinate remains
1733 unchanged.
1734
1735 Example (for a Turtle instance named turtle):
Georg Brandl33cece02008-05-20 06:58:21 +00001736 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001737 (0.00, 40.00)
1738 >>> turtle.sety(-10)
Georg Brandl33cece02008-05-20 06:58:21 +00001739 >>> turtle.position()
Martin v. Löwis87184592008-06-04 06:29:55 +00001740 (0.00, -10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001741 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001742 self._goto(Vec2D(self._position[0], y))
Georg Brandl33cece02008-05-20 06:58:21 +00001743
Martin v. Löwis87184592008-06-04 06:29:55 +00001744 def distance(self, x, y=None):
1745 """Return the distance from the turtle to (x,y) in turtle step units.
Georg Brandl33cece02008-05-20 06:58:21 +00001746
Martin v. Löwis87184592008-06-04 06:29:55 +00001747 Arguments:
1748 x -- a number or a pair/vector of numbers or a turtle instance
1749 y -- a number None None
Georg Brandl33cece02008-05-20 06:58:21 +00001750
Martin v. Löwis87184592008-06-04 06:29:55 +00001751 call: distance(x, y) # two coordinates
1752 --or: distance((x, y)) # a pair (tuple) of coordinates
1753 --or: distance(vec) # e.g. as returned by pos()
1754 --or: distance(mypen) # where mypen is another turtle
1755
1756 Example (for a Turtle instance named turtle):
1757 >>> turtle.pos()
1758 (0.00, 0.00)
1759 >>> turtle.distance(30,40)
1760 50.0
1761 >>> pen = Turtle()
1762 >>> pen.forward(77)
1763 >>> turtle.distance(pen)
1764 77.0
1765 """
1766 if y is not None:
1767 pos = Vec2D(x, y)
1768 if isinstance(x, Vec2D):
1769 pos = x
1770 elif isinstance(x, tuple):
1771 pos = Vec2D(*x)
1772 elif isinstance(x, TNavigator):
1773 pos = x._position
1774 return abs(pos - self._position)
1775
1776 def towards(self, x, y=None):
1777 """Return the angle of the line from the turtle's position to (x, y).
1778
1779 Arguments:
1780 x -- a number or a pair/vector of numbers or a turtle instance
1781 y -- a number None None
1782
1783 call: distance(x, y) # two coordinates
1784 --or: distance((x, y)) # a pair (tuple) of coordinates
1785 --or: distance(vec) # e.g. as returned by pos()
1786 --or: distance(mypen) # where mypen is another turtle
1787
1788 Return the angle, between the line from turtle-position to position
1789 specified by x, y and the turtle's start orientation. (Depends on
1790 modes - "standard" or "logo")
1791
1792 Example (for a Turtle instance named turtle):
1793 >>> turtle.pos()
1794 (10.00, 10.00)
Georg Brandl33cece02008-05-20 06:58:21 +00001795 >>> turtle.towards(0,0)
1796 225.0
1797 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001798 if y is not None:
1799 pos = Vec2D(x, y)
1800 if isinstance(x, Vec2D):
1801 pos = x
1802 elif isinstance(x, tuple):
1803 pos = Vec2D(*x)
1804 elif isinstance(x, TNavigator):
1805 pos = x._position
1806 x, y = pos - self._position
1807 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1808 result /= self._degreesPerAU
1809 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1810
1811 def heading(self):
1812 """ Return the turtle's current heading.
1813
1814 No arguments.
1815
1816 Example (for a Turtle instance named turtle):
1817 >>> turtle.left(67)
1818 >>> turtle.heading()
1819 67.0
1820 """
1821 x, y = self._orient
1822 result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
1823 result /= self._degreesPerAU
1824 return (self._angleOffset + self._angleOrient*result) % self._fullcircle
1825
1826 def setheading(self, to_angle):
1827 """Set the orientation of the turtle to to_angle.
1828
1829 Aliases: setheading | seth
1830
1831 Argument:
1832 to_angle -- a number (integer or float)
1833
1834 Set the orientation of the turtle to to_angle.
1835 Here are some common directions in degrees:
1836
1837 standard - mode: logo-mode:
1838 -------------------|--------------------
1839 0 - east 0 - north
1840 90 - north 90 - east
1841 180 - west 180 - south
1842 270 - south 270 - west
1843
1844 Example (for a Turtle instance named turtle):
1845 >>> turtle.setheading(90)
1846 >>> turtle.heading()
1847 90
1848 """
1849 angle = (to_angle - self.heading())*self._angleOrient
1850 full = self._fullcircle
1851 angle = (angle+full/2.)%full - full/2.
1852 self._rotate(angle)
1853
1854 def circle(self, radius, extent = None, steps = None):
1855 """ Draw a circle with given radius.
1856
1857 Arguments:
1858 radius -- a number
1859 extent (optional) -- a number
1860 steps (optional) -- an integer
1861
1862 Draw a circle with given radius. The center is radius units left
1863 of the turtle; extent - an angle - determines which part of the
1864 circle is drawn. If extent is not given, draw the entire circle.
1865 If extent is not a full circle, one endpoint of the arc is the
1866 current pen position. Draw the arc in counterclockwise direction
1867 if radius is positive, otherwise in clockwise direction. Finally
1868 the direction of the turtle is changed by the amount of extent.
1869
1870 As the circle is approximated by an inscribed regular polygon,
1871 steps determines the number of steps to use. If not given,
1872 it will be calculated automatically. Maybe used to draw regular
1873 polygons.
1874
1875 call: circle(radius) # full circle
1876 --or: circle(radius, extent) # arc
1877 --or: circle(radius, extent, steps)
1878 --or: circle(radius, steps=6) # 6-sided polygon
1879
1880 Example (for a Turtle instance named turtle):
1881 >>> turtle.circle(50)
1882 >>> turtle.circle(120, 180) # semicircle
1883 """
1884 if self.undobuffer:
1885 self.undobuffer.push(["seq"])
1886 self.undobuffer.cumulate = True
1887 speed = self.speed()
1888 if extent is None:
1889 extent = self._fullcircle
1890 if steps is None:
1891 frac = abs(extent)/self._fullcircle
1892 steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
1893 w = 1.0 * extent / steps
1894 w2 = 0.5 * w
1895 l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
1896 if radius < 0:
1897 l, w, w2 = -l, -w, -w2
1898 tr = self.tracer()
1899 dl = self._delay()
1900 if speed == 0:
1901 self.tracer(0, 0)
Georg Brandl33cece02008-05-20 06:58:21 +00001902 else:
Martin v. Löwis87184592008-06-04 06:29:55 +00001903 self.speed(0)
1904 self._rotate(w2)
1905 for i in range(steps):
1906 self.speed(speed)
1907 self._go(l)
1908 self.speed(0)
1909 self._rotate(w)
1910 self._rotate(-w2)
1911 if speed == 0:
1912 self.tracer(tr, dl)
1913 self.speed(speed)
1914 if self.undobuffer:
1915 self.undobuffer.cumulate = False
Georg Brandl33cece02008-05-20 06:58:21 +00001916
Martin v. Löwis87184592008-06-04 06:29:55 +00001917## three dummy methods to be implemented by child class:
Georg Brandl33cece02008-05-20 06:58:21 +00001918
Martin v. Löwis87184592008-06-04 06:29:55 +00001919 def speed(self, s=0):
1920 """dummy method - to be overwritten by child class"""
1921 def tracer(self, a=None, b=None):
1922 """dummy method - to be overwritten by child class"""
1923 def _delay(self, n=None):
1924 """dummy method - to be overwritten by child class"""
Georg Brandl33cece02008-05-20 06:58:21 +00001925
Martin v. Löwis87184592008-06-04 06:29:55 +00001926 fd = forward
1927 bk = back
1928 backward = back
1929 rt = right
1930 lt = left
1931 position = pos
1932 setpos = goto
1933 setposition = goto
1934 seth = setheading
Georg Brandl33cece02008-05-20 06:58:21 +00001935
Georg Brandl33cece02008-05-20 06:58:21 +00001936
Martin v. Löwis87184592008-06-04 06:29:55 +00001937class TPen(object):
1938 """Drawing part of the RawTurtle.
1939 Implements drawing properties.
1940 """
1941 def __init__(self, resizemode=_CFG["resizemode"]):
1942 self._resizemode = resizemode # or "user" or "noresize"
1943 self.undobuffer = None
1944 TPen._reset(self)
Georg Brandl33cece02008-05-20 06:58:21 +00001945
Martin v. Löwis87184592008-06-04 06:29:55 +00001946 def _reset(self, pencolor=_CFG["pencolor"],
1947 fillcolor=_CFG["fillcolor"]):
1948 self._pensize = 1
1949 self._shown = True
1950 self._pencolor = pencolor
1951 self._fillcolor = fillcolor
1952 self._drawing = True
1953 self._speed = 3
1954 self._stretchfactor = (1, 1)
1955 self._tilt = 0
1956 self._outlinewidth = 1
1957 ### self.screen = None # to override by child class
1958
1959 def resizemode(self, rmode=None):
1960 """Set resizemode to one of the values: "auto", "user", "noresize".
1961
1962 (Optional) Argument:
1963 rmode -- one of the strings "auto", "user", "noresize"
1964
1965 Different resizemodes have the following effects:
1966 - "auto" adapts the appearance of the turtle
1967 corresponding to the value of pensize.
1968 - "user" adapts the appearance of the turtle according to the
1969 values of stretchfactor and outlinewidth (outline),
1970 which are set by shapesize()
1971 - "noresize" no adaption of the turtle's appearance takes place.
1972 If no argument is given, return current resizemode.
1973 resizemode("user") is called by a call of shapesize with arguments.
1974
1975
1976 Examples (for a Turtle instance named turtle):
1977 >>> turtle.resizemode("noresize")
1978 >>> turtle.resizemode()
1979 'noresize'
Georg Brandl33cece02008-05-20 06:58:21 +00001980 """
Martin v. Löwis87184592008-06-04 06:29:55 +00001981 if rmode is None:
1982 return self._resizemode
1983 rmode = rmode.lower()
1984 if rmode in ["auto", "user", "noresize"]:
1985 self.pen(resizemode=rmode)
Georg Brandl33cece02008-05-20 06:58:21 +00001986
Martin v. Löwis87184592008-06-04 06:29:55 +00001987 def pensize(self, width=None):
1988 """Set or return the line thickness.
Georg Brandl33cece02008-05-20 06:58:21 +00001989
Martin v. Löwis87184592008-06-04 06:29:55 +00001990 Aliases: pensize | width
Georg Brandl33cece02008-05-20 06:58:21 +00001991
Martin v. Löwis87184592008-06-04 06:29:55 +00001992 Argument:
1993 width -- positive number
Georg Brandl33cece02008-05-20 06:58:21 +00001994
Martin v. Löwis87184592008-06-04 06:29:55 +00001995 Set the line thickness to width or return it. If resizemode is set
1996 to "auto" and turtleshape is a polygon, that polygon is drawn with
1997 the same line thickness. If no argument is given, current pensize
1998 is returned.
Georg Brandl33cece02008-05-20 06:58:21 +00001999
Martin v. Löwis87184592008-06-04 06:29:55 +00002000 Example (for a Turtle instance named turtle):
2001 >>> turtle.pensize()
2002 1
Petri Lehtinen49e49a22011-12-02 21:09:30 +02002003 >>> turtle.pensize(10) # from here on lines of width 10 are drawn
Georg Brandl33cece02008-05-20 06:58:21 +00002004 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002005 if width is None:
2006 return self._pensize
2007 self.pen(pensize=width)
Georg Brandl33cece02008-05-20 06:58:21 +00002008
2009
Martin v. Löwis87184592008-06-04 06:29:55 +00002010 def penup(self):
2011 """Pull the pen up -- no drawing when moving.
Georg Brandl33cece02008-05-20 06:58:21 +00002012
Martin v. Löwis87184592008-06-04 06:29:55 +00002013 Aliases: penup | pu | up
Georg Brandl33cece02008-05-20 06:58:21 +00002014
Martin v. Löwis87184592008-06-04 06:29:55 +00002015 No argument
2016
2017 Example (for a Turtle instance named turtle):
2018 >>> turtle.penup()
Georg Brandl33cece02008-05-20 06:58:21 +00002019 """
Martin v. Löwis87184592008-06-04 06:29:55 +00002020 if not self._drawing:
Georg Brandl33cece02008-05-20 06:58:21 +00002021 return
Martin v. Löwis87184592008-06-04 06:29:55 +00002022 self.pen(pendown=False)
Georg Brandl33cece02008-05-20 06:58:21 +00002023
Martin v. Löwis87184592008-06-04 06:29:55 +00002024 def pendown(self):
2025 """Pull the pen down -- drawing when moving.
2026
2027 Aliases: pendown | pd | down
2028
2029 No argument.
2030
2031 Example (for a Turtle instance named turtle):
2032 >>> turtle.pendown()
2033 """
2034 if self._drawing:
2035 return
2036 self.pen(pendown=True)
2037
2038 def isdown(self):
2039 """Return True if pen is down, False if it's up.
2040
2041 No argument.
2042
2043 Example (for a Turtle instance named turtle):
2044 >>> turtle.penup()
2045 >>> turtle.isdown()
2046 False
2047 >>> turtle.pendown()
2048 >>> turtle.isdown()
2049 True
2050 """
2051 return self._drawing
2052
2053 def speed(self, speed=None):
2054 """ Return or set the turtle's speed.
2055
2056 Optional argument:
2057 speed -- an integer in the range 0..10 or a speedstring (see below)
2058
2059 Set the turtle's speed to an integer value in the range 0 .. 10.
2060 If no argument is given: return current speed.
2061
2062 If input is a number greater than 10 or smaller than 0.5,
2063 speed is set to 0.
2064 Speedstrings are mapped to speedvalues in the following way:
2065 'fastest' : 0
2066 'fast' : 10
2067 'normal' : 6
2068 'slow' : 3
2069 'slowest' : 1
2070 speeds from 1 to 10 enforce increasingly faster animation of
2071 line drawing and turtle turning.
2072
2073 Attention:
2074 speed = 0 : *no* animation takes place. forward/back makes turtle jump
2075 and likewise left/right make the turtle turn instantly.
2076
2077 Example (for a Turtle instance named turtle):
2078 >>> turtle.speed(3)
2079 """
2080 speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
2081 if speed is None:
2082 return self._speed
2083 if speed in speeds:
2084 speed = speeds[speed]
2085 elif 0.5 < speed < 10.5:
2086 speed = int(round(speed))
2087 else:
2088 speed = 0
2089 self.pen(speed=speed)
2090
2091 def color(self, *args):
2092 """Return or set the pencolor and fillcolor.
2093
2094 Arguments:
2095 Several input formats are allowed.
2096 They use 0, 1, 2, or 3 arguments as follows:
2097
2098 color()
2099 Return the current pencolor and the current fillcolor
2100 as a pair of color specification strings as are returned
2101 by pencolor and fillcolor.
2102 color(colorstring), color((r,g,b)), color(r,g,b)
2103 inputs as in pencolor, set both, fillcolor and pencolor,
2104 to the given value.
2105 color(colorstring1, colorstring2),
2106 color((r1,g1,b1), (r2,g2,b2))
2107 equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
2108 and analogously, if the other input format is used.
2109
2110 If turtleshape is a polygon, outline and interior of that polygon
2111 is drawn with the newly set colors.
2112 For mor info see: pencolor, fillcolor
2113
2114 Example (for a Turtle instance named turtle):
2115 >>> turtle.color('red', 'green')
2116 >>> turtle.color()
2117 ('red', 'green')
2118 >>> colormode(255)
2119 >>> color((40, 80, 120), (160, 200, 240))
2120 >>> color()
2121 ('#285078', '#a0c8f0')
2122 """
2123 if args:
2124 l = len(args)
2125 if l == 1:
2126 pcolor = fcolor = args[0]
2127 elif l == 2:
2128 pcolor, fcolor = args
2129 elif l == 3:
2130 pcolor = fcolor = args
2131 pcolor = self._colorstr(pcolor)
2132 fcolor = self._colorstr(fcolor)
2133 self.pen(pencolor=pcolor, fillcolor=fcolor)
2134 else:
2135 return self._color(self._pencolor), self._color(self._fillcolor)
2136
2137 def pencolor(self, *args):
2138 """ Return or set the pencolor.
2139
2140 Arguments:
2141 Four input formats are allowed:
2142 - pencolor()
2143 Return the current pencolor as color specification string,
2144 possibly in hex-number format (see example).
2145 May be used as input to another color/pencolor/fillcolor call.
2146 - pencolor(colorstring)
2147 s is a Tk color specification string, such as "red" or "yellow"
2148 - pencolor((r, g, b))
2149 *a tuple* of r, g, and b, which represent, an RGB color,
2150 and each of r, g, and b are in the range 0..colormode,
2151 where colormode is either 1.0 or 255
2152 - pencolor(r, g, b)
2153 r, g, and b represent an RGB color, and each of r, g, and b
2154 are in the range 0..colormode
2155
2156 If turtleshape is a polygon, the outline of that polygon is drawn
2157 with the newly set pencolor.
2158
2159 Example (for a Turtle instance named turtle):
2160 >>> turtle.pencolor('brown')
2161 >>> tup = (0.2, 0.8, 0.55)
2162 >>> turtle.pencolor(tup)
2163 >>> turtle.pencolor()
2164 '#33cc8c'
2165 """
2166 if args:
2167 color = self._colorstr(args)
2168 if color == self._pencolor:
2169 return
2170 self.pen(pencolor=color)
2171 else:
2172 return self._color(self._pencolor)
2173
2174 def fillcolor(self, *args):
2175 """ Return or set the fillcolor.
2176
2177 Arguments:
2178 Four input formats are allowed:
2179 - fillcolor()
2180 Return the current fillcolor as color specification string,
2181 possibly in hex-number format (see example).
2182 May be used as input to another color/pencolor/fillcolor call.
2183 - fillcolor(colorstring)
2184 s is a Tk color specification string, such as "red" or "yellow"
2185 - fillcolor((r, g, b))
2186 *a tuple* of r, g, and b, which represent, an RGB color,
2187 and each of r, g, and b are in the range 0..colormode,
2188 where colormode is either 1.0 or 255
2189 - fillcolor(r, g, b)
2190 r, g, and b represent an RGB color, and each of r, g, and b
2191 are in the range 0..colormode
2192
2193 If turtleshape is a polygon, the interior of that polygon is drawn
2194 with the newly set fillcolor.
2195
2196 Example (for a Turtle instance named turtle):
2197 >>> turtle.fillcolor('violet')
2198 >>> col = turtle.pencolor()
2199 >>> turtle.fillcolor(col)
2200 >>> turtle.fillcolor(0, .5, 0)
2201 """
2202 if args:
2203 color = self._colorstr(args)
2204 if color == self._fillcolor:
2205 return
2206 self.pen(fillcolor=color)
2207 else:
2208 return self._color(self._fillcolor)
2209
2210 def showturtle(self):
2211 """Makes the turtle visible.
2212
2213 Aliases: showturtle | st
2214
2215 No argument.
2216
2217 Example (for a Turtle instance named turtle):
2218 >>> turtle.hideturtle()
2219 >>> turtle.showturtle()
2220 """
2221 self.pen(shown=True)
2222
2223 def hideturtle(self):
2224 """Makes the turtle invisible.
2225
2226 Aliases: hideturtle | ht
2227
2228 No argument.
2229
2230 It's a good idea to do this while you're in the
2231 middle of a complicated drawing, because hiding
2232 the turtle speeds up the drawing observably.
2233
2234 Example (for a Turtle instance named turtle):
2235 >>> turtle.hideturtle()
2236 """
2237 self.pen(shown=False)
2238
2239 def isvisible(self):
2240 """Return True if the Turtle is shown, False if it's hidden.
2241
2242 No argument.
2243
2244 Example (for a Turtle instance named turtle):
2245 >>> turtle.hideturtle()
2246 >>> print turtle.isvisible():
2247 False
2248 """
2249 return self._shown
2250
2251 def pen(self, pen=None, **pendict):
2252 """Return or set the pen's attributes.
2253
2254 Arguments:
2255 pen -- a dictionary with some or all of the below listed keys.
2256 **pendict -- one or more keyword-arguments with the below
2257 listed keys as keywords.
2258
2259 Return or set the pen's attributes in a 'pen-dictionary'
2260 with the following key/value pairs:
2261 "shown" : True/False
2262 "pendown" : True/False
2263 "pencolor" : color-string or color-tuple
2264 "fillcolor" : color-string or color-tuple
2265 "pensize" : positive number
2266 "speed" : number in range 0..10
2267 "resizemode" : "auto" or "user" or "noresize"
2268 "stretchfactor": (positive number, positive number)
2269 "outline" : positive number
2270 "tilt" : number
2271
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002272 This dictionary can be used as argument for a subsequent
Martin v. Löwis87184592008-06-04 06:29:55 +00002273 pen()-call to restore the former pen-state. Moreover one
2274 or more of these attributes can be provided as keyword-arguments.
2275 This can be used to set several pen attributes in one statement.
Georg Brandl33cece02008-05-20 06:58:21 +00002276
2277
Martin v. Löwis87184592008-06-04 06:29:55 +00002278 Examples (for a Turtle instance named turtle):
2279 >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
2280 >>> turtle.pen()
2281 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2282 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
2283 'stretchfactor': (1,1), 'speed': 3}
2284 >>> penstate=turtle.pen()
2285 >>> turtle.color("yellow","")
2286 >>> turtle.penup()
2287 >>> turtle.pen()
2288 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2289 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
2290 'stretchfactor': (1,1), 'speed': 3}
2291 >>> p.pen(penstate, fillcolor="green")
2292 >>> p.pen()
2293 {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
2294 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
2295 'stretchfactor': (1,1), 'speed': 3}
2296 """
2297 _pd = {"shown" : self._shown,
2298 "pendown" : self._drawing,
2299 "pencolor" : self._pencolor,
2300 "fillcolor" : self._fillcolor,
2301 "pensize" : self._pensize,
2302 "speed" : self._speed,
2303 "resizemode" : self._resizemode,
2304 "stretchfactor" : self._stretchfactor,
2305 "outline" : self._outlinewidth,
2306 "tilt" : self._tilt
2307 }
Georg Brandl33cece02008-05-20 06:58:21 +00002308
Martin v. Löwis87184592008-06-04 06:29:55 +00002309 if not (pen or pendict):
2310 return _pd
2311
2312 if isinstance(pen, dict):
2313 p = pen
2314 else:
2315 p = {}
2316 p.update(pendict)
2317
2318 _p_buf = {}
2319 for key in p:
2320 _p_buf[key] = _pd[key]
2321
2322 if self.undobuffer:
2323 self.undobuffer.push(("pen", _p_buf))
2324
2325 newLine = False
2326 if "pendown" in p:
2327 if self._drawing != p["pendown"]:
2328 newLine = True
2329 if "pencolor" in p:
2330 if isinstance(p["pencolor"], tuple):
2331 p["pencolor"] = self._colorstr((p["pencolor"],))
2332 if self._pencolor != p["pencolor"]:
2333 newLine = True
2334 if "pensize" in p:
2335 if self._pensize != p["pensize"]:
2336 newLine = True
2337 if newLine:
2338 self._newLine()
2339 if "pendown" in p:
2340 self._drawing = p["pendown"]
2341 if "pencolor" in p:
2342 self._pencolor = p["pencolor"]
2343 if "pensize" in p:
2344 self._pensize = p["pensize"]
2345 if "fillcolor" in p:
2346 if isinstance(p["fillcolor"], tuple):
2347 p["fillcolor"] = self._colorstr((p["fillcolor"],))
2348 self._fillcolor = p["fillcolor"]
2349 if "speed" in p:
2350 self._speed = p["speed"]
2351 if "resizemode" in p:
2352 self._resizemode = p["resizemode"]
2353 if "stretchfactor" in p:
2354 sf = p["stretchfactor"]
2355 if isinstance(sf, (int, float)):
2356 sf = (sf, sf)
2357 self._stretchfactor = sf
2358 if "outline" in p:
2359 self._outlinewidth = p["outline"]
2360 if "shown" in p:
2361 self._shown = p["shown"]
2362 if "tilt" in p:
2363 self._tilt = p["tilt"]
2364 self._update()
2365
2366## three dummy methods to be implemented by child class:
2367
2368 def _newLine(self, usePos = True):
2369 """dummy method - to be overwritten by child class"""
2370 def _update(self, count=True, forced=False):
2371 """dummy method - to be overwritten by child class"""
2372 def _color(self, args):
2373 """dummy method - to be overwritten by child class"""
2374 def _colorstr(self, args):
2375 """dummy method - to be overwritten by child class"""
2376
2377 width = pensize
2378 up = penup
2379 pu = penup
2380 pd = pendown
2381 down = pendown
2382 st = showturtle
2383 ht = hideturtle
2384
2385
2386class _TurtleImage(object):
2387 """Helper class: Datatype to store Turtle attributes
2388 """
2389
2390 def __init__(self, screen, shapeIndex):
2391 self.screen = screen
2392 self._type = None
2393 self._setshape(shapeIndex)
2394
2395 def _setshape(self, shapeIndex):
2396 screen = self.screen # RawTurtle.screens[self.screenIndex]
2397 self.shapeIndex = shapeIndex
2398 if self._type == "polygon" == screen._shapes[shapeIndex]._type:
2399 return
2400 if self._type == "image" == screen._shapes[shapeIndex]._type:
2401 return
2402 if self._type in ["image", "polygon"]:
2403 screen._delete(self._item)
2404 elif self._type == "compound":
2405 for item in self._item:
2406 screen._delete(item)
2407 self._type = screen._shapes[shapeIndex]._type
2408 if self._type == "polygon":
2409 self._item = screen._createpoly()
2410 elif self._type == "image":
2411 self._item = screen._createimage(screen._shapes["blank"]._data)
2412 elif self._type == "compound":
2413 self._item = [screen._createpoly() for item in
2414 screen._shapes[shapeIndex]._data]
2415
2416
2417class RawTurtle(TPen, TNavigator):
2418 """Animation part of the RawTurtle.
2419 Puts RawTurtle upon a TurtleScreen and provides tools for
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002420 its animation.
Martin v. Löwis87184592008-06-04 06:29:55 +00002421 """
2422 screens = []
2423
2424 def __init__(self, canvas=None,
2425 shape=_CFG["shape"],
2426 undobuffersize=_CFG["undobuffersize"],
2427 visible=_CFG["visible"]):
Martin v. Löwise563aa42008-09-29 22:09:07 +00002428 if isinstance(canvas, _Screen):
Martin v. Löwis87184592008-06-04 06:29:55 +00002429 self.screen = canvas
2430 elif isinstance(canvas, TurtleScreen):
2431 if canvas not in RawTurtle.screens:
2432 RawTurtle.screens.append(canvas)
2433 self.screen = canvas
2434 elif isinstance(canvas, (ScrolledCanvas, Canvas)):
2435 for screen in RawTurtle.screens:
2436 if screen.cv == canvas:
2437 self.screen = screen
2438 break
2439 else:
2440 self.screen = TurtleScreen(canvas)
2441 RawTurtle.screens.append(self.screen)
2442 else:
Ezio Melottif5469cf2013-08-17 15:43:51 +03002443 raise TurtleGraphicsError("bad canvas argument %s" % canvas)
Martin v. Löwis87184592008-06-04 06:29:55 +00002444
2445 screen = self.screen
2446 TNavigator.__init__(self, screen.mode())
2447 TPen.__init__(self)
2448 screen._turtles.append(self)
2449 self.drawingLineItem = screen._createline()
2450 self.turtle = _TurtleImage(screen, shape)
2451 self._poly = None
2452 self._creatingPoly = False
2453 self._fillitem = self._fillpath = None
2454 self._shown = visible
2455 self._hidden_from_screen = False
2456 self.currentLineItem = screen._createline()
2457 self.currentLine = [self._position]
2458 self.items = [self.currentLineItem]
2459 self.stampItems = []
2460 self._undobuffersize = undobuffersize
2461 self.undobuffer = Tbuffer(undobuffersize)
2462 self._update()
2463
2464 def reset(self):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002465 """Delete the turtle's drawings and restore its default values.
Martin v. Löwis87184592008-06-04 06:29:55 +00002466
2467 No argument.
2468,
2469 Delete the turtle's drawings from the screen, re-center the turtle
2470 and set variables to the default values.
2471
2472 Example (for a Turtle instance named turtle):
2473 >>> turtle.position()
2474 (0.00,-22.00)
2475 >>> turtle.heading()
2476 100.0
2477 >>> turtle.reset()
2478 >>> turtle.position()
2479 (0.00,0.00)
2480 >>> turtle.heading()
2481 0.0
2482 """
2483 TNavigator.reset(self)
2484 TPen._reset(self)
2485 self._clear()
2486 self._drawturtle()
2487 self._update()
2488
2489 def setundobuffer(self, size):
2490 """Set or disable undobuffer.
2491
2492 Argument:
2493 size -- an integer or None
2494
2495 If size is an integer an empty undobuffer of given size is installed.
2496 Size gives the maximum number of turtle-actions that can be undone
2497 by the undo() function.
2498 If size is None, no undobuffer is present.
2499
2500 Example (for a Turtle instance named turtle):
2501 >>> turtle.setundobuffer(42)
2502 """
Raymond Hettingerb606d452014-07-20 21:26:04 -07002503 if size is None or size <= 0:
Martin v. Löwis87184592008-06-04 06:29:55 +00002504 self.undobuffer = None
2505 else:
2506 self.undobuffer = Tbuffer(size)
2507
2508 def undobufferentries(self):
2509 """Return count of entries in the undobuffer.
2510
2511 No argument.
2512
2513 Example (for a Turtle instance named turtle):
2514 >>> while undobufferentries():
Petri Lehtinen49e49a22011-12-02 21:09:30 +02002515 ... undo()
Martin v. Löwis87184592008-06-04 06:29:55 +00002516 """
2517 if self.undobuffer is None:
2518 return 0
2519 return self.undobuffer.nr_of_items()
2520
2521 def _clear(self):
2522 """Delete all of pen's drawings"""
2523 self._fillitem = self._fillpath = None
2524 for item in self.items:
2525 self.screen._delete(item)
2526 self.currentLineItem = self.screen._createline()
2527 self.currentLine = []
2528 if self._drawing:
2529 self.currentLine.append(self._position)
2530 self.items = [self.currentLineItem]
2531 self.clearstamps()
2532 self.setundobuffer(self._undobuffersize)
2533
2534
2535 def clear(self):
2536 """Delete the turtle's drawings from the screen. Do not move turtle.
2537
2538 No arguments.
2539
2540 Delete the turtle's drawings from the screen. Do not move turtle.
2541 State and position of the turtle as well as drawings of other
2542 turtles are not affected.
2543
2544 Examples (for a Turtle instance named turtle):
2545 >>> turtle.clear()
2546 """
2547 self._clear()
2548 self._update()
2549
2550 def _update_data(self):
2551 self.screen._incrementudc()
2552 if self.screen._updatecounter != 0:
2553 return
2554 if len(self.currentLine)>1:
2555 self.screen._drawline(self.currentLineItem, self.currentLine,
2556 self._pencolor, self._pensize)
2557
2558 def _update(self):
2559 """Perform a Turtle-data update.
2560 """
2561 screen = self.screen
2562 if screen._tracing == 0:
2563 return
2564 elif screen._tracing == 1:
2565 self._update_data()
2566 self._drawturtle()
2567 screen._update() # TurtleScreenBase
2568 screen._delay(screen._delayvalue) # TurtleScreenBase
2569 else:
2570 self._update_data()
2571 if screen._updatecounter == 0:
2572 for t in screen.turtles():
2573 t._drawturtle()
2574 screen._update()
2575
2576 def tracer(self, flag=None, delay=None):
2577 """Turns turtle animation on/off and set delay for update drawings.
2578
2579 Optional arguments:
2580 n -- nonnegative integer
2581 delay -- nonnegative integer
2582
2583 If n is given, only each n-th regular screen update is really performed.
2584 (Can be used to accelerate the drawing of complex graphics.)
2585 Second arguments sets delay value (see RawTurtle.delay())
2586
2587 Example (for a Turtle instance named turtle):
2588 >>> turtle.tracer(8, 25)
2589 >>> dist = 2
2590 >>> for i in range(200):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02002591 ... turtle.fd(dist)
2592 ... turtle.rt(90)
2593 ... dist += 2
Martin v. Löwis87184592008-06-04 06:29:55 +00002594 """
2595 return self.screen.tracer(flag, delay)
2596
2597 def _color(self, args):
2598 return self.screen._color(args)
2599
2600 def _colorstr(self, args):
2601 return self.screen._colorstr(args)
2602
2603 def _cc(self, args):
2604 """Convert colortriples to hexstrings.
2605 """
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05002606 if isinstance(args, basestring):
Martin v. Löwis87184592008-06-04 06:29:55 +00002607 return args
2608 try:
2609 r, g, b = args
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +03002610 except (TypeError, ValueError):
Martin v. Löwis87184592008-06-04 06:29:55 +00002611 raise TurtleGraphicsError("bad color arguments: %s" % str(args))
2612 if self.screen._colormode == 1.0:
2613 r, g, b = [round(255.0*x) for x in (r, g, b)]
2614 if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
2615 raise TurtleGraphicsError("bad color sequence: %s" % str(args))
2616 return "#%02x%02x%02x" % (r, g, b)
2617
2618 def clone(self):
2619 """Create and return a clone of the turtle.
2620
2621 No argument.
2622
2623 Create and return a clone of the turtle with same position, heading
2624 and turtle properties.
2625
2626 Example (for a Turtle instance named mick):
2627 mick = Turtle()
2628 joe = mick.clone()
2629 """
2630 screen = self.screen
2631 self._newLine(self._drawing)
2632
2633 turtle = self.turtle
2634 self.screen = None
2635 self.turtle = None # too make self deepcopy-able
2636
2637 q = deepcopy(self)
2638
2639 self.screen = screen
2640 self.turtle = turtle
2641
2642 q.screen = screen
2643 q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
2644
2645 screen._turtles.append(q)
2646 ttype = screen._shapes[self.turtle.shapeIndex]._type
2647 if ttype == "polygon":
2648 q.turtle._item = screen._createpoly()
2649 elif ttype == "image":
2650 q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
2651 elif ttype == "compound":
2652 q.turtle._item = [screen._createpoly() for item in
2653 screen._shapes[self.turtle.shapeIndex]._data]
2654 q.currentLineItem = screen._createline()
2655 q._update()
2656 return q
2657
2658 def shape(self, name=None):
2659 """Set turtle shape to shape with given name / return current shapename.
2660
2661 Optional argument:
2662 name -- a string, which is a valid shapename
2663
2664 Set turtle shape to shape with given name or, if name is not given,
2665 return name of current shape.
2666 Shape with name must exist in the TurtleScreen's shape dictionary.
2667 Initially there are the following polygon shapes:
2668 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
2669 To learn about how to deal with shapes see Screen-method register_shape.
2670
2671 Example (for a Turtle instance named turtle):
2672 >>> turtle.shape()
2673 'arrow'
2674 >>> turtle.shape("turtle")
2675 >>> turtle.shape()
2676 'turtle'
2677 """
2678 if name is None:
2679 return self.turtle.shapeIndex
2680 if not name in self.screen.getshapes():
2681 raise TurtleGraphicsError("There is no shape named %s" % name)
2682 self.turtle._setshape(name)
2683 self._update()
2684
2685 def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
2686 """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
2687
Ezio Melottif5469cf2013-08-17 15:43:51 +03002688 Optional arguments:
Martin v. Löwis87184592008-06-04 06:29:55 +00002689 stretch_wid : positive number
2690 stretch_len : positive number
2691 outline : positive number
2692
2693 Return or set the pen's attributes x/y-stretchfactors and/or outline.
2694 Set resizemode to "user".
2695 If and only if resizemode is set to "user", the turtle will be displayed
2696 stretched according to its stretchfactors:
2697 stretch_wid is stretchfactor perpendicular to orientation
2698 stretch_len is stretchfactor in direction of turtles orientation.
2699 outline determines the width of the shapes's outline.
2700
2701 Examples (for a Turtle instance named turtle):
2702 >>> turtle.resizemode("user")
2703 >>> turtle.shapesize(5, 5, 12)
2704 >>> turtle.shapesize(outline=8)
2705 """
Florent Xiclunac8a730b2010-03-25 20:32:07 +00002706 if stretch_wid is stretch_len is outline is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00002707 stretch_wid, stretch_len = self._stretchfactor
2708 return stretch_wid, stretch_len, self._outlinewidth
2709 if stretch_wid is not None:
2710 if stretch_len is None:
2711 stretchfactor = stretch_wid, stretch_wid
2712 else:
2713 stretchfactor = stretch_wid, stretch_len
2714 elif stretch_len is not None:
2715 stretchfactor = self._stretchfactor[0], stretch_len
2716 else:
2717 stretchfactor = self._stretchfactor
2718 if outline is None:
2719 outline = self._outlinewidth
2720 self.pen(resizemode="user",
2721 stretchfactor=stretchfactor, outline=outline)
2722
2723 def settiltangle(self, angle):
2724 """Rotate the turtleshape to point in the specified direction
2725
2726 Optional argument:
2727 angle -- number
2728
2729 Rotate the turtleshape to point in the direction specified by angle,
2730 regardless of its current tilt-angle. DO NOT change the turtle's
2731 heading (direction of movement).
2732
2733
2734 Examples (for a Turtle instance named turtle):
2735 >>> turtle.shape("circle")
2736 >>> turtle.shapesize(5,2)
2737 >>> turtle.settiltangle(45)
2738 >>> stamp()
2739 >>> turtle.fd(50)
2740 >>> turtle.settiltangle(-45)
2741 >>> stamp()
2742 >>> turtle.fd(50)
2743 """
2744 tilt = -angle * self._degreesPerAU * self._angleOrient
2745 tilt = (tilt * math.pi / 180.0) % (2*math.pi)
2746 self.pen(resizemode="user", tilt=tilt)
2747
2748 def tiltangle(self):
2749 """Return the current tilt-angle.
2750
2751 No argument.
2752
2753 Return the current tilt-angle, i. e. the angle between the
2754 orientation of the turtleshape and the heading of the turtle
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002755 (its direction of movement).
Martin v. Löwis87184592008-06-04 06:29:55 +00002756
2757 Examples (for a Turtle instance named turtle):
2758 >>> turtle.shape("circle")
2759 >>> turtle.shapesize(5,2)
2760 >>> turtle.tilt(45)
2761 >>> turtle.tiltangle()
Martin v. Löwis87184592008-06-04 06:29:55 +00002762 """
2763 tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
2764 return (tilt / self._degreesPerAU) % self._fullcircle
2765
2766 def tilt(self, angle):
2767 """Rotate the turtleshape by angle.
2768
2769 Argument:
2770 angle - a number
2771
2772 Rotate the turtleshape by angle from its current tilt-angle,
2773 but do NOT change the turtle's heading (direction of movement).
2774
2775 Examples (for a Turtle instance named turtle):
2776 >>> turtle.shape("circle")
2777 >>> turtle.shapesize(5,2)
2778 >>> turtle.tilt(30)
2779 >>> turtle.fd(50)
2780 >>> turtle.tilt(30)
2781 >>> turtle.fd(50)
2782 """
2783 self.settiltangle(angle + self.tiltangle())
2784
2785 def _polytrafo(self, poly):
2786 """Computes transformed polygon shapes from a shape
2787 according to current position and heading.
2788 """
2789 screen = self.screen
2790 p0, p1 = self._position
2791 e0, e1 = self._orient
2792 e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
2793 e0, e1 = (1.0 / abs(e)) * e
2794 return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
2795 for (x, y) in poly]
2796
2797 def _drawturtle(self):
2798 """Manages the correct rendering of the turtle with respect to
Mark Dickinson3e4caeb2009-02-21 20:27:01 +00002799 its shape, resizemode, stretch and tilt etc."""
Martin v. Löwis87184592008-06-04 06:29:55 +00002800 screen = self.screen
2801 shape = screen._shapes[self.turtle.shapeIndex]
2802 ttype = shape._type
2803 titem = self.turtle._item
2804 if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
2805 self._hidden_from_screen = False
2806 tshape = shape._data
2807 if ttype == "polygon":
2808 if self._resizemode == "noresize":
2809 w = 1
2810 shape = tshape
2811 else:
2812 if self._resizemode == "auto":
2813 lx = ly = max(1, self._pensize/5.0)
2814 w = self._pensize
2815 tiltangle = 0
2816 elif self._resizemode == "user":
2817 lx, ly = self._stretchfactor
2818 w = self._outlinewidth
2819 tiltangle = self._tilt
2820 shape = [(lx*x, ly*y) for (x, y) in tshape]
2821 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2822 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2823 shape = self._polytrafo(shape)
2824 fc, oc = self._fillcolor, self._pencolor
2825 screen._drawpoly(titem, shape, fill=fc, outline=oc,
2826 width=w, top=True)
2827 elif ttype == "image":
2828 screen._drawimage(titem, self._position, tshape)
2829 elif ttype == "compound":
2830 lx, ly = self._stretchfactor
2831 w = self._outlinewidth
2832 for item, (poly, fc, oc) in zip(titem, tshape):
2833 poly = [(lx*x, ly*y) for (x, y) in poly]
2834 poly = self._polytrafo(poly)
2835 screen._drawpoly(item, poly, fill=self._cc(fc),
2836 outline=self._cc(oc), width=w, top=True)
2837 else:
2838 if self._hidden_from_screen:
2839 return
2840 if ttype == "polygon":
2841 screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
2842 elif ttype == "image":
2843 screen._drawimage(titem, self._position,
2844 screen._shapes["blank"]._data)
2845 elif ttype == "compound":
2846 for item in titem:
2847 screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
2848 self._hidden_from_screen = True
2849
2850############################## stamp stuff ###############################
2851
2852 def stamp(self):
Mark Dickinson2752e9b2009-02-20 20:42:53 +00002853 """Stamp a copy of the turtleshape onto the canvas and return its id.
Martin v. Löwis87184592008-06-04 06:29:55 +00002854
2855 No argument.
2856
2857 Stamp a copy of the turtle shape onto the canvas at the current
2858 turtle position. Return a stamp_id for that stamp, which can be
2859 used to delete it by calling clearstamp(stamp_id).
2860
2861 Example (for a Turtle instance named turtle):
2862 >>> turtle.color("blue")
2863 >>> turtle.stamp()
2864 13
2865 >>> turtle.fd(50)
2866 """
2867 screen = self.screen
2868 shape = screen._shapes[self.turtle.shapeIndex]
2869 ttype = shape._type
2870 tshape = shape._data
2871 if ttype == "polygon":
2872 stitem = screen._createpoly()
2873 if self._resizemode == "noresize":
2874 w = 1
2875 shape = tshape
2876 else:
2877 if self._resizemode == "auto":
2878 lx = ly = max(1, self._pensize/5.0)
2879 w = self._pensize
2880 tiltangle = 0
2881 elif self._resizemode == "user":
2882 lx, ly = self._stretchfactor
2883 w = self._outlinewidth
2884 tiltangle = self._tilt
2885 shape = [(lx*x, ly*y) for (x, y) in tshape]
2886 t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
2887 shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
2888 shape = self._polytrafo(shape)
2889 fc, oc = self._fillcolor, self._pencolor
2890 screen._drawpoly(stitem, shape, fill=fc, outline=oc,
2891 width=w, top=True)
2892 elif ttype == "image":
2893 stitem = screen._createimage("")
2894 screen._drawimage(stitem, self._position, tshape)
2895 elif ttype == "compound":
2896 stitem = []
2897 for element in tshape:
2898 item = screen._createpoly()
2899 stitem.append(item)
2900 stitem = tuple(stitem)
2901 lx, ly = self._stretchfactor
2902 w = self._outlinewidth
2903 for item, (poly, fc, oc) in zip(stitem, tshape):
2904 poly = [(lx*x, ly*y) for (x, y) in poly]
2905 poly = self._polytrafo(poly)
2906 screen._drawpoly(item, poly, fill=self._cc(fc),
2907 outline=self._cc(oc), width=w, top=True)
2908 self.stampItems.append(stitem)
2909 self.undobuffer.push(("stamp", stitem))
2910 return stitem
2911
2912 def _clearstamp(self, stampid):
2913 """does the work for clearstamp() and clearstamps()
2914 """
2915 if stampid in self.stampItems:
2916 if isinstance(stampid, tuple):
2917 for subitem in stampid:
2918 self.screen._delete(subitem)
2919 else:
2920 self.screen._delete(stampid)
2921 self.stampItems.remove(stampid)
2922 # Delete stampitem from undobuffer if necessary
2923 # if clearstamp is called directly.
2924 item = ("stamp", stampid)
2925 buf = self.undobuffer
2926 if item not in buf.buffer:
2927 return
2928 index = buf.buffer.index(item)
2929 buf.buffer.remove(item)
2930 if index <= buf.ptr:
2931 buf.ptr = (buf.ptr - 1) % buf.bufsize
2932 buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
2933
2934 def clearstamp(self, stampid):
2935 """Delete stamp with given stampid
2936
2937 Argument:
2938 stampid - an integer, must be return value of previous stamp() call.
2939
2940 Example (for a Turtle instance named turtle):
2941 >>> turtle.color("blue")
2942 >>> astamp = turtle.stamp()
2943 >>> turtle.fd(50)
2944 >>> turtle.clearstamp(astamp)
2945 """
2946 self._clearstamp(stampid)
2947 self._update()
2948
2949 def clearstamps(self, n=None):
2950 """Delete all or first/last n of turtle's stamps.
2951
2952 Optional argument:
2953 n -- an integer
2954
2955 If n is None, delete all of pen's stamps,
2956 else if n > 0 delete first n stamps
2957 else if n < 0 delete last n stamps.
2958
2959 Example (for a Turtle instance named turtle):
2960 >>> for i in range(8):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02002961 ... turtle.stamp(); turtle.fd(30)
Martin v. Löwis87184592008-06-04 06:29:55 +00002962 ...
2963 >>> turtle.clearstamps(2)
2964 >>> turtle.clearstamps(-2)
2965 >>> turtle.clearstamps()
2966 """
2967 if n is None:
2968 toDelete = self.stampItems[:]
2969 elif n >= 0:
2970 toDelete = self.stampItems[:n]
2971 else:
2972 toDelete = self.stampItems[n:]
2973 for item in toDelete:
2974 self._clearstamp(item)
2975 self._update()
2976
2977 def _goto(self, end):
2978 """Move the pen to the point end, thereby drawing a line
Ezio Melottif5469cf2013-08-17 15:43:51 +03002979 if pen is down. All other methods for turtle movement depend
Martin v. Löwis87184592008-06-04 06:29:55 +00002980 on this one.
2981 """
2982 ## Version mit undo-stuff
2983 go_modes = ( self._drawing,
2984 self._pencolor,
2985 self._pensize,
2986 isinstance(self._fillpath, list))
2987 screen = self.screen
2988 undo_entry = ("go", self._position, end, go_modes,
2989 (self.currentLineItem,
2990 self.currentLine[:],
2991 screen._pointlist(self.currentLineItem),
2992 self.items[:])
2993 )
2994 if self.undobuffer:
2995 self.undobuffer.push(undo_entry)
2996 start = self._position
2997 if self._speed and screen._tracing == 1:
2998 diff = (end-start)
2999 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3000 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3001 delta = diff * (1.0/nhops)
3002 for n in range(1, nhops):
3003 if n == 1:
3004 top = True
3005 else:
3006 top = False
3007 self._position = start + delta * n
3008 if self._drawing:
3009 screen._drawline(self.drawingLineItem,
3010 (start, self._position),
3011 self._pencolor, self._pensize, top)
3012 self._update()
3013 if self._drawing:
3014 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3015 fill="", width=self._pensize)
3016 # Turtle now at end,
3017 if self._drawing: # now update currentLine
3018 self.currentLine.append(end)
3019 if isinstance(self._fillpath, list):
3020 self._fillpath.append(end)
3021 ###### vererbung!!!!!!!!!!!!!!!!!!!!!!
3022 self._position = end
3023 if self._creatingPoly:
3024 self._poly.append(end)
3025 if len(self.currentLine) > 42: # 42! answer to the ultimate question
3026 # of life, the universe and everything
3027 self._newLine()
3028 self._update() #count=True)
3029
3030 def _undogoto(self, entry):
3031 """Reverse a _goto. Used for undo()
3032 """
3033 old, new, go_modes, coodata = entry
3034 drawing, pc, ps, filling = go_modes
3035 cLI, cL, pl, items = coodata
3036 screen = self.screen
3037 if abs(self._position - new) > 0.5:
3038 print "undogoto: HALLO-DA-STIMMT-WAS-NICHT!"
3039 # restore former situation
3040 self.currentLineItem = cLI
3041 self.currentLine = cL
3042
3043 if pl == [(0, 0), (0, 0)]:
3044 usepc = ""
3045 else:
3046 usepc = pc
3047 screen._drawline(cLI, pl, fill=usepc, width=ps)
3048
3049 todelete = [i for i in self.items if (i not in items) and
3050 (screen._type(i) == "line")]
3051 for i in todelete:
3052 screen._delete(i)
3053 self.items.remove(i)
3054
3055 start = old
3056 if self._speed and screen._tracing == 1:
3057 diff = old - new
3058 diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
3059 nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
3060 delta = diff * (1.0/nhops)
3061 for n in range(1, nhops):
3062 if n == 1:
3063 top = True
3064 else:
3065 top = False
3066 self._position = new + delta * n
3067 if drawing:
3068 screen._drawline(self.drawingLineItem,
3069 (start, self._position),
3070 pc, ps, top)
3071 self._update()
3072 if drawing:
3073 screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
3074 fill="", width=ps)
3075 # Turtle now at position old,
3076 self._position = old
Ezio Melottic2077b02011-03-16 12:34:31 +02003077 ## if undo is done during creating a polygon, the last vertex
3078 ## will be deleted. if the polygon is entirely deleted,
3079 ## creatingPoly will be set to False.
Martin v. Löwis87184592008-06-04 06:29:55 +00003080 ## Polygons created before the last one will not be affected by undo()
3081 if self._creatingPoly:
3082 if len(self._poly) > 0:
3083 self._poly.pop()
3084 if self._poly == []:
3085 self._creatingPoly = False
3086 self._poly = None
3087 if filling:
3088 if self._fillpath == []:
3089 self._fillpath = None
3090 print "Unwahrscheinlich in _undogoto!"
3091 elif self._fillpath is not None:
3092 self._fillpath.pop()
3093 self._update() #count=True)
3094
3095 def _rotate(self, angle):
3096 """Turns pen clockwise by angle.
3097 """
3098 if self.undobuffer:
3099 self.undobuffer.push(("rot", angle, self._degreesPerAU))
3100 angle *= self._degreesPerAU
3101 neworient = self._orient.rotate(angle)
3102 tracing = self.screen._tracing
3103 if tracing == 1 and self._speed > 0:
3104 anglevel = 3.0 * self._speed
3105 steps = 1 + int(abs(angle)/anglevel)
3106 delta = 1.0*angle/steps
3107 for _ in range(steps):
3108 self._orient = self._orient.rotate(delta)
3109 self._update()
3110 self._orient = neworient
3111 self._update()
3112
3113 def _newLine(self, usePos=True):
3114 """Closes current line item and starts a new one.
3115 Remark: if current line became too long, animation
3116 performance (via _drawline) slowed down considerably.
3117 """
3118 if len(self.currentLine) > 1:
3119 self.screen._drawline(self.currentLineItem, self.currentLine,
3120 self._pencolor, self._pensize)
3121 self.currentLineItem = self.screen._createline()
3122 self.items.append(self.currentLineItem)
3123 else:
3124 self.screen._drawline(self.currentLineItem, top=True)
3125 self.currentLine = []
3126 if usePos:
3127 self.currentLine = [self._position]
3128
3129 def fill(self, flag=None):
3130 """Call fill(True) before drawing a shape to fill, fill(False) when done.
3131
3132 Optional argument:
3133 flag -- True/False (or 1/0 respectively)
3134
3135 Call fill(True) before drawing the shape you want to fill,
3136 and fill(False) when done.
3137 When used without argument: return fillstate (True if filling,
3138 False else)
3139
3140 Example (for a Turtle instance named turtle):
3141 >>> turtle.fill(True)
3142 >>> turtle.forward(100)
3143 >>> turtle.left(90)
3144 >>> turtle.forward(100)
3145 >>> turtle.left(90)
3146 >>> turtle.forward(100)
3147 >>> turtle.left(90)
3148 >>> turtle.forward(100)
3149 >>> turtle.fill(False)
3150 """
3151 filling = isinstance(self._fillpath, list)
3152 if flag is None:
3153 return filling
3154 screen = self.screen
3155 entry1 = entry2 = ()
3156 if filling:
3157 if len(self._fillpath) > 2:
3158 self.screen._drawpoly(self._fillitem, self._fillpath,
3159 fill=self._fillcolor)
3160 entry1 = ("dofill", self._fillitem)
3161 if flag:
3162 self._fillitem = self.screen._createpoly()
3163 self.items.append(self._fillitem)
3164 self._fillpath = [self._position]
3165 entry2 = ("beginfill", self._fillitem) # , self._fillpath)
3166 self._newLine()
3167 else:
3168 self._fillitem = self._fillpath = None
3169 if self.undobuffer:
3170 if entry1 == ():
3171 if entry2 != ():
3172 self.undobuffer.push(entry2)
3173 else:
3174 if entry2 == ():
3175 self.undobuffer.push(entry1)
3176 else:
3177 self.undobuffer.push(["seq", entry1, entry2])
3178 self._update()
3179
3180 def begin_fill(self):
3181 """Called just before drawing a shape to be filled.
3182
3183 No argument.
3184
3185 Example (for a Turtle instance named turtle):
3186 >>> turtle.begin_fill()
3187 >>> turtle.forward(100)
3188 >>> turtle.left(90)
3189 >>> turtle.forward(100)
3190 >>> turtle.left(90)
3191 >>> turtle.forward(100)
3192 >>> turtle.left(90)
3193 >>> turtle.forward(100)
3194 >>> turtle.end_fill()
3195 """
3196 self.fill(True)
3197
3198 def end_fill(self):
3199 """Fill the shape drawn after the call begin_fill().
3200
3201 No argument.
3202
3203 Example (for a Turtle instance named turtle):
3204 >>> turtle.begin_fill()
3205 >>> turtle.forward(100)
3206 >>> turtle.left(90)
3207 >>> turtle.forward(100)
3208 >>> turtle.left(90)
3209 >>> turtle.forward(100)
3210 >>> turtle.left(90)
3211 >>> turtle.forward(100)
3212 >>> turtle.end_fill()
3213 """
3214 self.fill(False)
3215
3216 def dot(self, size=None, *color):
3217 """Draw a dot with diameter size, using color.
3218
Ezio Melotti24b07bc2011-03-15 18:55:01 +02003219 Optional arguments:
Martin v. Löwis87184592008-06-04 06:29:55 +00003220 size -- an integer >= 1 (if given)
3221 color -- a colorstring or a numeric color tuple
3222
3223 Draw a circular dot with diameter size, using color.
3224 If size is not given, the maximum of pensize+4 and 2*pensize is used.
3225
3226 Example (for a Turtle instance named turtle):
3227 >>> turtle.dot()
3228 >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
3229 """
3230 #print "dot-1:", size, color
3231 if not color:
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05003232 if isinstance(size, (basestring, tuple)):
Martin v. Löwis87184592008-06-04 06:29:55 +00003233 color = self._colorstr(size)
3234 size = self._pensize + max(self._pensize, 4)
3235 else:
3236 color = self._pencolor
3237 if not size:
3238 size = self._pensize + max(self._pensize, 4)
3239 else:
3240 if size is None:
3241 size = self._pensize + max(self._pensize, 4)
3242 color = self._colorstr(color)
3243 #print "dot-2:", size, color
3244 if hasattr(self.screen, "_dot"):
3245 item = self.screen._dot(self._position, size, color)
3246 #print "dot:", size, color, "item:", item
3247 self.items.append(item)
3248 if self.undobuffer:
3249 self.undobuffer.push(("dot", item))
3250 else:
3251 pen = self.pen()
3252 if self.undobuffer:
3253 self.undobuffer.push(["seq"])
3254 self.undobuffer.cumulate = True
3255 try:
3256 if self.resizemode() == 'auto':
3257 self.ht()
3258 self.pendown()
3259 self.pensize(size)
3260 self.pencolor(color)
3261 self.forward(0)
3262 finally:
3263 self.pen(pen)
3264 if self.undobuffer:
3265 self.undobuffer.cumulate = False
3266
3267 def _write(self, txt, align, font):
3268 """Performs the writing for write()
3269 """
3270 item, end = self.screen._write(self._position, txt, align, font,
3271 self._pencolor)
3272 self.items.append(item)
3273 if self.undobuffer:
3274 self.undobuffer.push(("wri", item))
3275 return end
3276
3277 def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
3278 """Write text at the current turtle position.
3279
3280 Arguments:
3281 arg -- info, which is to be written to the TurtleScreen
3282 move (optional) -- True/False
3283 align (optional) -- one of the strings "left", "center" or right"
3284 font (optional) -- a triple (fontname, fontsize, fonttype)
3285
3286 Write text - the string representation of arg - at the current
3287 turtle position according to align ("left", "center" or right")
3288 and with the given font.
3289 If move is True, the pen is moved to the bottom-right corner
3290 of the text. By default, move is False.
3291
3292 Example (for a Turtle instance named turtle):
3293 >>> turtle.write('Home = ', True, align="center")
3294 >>> turtle.write((0,0), True)
3295 """
3296 if self.undobuffer:
3297 self.undobuffer.push(["seq"])
3298 self.undobuffer.cumulate = True
3299 end = self._write(str(arg), align.lower(), font)
3300 if move:
3301 x, y = self.pos()
3302 self.setpos(end, y)
3303 if self.undobuffer:
3304 self.undobuffer.cumulate = False
3305
3306 def begin_poly(self):
3307 """Start recording the vertices of a polygon.
3308
3309 No argument.
3310
3311 Start recording the vertices of a polygon. Current turtle position
3312 is first point of polygon.
3313
3314 Example (for a Turtle instance named turtle):
3315 >>> turtle.begin_poly()
3316 """
3317 self._poly = [self._position]
3318 self._creatingPoly = True
3319
3320 def end_poly(self):
3321 """Stop recording the vertices of a polygon.
3322
3323 No argument.
3324
3325 Stop recording the vertices of a polygon. Current turtle position is
3326 last point of polygon. This will be connected with the first point.
3327
3328 Example (for a Turtle instance named turtle):
3329 >>> turtle.end_poly()
3330 """
3331 self._creatingPoly = False
3332
3333 def get_poly(self):
3334 """Return the lastly recorded polygon.
3335
3336 No argument.
3337
3338 Example (for a Turtle instance named turtle):
3339 >>> p = turtle.get_poly()
3340 >>> turtle.register_shape("myFavouriteShape", p)
3341 """
3342 ## check if there is any poly? -- 1st solution:
3343 if self._poly is not None:
3344 return tuple(self._poly)
3345
3346 def getscreen(self):
3347 """Return the TurtleScreen object, the turtle is drawing on.
3348
3349 No argument.
3350
3351 Return the TurtleScreen object, the turtle is drawing on.
3352 So TurtleScreen-methods can be called for that object.
3353
3354 Example (for a Turtle instance named turtle):
3355 >>> ts = turtle.getscreen()
3356 >>> ts
3357 <turtle.TurtleScreen object at 0x0106B770>
3358 >>> ts.bgcolor("pink")
3359 """
3360 return self.screen
3361
3362 def getturtle(self):
3363 """Return the Turtleobject itself.
3364
3365 No argument.
3366
3367 Only reasonable use: as a function to return the 'anonymous turtle':
3368
3369 Example:
3370 >>> pet = getturtle()
3371 >>> pet.fd(50)
3372 >>> pet
3373 <turtle.Turtle object at 0x0187D810>
3374 >>> turtles()
3375 [<turtle.Turtle object at 0x0187D810>]
3376 """
3377 return self
3378
3379 getpen = getturtle
3380
3381
3382 ################################################################
3383 ### screen oriented methods recurring to methods of TurtleScreen
3384 ################################################################
3385
3386 def window_width(self):
3387 """ Returns the width of the turtle window.
3388
3389 No argument.
3390
3391 Example (for a TurtleScreen instance named screen):
3392 >>> screen.window_width()
3393 640
3394 """
3395 return self.screen._window_size()[0]
3396
3397 def window_height(self):
3398 """ Return the height of the turtle window.
3399
3400 No argument.
3401
3402 Example (for a TurtleScreen instance named screen):
3403 >>> screen.window_height()
3404 480
3405 """
3406 return self.screen._window_size()[1]
3407
3408 def _delay(self, delay=None):
3409 """Set delay value which determines speed of turtle animation.
3410 """
3411 return self.screen.delay(delay)
3412
3413 ##### event binding methods #####
3414
3415 def onclick(self, fun, btn=1, add=None):
3416 """Bind fun to mouse-click event on this turtle on canvas.
3417
3418 Arguments:
3419 fun -- a function with two arguments, to which will be assigned
3420 the coordinates of the clicked point on the canvas.
3421 num -- number of the mouse-button defaults to 1 (left mouse button).
3422 add -- True or False. If True, new binding will be added, otherwise
3423 it will replace a former binding.
3424
3425 Example for the anonymous turtle, i. e. the procedural way:
3426
3427 >>> def turn(x, y):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02003428 ... left(360)
3429 ...
3430 >>> onclick(turn) # Now clicking into the turtle will turn it.
Martin v. Löwis87184592008-06-04 06:29:55 +00003431 >>> onclick(None) # event-binding will be removed
3432 """
3433 self.screen._onclick(self.turtle._item, fun, btn, add)
3434 self._update()
3435
3436 def onrelease(self, fun, btn=1, add=None):
3437 """Bind fun to mouse-button-release event on this turtle on canvas.
3438
3439 Arguments:
3440 fun -- a function with two arguments, to which will be assigned
3441 the coordinates of the clicked point on the canvas.
3442 num -- number of the mouse-button defaults to 1 (left mouse button).
3443
3444 Example (for a MyTurtle instance named joe):
3445 >>> class MyTurtle(Turtle):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02003446 ... def glow(self,x,y):
3447 ... self.fillcolor("red")
3448 ... def unglow(self,x,y):
3449 ... self.fillcolor("")
3450 ...
Martin v. Löwis87184592008-06-04 06:29:55 +00003451 >>> joe = MyTurtle()
3452 >>> joe.onclick(joe.glow)
3453 >>> joe.onrelease(joe.unglow)
Petri Lehtinen49e49a22011-12-02 21:09:30 +02003454
3455 Clicking on joe turns fillcolor red, unclicking turns it to
3456 transparent.
Martin v. Löwis87184592008-06-04 06:29:55 +00003457 """
3458 self.screen._onrelease(self.turtle._item, fun, btn, add)
3459 self._update()
3460
3461 def ondrag(self, fun, btn=1, add=None):
3462 """Bind fun to mouse-move event on this turtle on canvas.
3463
3464 Arguments:
3465 fun -- a function with two arguments, to which will be assigned
3466 the coordinates of the clicked point on the canvas.
3467 num -- number of the mouse-button defaults to 1 (left mouse button).
3468
3469 Every sequence of mouse-move-events on a turtle is preceded by a
3470 mouse-click event on that turtle.
3471
3472 Example (for a Turtle instance named turtle):
3473 >>> turtle.ondrag(turtle.goto)
3474
Petri Lehtinen49e49a22011-12-02 21:09:30 +02003475 Subsequently clicking and dragging a Turtle will move it
3476 across the screen thereby producing handdrawings (if pen is
3477 down).
Martin v. Löwis87184592008-06-04 06:29:55 +00003478 """
3479 self.screen._ondrag(self.turtle._item, fun, btn, add)
3480
3481
3482 def _undo(self, action, data):
3483 """Does the main part of the work for undo()
3484 """
3485 if self.undobuffer is None:
3486 return
3487 if action == "rot":
3488 angle, degPAU = data
3489 self._rotate(-angle*degPAU/self._degreesPerAU)
3490 dummy = self.undobuffer.pop()
3491 elif action == "stamp":
3492 stitem = data[0]
3493 self.clearstamp(stitem)
3494 elif action == "go":
3495 self._undogoto(data)
3496 elif action in ["wri", "dot"]:
3497 item = data[0]
3498 self.screen._delete(item)
3499 self.items.remove(item)
3500 elif action == "dofill":
3501 item = data[0]
3502 self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
3503 fill="", outline="")
3504 elif action == "beginfill":
3505 item = data[0]
3506 self._fillitem = self._fillpath = None
3507 self.screen._delete(item)
3508 self.items.remove(item)
3509 elif action == "pen":
3510 TPen.pen(self, data[0])
3511 self.undobuffer.pop()
3512
3513 def undo(self):
3514 """undo (repeatedly) the last turtle action.
3515
3516 No argument.
3517
3518 undo (repeatedly) the last turtle action.
3519 Number of available undo actions is determined by the size of
3520 the undobuffer.
3521
3522 Example (for a Turtle instance named turtle):
3523 >>> for i in range(4):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02003524 ... turtle.fd(50); turtle.lt(80)
3525 ...
Martin v. Löwis87184592008-06-04 06:29:55 +00003526 >>> for i in range(8):
Petri Lehtinen49e49a22011-12-02 21:09:30 +02003527 ... turtle.undo()
3528 ...
Martin v. Löwis87184592008-06-04 06:29:55 +00003529 """
3530 if self.undobuffer is None:
3531 return
3532 item = self.undobuffer.pop()
3533 action = item[0]
3534 data = item[1:]
3535 if action == "seq":
3536 while data:
3537 item = data.pop()
3538 self._undo(item[0], item[1:])
3539 else:
3540 self._undo(action, data)
3541
3542 turtlesize = shapesize
3543
3544RawPen = RawTurtle
3545
Martin v. Löwise563aa42008-09-29 22:09:07 +00003546### Screen - Singleton ########################
Martin v. Löwis87184592008-06-04 06:29:55 +00003547
Martin v. Löwise563aa42008-09-29 22:09:07 +00003548def Screen():
3549 """Return the singleton screen object.
3550 If none exists at the moment, create a new one and return it,
3551 else return the existing one."""
3552 if Turtle._screen is None:
3553 Turtle._screen = _Screen()
3554 return Turtle._screen
3555
3556class _Screen(TurtleScreen):
Martin v. Löwis87184592008-06-04 06:29:55 +00003557
3558 _root = None
3559 _canvas = None
3560 _title = _CFG["title"]
3561
Georg Brandl33cece02008-05-20 06:58:21 +00003562 def __init__(self):
Martin v. Löwise563aa42008-09-29 22:09:07 +00003563 # XXX there is no need for this code to be conditional,
3564 # as there will be only a single _Screen instance, anyway
3565 # XXX actually, the turtle demo is injecting root window,
3566 # so perhaps the conditional creation of a root should be
3567 # preserved (perhaps by passing it as an optional parameter)
3568 if _Screen._root is None:
3569 _Screen._root = self._root = _Root()
3570 self._root.title(_Screen._title)
Martin v. Löwis87184592008-06-04 06:29:55 +00003571 self._root.ondestroy(self._destroy)
Martin v. Löwise563aa42008-09-29 22:09:07 +00003572 if _Screen._canvas is None:
Martin v. Löwis87184592008-06-04 06:29:55 +00003573 width = _CFG["width"]
3574 height = _CFG["height"]
3575 canvwidth = _CFG["canvwidth"]
3576 canvheight = _CFG["canvheight"]
3577 leftright = _CFG["leftright"]
3578 topbottom = _CFG["topbottom"]
3579 self._root.setupcanvas(width, height, canvwidth, canvheight)
Martin v. Löwise563aa42008-09-29 22:09:07 +00003580 _Screen._canvas = self._root._getcanvas()
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003581 TurtleScreen.__init__(self, _Screen._canvas)
Martin v. Löwis87184592008-06-04 06:29:55 +00003582 self.setup(width, height, leftright, topbottom)
Georg Brandl33cece02008-05-20 06:58:21 +00003583
Martin v. Löwis87184592008-06-04 06:29:55 +00003584 def setup(self, width=_CFG["width"], height=_CFG["height"],
3585 startx=_CFG["leftright"], starty=_CFG["topbottom"]):
3586 """ Set the size and position of the main window.
Georg Brandl33cece02008-05-20 06:58:21 +00003587
Martin v. Löwis87184592008-06-04 06:29:55 +00003588 Arguments:
3589 width: as integer a size in pixels, as float a fraction of the screen.
3590 Default is 50% of screen.
3591 height: as integer the height in pixels, as float a fraction of the
3592 screen. Default is 75% of screen.
3593 startx: if positive, starting position in pixels from the left
3594 edge of the screen, if negative from the right edge
3595 Default, startx=None is to center window horizontally.
3596 starty: if positive, starting position in pixels from the top
3597 edge of the screen, if negative from the bottom edge
3598 Default, starty=None is to center window vertically.
Georg Brandl33cece02008-05-20 06:58:21 +00003599
Martin v. Löwis87184592008-06-04 06:29:55 +00003600 Examples (for a Screen instance named screen):
3601 >>> screen.setup (width=200, height=200, startx=0, starty=0)
3602
3603 sets window to 200x200 pixels, in upper left of screen
3604
3605 >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
3606
3607 sets window to 75% of screen by 50% of screen and centers
3608 """
3609 if not hasattr(self._root, "set_geometry"):
3610 return
3611 sw = self._root.win_width()
3612 sh = self._root.win_height()
3613 if isinstance(width, float) and 0 <= width <= 1:
3614 width = sw*width
3615 if startx is None:
3616 startx = (sw - width) / 2
3617 if isinstance(height, float) and 0 <= height <= 1:
3618 height = sh*height
3619 if starty is None:
3620 starty = (sh - height) / 2
3621 self._root.set_geometry(width, height, startx, starty)
R. David Murrayddfb6cd2009-09-28 18:29:28 +00003622 self.update()
Martin v. Löwis87184592008-06-04 06:29:55 +00003623
3624 def title(self, titlestring):
3625 """Set title of turtle-window
3626
3627 Argument:
3628 titlestring -- a string, to appear in the titlebar of the
3629 turtle graphics window.
3630
3631 This is a method of Screen-class. Not available for TurtleScreen-
3632 objects.
3633
3634 Example (for a Screen instance named screen):
3635 >>> screen.title("Welcome to the turtle-zoo!")
3636 """
Martin v. Löwise563aa42008-09-29 22:09:07 +00003637 if _Screen._root is not None:
3638 _Screen._root.title(titlestring)
3639 _Screen._title = titlestring
Georg Brandl33cece02008-05-20 06:58:21 +00003640
3641 def _destroy(self):
Martin v. Löwis87184592008-06-04 06:29:55 +00003642 root = self._root
Martin v. Löwise563aa42008-09-29 22:09:07 +00003643 if root is _Screen._root:
Martin v. Löwis87184592008-06-04 06:29:55 +00003644 Turtle._pen = None
3645 Turtle._screen = None
Martin v. Löwise563aa42008-09-29 22:09:07 +00003646 _Screen._root = None
3647 _Screen._canvas = None
Serhiy Storchakacc49aa12015-02-22 17:22:53 +02003648 TurtleScreen._RUNNING = False
Georg Brandl33cece02008-05-20 06:58:21 +00003649 root.destroy()
3650
Martin v. Löwis87184592008-06-04 06:29:55 +00003651 def bye(self):
3652 """Shut the turtlegraphics window.
3653
3654 Example (for a TurtleScreen instance named screen):
3655 >>> screen.bye()
3656 """
3657 self._destroy()
3658
3659 def exitonclick(self):
3660 """Go into mainloop until the mouse is clicked.
3661
3662 No arguments.
3663
3664 Bind bye() method to mouseclick on TurtleScreen.
3665 If "using_IDLE" - value in configuration dictionary is False
3666 (default value), enter mainloop.
3667 If IDLE with -n switch (no subprocess) is used, this value should be
3668 set to True in turtle.cfg. In this case IDLE's mainloop
3669 is active also for the client script.
3670
3671 This is a method of the Screen-class and not available for
3672 TurtleScreen instances.
3673
3674 Example (for a Screen instance named screen):
3675 >>> screen.exitonclick()
3676
3677 """
3678 def exitGracefully(x, y):
3679 """Screen.bye() with two dummy-parameters"""
3680 self.bye()
3681 self.onclick(exitGracefully)
3682 if _CFG["using_IDLE"]:
3683 return
3684 try:
3685 mainloop()
3686 except AttributeError:
3687 exit(0)
3688
Martin v. Löwis87184592008-06-04 06:29:55 +00003689class Turtle(RawTurtle):
Ezio Melottic2077b02011-03-16 12:34:31 +02003690 """RawTurtle auto-creating (scrolled) canvas.
Martin v. Löwis87184592008-06-04 06:29:55 +00003691
3692 When a Turtle object is created or a function derived from some
3693 Turtle method is called a TurtleScreen object is automatically created.
3694 """
3695 _pen = None
3696 _screen = None
3697
3698 def __init__(self,
3699 shape=_CFG["shape"],
3700 undobuffersize=_CFG["undobuffersize"],
3701 visible=_CFG["visible"]):
3702 if Turtle._screen is None:
3703 Turtle._screen = Screen()
3704 RawTurtle.__init__(self, Turtle._screen,
3705 shape=shape,
3706 undobuffersize=undobuffersize,
3707 visible=visible)
3708
3709Pen = Turtle
3710
Martin v. Löwis87184592008-06-04 06:29:55 +00003711def write_docstringdict(filename="turtle_docstringdict"):
3712 """Create and write docstring-dictionary to file.
Georg Brandl33cece02008-05-20 06:58:21 +00003713
Martin v. Löwis87184592008-06-04 06:29:55 +00003714 Optional argument:
3715 filename -- a string, used as filename
3716 default value is turtle_docstringdict
Georg Brandl33cece02008-05-20 06:58:21 +00003717
Ezio Melottic2077b02011-03-16 12:34:31 +02003718 Has to be called explicitly, (not used by the turtle-graphics classes)
Martin v. Löwis87184592008-06-04 06:29:55 +00003719 The docstring dictionary will be written to the Python script <filname>.py
3720 It is intended to serve as a template for translation of the docstrings
3721 into different languages.
Georg Brandl33cece02008-05-20 06:58:21 +00003722 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003723 docsdict = {}
Georg Brandl33cece02008-05-20 06:58:21 +00003724
Martin v. Löwis87184592008-06-04 06:29:55 +00003725 for methodname in _tg_screen_functions:
Martin v. Löwise563aa42008-09-29 22:09:07 +00003726 key = "_Screen."+methodname
Martin v. Löwis87184592008-06-04 06:29:55 +00003727 docsdict[key] = eval(key).__doc__
3728 for methodname in _tg_turtle_functions:
3729 key = "Turtle."+methodname
3730 docsdict[key] = eval(key).__doc__
Georg Brandl33cece02008-05-20 06:58:21 +00003731
Martin v. Löwis87184592008-06-04 06:29:55 +00003732 f = open("%s.py" % filename,"w")
3733 keys = sorted([x for x in docsdict.keys()
3734 if x.split('.')[1] not in _alias_list])
3735 f.write('docsdict = {\n\n')
3736 for key in keys[:-1]:
3737 f.write('%s :\n' % repr(key))
3738 f.write(' """%s\n""",\n\n' % docsdict[key])
3739 key = keys[-1]
3740 f.write('%s :\n' % repr(key))
3741 f.write(' """%s\n"""\n\n' % docsdict[key])
3742 f.write("}\n")
3743 f.close()
Georg Brandl33cece02008-05-20 06:58:21 +00003744
Martin v. Löwis87184592008-06-04 06:29:55 +00003745def read_docstrings(lang):
3746 """Read in docstrings from lang-specific docstring dictionary.
Georg Brandl33cece02008-05-20 06:58:21 +00003747
Martin v. Löwis87184592008-06-04 06:29:55 +00003748 Transfer docstrings, translated to lang, from a dictionary-file
3749 to the methods of classes Screen and Turtle and - in revised form -
3750 to the corresponding functions.
Georg Brandl33cece02008-05-20 06:58:21 +00003751 """
Martin v. Löwis87184592008-06-04 06:29:55 +00003752 modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
3753 module = __import__(modname)
3754 docsdict = module.docsdict
3755 for key in docsdict:
3756 #print key
3757 try:
3758 eval(key).im_func.__doc__ = docsdict[key]
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +03003759 except BaseException:
Martin v. Löwis87184592008-06-04 06:29:55 +00003760 print "Bad docstring-entry: %s" % key
Georg Brandl33cece02008-05-20 06:58:21 +00003761
Martin v. Löwis87184592008-06-04 06:29:55 +00003762_LANGUAGE = _CFG["language"]
Georg Brandl33cece02008-05-20 06:58:21 +00003763
Martin v. Löwis87184592008-06-04 06:29:55 +00003764try:
3765 if _LANGUAGE != "english":
3766 read_docstrings(_LANGUAGE)
3767except ImportError:
3768 print "Cannot find docsdict for", _LANGUAGE
Serhiy Storchaka77ccaaf2016-06-14 22:52:13 +03003769except BaseException:
Martin v. Löwis87184592008-06-04 06:29:55 +00003770 print ("Unknown Error when trying to import %s-docstring-dictionary" %
3771 _LANGUAGE)
3772
3773
3774def getmethparlist(ob):
3775 "Get strings describing the arguments for the given object"
3776 argText1 = argText2 = ""
3777 # bit of a hack for methods - turn it into a function
3778 # but we drop the "self" param.
3779 if type(ob)==types.MethodType:
3780 fob = ob.im_func
3781 argOffset = 1
3782 else:
3783 fob = ob
3784 argOffset = 0
3785 # Try and build one for Python defined functions
3786 if type(fob) in [types.FunctionType, types.LambdaType]:
3787 try:
3788 counter = fob.func_code.co_argcount
3789 items2 = list(fob.func_code.co_varnames[argOffset:counter])
3790 realArgs = fob.func_code.co_varnames[argOffset:counter]
3791 defaults = fob.func_defaults or []
3792 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
3793 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
3794 items1 = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
3795 if fob.func_code.co_flags & 0x4:
3796 items1.append("*"+fob.func_code.co_varnames[counter])
3797 items2.append("*"+fob.func_code.co_varnames[counter])
3798 counter += 1
3799 if fob.func_code.co_flags & 0x8:
3800 items1.append("**"+fob.func_code.co_varnames[counter])
3801 items2.append("**"+fob.func_code.co_varnames[counter])
3802 argText1 = ", ".join(items1)
3803 argText1 = "(%s)" % argText1
3804 argText2 = ", ".join(items2)
3805 argText2 = "(%s)" % argText2
3806 except:
3807 pass
3808 return argText1, argText2
3809
3810def _turtle_docrevise(docstr):
3811 """To reduce docstrings from RawTurtle class for functions
3812 """
3813 import re
3814 if docstr is None:
3815 return None
3816 turtlename = _CFG["exampleturtle"]
3817 newdocstr = docstr.replace("%s." % turtlename,"")
3818 parexp = re.compile(r' \(.+ %s\):' % turtlename)
3819 newdocstr = parexp.sub(":", newdocstr)
3820 return newdocstr
3821
3822def _screen_docrevise(docstr):
3823 """To reduce docstrings from TurtleScreen class for functions
3824 """
3825 import re
3826 if docstr is None:
3827 return None
3828 screenname = _CFG["examplescreen"]
3829 newdocstr = docstr.replace("%s." % screenname,"")
3830 parexp = re.compile(r' \(.+ %s\):' % screenname)
3831 newdocstr = parexp.sub(":", newdocstr)
3832 return newdocstr
3833
3834## The following mechanism makes all methods of RawTurtle and Turtle available
3835## as functions. So we can enhance, change, add, delete methods to these
3836## classes and do not need to change anything here.
3837
Serhiy Storchakacc49aa12015-02-22 17:22:53 +02003838__func_body = """\
3839def {name}{paramslist}:
3840 if {obj} is None:
3841 if not TurtleScreen._RUNNING:
3842 TurtleScreen._RUNNING = True
3843 raise Terminator
3844 {obj} = {init}
3845 try:
3846 return {obj}.{name}{argslist}
3847 except TK.TclError:
3848 if not TurtleScreen._RUNNING:
3849 TurtleScreen._RUNNING = True
3850 raise Terminator
3851 raise
3852"""
Martin v. Löwis87184592008-06-04 06:29:55 +00003853
Serhiy Storchakacc49aa12015-02-22 17:22:53 +02003854def _make_global_funcs(functions, cls, obj, init, docrevise):
3855 for methodname in functions:
3856 method = getattr(cls, methodname)
3857 pl1, pl2 = getmethparlist(method)
3858 if pl1 == "":
3859 print ">>>>>>", pl1, pl2
3860 continue
3861 defstr = __func_body.format(obj=obj, init=init, name=methodname,
3862 paramslist=pl1, argslist=pl2)
3863 exec defstr in globals()
3864 globals()[methodname].__doc__ = docrevise(method.__doc__)
Martin v. Löwis87184592008-06-04 06:29:55 +00003865
Serhiy Storchakacc49aa12015-02-22 17:22:53 +02003866_make_global_funcs(_tg_screen_functions, _Screen,
3867 'Turtle._screen', 'Screen()', _screen_docrevise)
3868_make_global_funcs(_tg_turtle_functions, Turtle,
3869 'Turtle._pen', 'Turtle()', _turtle_docrevise)
Martin v. Löwis87184592008-06-04 06:29:55 +00003870
3871
3872done = mainloop = TK.mainloop
Martin v. Löwis87184592008-06-04 06:29:55 +00003873
3874if __name__ == "__main__":
3875 def switchpen():
3876 if isdown():
3877 pu()
3878 else:
3879 pd()
3880
3881 def demo1():
3882 """Demo of old turtle.py - module"""
3883 reset()
3884 tracer(True)
3885 up()
3886 backward(100)
3887 down()
3888 # draw 3 squares; the last filled
3889 width(3)
3890 for i in range(3):
3891 if i == 2:
3892 fill(1)
3893 for _ in range(4):
3894 forward(20)
3895 left(90)
3896 if i == 2:
3897 color("maroon")
3898 fill(0)
3899 up()
3900 forward(30)
3901 down()
3902 width(1)
3903 color("black")
3904 # move out of the way
3905 tracer(False)
3906 up()
3907 right(90)
3908 forward(100)
3909 right(90)
3910 forward(100)
3911 right(180)
3912 down()
3913 # some text
3914 write("startstart", 1)
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05003915 write(u"start", 1)
Martin v. Löwis87184592008-06-04 06:29:55 +00003916 color("red")
3917 # staircase
3918 for i in range(5):
Georg Brandl33cece02008-05-20 06:58:21 +00003919 forward(20)
3920 left(90)
Martin v. Löwis87184592008-06-04 06:29:55 +00003921 forward(20)
3922 right(90)
3923 # filled staircase
3924 tracer(True)
3925 fill(1)
3926 for i in range(5):
3927 forward(20)
3928 left(90)
3929 forward(20)
3930 right(90)
3931 fill(0)
3932 # more text
Georg Brandl33cece02008-05-20 06:58:21 +00003933
Martin v. Löwis87184592008-06-04 06:29:55 +00003934 def demo2():
3935 """Demo of some new features."""
3936 speed(1)
3937 st()
3938 pensize(3)
3939 setheading(towards(0, 0))
3940 radius = distance(0, 0)/2.0
3941 rt(90)
3942 for _ in range(18):
3943 switchpen()
3944 circle(radius, 10)
3945 write("wait a moment...")
3946 while undobufferentries():
3947 undo()
3948 reset()
3949 lt(90)
3950 colormode(255)
3951 laenge = 10
3952 pencolor("green")
3953 pensize(3)
3954 lt(180)
3955 for i in range(-2, 16):
3956 if i > 0:
3957 begin_fill()
3958 fillcolor(255-15*i, 0, 15*i)
3959 for _ in range(3):
3960 fd(laenge)
3961 lt(120)
3962 laenge += 10
3963 lt(15)
3964 speed((speed()+1)%12)
3965 end_fill()
Georg Brandl33cece02008-05-20 06:58:21 +00003966
Martin v. Löwis87184592008-06-04 06:29:55 +00003967 lt(120)
3968 pu()
3969 fd(70)
3970 rt(30)
3971 pd()
3972 color("red","yellow")
3973 speed(0)
3974 fill(1)
3975 for _ in range(4):
3976 circle(50, 90)
3977 rt(90)
3978 fd(30)
3979 rt(90)
3980 fill(0)
3981 lt(90)
3982 pu()
3983 fd(30)
3984 pd()
3985 shape("turtle")
Georg Brandl33cece02008-05-20 06:58:21 +00003986
Martin v. Löwis87184592008-06-04 06:29:55 +00003987 tri = getturtle()
3988 tri.resizemode("auto")
3989 turtle = Turtle()
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05003990 turtle.resizemode(u"auto")
Martin v. Löwis87184592008-06-04 06:29:55 +00003991 turtle.shape("turtle")
3992 turtle.reset()
3993 turtle.left(90)
3994 turtle.speed(0)
3995 turtle.up()
3996 turtle.goto(280, 40)
3997 turtle.lt(30)
3998 turtle.down()
3999 turtle.speed(6)
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05004000 turtle.color("blue",u"orange")
Martin v. Löwis87184592008-06-04 06:29:55 +00004001 turtle.pensize(2)
4002 tri.speed(6)
Georg Brandl33cece02008-05-20 06:58:21 +00004003 setheading(towards(turtle))
Martin v. Löwis87184592008-06-04 06:29:55 +00004004 count = 1
4005 while tri.distance(turtle) > 4:
4006 turtle.fd(3.5)
4007 turtle.lt(0.6)
4008 tri.setheading(tri.towards(turtle))
4009 tri.fd(4)
4010 if count % 20 == 0:
4011 turtle.stamp()
4012 tri.stamp()
4013 switchpen()
4014 count += 1
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05004015 tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align=u"right")
Martin v. Löwis87184592008-06-04 06:29:55 +00004016 tri.pencolor("black")
Terry Jan Reedy09f4f252014-03-05 23:15:57 -05004017 tri.pencolor(u"red")
Georg Brandl33cece02008-05-20 06:58:21 +00004018
Martin v. Löwis87184592008-06-04 06:29:55 +00004019 def baba(xdummy, ydummy):
4020 clearscreen()
4021 bye()
Georg Brandl33cece02008-05-20 06:58:21 +00004022
Martin v. Löwis87184592008-06-04 06:29:55 +00004023 time.sleep(2)
Georg Brandl33cece02008-05-20 06:58:21 +00004024
Martin v. Löwis87184592008-06-04 06:29:55 +00004025 while undobufferentries():
4026 tri.undo()
4027 turtle.undo()
4028 tri.fd(50)
4029 tri.write(" Click me!", font = ("Courier", 12, "bold") )
4030 tri.onclick(baba, 1)
4031
4032 demo1()
Georg Brandl33cece02008-05-20 06:58:21 +00004033 demo2()
Martin v. Löwis87184592008-06-04 06:29:55 +00004034 exitonclick()