diff options
author | orivej <[email protected]> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/tools/python3/src/Lib/turtle.py | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/python3/src/Lib/turtle.py')
-rw-r--r-- | contrib/tools/python3/src/Lib/turtle.py | 8248 |
1 files changed, 4124 insertions, 4124 deletions
diff --git a/contrib/tools/python3/src/Lib/turtle.py b/contrib/tools/python3/src/Lib/turtle.py index 9c8f6ced250..9d5a3f4a886 100644 --- a/contrib/tools/python3/src/Lib/turtle.py +++ b/contrib/tools/python3/src/Lib/turtle.py @@ -1,4141 +1,4141 @@ -# -# turtle.py: a Tkinter based turtle graphics module for Python -# Version 1.1b - 4. 5. 2009 -# -# Copyright (C) 2006 - 2010 Gregor Lingl -# email: [email protected] -# -# This software is provided 'as-is', without any express or implied -# warranty. In no event will the authors be held liable for any damages -# arising from the use of this software. -# -# Permission is granted to anyone to use this software for any purpose, -# including commercial applications, and to alter it and redistribute it -# freely, subject to the following restrictions: -# -# 1. The origin of this software must not be misrepresented; you must not -# claim that you wrote the original software. If you use this software -# in a product, an acknowledgment in the product documentation would be -# appreciated but is not required. -# 2. Altered source versions must be plainly marked as such, and must not be -# misrepresented as being the original software. -# 3. This notice may not be removed or altered from any source distribution. - - -""" -Turtle graphics is a popular way for introducing programming to -kids. It was part of the original Logo programming language developed -by Wally Feurzig and Seymour Papert in 1966. - -Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it -the command turtle.forward(15), and it moves (on-screen!) 15 pixels in -the direction it is facing, drawing a line as it moves. Give it the -command turtle.right(25), and it rotates in-place 25 degrees clockwise. - -By combining together these and similar commands, intricate shapes and -pictures can easily be drawn. - ------ turtle.py - -This module is an extended reimplementation of turtle.py from the +# +# turtle.py: a Tkinter based turtle graphics module for Python +# Version 1.1b - 4. 5. 2009 +# +# Copyright (C) 2006 - 2010 Gregor Lingl +# email: [email protected] +# +# This software is provided 'as-is', without any express or implied +# warranty. In no event will the authors be held liable for any damages +# arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software +# in a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# 3. This notice may not be removed or altered from any source distribution. + + +""" +Turtle graphics is a popular way for introducing programming to +kids. It was part of the original Logo programming language developed +by Wally Feurzig and Seymour Papert in 1966. + +Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it +the command turtle.forward(15), and it moves (on-screen!) 15 pixels in +the direction it is facing, drawing a line as it moves. Give it the +command turtle.right(25), and it rotates in-place 25 degrees clockwise. + +By combining together these and similar commands, intricate shapes and +pictures can easily be drawn. + +----- turtle.py + +This module is an extended reimplementation of turtle.py from the Python standard distribution up to Python 2.5. (See: https://www.python.org) - -It tries to keep the merits of turtle.py and to be (nearly) 100% -compatible with it. This means in the first place to enable the -learning programmer to use all the commands, classes and methods -interactively when using the module from within IDLE run with -the -n switch. - -Roughly it has the following features added: - -- Better animation of the turtle movements, especially of turning the - turtle. So the turtles can more easily be used as a visual feedback - instrument by the (beginning) programmer. - -- Different turtle shapes, gif-images as turtle shapes, user defined - and user controllable turtle shapes, among them compound - (multicolored) shapes. Turtle shapes can be stretched and tilted, which - makes turtles very versatile geometrical objects. - -- Fine control over turtle movement and screen updates via delay(), - and enhanced tracer() and speed() methods. - -- Aliases for the most commonly used commands, like fd for forward etc., - following the early Logo traditions. This reduces the boring work of - typing long sequences of commands, which often occur in a natural way - when kids try to program fancy pictures on their first encounter with - turtle graphics. - -- Turtles now have an undo()-method with configurable undo-buffer. - -- Some simple commands/methods for creating event driven programs - (mouse-, key-, timer-events). Especially useful for programming games. - -- A scrollable Canvas class. The default scrollable Canvas can be - extended interactively as needed while playing around with the turtle(s). - -- A TurtleScreen class with methods controlling background color or - background image, window and canvas size and other properties of the - TurtleScreen. - -- There is a method, setworldcoordinates(), to install a user defined - coordinate-system for the TurtleScreen. - -- The implementation uses a 2-vector class named Vec2D, derived from tuple. - This class is public, so it can be imported by the application programmer, - which makes certain types of computations very natural and compact. - -- Appearance of the TurtleScreen and the Turtles at startup/import can be - configured by means of a turtle.cfg configuration file. - The default configuration mimics the appearance of the old turtle module. - -- If configured appropriately the module reads in docstrings from a docstring - dictionary in some different language, supplied separately and replaces - the English ones by those read in. There is a utility function - write_docstringdict() to write a dictionary with the original (English) - docstrings to disc, so it can serve as a template for translations. - -Behind the scenes there are some features included with possible -extensions in mind. These will be commented and documented elsewhere. - -""" - -_ver = "turtle 1.1b- - for Python 3.1 - 4. 5. 2009" - -# print(_ver) - -import tkinter as TK -import types -import math -import time -import inspect -import sys - -from os.path import isfile, split, join -from copy import deepcopy -from tkinter import simpledialog - -_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen', - 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D'] -_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye', - 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas', - 'getshapes', 'listen', 'mainloop', 'mode', 'numinput', - 'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer', - 'register_shape', 'resetscreen', 'screensize', 'setup', - 'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update', - 'window_height', 'window_width'] -_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk', - 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color', - 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd', - 'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly', - 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown', - 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd', - 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', - 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt', - 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle', - 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle', - 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', - 'turtlesize', 'undo', 'undobufferentries', 'up', 'width', - 'write', 'xcor', 'ycor'] -_tg_utilities = ['write_docstringdict', 'done'] - -__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + - _tg_utilities + ['Terminator']) # + _math_functions) - -_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', - 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', - 'turtlesize', 'up', 'width'] - -_CFG = {"width" : 0.5, # Screen - "height" : 0.75, - "canvwidth" : 400, - "canvheight": 300, - "leftright": None, - "topbottom": None, - "mode": "standard", # TurtleScreen - "colormode": 1.0, - "delay": 10, - "undobuffersize": 1000, # RawTurtle - "shape": "classic", - "pencolor" : "black", - "fillcolor" : "black", - "resizemode" : "noresize", - "visible" : True, - "language": "english", # docstrings - "exampleturtle": "turtle", - "examplescreen": "screen", - "title": "Python Turtle Graphics", - "using_IDLE": False - } - -def config_dict(filename): - """Convert content of config-file into dictionary.""" - with open(filename, "r") as f: - cfglines = f.readlines() - cfgdict = {} - for line in cfglines: - line = line.strip() - if not line or line.startswith("#"): - continue - try: - key, value = line.split("=") - except ValueError: - print("Bad line in config-file %s:\n%s" % (filename,line)) - continue - key = key.strip() - value = value.strip() - if value in ["True", "False", "None", "''", '""']: - value = eval(value) - else: - try: - if "." in value: - value = float(value) - else: - value = int(value) - except ValueError: - pass # value need not be converted - cfgdict[key] = value - return cfgdict - -def readconfig(cfgdict): - """Read config-files, change configuration-dict accordingly. - - If there is a turtle.cfg file in the current working directory, - read it from there. If this contains an importconfig-value, - say 'myway', construct filename turtle_mayway.cfg else use - turtle.cfg and read it from the import-directory, where - turtle.py is located. - Update configuration dictionary first according to config-file, - in the import directory, then according to config-file in the - current working directory. - If no config-file is found, the default configuration is used. - """ - default_cfg = "turtle.cfg" - cfgdict1 = {} - cfgdict2 = {} - if isfile(default_cfg): - cfgdict1 = config_dict(default_cfg) - if "importconfig" in cfgdict1: - default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"] - try: - head, tail = split(__file__) - cfg_file2 = join(head, default_cfg) - except Exception: - cfg_file2 = "" - if isfile(cfg_file2): - cfgdict2 = config_dict(cfg_file2) - _CFG.update(cfgdict2) - _CFG.update(cfgdict1) - -try: - readconfig(_CFG) -except Exception: - print ("No configfile read, reason unknown") - - -class Vec2D(tuple): - """A 2 dimensional vector class, used as a helper class - for implementing turtle graphics. - May be useful for turtle graphics programs also. - Derived from tuple, so a vector is a tuple! - - Provides (for a, b vectors, k number): - a+b vector addition - a-b vector subtraction - a*b inner product - k*a and a*k multiplication with scalar - |a| absolute value of a - a.rotate(angle) rotation - """ - def __new__(cls, x, y): - return tuple.__new__(cls, (x, y)) - def __add__(self, other): - return Vec2D(self[0]+other[0], self[1]+other[1]) - def __mul__(self, other): - if isinstance(other, Vec2D): - return self[0]*other[0]+self[1]*other[1] - return Vec2D(self[0]*other, self[1]*other) - def __rmul__(self, other): - if isinstance(other, int) or isinstance(other, float): - return Vec2D(self[0]*other, self[1]*other) + +It tries to keep the merits of turtle.py and to be (nearly) 100% +compatible with it. This means in the first place to enable the +learning programmer to use all the commands, classes and methods +interactively when using the module from within IDLE run with +the -n switch. + +Roughly it has the following features added: + +- Better animation of the turtle movements, especially of turning the + turtle. So the turtles can more easily be used as a visual feedback + instrument by the (beginning) programmer. + +- Different turtle shapes, gif-images as turtle shapes, user defined + and user controllable turtle shapes, among them compound + (multicolored) shapes. Turtle shapes can be stretched and tilted, which + makes turtles very versatile geometrical objects. + +- Fine control over turtle movement and screen updates via delay(), + and enhanced tracer() and speed() methods. + +- Aliases for the most commonly used commands, like fd for forward etc., + following the early Logo traditions. This reduces the boring work of + typing long sequences of commands, which often occur in a natural way + when kids try to program fancy pictures on their first encounter with + turtle graphics. + +- Turtles now have an undo()-method with configurable undo-buffer. + +- Some simple commands/methods for creating event driven programs + (mouse-, key-, timer-events). Especially useful for programming games. + +- A scrollable Canvas class. The default scrollable Canvas can be + extended interactively as needed while playing around with the turtle(s). + +- A TurtleScreen class with methods controlling background color or + background image, window and canvas size and other properties of the + TurtleScreen. + +- There is a method, setworldcoordinates(), to install a user defined + coordinate-system for the TurtleScreen. + +- The implementation uses a 2-vector class named Vec2D, derived from tuple. + This class is public, so it can be imported by the application programmer, + which makes certain types of computations very natural and compact. + +- Appearance of the TurtleScreen and the Turtles at startup/import can be + configured by means of a turtle.cfg configuration file. + The default configuration mimics the appearance of the old turtle module. + +- If configured appropriately the module reads in docstrings from a docstring + dictionary in some different language, supplied separately and replaces + the English ones by those read in. There is a utility function + write_docstringdict() to write a dictionary with the original (English) + docstrings to disc, so it can serve as a template for translations. + +Behind the scenes there are some features included with possible +extensions in mind. These will be commented and documented elsewhere. + +""" + +_ver = "turtle 1.1b- - for Python 3.1 - 4. 5. 2009" + +# print(_ver) + +import tkinter as TK +import types +import math +import time +import inspect +import sys + +from os.path import isfile, split, join +from copy import deepcopy +from tkinter import simpledialog + +_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen', + 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D'] +_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye', + 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas', + 'getshapes', 'listen', 'mainloop', 'mode', 'numinput', + 'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer', + 'register_shape', 'resetscreen', 'screensize', 'setup', + 'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update', + 'window_height', 'window_width'] +_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk', + 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color', + 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd', + 'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly', + 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown', + 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd', + 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', + 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt', + 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle', + 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle', + 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', + 'turtlesize', 'undo', 'undobufferentries', 'up', 'width', + 'write', 'xcor', 'ycor'] +_tg_utilities = ['write_docstringdict', 'done'] + +__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + + _tg_utilities + ['Terminator']) # + _math_functions) + +_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', + 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', + 'turtlesize', 'up', 'width'] + +_CFG = {"width" : 0.5, # Screen + "height" : 0.75, + "canvwidth" : 400, + "canvheight": 300, + "leftright": None, + "topbottom": None, + "mode": "standard", # TurtleScreen + "colormode": 1.0, + "delay": 10, + "undobuffersize": 1000, # RawTurtle + "shape": "classic", + "pencolor" : "black", + "fillcolor" : "black", + "resizemode" : "noresize", + "visible" : True, + "language": "english", # docstrings + "exampleturtle": "turtle", + "examplescreen": "screen", + "title": "Python Turtle Graphics", + "using_IDLE": False + } + +def config_dict(filename): + """Convert content of config-file into dictionary.""" + with open(filename, "r") as f: + cfglines = f.readlines() + cfgdict = {} + for line in cfglines: + line = line.strip() + if not line or line.startswith("#"): + continue + try: + key, value = line.split("=") + except ValueError: + print("Bad line in config-file %s:\n%s" % (filename,line)) + continue + key = key.strip() + value = value.strip() + if value in ["True", "False", "None", "''", '""']: + value = eval(value) + else: + try: + if "." in value: + value = float(value) + else: + value = int(value) + except ValueError: + pass # value need not be converted + cfgdict[key] = value + return cfgdict + +def readconfig(cfgdict): + """Read config-files, change configuration-dict accordingly. + + If there is a turtle.cfg file in the current working directory, + read it from there. If this contains an importconfig-value, + say 'myway', construct filename turtle_mayway.cfg else use + turtle.cfg and read it from the import-directory, where + turtle.py is located. + Update configuration dictionary first according to config-file, + in the import directory, then according to config-file in the + current working directory. + If no config-file is found, the default configuration is used. + """ + default_cfg = "turtle.cfg" + cfgdict1 = {} + cfgdict2 = {} + if isfile(default_cfg): + cfgdict1 = config_dict(default_cfg) + if "importconfig" in cfgdict1: + default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"] + try: + head, tail = split(__file__) + cfg_file2 = join(head, default_cfg) + except Exception: + cfg_file2 = "" + if isfile(cfg_file2): + cfgdict2 = config_dict(cfg_file2) + _CFG.update(cfgdict2) + _CFG.update(cfgdict1) + +try: + readconfig(_CFG) +except Exception: + print ("No configfile read, reason unknown") + + +class Vec2D(tuple): + """A 2 dimensional vector class, used as a helper class + for implementing turtle graphics. + May be useful for turtle graphics programs also. + Derived from tuple, so a vector is a tuple! + + Provides (for a, b vectors, k number): + a+b vector addition + a-b vector subtraction + a*b inner product + k*a and a*k multiplication with scalar + |a| absolute value of a + a.rotate(angle) rotation + """ + def __new__(cls, x, y): + return tuple.__new__(cls, (x, y)) + def __add__(self, other): + return Vec2D(self[0]+other[0], self[1]+other[1]) + def __mul__(self, other): + if isinstance(other, Vec2D): + return self[0]*other[0]+self[1]*other[1] + return Vec2D(self[0]*other, self[1]*other) + def __rmul__(self, other): + if isinstance(other, int) or isinstance(other, float): + return Vec2D(self[0]*other, self[1]*other) return NotImplemented - def __sub__(self, other): - return Vec2D(self[0]-other[0], self[1]-other[1]) - def __neg__(self): - return Vec2D(-self[0], -self[1]) - def __abs__(self): - return (self[0]**2 + self[1]**2)**0.5 - def rotate(self, angle): - """rotate self counterclockwise by angle - """ - perp = Vec2D(-self[1], self[0]) - angle = angle * math.pi / 180.0 - c, s = math.cos(angle), math.sin(angle) - return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s) - def __getnewargs__(self): - return (self[0], self[1]) - def __repr__(self): - return "(%.2f,%.2f)" % self - - -############################################################################## -### From here up to line : Tkinter - Interface for turtle.py ### -### May be replaced by an interface to some different graphics toolkit ### -############################################################################## - -## helper functions for Scrolled Canvas, to forward Canvas-methods -## to ScrolledCanvas class - -def __methodDict(cls, _dict): - """helper function for Scrolled Canvas""" - baseList = list(cls.__bases__) - baseList.reverse() - for _super in baseList: - __methodDict(_super, _dict) - for key, value in cls.__dict__.items(): - if type(value) == types.FunctionType: - _dict[key] = value - -def __methods(cls): - """helper function for Scrolled Canvas""" - _dict = {} - __methodDict(cls, _dict) - return _dict.keys() - -__stringBody = ( - 'def %(method)s(self, *args, **kw): return ' + - 'self.%(attribute)s.%(method)s(*args, **kw)') - -def __forwardmethods(fromClass, toClass, toPart, exclude = ()): - ### MANY CHANGES ### - _dict_1 = {} - __methodDict(toClass, _dict_1) - _dict = {} - mfc = __methods(fromClass) - for ex in _dict_1.keys(): - if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc: - pass - else: - _dict[ex] = _dict_1[ex] - - for method, func in _dict.items(): - d = {'method': method, 'func': func} - if isinstance(toPart, str): - execString = \ - __stringBody % {'method' : method, 'attribute' : toPart} - exec(execString, d) - setattr(fromClass, method, d[method]) ### NEWU! - - -class ScrolledCanvas(TK.Frame): - """Modeled after the scrolled canvas class from Grayons's Tkinter book. - - Used as the default canvas, which pops up automatically when - using turtle graphics functions or the Turtle class. - """ - def __init__(self, master, width=500, height=350, - canvwidth=600, canvheight=500): - TK.Frame.__init__(self, master, width=width, height=height) - self._rootwindow = self.winfo_toplevel() - self.width, self.height = width, height - self.canvwidth, self.canvheight = canvwidth, canvheight - self.bg = "white" - self._canvas = TK.Canvas(master, width=width, height=height, - bg=self.bg, relief=TK.SUNKEN, borderwidth=2) - self.hscroll = TK.Scrollbar(master, command=self._canvas.xview, - orient=TK.HORIZONTAL) - self.vscroll = TK.Scrollbar(master, command=self._canvas.yview) - self._canvas.configure(xscrollcommand=self.hscroll.set, - yscrollcommand=self.vscroll.set) - self.rowconfigure(0, weight=1, minsize=0) - self.columnconfigure(0, weight=1, minsize=0) - self._canvas.grid(padx=1, in_ = self, pady=1, row=0, - column=0, rowspan=1, columnspan=1, sticky='news') - self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, - column=1, rowspan=1, columnspan=1, sticky='news') - self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, - column=0, rowspan=1, columnspan=1, sticky='news') - self.reset() - self._rootwindow.bind('<Configure>', self.onResize) - - def reset(self, canvwidth=None, canvheight=None, bg = None): - """Adjust canvas and scrollbars according to given canvas size.""" - if canvwidth: - self.canvwidth = canvwidth - if canvheight: - self.canvheight = canvheight - if bg: - self.bg = bg - self._canvas.config(bg=bg, - scrollregion=(-self.canvwidth//2, -self.canvheight//2, - self.canvwidth//2, self.canvheight//2)) - self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) / - self.canvwidth) - self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) / - self.canvheight) - self.adjustScrolls() - - - def adjustScrolls(self): - """ Adjust scrollbars according to window- and canvas-size. - """ - cwidth = self._canvas.winfo_width() - cheight = self._canvas.winfo_height() - self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) - self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) - if cwidth < self.canvwidth or cheight < self.canvheight: - self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, - column=0, rowspan=1, columnspan=1, sticky='news') - self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, - column=1, rowspan=1, columnspan=1, sticky='news') - else: - self.hscroll.grid_forget() - self.vscroll.grid_forget() - - def onResize(self, event): - """self-explanatory""" - self.adjustScrolls() - - def bbox(self, *args): - """ 'forward' method, which canvas itself has inherited... - """ - return self._canvas.bbox(*args) - - def cget(self, *args, **kwargs): - """ 'forward' method, which canvas itself has inherited... - """ - return self._canvas.cget(*args, **kwargs) - - def config(self, *args, **kwargs): - """ 'forward' method, which canvas itself has inherited... - """ - self._canvas.config(*args, **kwargs) - - def bind(self, *args, **kwargs): - """ 'forward' method, which canvas itself has inherited... - """ - self._canvas.bind(*args, **kwargs) - - def unbind(self, *args, **kwargs): - """ 'forward' method, which canvas itself has inherited... - """ - self._canvas.unbind(*args, **kwargs) - - def focus_force(self): - """ 'forward' method, which canvas itself has inherited... - """ - self._canvas.focus_force() - -__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas') - - -class _Root(TK.Tk): - """Root class for Screen based on Tkinter.""" - def __init__(self): - TK.Tk.__init__(self) - - def setupcanvas(self, width, height, cwidth, cheight): - self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight) - self._canvas.pack(expand=1, fill="both") - - def _getcanvas(self): - return self._canvas - - def set_geometry(self, width, height, startx, starty): - self.geometry("%dx%d%+d%+d"%(width, height, startx, starty)) - - def ondestroy(self, destroy): - self.wm_protocol("WM_DELETE_WINDOW", destroy) - - def win_width(self): - return self.winfo_screenwidth() - - def win_height(self): - return self.winfo_screenheight() - -Canvas = TK.Canvas - - -class TurtleScreenBase(object): - """Provide the basic graphics functionality. - Interface between Tkinter and turtle.py. - - To port turtle.py to some different graphics toolkit - a corresponding TurtleScreenBase class has to be implemented. - """ - + def __sub__(self, other): + return Vec2D(self[0]-other[0], self[1]-other[1]) + def __neg__(self): + return Vec2D(-self[0], -self[1]) + def __abs__(self): + return (self[0]**2 + self[1]**2)**0.5 + def rotate(self, angle): + """rotate self counterclockwise by angle + """ + perp = Vec2D(-self[1], self[0]) + angle = angle * math.pi / 180.0 + c, s = math.cos(angle), math.sin(angle) + return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s) + def __getnewargs__(self): + return (self[0], self[1]) + def __repr__(self): + return "(%.2f,%.2f)" % self + + +############################################################################## +### From here up to line : Tkinter - Interface for turtle.py ### +### May be replaced by an interface to some different graphics toolkit ### +############################################################################## + +## helper functions for Scrolled Canvas, to forward Canvas-methods +## to ScrolledCanvas class + +def __methodDict(cls, _dict): + """helper function for Scrolled Canvas""" + baseList = list(cls.__bases__) + baseList.reverse() + for _super in baseList: + __methodDict(_super, _dict) + for key, value in cls.__dict__.items(): + if type(value) == types.FunctionType: + _dict[key] = value + +def __methods(cls): + """helper function for Scrolled Canvas""" + _dict = {} + __methodDict(cls, _dict) + return _dict.keys() + +__stringBody = ( + 'def %(method)s(self, *args, **kw): return ' + + 'self.%(attribute)s.%(method)s(*args, **kw)') + +def __forwardmethods(fromClass, toClass, toPart, exclude = ()): + ### MANY CHANGES ### + _dict_1 = {} + __methodDict(toClass, _dict_1) + _dict = {} + mfc = __methods(fromClass) + for ex in _dict_1.keys(): + if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc: + pass + else: + _dict[ex] = _dict_1[ex] + + for method, func in _dict.items(): + d = {'method': method, 'func': func} + if isinstance(toPart, str): + execString = \ + __stringBody % {'method' : method, 'attribute' : toPart} + exec(execString, d) + setattr(fromClass, method, d[method]) ### NEWU! + + +class ScrolledCanvas(TK.Frame): + """Modeled after the scrolled canvas class from Grayons's Tkinter book. + + Used as the default canvas, which pops up automatically when + using turtle graphics functions or the Turtle class. + """ + def __init__(self, master, width=500, height=350, + canvwidth=600, canvheight=500): + TK.Frame.__init__(self, master, width=width, height=height) + self._rootwindow = self.winfo_toplevel() + self.width, self.height = width, height + self.canvwidth, self.canvheight = canvwidth, canvheight + self.bg = "white" + self._canvas = TK.Canvas(master, width=width, height=height, + bg=self.bg, relief=TK.SUNKEN, borderwidth=2) + self.hscroll = TK.Scrollbar(master, command=self._canvas.xview, + orient=TK.HORIZONTAL) + self.vscroll = TK.Scrollbar(master, command=self._canvas.yview) + self._canvas.configure(xscrollcommand=self.hscroll.set, + yscrollcommand=self.vscroll.set) + self.rowconfigure(0, weight=1, minsize=0) + self.columnconfigure(0, weight=1, minsize=0) + self._canvas.grid(padx=1, in_ = self, pady=1, row=0, + column=0, rowspan=1, columnspan=1, sticky='news') + self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, + column=1, rowspan=1, columnspan=1, sticky='news') + self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, + column=0, rowspan=1, columnspan=1, sticky='news') + self.reset() + self._rootwindow.bind('<Configure>', self.onResize) + + def reset(self, canvwidth=None, canvheight=None, bg = None): + """Adjust canvas and scrollbars according to given canvas size.""" + if canvwidth: + self.canvwidth = canvwidth + if canvheight: + self.canvheight = canvheight + if bg: + self.bg = bg + self._canvas.config(bg=bg, + scrollregion=(-self.canvwidth//2, -self.canvheight//2, + self.canvwidth//2, self.canvheight//2)) + self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) / + self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) / + self.canvheight) + self.adjustScrolls() + + + def adjustScrolls(self): + """ Adjust scrollbars according to window- and canvas-size. + """ + cwidth = self._canvas.winfo_width() + cheight = self._canvas.winfo_height() + self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) + if cwidth < self.canvwidth or cheight < self.canvheight: + self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, + column=0, rowspan=1, columnspan=1, sticky='news') + self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, + column=1, rowspan=1, columnspan=1, sticky='news') + else: + self.hscroll.grid_forget() + self.vscroll.grid_forget() + + def onResize(self, event): + """self-explanatory""" + self.adjustScrolls() + + def bbox(self, *args): + """ 'forward' method, which canvas itself has inherited... + """ + return self._canvas.bbox(*args) + + def cget(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + return self._canvas.cget(*args, **kwargs) + + def config(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.config(*args, **kwargs) + + def bind(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.bind(*args, **kwargs) + + def unbind(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.unbind(*args, **kwargs) + + def focus_force(self): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.focus_force() + +__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas') + + +class _Root(TK.Tk): + """Root class for Screen based on Tkinter.""" + def __init__(self): + TK.Tk.__init__(self) + + def setupcanvas(self, width, height, cwidth, cheight): + self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight) + self._canvas.pack(expand=1, fill="both") + + def _getcanvas(self): + return self._canvas + + def set_geometry(self, width, height, startx, starty): + self.geometry("%dx%d%+d%+d"%(width, height, startx, starty)) + + def ondestroy(self, destroy): + self.wm_protocol("WM_DELETE_WINDOW", destroy) + + def win_width(self): + return self.winfo_screenwidth() + + def win_height(self): + return self.winfo_screenheight() + +Canvas = TK.Canvas + + +class TurtleScreenBase(object): + """Provide the basic graphics functionality. + Interface between Tkinter and turtle.py. + + To port turtle.py to some different graphics toolkit + a corresponding TurtleScreenBase class has to be implemented. + """ + def _blankimage(self): - """return a blank image object - """ + """return a blank image object + """ img = TK.PhotoImage(width=1, height=1, master=self.cv) - img.blank() - return img - + img.blank() + return img + def _image(self, filename): - """return an image object containing the - imagedata from a gif-file named filename. - """ + """return an image object containing the + imagedata from a gif-file named filename. + """ return TK.PhotoImage(file=filename, master=self.cv) - - def __init__(self, cv): - self.cv = cv - if isinstance(cv, ScrolledCanvas): - w = self.cv.canvwidth - h = self.cv.canvheight - else: # expected: ordinary TK.Canvas - w = int(self.cv.cget("width")) - h = int(self.cv.cget("height")) - self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 )) - self.canvwidth = w - self.canvheight = h - self.xscale = self.yscale = 1.0 - - def _createpoly(self): - """Create an invisible polygon item on canvas self.cv) - """ - return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="") - - def _drawpoly(self, polyitem, coordlist, fill=None, - outline=None, width=None, top=False): - """Configure polygonitem polyitem according to provided - arguments: - coordlist is sequence of coordinates - fill is filling color - outline is outline color - top is a boolean value, which specifies if polyitem - will be put on top of the canvas' displaylist so it - will not be covered by other items. - """ - cl = [] - for x, y in coordlist: - cl.append(x * self.xscale) - cl.append(-y * self.yscale) - self.cv.coords(polyitem, *cl) - if fill is not None: - self.cv.itemconfigure(polyitem, fill=fill) - if outline is not None: - self.cv.itemconfigure(polyitem, outline=outline) - if width is not None: - self.cv.itemconfigure(polyitem, width=width) - if top: - self.cv.tag_raise(polyitem) - - def _createline(self): - """Create an invisible line item on canvas self.cv) - """ - return self.cv.create_line(0, 0, 0, 0, fill="", width=2, - capstyle = TK.ROUND) - - def _drawline(self, lineitem, coordlist=None, - fill=None, width=None, top=False): - """Configure lineitem according to provided arguments: - coordlist is sequence of coordinates - fill is drawing color - width is width of drawn line. - top is a boolean value, which specifies if polyitem - will be put on top of the canvas' displaylist so it - will not be covered by other items. - """ - if coordlist is not None: - cl = [] - for x, y in coordlist: - cl.append(x * self.xscale) - cl.append(-y * self.yscale) - self.cv.coords(lineitem, *cl) - if fill is not None: - self.cv.itemconfigure(lineitem, fill=fill) - if width is not None: - self.cv.itemconfigure(lineitem, width=width) - if top: - self.cv.tag_raise(lineitem) - - def _delete(self, item): - """Delete graphics item from canvas. - If item is"all" delete all graphics items. - """ - self.cv.delete(item) - - def _update(self): - """Redraw graphics items on canvas - """ - self.cv.update() - - def _delay(self, delay): - """Delay subsequent canvas actions for delay ms.""" - self.cv.after(delay) - - def _iscolorstring(self, color): - """Check if the string color is a legal Tkinter color string. - """ - try: - rgb = self.cv.winfo_rgb(color) - ok = True - except TK.TclError: - ok = False - return ok - - def _bgcolor(self, color=None): - """Set canvas' backgroundcolor if color is not None, - else return backgroundcolor.""" - if color is not None: - self.cv.config(bg = color) - self._update() - else: - return self.cv.cget("bg") - - def _write(self, pos, txt, align, font, pencolor): - """Write txt at pos in canvas with specified font - and color. - Return text item and x-coord of right bottom corner - of text's bounding box.""" - x, y = pos - x = x * self.xscale - y = y * self.yscale - anchor = {"left":"sw", "center":"s", "right":"se" } - item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align], - fill = pencolor, font = font) - x0, y0, x1, y1 = self.cv.bbox(item) - self.cv.update() - return item, x1-1 - -## def _dot(self, pos, size, color): -## """may be implemented for some other graphics toolkit""" - - def _onclick(self, item, fun, num=1, add=None): - """Bind fun to mouse-click event on turtle. - fun must be a function with two arguments, the coordinates - of the clicked point on the canvas. - num, the number of the mouse-button defaults to 1 - """ - if fun is None: - self.cv.tag_unbind(item, "<Button-%s>" % num) - else: - def eventfun(event): - x, y = (self.cv.canvasx(event.x)/self.xscale, - -self.cv.canvasy(event.y)/self.yscale) - fun(x, y) - self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add) - - def _onrelease(self, item, fun, num=1, add=None): - """Bind fun to mouse-button-release event on turtle. - fun must be a function with two arguments, the coordinates - of the point on the canvas where mouse button is released. - num, the number of the mouse-button defaults to 1 - - If a turtle is clicked, first _onclick-event will be performed, - then _onscreensclick-event. - """ - if fun is None: - self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num) - else: - def eventfun(event): - x, y = (self.cv.canvasx(event.x)/self.xscale, - -self.cv.canvasy(event.y)/self.yscale) - fun(x, y) - self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num, - eventfun, add) - - def _ondrag(self, item, fun, num=1, add=None): - """Bind fun to mouse-move-event (with pressed mouse button) on turtle. - fun must be a function with two arguments, the coordinates of the - actual mouse position on the canvas. - num, the number of the mouse-button defaults to 1 - - Every sequence of mouse-move-events on a turtle is preceded by a - mouse-click event on that turtle. - """ - if fun is None: - self.cv.tag_unbind(item, "<Button%s-Motion>" % num) - else: - def eventfun(event): - try: - x, y = (self.cv.canvasx(event.x)/self.xscale, - -self.cv.canvasy(event.y)/self.yscale) - fun(x, y) - except Exception: - pass - self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add) - - def _onscreenclick(self, fun, num=1, add=None): - """Bind fun to mouse-click event on canvas. - fun must be a function with two arguments, the coordinates - of the clicked point on the canvas. - num, the number of the mouse-button defaults to 1 - - If a turtle is clicked, first _onclick-event will be performed, - then _onscreensclick-event. - """ - if fun is None: - self.cv.unbind("<Button-%s>" % num) - else: - def eventfun(event): - x, y = (self.cv.canvasx(event.x)/self.xscale, - -self.cv.canvasy(event.y)/self.yscale) - fun(x, y) - self.cv.bind("<Button-%s>" % num, eventfun, add) - - def _onkeyrelease(self, fun, key): - """Bind fun to key-release event of key. - Canvas must have focus. See method listen - """ - if fun is None: - self.cv.unbind("<KeyRelease-%s>" % key, None) - else: - def eventfun(event): - fun() - self.cv.bind("<KeyRelease-%s>" % key, eventfun) - - def _onkeypress(self, fun, key=None): - """If key is given, bind fun to key-press event of key. - Otherwise bind fun to any key-press. - Canvas must have focus. See method listen. - """ - if fun is None: - if key is None: - self.cv.unbind("<KeyPress>", None) - else: - self.cv.unbind("<KeyPress-%s>" % key, None) - else: - def eventfun(event): - fun() - if key is None: - self.cv.bind("<KeyPress>", eventfun) - else: - self.cv.bind("<KeyPress-%s>" % key, eventfun) - - def _listen(self): - """Set focus on canvas (in order to collect key-events) - """ - self.cv.focus_force() - - def _ontimer(self, fun, t): - """Install a timer, which calls fun after t milliseconds. - """ - if t == 0: - self.cv.after_idle(fun) - else: - self.cv.after(t, fun) - - def _createimage(self, image): - """Create and return image item on canvas. - """ - return self.cv.create_image(0, 0, image=image) - - def _drawimage(self, item, pos, image): - """Configure image item as to draw image object - at position (x,y) on canvas) - """ - x, y = pos - self.cv.coords(item, (x * self.xscale, -y * self.yscale)) - self.cv.itemconfig(item, image=image) - - def _setbgpic(self, item, image): - """Configure image item as to draw image object - at center of canvas. Set item to the first item - in the displaylist, so it will be drawn below - any other item .""" - self.cv.itemconfig(item, image=image) - self.cv.tag_lower(item) - - def _type(self, item): - """Return 'line' or 'polygon' or 'image' depending on - type of item. - """ - return self.cv.type(item) - - def _pointlist(self, item): - """returns list of coordinate-pairs of points of item - Example (for insiders): - >>> from turtle import * - >>> getscreen()._pointlist(getturtle().turtle._item) - [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982), - (9.9999999999999982, 0.0)] - >>> """ - cl = self.cv.coords(item) - pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)] - return pl - - def _setscrollregion(self, srx1, sry1, srx2, sry2): - self.cv.config(scrollregion=(srx1, sry1, srx2, sry2)) - - def _rescale(self, xscalefactor, yscalefactor): - items = self.cv.find_all() - for item in items: - coordinates = list(self.cv.coords(item)) - newcoordlist = [] - while coordinates: - x, y = coordinates[:2] - newcoordlist.append(x * xscalefactor) - newcoordlist.append(y * yscalefactor) - coordinates = coordinates[2:] - self.cv.coords(item, *newcoordlist) - - def _resize(self, canvwidth=None, canvheight=None, bg=None): - """Resize the canvas the turtles are drawing on. Does - not alter the drawing window. - """ - # needs amendment - if not isinstance(self.cv, ScrolledCanvas): - return self.canvwidth, self.canvheight - if canvwidth is canvheight is bg is None: - return self.cv.canvwidth, self.cv.canvheight - if canvwidth is not None: - self.canvwidth = canvwidth - if canvheight is not None: - self.canvheight = canvheight - self.cv.reset(canvwidth, canvheight, bg) - - def _window_size(self): - """ Return the width and height of the turtle window. - """ - width = self.cv.winfo_width() - if width <= 1: # the window isn't managed by a geometry manager - width = self.cv['width'] - height = self.cv.winfo_height() - if height <= 1: # the window isn't managed by a geometry manager - height = self.cv['height'] - return width, height - - def mainloop(self): - """Starts event loop - calling Tkinter's mainloop function. - - No argument. - - Must be last statement in a turtle graphics program. - Must NOT be used if a script is run from within IDLE in -n mode - (No subprocess) - for interactive use of turtle graphics. - - Example (for a TurtleScreen instance named screen): - >>> screen.mainloop() - - """ + + def __init__(self, cv): + self.cv = cv + if isinstance(cv, ScrolledCanvas): + w = self.cv.canvwidth + h = self.cv.canvheight + else: # expected: ordinary TK.Canvas + w = int(self.cv.cget("width")) + h = int(self.cv.cget("height")) + self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 )) + self.canvwidth = w + self.canvheight = h + self.xscale = self.yscale = 1.0 + + def _createpoly(self): + """Create an invisible polygon item on canvas self.cv) + """ + return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="") + + def _drawpoly(self, polyitem, coordlist, fill=None, + outline=None, width=None, top=False): + """Configure polygonitem polyitem according to provided + arguments: + coordlist is sequence of coordinates + fill is filling color + outline is outline color + top is a boolean value, which specifies if polyitem + will be put on top of the canvas' displaylist so it + will not be covered by other items. + """ + cl = [] + for x, y in coordlist: + cl.append(x * self.xscale) + cl.append(-y * self.yscale) + self.cv.coords(polyitem, *cl) + if fill is not None: + self.cv.itemconfigure(polyitem, fill=fill) + if outline is not None: + self.cv.itemconfigure(polyitem, outline=outline) + if width is not None: + self.cv.itemconfigure(polyitem, width=width) + if top: + self.cv.tag_raise(polyitem) + + def _createline(self): + """Create an invisible line item on canvas self.cv) + """ + return self.cv.create_line(0, 0, 0, 0, fill="", width=2, + capstyle = TK.ROUND) + + def _drawline(self, lineitem, coordlist=None, + fill=None, width=None, top=False): + """Configure lineitem according to provided arguments: + coordlist is sequence of coordinates + fill is drawing color + width is width of drawn line. + top is a boolean value, which specifies if polyitem + will be put on top of the canvas' displaylist so it + will not be covered by other items. + """ + if coordlist is not None: + cl = [] + for x, y in coordlist: + cl.append(x * self.xscale) + cl.append(-y * self.yscale) + self.cv.coords(lineitem, *cl) + if fill is not None: + self.cv.itemconfigure(lineitem, fill=fill) + if width is not None: + self.cv.itemconfigure(lineitem, width=width) + if top: + self.cv.tag_raise(lineitem) + + def _delete(self, item): + """Delete graphics item from canvas. + If item is"all" delete all graphics items. + """ + self.cv.delete(item) + + def _update(self): + """Redraw graphics items on canvas + """ + self.cv.update() + + def _delay(self, delay): + """Delay subsequent canvas actions for delay ms.""" + self.cv.after(delay) + + def _iscolorstring(self, color): + """Check if the string color is a legal Tkinter color string. + """ + try: + rgb = self.cv.winfo_rgb(color) + ok = True + except TK.TclError: + ok = False + return ok + + def _bgcolor(self, color=None): + """Set canvas' backgroundcolor if color is not None, + else return backgroundcolor.""" + if color is not None: + self.cv.config(bg = color) + self._update() + else: + return self.cv.cget("bg") + + def _write(self, pos, txt, align, font, pencolor): + """Write txt at pos in canvas with specified font + and color. + Return text item and x-coord of right bottom corner + of text's bounding box.""" + x, y = pos + x = x * self.xscale + y = y * self.yscale + anchor = {"left":"sw", "center":"s", "right":"se" } + item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align], + fill = pencolor, font = font) + x0, y0, x1, y1 = self.cv.bbox(item) + self.cv.update() + return item, x1-1 + +## def _dot(self, pos, size, color): +## """may be implemented for some other graphics toolkit""" + + def _onclick(self, item, fun, num=1, add=None): + """Bind fun to mouse-click event on turtle. + fun must be a function with two arguments, the coordinates + of the clicked point on the canvas. + num, the number of the mouse-button defaults to 1 + """ + if fun is None: + self.cv.tag_unbind(item, "<Button-%s>" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add) + + def _onrelease(self, item, fun, num=1, add=None): + """Bind fun to mouse-button-release event on turtle. + fun must be a function with two arguments, the coordinates + of the point on the canvas where mouse button is released. + num, the number of the mouse-button defaults to 1 + + If a turtle is clicked, first _onclick-event will be performed, + then _onscreensclick-event. + """ + if fun is None: + self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num, + eventfun, add) + + def _ondrag(self, item, fun, num=1, add=None): + """Bind fun to mouse-move-event (with pressed mouse button) on turtle. + fun must be a function with two arguments, the coordinates of the + actual mouse position on the canvas. + num, the number of the mouse-button defaults to 1 + + Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + """ + if fun is None: + self.cv.tag_unbind(item, "<Button%s-Motion>" % num) + else: + def eventfun(event): + try: + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + except Exception: + pass + self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add) + + def _onscreenclick(self, fun, num=1, add=None): + """Bind fun to mouse-click event on canvas. + fun must be a function with two arguments, the coordinates + of the clicked point on the canvas. + num, the number of the mouse-button defaults to 1 + + If a turtle is clicked, first _onclick-event will be performed, + then _onscreensclick-event. + """ + if fun is None: + self.cv.unbind("<Button-%s>" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.bind("<Button-%s>" % num, eventfun, add) + + def _onkeyrelease(self, fun, key): + """Bind fun to key-release event of key. + Canvas must have focus. See method listen + """ + if fun is None: + self.cv.unbind("<KeyRelease-%s>" % key, None) + else: + def eventfun(event): + fun() + self.cv.bind("<KeyRelease-%s>" % key, eventfun) + + def _onkeypress(self, fun, key=None): + """If key is given, bind fun to key-press event of key. + Otherwise bind fun to any key-press. + Canvas must have focus. See method listen. + """ + if fun is None: + if key is None: + self.cv.unbind("<KeyPress>", None) + else: + self.cv.unbind("<KeyPress-%s>" % key, None) + else: + def eventfun(event): + fun() + if key is None: + self.cv.bind("<KeyPress>", eventfun) + else: + self.cv.bind("<KeyPress-%s>" % key, eventfun) + + def _listen(self): + """Set focus on canvas (in order to collect key-events) + """ + self.cv.focus_force() + + def _ontimer(self, fun, t): + """Install a timer, which calls fun after t milliseconds. + """ + if t == 0: + self.cv.after_idle(fun) + else: + self.cv.after(t, fun) + + def _createimage(self, image): + """Create and return image item on canvas. + """ + return self.cv.create_image(0, 0, image=image) + + def _drawimage(self, item, pos, image): + """Configure image item as to draw image object + at position (x,y) on canvas) + """ + x, y = pos + self.cv.coords(item, (x * self.xscale, -y * self.yscale)) + self.cv.itemconfig(item, image=image) + + def _setbgpic(self, item, image): + """Configure image item as to draw image object + at center of canvas. Set item to the first item + in the displaylist, so it will be drawn below + any other item .""" + self.cv.itemconfig(item, image=image) + self.cv.tag_lower(item) + + def _type(self, item): + """Return 'line' or 'polygon' or 'image' depending on + type of item. + """ + return self.cv.type(item) + + def _pointlist(self, item): + """returns list of coordinate-pairs of points of item + Example (for insiders): + >>> from turtle import * + >>> getscreen()._pointlist(getturtle().turtle._item) + [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982), + (9.9999999999999982, 0.0)] + >>> """ + cl = self.cv.coords(item) + pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)] + return pl + + def _setscrollregion(self, srx1, sry1, srx2, sry2): + self.cv.config(scrollregion=(srx1, sry1, srx2, sry2)) + + def _rescale(self, xscalefactor, yscalefactor): + items = self.cv.find_all() + for item in items: + coordinates = list(self.cv.coords(item)) + newcoordlist = [] + while coordinates: + x, y = coordinates[:2] + newcoordlist.append(x * xscalefactor) + newcoordlist.append(y * yscalefactor) + coordinates = coordinates[2:] + self.cv.coords(item, *newcoordlist) + + def _resize(self, canvwidth=None, canvheight=None, bg=None): + """Resize the canvas the turtles are drawing on. Does + not alter the drawing window. + """ + # needs amendment + if not isinstance(self.cv, ScrolledCanvas): + return self.canvwidth, self.canvheight + if canvwidth is canvheight is bg is None: + return self.cv.canvwidth, self.cv.canvheight + if canvwidth is not None: + self.canvwidth = canvwidth + if canvheight is not None: + self.canvheight = canvheight + self.cv.reset(canvwidth, canvheight, bg) + + def _window_size(self): + """ Return the width and height of the turtle window. + """ + width = self.cv.winfo_width() + if width <= 1: # the window isn't managed by a geometry manager + width = self.cv['width'] + height = self.cv.winfo_height() + if height <= 1: # the window isn't managed by a geometry manager + height = self.cv['height'] + return width, height + + def mainloop(self): + """Starts event loop - calling Tkinter's mainloop function. + + No argument. + + Must be last statement in a turtle graphics program. + Must NOT be used if a script is run from within IDLE in -n mode + (No subprocess) - for interactive use of turtle graphics. + + Example (for a TurtleScreen instance named screen): + >>> screen.mainloop() + + """ self.cv.tk.mainloop() - - def textinput(self, title, prompt): - """Pop up a dialog window for input of a string. - - Arguments: title is the title of the dialog window, - prompt is a text mostly describing what information to input. - - Return the string input - If the dialog is canceled, return None. - - Example (for a TurtleScreen instance named screen): - >>> screen.textinput("NIM", "Name of first player:") - - """ + + def textinput(self, title, prompt): + """Pop up a dialog window for input of a string. + + Arguments: title is the title of the dialog window, + prompt is a text mostly describing what information to input. + + Return the string input + If the dialog is canceled, return None. + + Example (for a TurtleScreen instance named screen): + >>> screen.textinput("NIM", "Name of first player:") + + """ return simpledialog.askstring(title, prompt, parent=self.cv) - - def numinput(self, title, prompt, default=None, minval=None, maxval=None): - """Pop up a dialog window for input of a number. - - Arguments: title is the title of the dialog window, - prompt is a text mostly describing what numerical information to input. - default: default value + + def numinput(self, title, prompt, default=None, minval=None, maxval=None): + """Pop up a dialog window for input of a number. + + Arguments: title is the title of the dialog window, + prompt is a text mostly describing what numerical information to input. + default: default value minval: minimum value for input - maxval: maximum value for input - - The number input must be in the range minval .. maxval if these are - given. If not, a hint is issued and the dialog remains open for - correction. Return the number input. - If the dialog is canceled, return None. - - Example (for a TurtleScreen instance named screen): - >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000) - - """ - return simpledialog.askfloat(title, prompt, initialvalue=default, + maxval: maximum value for input + + The number input must be in the range minval .. maxval if these are + given. If not, a hint is issued and the dialog remains open for + correction. Return the number input. + If the dialog is canceled, return None. + + Example (for a TurtleScreen instance named screen): + >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000) + + """ + return simpledialog.askfloat(title, prompt, initialvalue=default, minvalue=minval, maxvalue=maxval, parent=self.cv) - - -############################################################################## -### End of Tkinter - interface ### -############################################################################## - - -class Terminator (Exception): - """Will be raised in TurtleScreen.update, if _RUNNING becomes False. - - This stops execution of a turtle graphics script. - Main purpose: use in the Demo-Viewer turtle.Demo.py. - """ - pass - - -class TurtleGraphicsError(Exception): - """Some TurtleGraphics Error - """ - - -class Shape(object): - """Data structure modeling shapes. - - attribute _type is one of "polygon", "image", "compound" - attribute _data is - depending on _type a poygon-tuple, - an image or a list constructed using the addcomponent method. - """ - def __init__(self, type_, data=None): - self._type = type_ - if type_ == "polygon": - if isinstance(data, list): - data = tuple(data) - elif type_ == "image": - if isinstance(data, str): - if data.lower().endswith(".gif") and isfile(data): - data = TurtleScreen._image(data) - # else data assumed to be Photoimage - elif type_ == "compound": - data = [] - else: - raise TurtleGraphicsError("There is no shape type %s" % type_) - self._data = data - - def addcomponent(self, poly, fill, outline=None): - """Add component to a shape of type compound. - - Arguments: poly is a polygon, i. e. a tuple of number pairs. - fill is the fillcolor of the component, - outline is the outline color of the component. - - call (for a Shapeobject namend s): - -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue") - - Example: - >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) - >>> s = Shape("compound") - >>> s.addcomponent(poly, "red", "blue") - >>> # .. add more components and then use register_shape() - """ - if self._type != "compound": - raise TurtleGraphicsError("Cannot add component to %s Shape" - % self._type) - if outline is None: - outline = fill - self._data.append([poly, fill, outline]) - - -class Tbuffer(object): - """Ring buffer used as undobuffer for RawTurtle objects.""" - def __init__(self, bufsize=10): - self.bufsize = bufsize - self.buffer = [[None]] * bufsize - self.ptr = -1 - self.cumulate = False - def reset(self, bufsize=None): - if bufsize is None: - for i in range(self.bufsize): - self.buffer[i] = [None] - else: - self.bufsize = bufsize - self.buffer = [[None]] * bufsize - self.ptr = -1 - def push(self, item): - if self.bufsize > 0: - if not self.cumulate: - self.ptr = (self.ptr + 1) % self.bufsize - self.buffer[self.ptr] = item - else: - self.buffer[self.ptr].append(item) - def pop(self): - if self.bufsize > 0: - item = self.buffer[self.ptr] - if item is None: - return None - else: - self.buffer[self.ptr] = [None] - self.ptr = (self.ptr - 1) % self.bufsize - return (item) - def nr_of_items(self): - return self.bufsize - self.buffer.count([None]) - def __repr__(self): - return str(self.buffer) + " " + str(self.ptr) - - - -class TurtleScreen(TurtleScreenBase): - """Provides screen oriented methods like setbg etc. - - Only relies upon the methods of TurtleScreenBase and NOT - upon components of the underlying graphics toolkit - - which is Tkinter in this case. - """ - _RUNNING = True - - def __init__(self, cv, mode=_CFG["mode"], - colormode=_CFG["colormode"], delay=_CFG["delay"]): + + +############################################################################## +### End of Tkinter - interface ### +############################################################################## + + +class Terminator (Exception): + """Will be raised in TurtleScreen.update, if _RUNNING becomes False. + + This stops execution of a turtle graphics script. + Main purpose: use in the Demo-Viewer turtle.Demo.py. + """ + pass + + +class TurtleGraphicsError(Exception): + """Some TurtleGraphics Error + """ + + +class Shape(object): + """Data structure modeling shapes. + + attribute _type is one of "polygon", "image", "compound" + attribute _data is - depending on _type a poygon-tuple, + an image or a list constructed using the addcomponent method. + """ + def __init__(self, type_, data=None): + self._type = type_ + if type_ == "polygon": + if isinstance(data, list): + data = tuple(data) + elif type_ == "image": + if isinstance(data, str): + if data.lower().endswith(".gif") and isfile(data): + data = TurtleScreen._image(data) + # else data assumed to be Photoimage + elif type_ == "compound": + data = [] + else: + raise TurtleGraphicsError("There is no shape type %s" % type_) + self._data = data + + def addcomponent(self, poly, fill, outline=None): + """Add component to a shape of type compound. + + Arguments: poly is a polygon, i. e. a tuple of number pairs. + fill is the fillcolor of the component, + outline is the outline color of the component. + + call (for a Shapeobject namend s): + -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue") + + Example: + >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s = Shape("compound") + >>> s.addcomponent(poly, "red", "blue") + >>> # .. add more components and then use register_shape() + """ + if self._type != "compound": + raise TurtleGraphicsError("Cannot add component to %s Shape" + % self._type) + if outline is None: + outline = fill + self._data.append([poly, fill, outline]) + + +class Tbuffer(object): + """Ring buffer used as undobuffer for RawTurtle objects.""" + def __init__(self, bufsize=10): + self.bufsize = bufsize + self.buffer = [[None]] * bufsize + self.ptr = -1 + self.cumulate = False + def reset(self, bufsize=None): + if bufsize is None: + for i in range(self.bufsize): + self.buffer[i] = [None] + else: + self.bufsize = bufsize + self.buffer = [[None]] * bufsize + self.ptr = -1 + def push(self, item): + if self.bufsize > 0: + if not self.cumulate: + self.ptr = (self.ptr + 1) % self.bufsize + self.buffer[self.ptr] = item + else: + self.buffer[self.ptr].append(item) + def pop(self): + if self.bufsize > 0: + item = self.buffer[self.ptr] + if item is None: + return None + else: + self.buffer[self.ptr] = [None] + self.ptr = (self.ptr - 1) % self.bufsize + return (item) + def nr_of_items(self): + return self.bufsize - self.buffer.count([None]) + def __repr__(self): + return str(self.buffer) + " " + str(self.ptr) + + + +class TurtleScreen(TurtleScreenBase): + """Provides screen oriented methods like setbg etc. + + Only relies upon the methods of TurtleScreenBase and NOT + upon components of the underlying graphics toolkit - + which is Tkinter in this case. + """ + _RUNNING = True + + def __init__(self, cv, mode=_CFG["mode"], + colormode=_CFG["colormode"], delay=_CFG["delay"]): TurtleScreenBase.__init__(self, cv) - self._shapes = { - "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))), - "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7), - (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6), - (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6), - (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10), - (2,14))), - "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88), - (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51), - (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0), - (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09), - (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51), - (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))), - "square" : Shape("polygon", ((10,-10), (10,10), (-10,10), - (-10,-10))), - "triangle" : Shape("polygon", ((10,-5.77), (0,11.55), - (-10,-5.77))), - "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))), - "blank" : Shape("image", self._blankimage()) - } - - self._bgpics = {"nopic" : ""} - - self._mode = mode - self._delayvalue = delay - self._colormode = _CFG["colormode"] - self._keys = [] - self.clear() - if sys.platform == 'darwin': - # Force Turtle window to the front on OS X. This is needed because - # the Turtle window will show behind the Terminal window when you - # start the demo from the command line. - rootwindow = cv.winfo_toplevel() - rootwindow.call('wm', 'attributes', '.', '-topmost', '1') - rootwindow.call('wm', 'attributes', '.', '-topmost', '0') - - def clear(self): - """Delete all drawings and all turtles from the TurtleScreen. - - No argument. - - Reset empty TurtleScreen to its initial state: white background, - no backgroundimage, no eventbindings and tracing on. - - Example (for a TurtleScreen instance named screen): - >>> screen.clear() - - Note: this method is not available as function. - """ - self._delayvalue = _CFG["delay"] - self._colormode = _CFG["colormode"] - self._delete("all") - self._bgpic = self._createimage("") - self._bgpicname = "nopic" - self._tracing = 1 - self._updatecounter = 0 - self._turtles = [] - self.bgcolor("white") - for btn in 1, 2, 3: - self.onclick(None, btn) - self.onkeypress(None) - for key in self._keys[:]: - self.onkey(None, key) - self.onkeypress(None, key) - Turtle._pen = None - - def mode(self, mode=None): - """Set turtle-mode ('standard', 'logo' or 'world') and perform reset. - - Optional argument: - mode -- one of the strings 'standard', 'logo' or 'world' - - Mode 'standard' is compatible with turtle.py. - Mode 'logo' is compatible with most Logo-Turtle-Graphics. - Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in - this mode angles appear distorted if x/y unit-ratio doesn't equal 1. - If mode is not given, return the current mode. - - Mode Initial turtle heading positive angles - ------------|-------------------------|------------------- - 'standard' to the right (east) counterclockwise - 'logo' upward (north) clockwise - - Examples: - >>> mode('logo') # resets turtle heading to north - >>> mode() - 'logo' - """ - if mode is None: - return self._mode - mode = mode.lower() - if mode not in ["standard", "logo", "world"]: - raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode) - self._mode = mode - if mode in ["standard", "logo"]: - self._setscrollregion(-self.canvwidth//2, -self.canvheight//2, - self.canvwidth//2, self.canvheight//2) - self.xscale = self.yscale = 1.0 - self.reset() - - def setworldcoordinates(self, llx, lly, urx, ury): - """Set up a user defined coordinate-system. - - Arguments: - llx -- a number, x-coordinate of lower left corner of canvas - lly -- a number, y-coordinate of lower left corner of canvas - urx -- a number, x-coordinate of upper right corner of canvas - ury -- a number, y-coordinate of upper right corner of canvas - - Set up user coodinat-system and switch to mode 'world' if necessary. - This performs a screen.reset. If mode 'world' is already active, - all drawings are redrawn according to the new coordinates. - - But ATTENTION: in user-defined coordinatesystems angles may appear - distorted. (see Screen.mode()) - - Example (for a TurtleScreen instance named screen): - >>> screen.setworldcoordinates(-10,-0.5,50,1.5) - >>> for _ in range(36): - ... left(10) - ... forward(0.5) - """ - if self.mode() != "world": - self.mode("world") - xspan = float(urx - llx) - yspan = float(ury - lly) - wx, wy = self._window_size() - self.screensize(wx-20, wy-20) - oldxscale, oldyscale = self.xscale, self.yscale - self.xscale = self.canvwidth / xspan - self.yscale = self.canvheight / yspan - srx1 = llx * self.xscale - sry1 = -ury * self.yscale - srx2 = self.canvwidth + srx1 - sry2 = self.canvheight + sry1 - self._setscrollregion(srx1, sry1, srx2, sry2) - self._rescale(self.xscale/oldxscale, self.yscale/oldyscale) - self.update() - - def register_shape(self, name, shape=None): - """Adds a turtle shape to TurtleScreen's shapelist. - - Arguments: - (1) name is the name of a gif-file and shape is None. - Installs the corresponding image shape. - !! Image-shapes DO NOT rotate when turning the turtle, - !! so they do not display the heading of the turtle! - (2) name is an arbitrary string and shape is a tuple - of pairs of coordinates. Installs the corresponding - polygon shape - (3) name is an arbitrary string and shape is a - (compound) Shape object. Installs the corresponding - compound shape. - To use a shape, you have to issue the command shape(shapename). - - call: register_shape("turtle.gif") - --or: register_shape("tri", ((0,0), (10,10), (-10,10))) - - Example (for a TurtleScreen instance named screen): - >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3))) - - """ - if shape is None: - # image - if name.lower().endswith(".gif"): - shape = Shape("image", self._image(name)) - else: - raise TurtleGraphicsError("Bad arguments for register_shape.\n" - + "Use help(register_shape)" ) - elif isinstance(shape, tuple): - shape = Shape("polygon", shape) - ## else shape assumed to be Shape-instance - self._shapes[name] = shape - - def _colorstr(self, color): - """Return color string corresponding to args. - - Argument may be a string or a tuple of three - numbers corresponding to actual colormode, - i.e. in the range 0<=n<=colormode. - - If the argument doesn't represent a color, - an error is raised. - """ - if len(color) == 1: - color = color[0] - if isinstance(color, str): - if self._iscolorstring(color) or color == "": - return color - else: - raise TurtleGraphicsError("bad color string: %s" % str(color)) - try: - r, g, b = color - except (TypeError, ValueError): - raise TurtleGraphicsError("bad color arguments: %s" % str(color)) - if self._colormode == 1.0: - r, g, b = [round(255.0*x) for x in (r, g, b)] - if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): - raise TurtleGraphicsError("bad color sequence: %s" % str(color)) - return "#%02x%02x%02x" % (r, g, b) - - def _color(self, cstr): - if not cstr.startswith("#"): - return cstr - if len(cstr) == 7: - cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)] - elif len(cstr) == 4: - cl = [16*int(cstr[h], 16) for h in cstr[1:]] - else: - raise TurtleGraphicsError("bad colorstring: %s" % cstr) - return tuple(c * self._colormode/255 for c in cl) - - def colormode(self, cmode=None): - """Return the colormode or set it to 1.0 or 255. - - Optional argument: - cmode -- one of the values 1.0 or 255 - - r, g, b values of colortriples have to be in range 0..cmode. - - Example (for a TurtleScreen instance named screen): - >>> screen.colormode() - 1.0 - >>> screen.colormode(255) - >>> pencolor(240,160,80) - """ - if cmode is None: - return self._colormode - if cmode == 1.0: - self._colormode = float(cmode) - elif cmode == 255: - self._colormode = int(cmode) - - def reset(self): - """Reset all Turtles on the Screen to their initial state. - - No argument. - - Example (for a TurtleScreen instance named screen): - >>> screen.reset() - """ - for turtle in self._turtles: - turtle._setmode(self._mode) - turtle.reset() - - def turtles(self): - """Return the list of turtles on the screen. - - Example (for a TurtleScreen instance named screen): - >>> screen.turtles() - [<turtle.Turtle object at 0x00E11FB0>] - """ - return self._turtles - - def bgcolor(self, *args): - """Set or return backgroundcolor of the TurtleScreen. - - Arguments (if given): a color string or three numbers - in the range 0..colormode or a 3-tuple of such numbers. - - Example (for a TurtleScreen instance named screen): - >>> screen.bgcolor("orange") - >>> screen.bgcolor() - 'orange' - >>> screen.bgcolor(0.5,0,0.5) - >>> screen.bgcolor() - '#800080' - """ - if args: - color = self._colorstr(args) - else: - color = None - color = self._bgcolor(color) - if color is not None: - color = self._color(color) - return color - - def tracer(self, n=None, delay=None): - """Turns turtle animation on/off and set delay for update drawings. - - Optional arguments: - n -- nonnegative integer - delay -- nonnegative integer - - If n is given, only each n-th regular screen update is really performed. - (Can be used to accelerate the drawing of complex graphics.) - Second arguments sets delay value (see RawTurtle.delay()) - - Example (for a TurtleScreen instance named screen): - >>> screen.tracer(8, 25) - >>> dist = 2 - >>> for i in range(200): - ... fd(dist) - ... rt(90) - ... dist += 2 - """ - if n is None: - return self._tracing - self._tracing = int(n) - self._updatecounter = 0 - if delay is not None: - self._delayvalue = int(delay) - if self._tracing: - self.update() - - def delay(self, delay=None): - """ Return or set the drawing delay in milliseconds. - - Optional argument: - delay -- positive integer - - Example (for a TurtleScreen instance named screen): - >>> screen.delay(15) - >>> screen.delay() - 15 - """ - if delay is None: - return self._delayvalue - self._delayvalue = int(delay) - - def _incrementudc(self): - """Increment update counter.""" - if not TurtleScreen._RUNNING: - TurtleScreen._RUNNING = True - raise Terminator - if self._tracing > 0: - self._updatecounter += 1 - self._updatecounter %= self._tracing - - def update(self): - """Perform a TurtleScreen update. - """ - tracing = self._tracing - self._tracing = True - for t in self.turtles(): - t._update_data() - t._drawturtle() - self._tracing = tracing - self._update() - - def window_width(self): - """ Return the width of the turtle window. - - Example (for a TurtleScreen instance named screen): - >>> screen.window_width() - 640 - """ - return self._window_size()[0] - - def window_height(self): - """ Return the height of the turtle window. - - Example (for a TurtleScreen instance named screen): - >>> screen.window_height() - 480 - """ - return self._window_size()[1] - - def getcanvas(self): - """Return the Canvas of this TurtleScreen. - - No argument. - - Example (for a Screen instance named screen): - >>> cv = screen.getcanvas() - >>> cv - <turtle.ScrolledCanvas instance at 0x010742D8> - """ - return self.cv - - def getshapes(self): - """Return a list of names of all currently available turtle shapes. - - No argument. - - Example (for a TurtleScreen instance named screen): - >>> screen.getshapes() - ['arrow', 'blank', 'circle', ... , 'turtle'] - """ - return sorted(self._shapes.keys()) - - def onclick(self, fun, btn=1, add=None): - """Bind fun to mouse-click event on canvas. - - Arguments: - fun -- a function with two arguments, the coordinates of the - clicked point on the canvas. - btn -- the number of the mouse-button, defaults to 1 - - Example (for a TurtleScreen instance named screen) - - >>> screen.onclick(goto) - >>> # Subsequently clicking into the TurtleScreen will - >>> # make the turtle move to the clicked point. - >>> screen.onclick(None) - """ - self._onscreenclick(fun, btn, add) - - def onkey(self, fun, key): - """Bind fun to key-release event of key. - - Arguments: - fun -- a function with no arguments - key -- a string: key (e.g. "a") or key-symbol (e.g. "space") - - In order to be able to register key-events, TurtleScreen - must have focus. (See method listen.) - - Example (for a TurtleScreen instance named screen): - - >>> def f(): - ... fd(50) - ... lt(60) - ... - >>> screen.onkey(f, "Up") - >>> screen.listen() - - Subsequently the turtle can be moved by repeatedly pressing - the up-arrow key, consequently drawing a hexagon - - """ - if fun is None: - if key in self._keys: - self._keys.remove(key) - elif key not in self._keys: - self._keys.append(key) - self._onkeyrelease(fun, key) - - def onkeypress(self, fun, key=None): - """Bind fun to key-press event of key if key is given, - or to any key-press-event if no key is given. - - Arguments: - fun -- a function with no arguments - key -- a string: key (e.g. "a") or key-symbol (e.g. "space") - - In order to be able to register key-events, TurtleScreen - must have focus. (See method listen.) - - Example (for a TurtleScreen instance named screen - and a Turtle instance named turtle): - - >>> def f(): - ... fd(50) - ... lt(60) - ... - >>> screen.onkeypress(f, "Up") - >>> screen.listen() - - Subsequently the turtle can be moved by repeatedly pressing - the up-arrow key, or by keeping pressed the up-arrow key. - consequently drawing a hexagon. - """ - if fun is None: - if key in self._keys: - self._keys.remove(key) - elif key is not None and key not in self._keys: - self._keys.append(key) - self._onkeypress(fun, key) - - def listen(self, xdummy=None, ydummy=None): - """Set focus on TurtleScreen (in order to collect key-events) - - No arguments. - Dummy arguments are provided in order - to be able to pass listen to the onclick method. - - Example (for a TurtleScreen instance named screen): - >>> screen.listen() - """ - self._listen() - - def ontimer(self, fun, t=0): - """Install a timer, which calls fun after t milliseconds. - - Arguments: - fun -- a function with no arguments. - t -- a number >= 0 - - Example (for a TurtleScreen instance named screen): - - >>> running = True - >>> def f(): - ... if running: - ... fd(50) - ... lt(60) - ... screen.ontimer(f, 250) - ... - >>> f() # makes the turtle marching around - >>> running = False - """ - self._ontimer(fun, t) - - def bgpic(self, picname=None): - """Set background image or return name of current backgroundimage. - - Optional argument: - picname -- a string, name of a gif-file or "nopic". - - If picname is a filename, set the corresponding image as background. - If picname is "nopic", delete backgroundimage, if present. - If picname is None, return the filename of the current backgroundimage. - - Example (for a TurtleScreen instance named screen): - >>> screen.bgpic() - 'nopic' - >>> screen.bgpic("landscape.gif") - >>> screen.bgpic() - 'landscape.gif' - """ - if picname is None: - return self._bgpicname - if picname not in self._bgpics: - self._bgpics[picname] = self._image(picname) - self._setbgpic(self._bgpic, self._bgpics[picname]) - self._bgpicname = picname - - def screensize(self, canvwidth=None, canvheight=None, bg=None): - """Resize the canvas the turtles are drawing on. - - Optional arguments: - canvwidth -- positive integer, new width of canvas in pixels - canvheight -- positive integer, new height of canvas in pixels - bg -- colorstring or color-tuple, new backgroundcolor - If no arguments are given, return current (canvaswidth, canvasheight) - - Do not alter the drawing window. To observe hidden parts of - the canvas use the scrollbars. (Can make visible those parts - of a drawing, which were outside the canvas before!) - - Example (for a Turtle instance named turtle): - >>> turtle.screensize(2000,1500) - >>> # e.g. to search for an erroneously escaped turtle ;-) - """ - return self._resize(canvwidth, canvheight, bg) - - onscreenclick = onclick - resetscreen = reset - clearscreen = clear - addshape = register_shape - onkeyrelease = onkey - -class TNavigator(object): - """Navigation part of the RawTurtle. - Implements methods for turtle movement. - """ - START_ORIENTATION = { - "standard": Vec2D(1.0, 0.0), - "world" : Vec2D(1.0, 0.0), - "logo" : Vec2D(0.0, 1.0) } - DEFAULT_MODE = "standard" - DEFAULT_ANGLEOFFSET = 0 - DEFAULT_ANGLEORIENT = 1 - - def __init__(self, mode=DEFAULT_MODE): - self._angleOffset = self.DEFAULT_ANGLEOFFSET - self._angleOrient = self.DEFAULT_ANGLEORIENT - self._mode = mode - self.undobuffer = None - self.degrees() - self._mode = None - self._setmode(mode) - TNavigator.reset(self) - - def reset(self): - """reset turtle to its initial values - - Will be overwritten by parent class - """ - self._position = Vec2D(0.0, 0.0) - self._orient = TNavigator.START_ORIENTATION[self._mode] - - def _setmode(self, mode=None): - """Set turtle-mode to 'standard', 'world' or 'logo'. - """ - if mode is None: - return self._mode - if mode not in ["standard", "logo", "world"]: - return - self._mode = mode - if mode in ["standard", "world"]: - self._angleOffset = 0 - self._angleOrient = 1 - else: # mode == "logo": - self._angleOffset = self._fullcircle/4. - self._angleOrient = -1 - - def _setDegreesPerAU(self, fullcircle): - """Helper function for degrees() and radians()""" - self._fullcircle = fullcircle - self._degreesPerAU = 360/fullcircle - if self._mode == "standard": - self._angleOffset = 0 - else: - self._angleOffset = fullcircle/4. - - def degrees(self, fullcircle=360.0): - """ Set angle measurement units to degrees. - - Optional argument: - fullcircle - a number - - Set angle measurement units, i. e. set number + self._shapes = { + "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))), + "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7), + (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6), + (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6), + (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10), + (2,14))), + "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88), + (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51), + (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0), + (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09), + (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51), + (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))), + "square" : Shape("polygon", ((10,-10), (10,10), (-10,10), + (-10,-10))), + "triangle" : Shape("polygon", ((10,-5.77), (0,11.55), + (-10,-5.77))), + "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))), + "blank" : Shape("image", self._blankimage()) + } + + self._bgpics = {"nopic" : ""} + + self._mode = mode + self._delayvalue = delay + self._colormode = _CFG["colormode"] + self._keys = [] + self.clear() + if sys.platform == 'darwin': + # Force Turtle window to the front on OS X. This is needed because + # the Turtle window will show behind the Terminal window when you + # start the demo from the command line. + rootwindow = cv.winfo_toplevel() + rootwindow.call('wm', 'attributes', '.', '-topmost', '1') + rootwindow.call('wm', 'attributes', '.', '-topmost', '0') + + def clear(self): + """Delete all drawings and all turtles from the TurtleScreen. + + No argument. + + Reset empty TurtleScreen to its initial state: white background, + no backgroundimage, no eventbindings and tracing on. + + Example (for a TurtleScreen instance named screen): + >>> screen.clear() + + Note: this method is not available as function. + """ + self._delayvalue = _CFG["delay"] + self._colormode = _CFG["colormode"] + self._delete("all") + self._bgpic = self._createimage("") + self._bgpicname = "nopic" + self._tracing = 1 + self._updatecounter = 0 + self._turtles = [] + self.bgcolor("white") + for btn in 1, 2, 3: + self.onclick(None, btn) + self.onkeypress(None) + for key in self._keys[:]: + self.onkey(None, key) + self.onkeypress(None, key) + Turtle._pen = None + + def mode(self, mode=None): + """Set turtle-mode ('standard', 'logo' or 'world') and perform reset. + + Optional argument: + mode -- one of the strings 'standard', 'logo' or 'world' + + Mode 'standard' is compatible with turtle.py. + Mode 'logo' is compatible with most Logo-Turtle-Graphics. + Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in + this mode angles appear distorted if x/y unit-ratio doesn't equal 1. + If mode is not given, return the current mode. + + Mode Initial turtle heading positive angles + ------------|-------------------------|------------------- + 'standard' to the right (east) counterclockwise + 'logo' upward (north) clockwise + + Examples: + >>> mode('logo') # resets turtle heading to north + >>> mode() + 'logo' + """ + if mode is None: + return self._mode + mode = mode.lower() + if mode not in ["standard", "logo", "world"]: + raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode) + self._mode = mode + if mode in ["standard", "logo"]: + self._setscrollregion(-self.canvwidth//2, -self.canvheight//2, + self.canvwidth//2, self.canvheight//2) + self.xscale = self.yscale = 1.0 + self.reset() + + def setworldcoordinates(self, llx, lly, urx, ury): + """Set up a user defined coordinate-system. + + Arguments: + llx -- a number, x-coordinate of lower left corner of canvas + lly -- a number, y-coordinate of lower left corner of canvas + urx -- a number, x-coordinate of upper right corner of canvas + ury -- a number, y-coordinate of upper right corner of canvas + + Set up user coodinat-system and switch to mode 'world' if necessary. + This performs a screen.reset. If mode 'world' is already active, + all drawings are redrawn according to the new coordinates. + + But ATTENTION: in user-defined coordinatesystems angles may appear + distorted. (see Screen.mode()) + + Example (for a TurtleScreen instance named screen): + >>> screen.setworldcoordinates(-10,-0.5,50,1.5) + >>> for _ in range(36): + ... left(10) + ... forward(0.5) + """ + if self.mode() != "world": + self.mode("world") + xspan = float(urx - llx) + yspan = float(ury - lly) + wx, wy = self._window_size() + self.screensize(wx-20, wy-20) + oldxscale, oldyscale = self.xscale, self.yscale + self.xscale = self.canvwidth / xspan + self.yscale = self.canvheight / yspan + srx1 = llx * self.xscale + sry1 = -ury * self.yscale + srx2 = self.canvwidth + srx1 + sry2 = self.canvheight + sry1 + self._setscrollregion(srx1, sry1, srx2, sry2) + self._rescale(self.xscale/oldxscale, self.yscale/oldyscale) + self.update() + + def register_shape(self, name, shape=None): + """Adds a turtle shape to TurtleScreen's shapelist. + + Arguments: + (1) name is the name of a gif-file and shape is None. + Installs the corresponding image shape. + !! Image-shapes DO NOT rotate when turning the turtle, + !! so they do not display the heading of the turtle! + (2) name is an arbitrary string and shape is a tuple + of pairs of coordinates. Installs the corresponding + polygon shape + (3) name is an arbitrary string and shape is a + (compound) Shape object. Installs the corresponding + compound shape. + To use a shape, you have to issue the command shape(shapename). + + call: register_shape("turtle.gif") + --or: register_shape("tri", ((0,0), (10,10), (-10,10))) + + Example (for a TurtleScreen instance named screen): + >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3))) + + """ + if shape is None: + # image + if name.lower().endswith(".gif"): + shape = Shape("image", self._image(name)) + else: + raise TurtleGraphicsError("Bad arguments for register_shape.\n" + + "Use help(register_shape)" ) + elif isinstance(shape, tuple): + shape = Shape("polygon", shape) + ## else shape assumed to be Shape-instance + self._shapes[name] = shape + + def _colorstr(self, color): + """Return color string corresponding to args. + + Argument may be a string or a tuple of three + numbers corresponding to actual colormode, + i.e. in the range 0<=n<=colormode. + + If the argument doesn't represent a color, + an error is raised. + """ + if len(color) == 1: + color = color[0] + if isinstance(color, str): + if self._iscolorstring(color) or color == "": + return color + else: + raise TurtleGraphicsError("bad color string: %s" % str(color)) + try: + r, g, b = color + except (TypeError, ValueError): + raise TurtleGraphicsError("bad color arguments: %s" % str(color)) + if self._colormode == 1.0: + r, g, b = [round(255.0*x) for x in (r, g, b)] + if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): + raise TurtleGraphicsError("bad color sequence: %s" % str(color)) + return "#%02x%02x%02x" % (r, g, b) + + def _color(self, cstr): + if not cstr.startswith("#"): + return cstr + if len(cstr) == 7: + cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)] + elif len(cstr) == 4: + cl = [16*int(cstr[h], 16) for h in cstr[1:]] + else: + raise TurtleGraphicsError("bad colorstring: %s" % cstr) + return tuple(c * self._colormode/255 for c in cl) + + def colormode(self, cmode=None): + """Return the colormode or set it to 1.0 or 255. + + Optional argument: + cmode -- one of the values 1.0 or 255 + + r, g, b values of colortriples have to be in range 0..cmode. + + Example (for a TurtleScreen instance named screen): + >>> screen.colormode() + 1.0 + >>> screen.colormode(255) + >>> pencolor(240,160,80) + """ + if cmode is None: + return self._colormode + if cmode == 1.0: + self._colormode = float(cmode) + elif cmode == 255: + self._colormode = int(cmode) + + def reset(self): + """Reset all Turtles on the Screen to their initial state. + + No argument. + + Example (for a TurtleScreen instance named screen): + >>> screen.reset() + """ + for turtle in self._turtles: + turtle._setmode(self._mode) + turtle.reset() + + def turtles(self): + """Return the list of turtles on the screen. + + Example (for a TurtleScreen instance named screen): + >>> screen.turtles() + [<turtle.Turtle object at 0x00E11FB0>] + """ + return self._turtles + + def bgcolor(self, *args): + """Set or return backgroundcolor of the TurtleScreen. + + Arguments (if given): a color string or three numbers + in the range 0..colormode or a 3-tuple of such numbers. + + Example (for a TurtleScreen instance named screen): + >>> screen.bgcolor("orange") + >>> screen.bgcolor() + 'orange' + >>> screen.bgcolor(0.5,0,0.5) + >>> screen.bgcolor() + '#800080' + """ + if args: + color = self._colorstr(args) + else: + color = None + color = self._bgcolor(color) + if color is not None: + color = self._color(color) + return color + + def tracer(self, n=None, delay=None): + """Turns turtle animation on/off and set delay for update drawings. + + Optional arguments: + n -- nonnegative integer + delay -- nonnegative integer + + If n is given, only each n-th regular screen update is really performed. + (Can be used to accelerate the drawing of complex graphics.) + Second arguments sets delay value (see RawTurtle.delay()) + + Example (for a TurtleScreen instance named screen): + >>> screen.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + ... fd(dist) + ... rt(90) + ... dist += 2 + """ + if n is None: + return self._tracing + self._tracing = int(n) + self._updatecounter = 0 + if delay is not None: + self._delayvalue = int(delay) + if self._tracing: + self.update() + + def delay(self, delay=None): + """ Return or set the drawing delay in milliseconds. + + Optional argument: + delay -- positive integer + + Example (for a TurtleScreen instance named screen): + >>> screen.delay(15) + >>> screen.delay() + 15 + """ + if delay is None: + return self._delayvalue + self._delayvalue = int(delay) + + def _incrementudc(self): + """Increment update counter.""" + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNING = True + raise Terminator + if self._tracing > 0: + self._updatecounter += 1 + self._updatecounter %= self._tracing + + def update(self): + """Perform a TurtleScreen update. + """ + tracing = self._tracing + self._tracing = True + for t in self.turtles(): + t._update_data() + t._drawturtle() + self._tracing = tracing + self._update() + + def window_width(self): + """ Return the width of the turtle window. + + Example (for a TurtleScreen instance named screen): + >>> screen.window_width() + 640 + """ + return self._window_size()[0] + + def window_height(self): + """ Return the height of the turtle window. + + Example (for a TurtleScreen instance named screen): + >>> screen.window_height() + 480 + """ + return self._window_size()[1] + + def getcanvas(self): + """Return the Canvas of this TurtleScreen. + + No argument. + + Example (for a Screen instance named screen): + >>> cv = screen.getcanvas() + >>> cv + <turtle.ScrolledCanvas instance at 0x010742D8> + """ + return self.cv + + def getshapes(self): + """Return a list of names of all currently available turtle shapes. + + No argument. + + Example (for a TurtleScreen instance named screen): + >>> screen.getshapes() + ['arrow', 'blank', 'circle', ... , 'turtle'] + """ + return sorted(self._shapes.keys()) + + def onclick(self, fun, btn=1, add=None): + """Bind fun to mouse-click event on canvas. + + Arguments: + fun -- a function with two arguments, the coordinates of the + clicked point on the canvas. + btn -- the number of the mouse-button, defaults to 1 + + Example (for a TurtleScreen instance named screen) + + >>> screen.onclick(goto) + >>> # Subsequently clicking into the TurtleScreen will + >>> # make the turtle move to the clicked point. + >>> screen.onclick(None) + """ + self._onscreenclick(fun, btn, add) + + def onkey(self, fun, key): + """Bind fun to key-release event of key. + + Arguments: + fun -- a function with no arguments + key -- a string: key (e.g. "a") or key-symbol (e.g. "space") + + In order to be able to register key-events, TurtleScreen + must have focus. (See method listen.) + + Example (for a TurtleScreen instance named screen): + + >>> def f(): + ... fd(50) + ... lt(60) + ... + >>> screen.onkey(f, "Up") + >>> screen.listen() + + Subsequently the turtle can be moved by repeatedly pressing + the up-arrow key, consequently drawing a hexagon + + """ + if fun is None: + if key in self._keys: + self._keys.remove(key) + elif key not in self._keys: + self._keys.append(key) + self._onkeyrelease(fun, key) + + def onkeypress(self, fun, key=None): + """Bind fun to key-press event of key if key is given, + or to any key-press-event if no key is given. + + Arguments: + fun -- a function with no arguments + key -- a string: key (e.g. "a") or key-symbol (e.g. "space") + + In order to be able to register key-events, TurtleScreen + must have focus. (See method listen.) + + Example (for a TurtleScreen instance named screen + and a Turtle instance named turtle): + + >>> def f(): + ... fd(50) + ... lt(60) + ... + >>> screen.onkeypress(f, "Up") + >>> screen.listen() + + Subsequently the turtle can be moved by repeatedly pressing + the up-arrow key, or by keeping pressed the up-arrow key. + consequently drawing a hexagon. + """ + if fun is None: + if key in self._keys: + self._keys.remove(key) + elif key is not None and key not in self._keys: + self._keys.append(key) + self._onkeypress(fun, key) + + def listen(self, xdummy=None, ydummy=None): + """Set focus on TurtleScreen (in order to collect key-events) + + No arguments. + Dummy arguments are provided in order + to be able to pass listen to the onclick method. + + Example (for a TurtleScreen instance named screen): + >>> screen.listen() + """ + self._listen() + + def ontimer(self, fun, t=0): + """Install a timer, which calls fun after t milliseconds. + + Arguments: + fun -- a function with no arguments. + t -- a number >= 0 + + Example (for a TurtleScreen instance named screen): + + >>> running = True + >>> def f(): + ... if running: + ... fd(50) + ... lt(60) + ... screen.ontimer(f, 250) + ... + >>> f() # makes the turtle marching around + >>> running = False + """ + self._ontimer(fun, t) + + def bgpic(self, picname=None): + """Set background image or return name of current backgroundimage. + + Optional argument: + picname -- a string, name of a gif-file or "nopic". + + If picname is a filename, set the corresponding image as background. + If picname is "nopic", delete backgroundimage, if present. + If picname is None, return the filename of the current backgroundimage. + + Example (for a TurtleScreen instance named screen): + >>> screen.bgpic() + 'nopic' + >>> screen.bgpic("landscape.gif") + >>> screen.bgpic() + 'landscape.gif' + """ + if picname is None: + return self._bgpicname + if picname not in self._bgpics: + self._bgpics[picname] = self._image(picname) + self._setbgpic(self._bgpic, self._bgpics[picname]) + self._bgpicname = picname + + def screensize(self, canvwidth=None, canvheight=None, bg=None): + """Resize the canvas the turtles are drawing on. + + Optional arguments: + canvwidth -- positive integer, new width of canvas in pixels + canvheight -- positive integer, new height of canvas in pixels + bg -- colorstring or color-tuple, new backgroundcolor + If no arguments are given, return current (canvaswidth, canvasheight) + + Do not alter the drawing window. To observe hidden parts of + the canvas use the scrollbars. (Can make visible those parts + of a drawing, which were outside the canvas before!) + + Example (for a Turtle instance named turtle): + >>> turtle.screensize(2000,1500) + >>> # e.g. to search for an erroneously escaped turtle ;-) + """ + return self._resize(canvwidth, canvheight, bg) + + onscreenclick = onclick + resetscreen = reset + clearscreen = clear + addshape = register_shape + onkeyrelease = onkey + +class TNavigator(object): + """Navigation part of the RawTurtle. + Implements methods for turtle movement. + """ + START_ORIENTATION = { + "standard": Vec2D(1.0, 0.0), + "world" : Vec2D(1.0, 0.0), + "logo" : Vec2D(0.0, 1.0) } + DEFAULT_MODE = "standard" + DEFAULT_ANGLEOFFSET = 0 + DEFAULT_ANGLEORIENT = 1 + + def __init__(self, mode=DEFAULT_MODE): + self._angleOffset = self.DEFAULT_ANGLEOFFSET + self._angleOrient = self.DEFAULT_ANGLEORIENT + self._mode = mode + self.undobuffer = None + self.degrees() + self._mode = None + self._setmode(mode) + TNavigator.reset(self) + + def reset(self): + """reset turtle to its initial values + + Will be overwritten by parent class + """ + self._position = Vec2D(0.0, 0.0) + self._orient = TNavigator.START_ORIENTATION[self._mode] + + def _setmode(self, mode=None): + """Set turtle-mode to 'standard', 'world' or 'logo'. + """ + if mode is None: + return self._mode + if mode not in ["standard", "logo", "world"]: + return + self._mode = mode + if mode in ["standard", "world"]: + self._angleOffset = 0 + self._angleOrient = 1 + else: # mode == "logo": + self._angleOffset = self._fullcircle/4. + self._angleOrient = -1 + + def _setDegreesPerAU(self, fullcircle): + """Helper function for degrees() and radians()""" + self._fullcircle = fullcircle + self._degreesPerAU = 360/fullcircle + if self._mode == "standard": + self._angleOffset = 0 + else: + self._angleOffset = fullcircle/4. + + def degrees(self, fullcircle=360.0): + """ Set angle measurement units to degrees. + + Optional argument: + fullcircle - a number + + Set angle measurement units, i. e. set number of 'degrees' for a full circle. Default value is - 360 degrees. - - Example (for a Turtle instance named turtle): - >>> turtle.left(90) - >>> turtle.heading() - 90 - - Change angle measurement unit to grad (also known as gon, - grade, or gradian and equals 1/100-th of the right angle.) - >>> turtle.degrees(400.0) - >>> turtle.heading() - 100 - - """ - self._setDegreesPerAU(fullcircle) - - def radians(self): - """ Set the angle measurement units to radians. - - No arguments. - - Example (for a Turtle instance named turtle): - >>> turtle.heading() - 90 - >>> turtle.radians() - >>> turtle.heading() - 1.5707963267948966 - """ - self._setDegreesPerAU(2*math.pi) - - def _go(self, distance): - """move turtle forward by specified distance""" - ende = self._position + self._orient * distance - self._goto(ende) - - def _rotate(self, angle): - """Turn turtle counterclockwise by specified angle if angle > 0.""" - angle *= self._degreesPerAU - self._orient = self._orient.rotate(angle) - - def _goto(self, end): - """move turtle to position end.""" - self._position = end - - def forward(self, distance): - """Move the turtle forward by the specified distance. - - Aliases: forward | fd - - Argument: - distance -- a number (integer or float) - - Move the turtle forward by the specified distance, in the direction - the turtle is headed. - - Example (for a Turtle instance named turtle): - >>> turtle.position() - (0.00, 0.00) - >>> turtle.forward(25) - >>> turtle.position() - (25.00,0.00) - >>> turtle.forward(-75) - >>> turtle.position() - (-50.00,0.00) - """ - self._go(distance) - - def back(self, distance): - """Move the turtle backward by distance. - - Aliases: back | backward | bk - - Argument: - distance -- a number - + 360 degrees. + + Example (for a Turtle instance named turtle): + >>> turtle.left(90) + >>> turtle.heading() + 90 + + Change angle measurement unit to grad (also known as gon, + grade, or gradian and equals 1/100-th of the right angle.) + >>> turtle.degrees(400.0) + >>> turtle.heading() + 100 + + """ + self._setDegreesPerAU(fullcircle) + + def radians(self): + """ Set the angle measurement units to radians. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 90 + >>> turtle.radians() + >>> turtle.heading() + 1.5707963267948966 + """ + self._setDegreesPerAU(2*math.pi) + + def _go(self, distance): + """move turtle forward by specified distance""" + ende = self._position + self._orient * distance + self._goto(ende) + + def _rotate(self, angle): + """Turn turtle counterclockwise by specified angle if angle > 0.""" + angle *= self._degreesPerAU + self._orient = self._orient.rotate(angle) + + def _goto(self, end): + """move turtle to position end.""" + self._position = end + + def forward(self, distance): + """Move the turtle forward by the specified distance. + + Aliases: forward | fd + + Argument: + distance -- a number (integer or float) + + Move the turtle forward by the specified distance, in the direction + the turtle is headed. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 0.00) + >>> turtle.forward(25) + >>> turtle.position() + (25.00,0.00) + >>> turtle.forward(-75) + >>> turtle.position() + (-50.00,0.00) + """ + self._go(distance) + + def back(self, distance): + """Move the turtle backward by distance. + + Aliases: back | backward | bk + + Argument: + distance -- a number + Move the turtle backward by distance, opposite to the direction the - turtle is headed. Do not change the turtle's heading. - - Example (for a Turtle instance named turtle): - >>> turtle.position() - (0.00, 0.00) - >>> turtle.backward(30) - >>> turtle.position() - (-30.00, 0.00) - """ - self._go(-distance) - - def right(self, angle): - """Turn turtle right by angle units. - - Aliases: right | rt - - Argument: - angle -- a number (integer or float) - - Turn turtle right by angle units. (Units are by default degrees, - but can be set via the degrees() and radians() functions.) - Angle orientation depends on mode. (See this.) - - Example (for a Turtle instance named turtle): - >>> turtle.heading() - 22.0 - >>> turtle.right(45) - >>> turtle.heading() - 337.0 - """ - self._rotate(-angle) - - def left(self, angle): - """Turn turtle left by angle units. - - Aliases: left | lt - - Argument: - angle -- a number (integer or float) - - Turn turtle left by angle units. (Units are by default degrees, - but can be set via the degrees() and radians() functions.) - Angle orientation depends on mode. (See this.) - - Example (for a Turtle instance named turtle): - >>> turtle.heading() - 22.0 - >>> turtle.left(45) - >>> turtle.heading() - 67.0 - """ - self._rotate(angle) - - def pos(self): - """Return the turtle's current location (x,y), as a Vec2D-vector. - - Aliases: pos | position - - No arguments. - - Example (for a Turtle instance named turtle): - >>> turtle.pos() - (0.00, 240.00) - """ - return self._position - - def xcor(self): - """ Return the turtle's x coordinate. - - No arguments. - - Example (for a Turtle instance named turtle): - >>> reset() - >>> turtle.left(60) - >>> turtle.forward(100) - >>> print turtle.xcor() - 50.0 - """ - return self._position[0] - - def ycor(self): - """ Return the turtle's y coordinate - --- - No arguments. - - Example (for a Turtle instance named turtle): - >>> reset() - >>> turtle.left(60) - >>> turtle.forward(100) - >>> print turtle.ycor() - 86.6025403784 - """ - return self._position[1] - - - def goto(self, x, y=None): - """Move turtle to an absolute position. - - Aliases: setpos | setposition | goto: - - Arguments: - x -- a number or a pair/vector of numbers - y -- a number None - - call: goto(x, y) # two coordinates - --or: goto((x, y)) # a pair (tuple) of coordinates - --or: goto(vec) # e.g. as returned by pos() - - Move turtle to an absolute position. If the pen is down, - a line will be drawn. The turtle's orientation does not change. - - Example (for a Turtle instance named turtle): - >>> tp = turtle.pos() - >>> tp - (0.00, 0.00) - >>> turtle.setpos(60,30) - >>> turtle.pos() - (60.00,30.00) - >>> turtle.setpos((20,80)) - >>> turtle.pos() - (20.00,80.00) - >>> turtle.setpos(tp) - >>> turtle.pos() - (0.00,0.00) - """ - if y is None: - self._goto(Vec2D(*x)) - else: - self._goto(Vec2D(x, y)) - - def home(self): - """Move turtle to the origin - coordinates (0,0). - - No arguments. - - Move turtle to the origin - coordinates (0,0) and set its - heading to its start-orientation (which depends on mode). - - Example (for a Turtle instance named turtle): - >>> turtle.home() - """ - self.goto(0, 0) - self.setheading(0) - - def setx(self, x): - """Set the turtle's first coordinate to x - - Argument: - x -- a number (integer or float) - - Set the turtle's first coordinate to x, leave second coordinate - unchanged. - - Example (for a Turtle instance named turtle): - >>> turtle.position() - (0.00, 240.00) - >>> turtle.setx(10) - >>> turtle.position() - (10.00, 240.00) - """ - self._goto(Vec2D(x, self._position[1])) - - def sety(self, y): - """Set the turtle's second coordinate to y - - Argument: - y -- a number (integer or float) - - Set the turtle's first coordinate to x, second coordinate remains - unchanged. - - Example (for a Turtle instance named turtle): - >>> turtle.position() - (0.00, 40.00) - >>> turtle.sety(-10) - >>> turtle.position() - (0.00, -10.00) - """ - self._goto(Vec2D(self._position[0], y)) - - def distance(self, x, y=None): - """Return the distance from the turtle to (x,y) in turtle step units. - - Arguments: - x -- a number or a pair/vector of numbers or a turtle instance - y -- a number None None - - call: distance(x, y) # two coordinates - --or: distance((x, y)) # a pair (tuple) of coordinates - --or: distance(vec) # e.g. as returned by pos() - --or: distance(mypen) # where mypen is another turtle - - Example (for a Turtle instance named turtle): - >>> turtle.pos() - (0.00, 0.00) - >>> turtle.distance(30,40) - 50.0 - >>> pen = Turtle() - >>> pen.forward(77) - >>> turtle.distance(pen) - 77.0 - """ - if y is not None: - pos = Vec2D(x, y) - if isinstance(x, Vec2D): - pos = x - elif isinstance(x, tuple): - pos = Vec2D(*x) - elif isinstance(x, TNavigator): - pos = x._position - return abs(pos - self._position) - - def towards(self, x, y=None): - """Return the angle of the line from the turtle's position to (x, y). - - Arguments: - x -- a number or a pair/vector of numbers or a turtle instance - y -- a number None None - - call: distance(x, y) # two coordinates - --or: distance((x, y)) # a pair (tuple) of coordinates - --or: distance(vec) # e.g. as returned by pos() - --or: distance(mypen) # where mypen is another turtle - - Return the angle, between the line from turtle-position to position - specified by x, y and the turtle's start orientation. (Depends on - modes - "standard" or "logo") - - Example (for a Turtle instance named turtle): - >>> turtle.pos() - (10.00, 10.00) - >>> turtle.towards(0,0) - 225.0 - """ - if y is not None: - pos = Vec2D(x, y) - if isinstance(x, Vec2D): - pos = x - elif isinstance(x, tuple): - pos = Vec2D(*x) - elif isinstance(x, TNavigator): - pos = x._position - x, y = pos - self._position - result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 - result /= self._degreesPerAU - return (self._angleOffset + self._angleOrient*result) % self._fullcircle - - def heading(self): - """ Return the turtle's current heading. - - No arguments. - - Example (for a Turtle instance named turtle): - >>> turtle.left(67) - >>> turtle.heading() - 67.0 - """ - x, y = self._orient - result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 - result /= self._degreesPerAU - return (self._angleOffset + self._angleOrient*result) % self._fullcircle - - def setheading(self, to_angle): - """Set the orientation of the turtle to to_angle. - - Aliases: setheading | seth - - Argument: - to_angle -- a number (integer or float) - - Set the orientation of the turtle to to_angle. - Here are some common directions in degrees: - - standard - mode: logo-mode: - -------------------|-------------------- - 0 - east 0 - north - 90 - north 90 - east - 180 - west 180 - south - 270 - south 270 - west - - Example (for a Turtle instance named turtle): - >>> turtle.setheading(90) - >>> turtle.heading() - 90 - """ - angle = (to_angle - self.heading())*self._angleOrient - full = self._fullcircle - angle = (angle+full/2.)%full - full/2. - self._rotate(angle) - - def circle(self, radius, extent = None, steps = None): - """ Draw a circle with given radius. - - Arguments: - radius -- a number - extent (optional) -- a number - steps (optional) -- an integer - - Draw a circle with given radius. The center is radius units left - of the turtle; extent - an angle - determines which part of the - circle is drawn. If extent is not given, draw the entire circle. - If extent is not a full circle, one endpoint of the arc is the - current pen position. Draw the arc in counterclockwise direction - if radius is positive, otherwise in clockwise direction. Finally - the direction of the turtle is changed by the amount of extent. - - As the circle is approximated by an inscribed regular polygon, - steps determines the number of steps to use. If not given, - it will be calculated automatically. Maybe used to draw regular - polygons. - - call: circle(radius) # full circle - --or: circle(radius, extent) # arc - --or: circle(radius, extent, steps) - --or: circle(radius, steps=6) # 6-sided polygon - - Example (for a Turtle instance named turtle): - >>> turtle.circle(50) - >>> turtle.circle(120, 180) # semicircle - """ - if self.undobuffer: - self.undobuffer.push(["seq"]) - self.undobuffer.cumulate = True - speed = self.speed() - if extent is None: - extent = self._fullcircle - if steps is None: - frac = abs(extent)/self._fullcircle - steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac) - w = 1.0 * extent / steps - w2 = 0.5 * w - l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU) - if radius < 0: - l, w, w2 = -l, -w, -w2 - tr = self._tracer() - dl = self._delay() - if speed == 0: - self._tracer(0, 0) - else: - self.speed(0) - self._rotate(w2) - for i in range(steps): - self.speed(speed) - self._go(l) - self.speed(0) - self._rotate(w) - self._rotate(-w2) - if speed == 0: - self._tracer(tr, dl) - self.speed(speed) - if self.undobuffer: - self.undobuffer.cumulate = False - -## three dummy methods to be implemented by child class: - - def speed(self, s=0): - """dummy method - to be overwritten by child class""" - def _tracer(self, a=None, b=None): - """dummy method - to be overwritten by child class""" - def _delay(self, n=None): - """dummy method - to be overwritten by child class""" - - fd = forward - bk = back - backward = back - rt = right - lt = left - position = pos - setpos = goto - setposition = goto - seth = setheading - - -class TPen(object): - """Drawing part of the RawTurtle. - Implements drawing properties. - """ - def __init__(self, resizemode=_CFG["resizemode"]): - self._resizemode = resizemode # or "user" or "noresize" - self.undobuffer = None - TPen._reset(self) - - def _reset(self, pencolor=_CFG["pencolor"], - fillcolor=_CFG["fillcolor"]): - self._pensize = 1 - self._shown = True - self._pencolor = pencolor - self._fillcolor = fillcolor - self._drawing = True - self._speed = 3 - self._stretchfactor = (1., 1.) - self._shearfactor = 0. - self._tilt = 0. - self._shapetrafo = (1., 0., 0., 1.) - self._outlinewidth = 1 - - def resizemode(self, rmode=None): - """Set resizemode to one of the values: "auto", "user", "noresize". - - (Optional) Argument: - rmode -- one of the strings "auto", "user", "noresize" - - Different resizemodes have the following effects: - - "auto" adapts the appearance of the turtle - corresponding to the value of pensize. - - "user" adapts the appearance of the turtle according to the - values of stretchfactor and outlinewidth (outline), - which are set by shapesize() - - "noresize" no adaption of the turtle's appearance takes place. - If no argument is given, return current resizemode. - resizemode("user") is called by a call of shapesize with arguments. - - - Examples (for a Turtle instance named turtle): - >>> turtle.resizemode("noresize") - >>> turtle.resizemode() - 'noresize' - """ - if rmode is None: - return self._resizemode - rmode = rmode.lower() - if rmode in ["auto", "user", "noresize"]: - self.pen(resizemode=rmode) - - def pensize(self, width=None): - """Set or return the line thickness. - - Aliases: pensize | width - - Argument: - width -- positive number - - Set the line thickness to width or return it. If resizemode is set - to "auto" and turtleshape is a polygon, that polygon is drawn with - the same line thickness. If no argument is given, current pensize - is returned. - - Example (for a Turtle instance named turtle): - >>> turtle.pensize() - 1 - >>> turtle.pensize(10) # from here on lines of width 10 are drawn - """ - if width is None: - return self._pensize - self.pen(pensize=width) - - - def penup(self): - """Pull the pen up -- no drawing when moving. - - Aliases: penup | pu | up - - No argument - - Example (for a Turtle instance named turtle): - >>> turtle.penup() - """ - if not self._drawing: - return - self.pen(pendown=False) - - def pendown(self): - """Pull the pen down -- drawing when moving. - - Aliases: pendown | pd | down - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.pendown() - """ - if self._drawing: - return - self.pen(pendown=True) - - def isdown(self): - """Return True if pen is down, False if it's up. - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.penup() - >>> turtle.isdown() - False - >>> turtle.pendown() - >>> turtle.isdown() - True - """ - return self._drawing - - def speed(self, speed=None): - """ Return or set the turtle's speed. - - Optional argument: - speed -- an integer in the range 0..10 or a speedstring (see below) - - Set the turtle's speed to an integer value in the range 0 .. 10. - If no argument is given: return current speed. - - If input is a number greater than 10 or smaller than 0.5, - speed is set to 0. - Speedstrings are mapped to speedvalues in the following way: - 'fastest' : 0 - 'fast' : 10 - 'normal' : 6 - 'slow' : 3 - 'slowest' : 1 - speeds from 1 to 10 enforce increasingly faster animation of - line drawing and turtle turning. - - Attention: - speed = 0 : *no* animation takes place. forward/back makes turtle jump - and likewise left/right make the turtle turn instantly. - - Example (for a Turtle instance named turtle): - >>> turtle.speed(3) - """ - speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 } - if speed is None: - return self._speed - if speed in speeds: - speed = speeds[speed] - elif 0.5 < speed < 10.5: - speed = int(round(speed)) - else: - speed = 0 - self.pen(speed=speed) - - def color(self, *args): - """Return or set the pencolor and fillcolor. - - Arguments: - Several input formats are allowed. - They use 0, 1, 2, or 3 arguments as follows: - - color() - Return the current pencolor and the current fillcolor - as a pair of color specification strings as are returned - by pencolor and fillcolor. - color(colorstring), color((r,g,b)), color(r,g,b) - inputs as in pencolor, set both, fillcolor and pencolor, - to the given value. - color(colorstring1, colorstring2), - color((r1,g1,b1), (r2,g2,b2)) - equivalent to pencolor(colorstring1) and fillcolor(colorstring2) - and analogously, if the other input format is used. - - If turtleshape is a polygon, outline and interior of that polygon - is drawn with the newly set colors. - For more info see: pencolor, fillcolor - - Example (for a Turtle instance named turtle): - >>> turtle.color('red', 'green') - >>> turtle.color() - ('red', 'green') - >>> colormode(255) - >>> color((40, 80, 120), (160, 200, 240)) - >>> color() - ('#285078', '#a0c8f0') - """ - if args: - l = len(args) - if l == 1: - pcolor = fcolor = args[0] - elif l == 2: - pcolor, fcolor = args - elif l == 3: - pcolor = fcolor = args - pcolor = self._colorstr(pcolor) - fcolor = self._colorstr(fcolor) - self.pen(pencolor=pcolor, fillcolor=fcolor) - else: - return self._color(self._pencolor), self._color(self._fillcolor) - - def pencolor(self, *args): - """ Return or set the pencolor. - - Arguments: - Four input formats are allowed: - - pencolor() - Return the current pencolor as color specification string, - possibly in hex-number format (see example). - May be used as input to another color/pencolor/fillcolor call. - - pencolor(colorstring) - s is a Tk color specification string, such as "red" or "yellow" - - pencolor((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range 0..colormode, - where colormode is either 1.0 or 255 - - pencolor(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range 0..colormode - - If turtleshape is a polygon, the outline of that polygon is drawn - with the newly set pencolor. - - Example (for a Turtle instance named turtle): - >>> turtle.pencolor('brown') - >>> tup = (0.2, 0.8, 0.55) - >>> turtle.pencolor(tup) - >>> turtle.pencolor() - '#33cc8c' - """ - if args: - color = self._colorstr(args) - if color == self._pencolor: - return - self.pen(pencolor=color) - else: - return self._color(self._pencolor) - - def fillcolor(self, *args): - """ Return or set the fillcolor. - - Arguments: - Four input formats are allowed: - - fillcolor() - Return the current fillcolor as color specification string, - possibly in hex-number format (see example). - May be used as input to another color/pencolor/fillcolor call. - - fillcolor(colorstring) - s is a Tk color specification string, such as "red" or "yellow" - - fillcolor((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range 0..colormode, - where colormode is either 1.0 or 255 - - fillcolor(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range 0..colormode - - If turtleshape is a polygon, the interior of that polygon is drawn - with the newly set fillcolor. - - Example (for a Turtle instance named turtle): - >>> turtle.fillcolor('violet') - >>> col = turtle.pencolor() - >>> turtle.fillcolor(col) - >>> turtle.fillcolor(0, .5, 0) - """ - if args: - color = self._colorstr(args) - if color == self._fillcolor: - return - self.pen(fillcolor=color) - else: - return self._color(self._fillcolor) - - def showturtle(self): - """Makes the turtle visible. - - Aliases: showturtle | st - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.hideturtle() - >>> turtle.showturtle() - """ - self.pen(shown=True) - - def hideturtle(self): - """Makes the turtle invisible. - - Aliases: hideturtle | ht - - No argument. - - It's a good idea to do this while you're in the - middle of a complicated drawing, because hiding - the turtle speeds up the drawing observably. - - Example (for a Turtle instance named turtle): - >>> turtle.hideturtle() - """ - self.pen(shown=False) - - def isvisible(self): - """Return True if the Turtle is shown, False if it's hidden. - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.hideturtle() - >>> print turtle.isvisible(): - False - """ - return self._shown - - def pen(self, pen=None, **pendict): - """Return or set the pen's attributes. - - Arguments: - pen -- a dictionary with some or all of the below listed keys. - **pendict -- one or more keyword-arguments with the below - listed keys as keywords. - - Return or set the pen's attributes in a 'pen-dictionary' - with the following key/value pairs: - "shown" : True/False - "pendown" : True/False - "pencolor" : color-string or color-tuple - "fillcolor" : color-string or color-tuple - "pensize" : positive number - "speed" : number in range 0..10 - "resizemode" : "auto" or "user" or "noresize" - "stretchfactor": (positive number, positive number) - "shearfactor": number - "outline" : positive number - "tilt" : number - - This dictionary can be used as argument for a subsequent - pen()-call to restore the former pen-state. Moreover one - or more of these attributes can be provided as keyword-arguments. - This can be used to set several pen attributes in one statement. - - - Examples (for a Turtle instance named turtle): - >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) - >>> turtle.pen() - {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, - 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black', - 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} - >>> penstate=turtle.pen() - >>> turtle.color("yellow","") - >>> turtle.penup() - >>> turtle.pen() - {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, - 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '', - 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} - >>> p.pen(penstate, fillcolor="green") - >>> p.pen() - {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, - 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green', - 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} - """ - _pd = {"shown" : self._shown, - "pendown" : self._drawing, - "pencolor" : self._pencolor, - "fillcolor" : self._fillcolor, - "pensize" : self._pensize, - "speed" : self._speed, - "resizemode" : self._resizemode, - "stretchfactor" : self._stretchfactor, - "shearfactor" : self._shearfactor, - "outline" : self._outlinewidth, - "tilt" : self._tilt - } - - if not (pen or pendict): - return _pd - - if isinstance(pen, dict): - p = pen - else: - p = {} - p.update(pendict) - - _p_buf = {} - for key in p: - _p_buf[key] = _pd[key] - - if self.undobuffer: - self.undobuffer.push(("pen", _p_buf)) - - newLine = False - if "pendown" in p: - if self._drawing != p["pendown"]: - newLine = True - if "pencolor" in p: - if isinstance(p["pencolor"], tuple): - p["pencolor"] = self._colorstr((p["pencolor"],)) - if self._pencolor != p["pencolor"]: - newLine = True - if "pensize" in p: - if self._pensize != p["pensize"]: - newLine = True - if newLine: - self._newLine() - if "pendown" in p: - self._drawing = p["pendown"] - if "pencolor" in p: - self._pencolor = p["pencolor"] - if "pensize" in p: - self._pensize = p["pensize"] - if "fillcolor" in p: - if isinstance(p["fillcolor"], tuple): - p["fillcolor"] = self._colorstr((p["fillcolor"],)) - self._fillcolor = p["fillcolor"] - if "speed" in p: - self._speed = p["speed"] - if "resizemode" in p: - self._resizemode = p["resizemode"] - if "stretchfactor" in p: - sf = p["stretchfactor"] - if isinstance(sf, (int, float)): - sf = (sf, sf) - self._stretchfactor = sf - if "shearfactor" in p: - self._shearfactor = p["shearfactor"] - if "outline" in p: - self._outlinewidth = p["outline"] - if "shown" in p: - self._shown = p["shown"] - if "tilt" in p: - self._tilt = p["tilt"] - if "stretchfactor" in p or "tilt" in p or "shearfactor" in p: - scx, scy = self._stretchfactor - shf = self._shearfactor - sa, ca = math.sin(self._tilt), math.cos(self._tilt) - self._shapetrafo = ( scx*ca, scy*(shf*ca + sa), - -scx*sa, scy*(ca - shf*sa)) - self._update() - -## three dummy methods to be implemented by child class: - - def _newLine(self, usePos = True): - """dummy method - to be overwritten by child class""" - def _update(self, count=True, forced=False): - """dummy method - to be overwritten by child class""" - def _color(self, args): - """dummy method - to be overwritten by child class""" - def _colorstr(self, args): - """dummy method - to be overwritten by child class""" - - width = pensize - up = penup - pu = penup - pd = pendown - down = pendown - st = showturtle - ht = hideturtle - - -class _TurtleImage(object): - """Helper class: Datatype to store Turtle attributes - """ - - def __init__(self, screen, shapeIndex): - self.screen = screen - self._type = None - self._setshape(shapeIndex) - - def _setshape(self, shapeIndex): - screen = self.screen - self.shapeIndex = shapeIndex - if self._type == "polygon" == screen._shapes[shapeIndex]._type: - return - if self._type == "image" == screen._shapes[shapeIndex]._type: - return - if self._type in ["image", "polygon"]: - screen._delete(self._item) - elif self._type == "compound": - for item in self._item: - screen._delete(item) - self._type = screen._shapes[shapeIndex]._type - if self._type == "polygon": - self._item = screen._createpoly() - elif self._type == "image": - self._item = screen._createimage(screen._shapes["blank"]._data) - elif self._type == "compound": - self._item = [screen._createpoly() for item in - screen._shapes[shapeIndex]._data] - - -class RawTurtle(TPen, TNavigator): - """Animation part of the RawTurtle. - Puts RawTurtle upon a TurtleScreen and provides tools for - its animation. - """ - screens = [] - - def __init__(self, canvas=None, - shape=_CFG["shape"], - undobuffersize=_CFG["undobuffersize"], - visible=_CFG["visible"]): - if isinstance(canvas, _Screen): - self.screen = canvas - elif isinstance(canvas, TurtleScreen): - if canvas not in RawTurtle.screens: - RawTurtle.screens.append(canvas) - self.screen = canvas - elif isinstance(canvas, (ScrolledCanvas, Canvas)): - for screen in RawTurtle.screens: - if screen.cv == canvas: - self.screen = screen - break - else: - self.screen = TurtleScreen(canvas) - RawTurtle.screens.append(self.screen) - else: - raise TurtleGraphicsError("bad canvas argument %s" % canvas) - - screen = self.screen - TNavigator.__init__(self, screen.mode()) - TPen.__init__(self) - screen._turtles.append(self) - self.drawingLineItem = screen._createline() - self.turtle = _TurtleImage(screen, shape) - self._poly = None - self._creatingPoly = False - self._fillitem = self._fillpath = None - self._shown = visible - self._hidden_from_screen = False - self.currentLineItem = screen._createline() - self.currentLine = [self._position] - self.items = [self.currentLineItem] - self.stampItems = [] - self._undobuffersize = undobuffersize - self.undobuffer = Tbuffer(undobuffersize) - self._update() - - def reset(self): - """Delete the turtle's drawings and restore its default values. - - No argument. - - Delete the turtle's drawings from the screen, re-center the turtle - and set variables to the default values. - - Example (for a Turtle instance named turtle): - >>> turtle.position() - (0.00,-22.00) - >>> turtle.heading() - 100.0 - >>> turtle.reset() - >>> turtle.position() - (0.00,0.00) - >>> turtle.heading() - 0.0 - """ - TNavigator.reset(self) - TPen._reset(self) - self._clear() - self._drawturtle() - self._update() - - def setundobuffer(self, size): - """Set or disable undobuffer. - - Argument: - size -- an integer or None - - If size is an integer an empty undobuffer of given size is installed. - Size gives the maximum number of turtle-actions that can be undone - by the undo() function. - If size is None, no undobuffer is present. - - Example (for a Turtle instance named turtle): - >>> turtle.setundobuffer(42) - """ - if size is None or size <= 0: - self.undobuffer = None - else: - self.undobuffer = Tbuffer(size) - - def undobufferentries(self): - """Return count of entries in the undobuffer. - - No argument. - - Example (for a Turtle instance named turtle): - >>> while undobufferentries(): - ... undo() - """ - if self.undobuffer is None: - return 0 - return self.undobuffer.nr_of_items() - - def _clear(self): - """Delete all of pen's drawings""" - self._fillitem = self._fillpath = None - for item in self.items: - self.screen._delete(item) - self.currentLineItem = self.screen._createline() - self.currentLine = [] - if self._drawing: - self.currentLine.append(self._position) - self.items = [self.currentLineItem] - self.clearstamps() - self.setundobuffer(self._undobuffersize) - - - def clear(self): - """Delete the turtle's drawings from the screen. Do not move turtle. - - No arguments. - - Delete the turtle's drawings from the screen. Do not move turtle. - State and position of the turtle as well as drawings of other - turtles are not affected. - - Examples (for a Turtle instance named turtle): - >>> turtle.clear() - """ - self._clear() - self._update() - - def _update_data(self): - self.screen._incrementudc() - if self.screen._updatecounter != 0: - return - if len(self.currentLine)>1: - self.screen._drawline(self.currentLineItem, self.currentLine, - self._pencolor, self._pensize) - - def _update(self): - """Perform a Turtle-data update. - """ - screen = self.screen - if screen._tracing == 0: - return - elif screen._tracing == 1: - self._update_data() - self._drawturtle() - screen._update() # TurtleScreenBase - screen._delay(screen._delayvalue) # TurtleScreenBase - else: - self._update_data() - if screen._updatecounter == 0: - for t in screen.turtles(): - t._drawturtle() - screen._update() - - def _tracer(self, flag=None, delay=None): - """Turns turtle animation on/off and set delay for update drawings. - - Optional arguments: - n -- nonnegative integer - delay -- nonnegative integer - - If n is given, only each n-th regular screen update is really performed. - (Can be used to accelerate the drawing of complex graphics.) - Second arguments sets delay value (see RawTurtle.delay()) - - Example (for a Turtle instance named turtle): - >>> turtle.tracer(8, 25) - >>> dist = 2 - >>> for i in range(200): - ... turtle.fd(dist) - ... turtle.rt(90) - ... dist += 2 - """ - return self.screen.tracer(flag, delay) - - def _color(self, args): - return self.screen._color(args) - - def _colorstr(self, args): - return self.screen._colorstr(args) - - def _cc(self, args): - """Convert colortriples to hexstrings. - """ - if isinstance(args, str): - return args - try: - r, g, b = args - except (TypeError, ValueError): - raise TurtleGraphicsError("bad color arguments: %s" % str(args)) - if self.screen._colormode == 1.0: - r, g, b = [round(255.0*x) for x in (r, g, b)] - if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): - raise TurtleGraphicsError("bad color sequence: %s" % str(args)) - return "#%02x%02x%02x" % (r, g, b) - - def clone(self): - """Create and return a clone of the turtle. - - No argument. - - Create and return a clone of the turtle with same position, heading - and turtle properties. - - Example (for a Turtle instance named mick): - mick = Turtle() - joe = mick.clone() - """ - screen = self.screen - self._newLine(self._drawing) - - turtle = self.turtle - self.screen = None - self.turtle = None # too make self deepcopy-able - - q = deepcopy(self) - - self.screen = screen - self.turtle = turtle - - q.screen = screen - q.turtle = _TurtleImage(screen, self.turtle.shapeIndex) - - screen._turtles.append(q) - ttype = screen._shapes[self.turtle.shapeIndex]._type - if ttype == "polygon": - q.turtle._item = screen._createpoly() - elif ttype == "image": - q.turtle._item = screen._createimage(screen._shapes["blank"]._data) - elif ttype == "compound": - q.turtle._item = [screen._createpoly() for item in - screen._shapes[self.turtle.shapeIndex]._data] - q.currentLineItem = screen._createline() - q._update() - return q - - def shape(self, name=None): - """Set turtle shape to shape with given name / return current shapename. - - Optional argument: - name -- a string, which is a valid shapename - - Set turtle shape to shape with given name or, if name is not given, - return name of current shape. - Shape with name must exist in the TurtleScreen's shape dictionary. - Initially there are the following polygon shapes: - 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'. - To learn about how to deal with shapes see Screen-method register_shape. - - Example (for a Turtle instance named turtle): - >>> turtle.shape() - 'arrow' - >>> turtle.shape("turtle") - >>> turtle.shape() - 'turtle' - """ - if name is None: - return self.turtle.shapeIndex - if not name in self.screen.getshapes(): - raise TurtleGraphicsError("There is no shape named %s" % name) - self.turtle._setshape(name) - self._update() - - def shapesize(self, stretch_wid=None, stretch_len=None, outline=None): - """Set/return turtle's stretchfactors/outline. Set resizemode to "user". - - Optional arguments: - stretch_wid : positive number - stretch_len : positive number - outline : positive number - - Return or set the pen's attributes x/y-stretchfactors and/or outline. - Set resizemode to "user". - If and only if resizemode is set to "user", the turtle will be displayed - stretched according to its stretchfactors: - stretch_wid is stretchfactor perpendicular to orientation - stretch_len is stretchfactor in direction of turtles orientation. - outline determines the width of the shapes's outline. - - Examples (for a Turtle instance named turtle): - >>> turtle.resizemode("user") - >>> turtle.shapesize(5, 5, 12) - >>> turtle.shapesize(outline=8) - """ - if stretch_wid is stretch_len is outline is None: - stretch_wid, stretch_len = self._stretchfactor - return stretch_wid, stretch_len, self._outlinewidth - if stretch_wid == 0 or stretch_len == 0: - raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero") - if stretch_wid is not None: - if stretch_len is None: - stretchfactor = stretch_wid, stretch_wid - else: - stretchfactor = stretch_wid, stretch_len - elif stretch_len is not None: - stretchfactor = self._stretchfactor[0], stretch_len - else: - stretchfactor = self._stretchfactor - if outline is None: - outline = self._outlinewidth - self.pen(resizemode="user", - stretchfactor=stretchfactor, outline=outline) - - def shearfactor(self, shear=None): - """Set or return the current shearfactor. - - Optional argument: shear -- number, tangent of the shear angle - - Shear the turtleshape according to the given shearfactor shear, - which is the tangent of the shear angle. DO NOT change the - turtle's heading (direction of movement). - If shear is not given: return the current shearfactor, i. e. the - tangent of the shear angle, by which lines parallel to the - heading of the turtle are sheared. - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("circle") - >>> turtle.shapesize(5,2) - >>> turtle.shearfactor(0.5) - >>> turtle.shearfactor() - >>> 0.5 - """ - if shear is None: - return self._shearfactor - self.pen(resizemode="user", shearfactor=shear) - - def settiltangle(self, angle): - """Rotate the turtleshape to point in the specified direction - - Argument: angle -- number - - Rotate the turtleshape to point in the direction specified by angle, - regardless of its current tilt-angle. DO NOT change the turtle's - heading (direction of movement). - - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("circle") - >>> turtle.shapesize(5,2) - >>> turtle.settiltangle(45) - >>> stamp() - >>> turtle.fd(50) - >>> turtle.settiltangle(-45) - >>> stamp() - >>> turtle.fd(50) - """ - tilt = -angle * self._degreesPerAU * self._angleOrient - tilt = (tilt * math.pi / 180.0) % (2*math.pi) - self.pen(resizemode="user", tilt=tilt) - - def tiltangle(self, angle=None): - """Set or return the current tilt-angle. - - Optional argument: angle -- number - - Rotate the turtleshape to point in the direction specified by angle, - regardless of its current tilt-angle. DO NOT change the turtle's - heading (direction of movement). - If angle is not given: return the current tilt-angle, i. e. the angle - between the orientation of the turtleshape and the heading of the - turtle (its direction of movement). - + turtle is headed. Do not change the turtle's heading. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 0.00) + >>> turtle.backward(30) + >>> turtle.position() + (-30.00, 0.00) + """ + self._go(-distance) + + def right(self, angle): + """Turn turtle right by angle units. + + Aliases: right | rt + + Argument: + angle -- a number (integer or float) + + Turn turtle right by angle units. (Units are by default degrees, + but can be set via the degrees() and radians() functions.) + Angle orientation depends on mode. (See this.) + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 22.0 + >>> turtle.right(45) + >>> turtle.heading() + 337.0 + """ + self._rotate(-angle) + + def left(self, angle): + """Turn turtle left by angle units. + + Aliases: left | lt + + Argument: + angle -- a number (integer or float) + + Turn turtle left by angle units. (Units are by default degrees, + but can be set via the degrees() and radians() functions.) + Angle orientation depends on mode. (See this.) + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 22.0 + >>> turtle.left(45) + >>> turtle.heading() + 67.0 + """ + self._rotate(angle) + + def pos(self): + """Return the turtle's current location (x,y), as a Vec2D-vector. + + Aliases: pos | position + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (0.00, 240.00) + """ + return self._position + + def xcor(self): + """ Return the turtle's x coordinate. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.xcor() + 50.0 + """ + return self._position[0] + + def ycor(self): + """ Return the turtle's y coordinate + --- + No arguments. + + Example (for a Turtle instance named turtle): + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.ycor() + 86.6025403784 + """ + return self._position[1] + + + def goto(self, x, y=None): + """Move turtle to an absolute position. + + Aliases: setpos | setposition | goto: + + Arguments: + x -- a number or a pair/vector of numbers + y -- a number None + + call: goto(x, y) # two coordinates + --or: goto((x, y)) # a pair (tuple) of coordinates + --or: goto(vec) # e.g. as returned by pos() + + Move turtle to an absolute position. If the pen is down, + a line will be drawn. The turtle's orientation does not change. + + Example (for a Turtle instance named turtle): + >>> tp = turtle.pos() + >>> tp + (0.00, 0.00) + >>> turtle.setpos(60,30) + >>> turtle.pos() + (60.00,30.00) + >>> turtle.setpos((20,80)) + >>> turtle.pos() + (20.00,80.00) + >>> turtle.setpos(tp) + >>> turtle.pos() + (0.00,0.00) + """ + if y is None: + self._goto(Vec2D(*x)) + else: + self._goto(Vec2D(x, y)) + + def home(self): + """Move turtle to the origin - coordinates (0,0). + + No arguments. + + Move turtle to the origin - coordinates (0,0) and set its + heading to its start-orientation (which depends on mode). + + Example (for a Turtle instance named turtle): + >>> turtle.home() + """ + self.goto(0, 0) + self.setheading(0) + + def setx(self, x): + """Set the turtle's first coordinate to x + + Argument: + x -- a number (integer or float) + + Set the turtle's first coordinate to x, leave second coordinate + unchanged. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 240.00) + >>> turtle.setx(10) + >>> turtle.position() + (10.00, 240.00) + """ + self._goto(Vec2D(x, self._position[1])) + + def sety(self, y): + """Set the turtle's second coordinate to y + + Argument: + y -- a number (integer or float) + + Set the turtle's first coordinate to x, second coordinate remains + unchanged. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 40.00) + >>> turtle.sety(-10) + >>> turtle.position() + (0.00, -10.00) + """ + self._goto(Vec2D(self._position[0], y)) + + def distance(self, x, y=None): + """Return the distance from the turtle to (x,y) in turtle step units. + + Arguments: + x -- a number or a pair/vector of numbers or a turtle instance + y -- a number None None + + call: distance(x, y) # two coordinates + --or: distance((x, y)) # a pair (tuple) of coordinates + --or: distance(vec) # e.g. as returned by pos() + --or: distance(mypen) # where mypen is another turtle + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (0.00, 0.00) + >>> turtle.distance(30,40) + 50.0 + >>> pen = Turtle() + >>> pen.forward(77) + >>> turtle.distance(pen) + 77.0 + """ + if y is not None: + pos = Vec2D(x, y) + if isinstance(x, Vec2D): + pos = x + elif isinstance(x, tuple): + pos = Vec2D(*x) + elif isinstance(x, TNavigator): + pos = x._position + return abs(pos - self._position) + + def towards(self, x, y=None): + """Return the angle of the line from the turtle's position to (x, y). + + Arguments: + x -- a number or a pair/vector of numbers or a turtle instance + y -- a number None None + + call: distance(x, y) # two coordinates + --or: distance((x, y)) # a pair (tuple) of coordinates + --or: distance(vec) # e.g. as returned by pos() + --or: distance(mypen) # where mypen is another turtle + + Return the angle, between the line from turtle-position to position + specified by x, y and the turtle's start orientation. (Depends on + modes - "standard" or "logo") + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (10.00, 10.00) + >>> turtle.towards(0,0) + 225.0 + """ + if y is not None: + pos = Vec2D(x, y) + if isinstance(x, Vec2D): + pos = x + elif isinstance(x, tuple): + pos = Vec2D(*x) + elif isinstance(x, TNavigator): + pos = x._position + x, y = pos - self._position + result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 + result /= self._degreesPerAU + return (self._angleOffset + self._angleOrient*result) % self._fullcircle + + def heading(self): + """ Return the turtle's current heading. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.left(67) + >>> turtle.heading() + 67.0 + """ + x, y = self._orient + result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 + result /= self._degreesPerAU + return (self._angleOffset + self._angleOrient*result) % self._fullcircle + + def setheading(self, to_angle): + """Set the orientation of the turtle to to_angle. + + Aliases: setheading | seth + + Argument: + to_angle -- a number (integer or float) + + Set the orientation of the turtle to to_angle. + Here are some common directions in degrees: + + standard - mode: logo-mode: + -------------------|-------------------- + 0 - east 0 - north + 90 - north 90 - east + 180 - west 180 - south + 270 - south 270 - west + + Example (for a Turtle instance named turtle): + >>> turtle.setheading(90) + >>> turtle.heading() + 90 + """ + angle = (to_angle - self.heading())*self._angleOrient + full = self._fullcircle + angle = (angle+full/2.)%full - full/2. + self._rotate(angle) + + def circle(self, radius, extent = None, steps = None): + """ Draw a circle with given radius. + + Arguments: + radius -- a number + extent (optional) -- a number + steps (optional) -- an integer + + Draw a circle with given radius. The center is radius units left + of the turtle; extent - an angle - determines which part of the + circle is drawn. If extent is not given, draw the entire circle. + If extent is not a full circle, one endpoint of the arc is the + current pen position. Draw the arc in counterclockwise direction + if radius is positive, otherwise in clockwise direction. Finally + the direction of the turtle is changed by the amount of extent. + + As the circle is approximated by an inscribed regular polygon, + steps determines the number of steps to use. If not given, + it will be calculated automatically. Maybe used to draw regular + polygons. + + call: circle(radius) # full circle + --or: circle(radius, extent) # arc + --or: circle(radius, extent, steps) + --or: circle(radius, steps=6) # 6-sided polygon + + Example (for a Turtle instance named turtle): + >>> turtle.circle(50) + >>> turtle.circle(120, 180) # semicircle + """ + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + speed = self.speed() + if extent is None: + extent = self._fullcircle + if steps is None: + frac = abs(extent)/self._fullcircle + steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac) + w = 1.0 * extent / steps + w2 = 0.5 * w + l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU) + if radius < 0: + l, w, w2 = -l, -w, -w2 + tr = self._tracer() + dl = self._delay() + if speed == 0: + self._tracer(0, 0) + else: + self.speed(0) + self._rotate(w2) + for i in range(steps): + self.speed(speed) + self._go(l) + self.speed(0) + self._rotate(w) + self._rotate(-w2) + if speed == 0: + self._tracer(tr, dl) + self.speed(speed) + if self.undobuffer: + self.undobuffer.cumulate = False + +## three dummy methods to be implemented by child class: + + def speed(self, s=0): + """dummy method - to be overwritten by child class""" + def _tracer(self, a=None, b=None): + """dummy method - to be overwritten by child class""" + def _delay(self, n=None): + """dummy method - to be overwritten by child class""" + + fd = forward + bk = back + backward = back + rt = right + lt = left + position = pos + setpos = goto + setposition = goto + seth = setheading + + +class TPen(object): + """Drawing part of the RawTurtle. + Implements drawing properties. + """ + def __init__(self, resizemode=_CFG["resizemode"]): + self._resizemode = resizemode # or "user" or "noresize" + self.undobuffer = None + TPen._reset(self) + + def _reset(self, pencolor=_CFG["pencolor"], + fillcolor=_CFG["fillcolor"]): + self._pensize = 1 + self._shown = True + self._pencolor = pencolor + self._fillcolor = fillcolor + self._drawing = True + self._speed = 3 + self._stretchfactor = (1., 1.) + self._shearfactor = 0. + self._tilt = 0. + self._shapetrafo = (1., 0., 0., 1.) + self._outlinewidth = 1 + + def resizemode(self, rmode=None): + """Set resizemode to one of the values: "auto", "user", "noresize". + + (Optional) Argument: + rmode -- one of the strings "auto", "user", "noresize" + + Different resizemodes have the following effects: + - "auto" adapts the appearance of the turtle + corresponding to the value of pensize. + - "user" adapts the appearance of the turtle according to the + values of stretchfactor and outlinewidth (outline), + which are set by shapesize() + - "noresize" no adaption of the turtle's appearance takes place. + If no argument is given, return current resizemode. + resizemode("user") is called by a call of shapesize with arguments. + + + Examples (for a Turtle instance named turtle): + >>> turtle.resizemode("noresize") + >>> turtle.resizemode() + 'noresize' + """ + if rmode is None: + return self._resizemode + rmode = rmode.lower() + if rmode in ["auto", "user", "noresize"]: + self.pen(resizemode=rmode) + + def pensize(self, width=None): + """Set or return the line thickness. + + Aliases: pensize | width + + Argument: + width -- positive number + + Set the line thickness to width or return it. If resizemode is set + to "auto" and turtleshape is a polygon, that polygon is drawn with + the same line thickness. If no argument is given, current pensize + is returned. + + Example (for a Turtle instance named turtle): + >>> turtle.pensize() + 1 + >>> turtle.pensize(10) # from here on lines of width 10 are drawn + """ + if width is None: + return self._pensize + self.pen(pensize=width) + + + def penup(self): + """Pull the pen up -- no drawing when moving. + + Aliases: penup | pu | up + + No argument + + Example (for a Turtle instance named turtle): + >>> turtle.penup() + """ + if not self._drawing: + return + self.pen(pendown=False) + + def pendown(self): + """Pull the pen down -- drawing when moving. + + Aliases: pendown | pd | down + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.pendown() + """ + if self._drawing: + return + self.pen(pendown=True) + + def isdown(self): + """Return True if pen is down, False if it's up. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.penup() + >>> turtle.isdown() + False + >>> turtle.pendown() + >>> turtle.isdown() + True + """ + return self._drawing + + def speed(self, speed=None): + """ Return or set the turtle's speed. + + Optional argument: + speed -- an integer in the range 0..10 or a speedstring (see below) + + Set the turtle's speed to an integer value in the range 0 .. 10. + If no argument is given: return current speed. + + If input is a number greater than 10 or smaller than 0.5, + speed is set to 0. + Speedstrings are mapped to speedvalues in the following way: + 'fastest' : 0 + 'fast' : 10 + 'normal' : 6 + 'slow' : 3 + 'slowest' : 1 + speeds from 1 to 10 enforce increasingly faster animation of + line drawing and turtle turning. + + Attention: + speed = 0 : *no* animation takes place. forward/back makes turtle jump + and likewise left/right make the turtle turn instantly. + + Example (for a Turtle instance named turtle): + >>> turtle.speed(3) + """ + speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 } + if speed is None: + return self._speed + if speed in speeds: + speed = speeds[speed] + elif 0.5 < speed < 10.5: + speed = int(round(speed)) + else: + speed = 0 + self.pen(speed=speed) + + def color(self, *args): + """Return or set the pencolor and fillcolor. + + Arguments: + Several input formats are allowed. + They use 0, 1, 2, or 3 arguments as follows: + + color() + Return the current pencolor and the current fillcolor + as a pair of color specification strings as are returned + by pencolor and fillcolor. + color(colorstring), color((r,g,b)), color(r,g,b) + inputs as in pencolor, set both, fillcolor and pencolor, + to the given value. + color(colorstring1, colorstring2), + color((r1,g1,b1), (r2,g2,b2)) + equivalent to pencolor(colorstring1) and fillcolor(colorstring2) + and analogously, if the other input format is used. + + If turtleshape is a polygon, outline and interior of that polygon + is drawn with the newly set colors. + For more info see: pencolor, fillcolor + + Example (for a Turtle instance named turtle): + >>> turtle.color('red', 'green') + >>> turtle.color() + ('red', 'green') + >>> colormode(255) + >>> color((40, 80, 120), (160, 200, 240)) + >>> color() + ('#285078', '#a0c8f0') + """ + if args: + l = len(args) + if l == 1: + pcolor = fcolor = args[0] + elif l == 2: + pcolor, fcolor = args + elif l == 3: + pcolor = fcolor = args + pcolor = self._colorstr(pcolor) + fcolor = self._colorstr(fcolor) + self.pen(pencolor=pcolor, fillcolor=fcolor) + else: + return self._color(self._pencolor), self._color(self._fillcolor) + + def pencolor(self, *args): + """ Return or set the pencolor. + + Arguments: + Four input formats are allowed: + - pencolor() + Return the current pencolor as color specification string, + possibly in hex-number format (see example). + May be used as input to another color/pencolor/fillcolor call. + - pencolor(colorstring) + s is a Tk color specification string, such as "red" or "yellow" + - pencolor((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range 0..colormode, + where colormode is either 1.0 or 255 + - pencolor(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range 0..colormode + + If turtleshape is a polygon, the outline of that polygon is drawn + with the newly set pencolor. + + Example (for a Turtle instance named turtle): + >>> turtle.pencolor('brown') + >>> tup = (0.2, 0.8, 0.55) + >>> turtle.pencolor(tup) + >>> turtle.pencolor() + '#33cc8c' + """ + if args: + color = self._colorstr(args) + if color == self._pencolor: + return + self.pen(pencolor=color) + else: + return self._color(self._pencolor) + + def fillcolor(self, *args): + """ Return or set the fillcolor. + + Arguments: + Four input formats are allowed: + - fillcolor() + Return the current fillcolor as color specification string, + possibly in hex-number format (see example). + May be used as input to another color/pencolor/fillcolor call. + - fillcolor(colorstring) + s is a Tk color specification string, such as "red" or "yellow" + - fillcolor((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range 0..colormode, + where colormode is either 1.0 or 255 + - fillcolor(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range 0..colormode + + If turtleshape is a polygon, the interior of that polygon is drawn + with the newly set fillcolor. + + Example (for a Turtle instance named turtle): + >>> turtle.fillcolor('violet') + >>> col = turtle.pencolor() + >>> turtle.fillcolor(col) + >>> turtle.fillcolor(0, .5, 0) + """ + if args: + color = self._colorstr(args) + if color == self._fillcolor: + return + self.pen(fillcolor=color) + else: + return self._color(self._fillcolor) + + def showturtle(self): + """Makes the turtle visible. + + Aliases: showturtle | st + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + >>> turtle.showturtle() + """ + self.pen(shown=True) + + def hideturtle(self): + """Makes the turtle invisible. + + Aliases: hideturtle | ht + + No argument. + + It's a good idea to do this while you're in the + middle of a complicated drawing, because hiding + the turtle speeds up the drawing observably. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + """ + self.pen(shown=False) + + def isvisible(self): + """Return True if the Turtle is shown, False if it's hidden. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + >>> print turtle.isvisible(): + False + """ + return self._shown + + def pen(self, pen=None, **pendict): + """Return or set the pen's attributes. + + Arguments: + pen -- a dictionary with some or all of the below listed keys. + **pendict -- one or more keyword-arguments with the below + listed keys as keywords. + + Return or set the pen's attributes in a 'pen-dictionary' + with the following key/value pairs: + "shown" : True/False + "pendown" : True/False + "pencolor" : color-string or color-tuple + "fillcolor" : color-string or color-tuple + "pensize" : positive number + "speed" : number in range 0..10 + "resizemode" : "auto" or "user" or "noresize" + "stretchfactor": (positive number, positive number) + "shearfactor": number + "outline" : positive number + "tilt" : number + + This dictionary can be used as argument for a subsequent + pen()-call to restore the former pen-state. Moreover one + or more of these attributes can be provided as keyword-arguments. + This can be used to set several pen attributes in one statement. + + + Examples (for a Turtle instance named turtle): + >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black', + 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} + >>> penstate=turtle.pen() + >>> turtle.color("yellow","") + >>> turtle.penup() + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '', + 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} + >>> p.pen(penstate, fillcolor="green") + >>> p.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green', + 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} + """ + _pd = {"shown" : self._shown, + "pendown" : self._drawing, + "pencolor" : self._pencolor, + "fillcolor" : self._fillcolor, + "pensize" : self._pensize, + "speed" : self._speed, + "resizemode" : self._resizemode, + "stretchfactor" : self._stretchfactor, + "shearfactor" : self._shearfactor, + "outline" : self._outlinewidth, + "tilt" : self._tilt + } + + if not (pen or pendict): + return _pd + + if isinstance(pen, dict): + p = pen + else: + p = {} + p.update(pendict) + + _p_buf = {} + for key in p: + _p_buf[key] = _pd[key] + + if self.undobuffer: + self.undobuffer.push(("pen", _p_buf)) + + newLine = False + if "pendown" in p: + if self._drawing != p["pendown"]: + newLine = True + if "pencolor" in p: + if isinstance(p["pencolor"], tuple): + p["pencolor"] = self._colorstr((p["pencolor"],)) + if self._pencolor != p["pencolor"]: + newLine = True + if "pensize" in p: + if self._pensize != p["pensize"]: + newLine = True + if newLine: + self._newLine() + if "pendown" in p: + self._drawing = p["pendown"] + if "pencolor" in p: + self._pencolor = p["pencolor"] + if "pensize" in p: + self._pensize = p["pensize"] + if "fillcolor" in p: + if isinstance(p["fillcolor"], tuple): + p["fillcolor"] = self._colorstr((p["fillcolor"],)) + self._fillcolor = p["fillcolor"] + if "speed" in p: + self._speed = p["speed"] + if "resizemode" in p: + self._resizemode = p["resizemode"] + if "stretchfactor" in p: + sf = p["stretchfactor"] + if isinstance(sf, (int, float)): + sf = (sf, sf) + self._stretchfactor = sf + if "shearfactor" in p: + self._shearfactor = p["shearfactor"] + if "outline" in p: + self._outlinewidth = p["outline"] + if "shown" in p: + self._shown = p["shown"] + if "tilt" in p: + self._tilt = p["tilt"] + if "stretchfactor" in p or "tilt" in p or "shearfactor" in p: + scx, scy = self._stretchfactor + shf = self._shearfactor + sa, ca = math.sin(self._tilt), math.cos(self._tilt) + self._shapetrafo = ( scx*ca, scy*(shf*ca + sa), + -scx*sa, scy*(ca - shf*sa)) + self._update() + +## three dummy methods to be implemented by child class: + + def _newLine(self, usePos = True): + """dummy method - to be overwritten by child class""" + def _update(self, count=True, forced=False): + """dummy method - to be overwritten by child class""" + def _color(self, args): + """dummy method - to be overwritten by child class""" + def _colorstr(self, args): + """dummy method - to be overwritten by child class""" + + width = pensize + up = penup + pu = penup + pd = pendown + down = pendown + st = showturtle + ht = hideturtle + + +class _TurtleImage(object): + """Helper class: Datatype to store Turtle attributes + """ + + def __init__(self, screen, shapeIndex): + self.screen = screen + self._type = None + self._setshape(shapeIndex) + + def _setshape(self, shapeIndex): + screen = self.screen + self.shapeIndex = shapeIndex + if self._type == "polygon" == screen._shapes[shapeIndex]._type: + return + if self._type == "image" == screen._shapes[shapeIndex]._type: + return + if self._type in ["image", "polygon"]: + screen._delete(self._item) + elif self._type == "compound": + for item in self._item: + screen._delete(item) + self._type = screen._shapes[shapeIndex]._type + if self._type == "polygon": + self._item = screen._createpoly() + elif self._type == "image": + self._item = screen._createimage(screen._shapes["blank"]._data) + elif self._type == "compound": + self._item = [screen._createpoly() for item in + screen._shapes[shapeIndex]._data] + + +class RawTurtle(TPen, TNavigator): + """Animation part of the RawTurtle. + Puts RawTurtle upon a TurtleScreen and provides tools for + its animation. + """ + screens = [] + + def __init__(self, canvas=None, + shape=_CFG["shape"], + undobuffersize=_CFG["undobuffersize"], + visible=_CFG["visible"]): + if isinstance(canvas, _Screen): + self.screen = canvas + elif isinstance(canvas, TurtleScreen): + if canvas not in RawTurtle.screens: + RawTurtle.screens.append(canvas) + self.screen = canvas + elif isinstance(canvas, (ScrolledCanvas, Canvas)): + for screen in RawTurtle.screens: + if screen.cv == canvas: + self.screen = screen + break + else: + self.screen = TurtleScreen(canvas) + RawTurtle.screens.append(self.screen) + else: + raise TurtleGraphicsError("bad canvas argument %s" % canvas) + + screen = self.screen + TNavigator.__init__(self, screen.mode()) + TPen.__init__(self) + screen._turtles.append(self) + self.drawingLineItem = screen._createline() + self.turtle = _TurtleImage(screen, shape) + self._poly = None + self._creatingPoly = False + self._fillitem = self._fillpath = None + self._shown = visible + self._hidden_from_screen = False + self.currentLineItem = screen._createline() + self.currentLine = [self._position] + self.items = [self.currentLineItem] + self.stampItems = [] + self._undobuffersize = undobuffersize + self.undobuffer = Tbuffer(undobuffersize) + self._update() + + def reset(self): + """Delete the turtle's drawings and restore its default values. + + No argument. + + Delete the turtle's drawings from the screen, re-center the turtle + and set variables to the default values. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00,-22.00) + >>> turtle.heading() + 100.0 + >>> turtle.reset() + >>> turtle.position() + (0.00,0.00) + >>> turtle.heading() + 0.0 + """ + TNavigator.reset(self) + TPen._reset(self) + self._clear() + self._drawturtle() + self._update() + + def setundobuffer(self, size): + """Set or disable undobuffer. + + Argument: + size -- an integer or None + + If size is an integer an empty undobuffer of given size is installed. + Size gives the maximum number of turtle-actions that can be undone + by the undo() function. + If size is None, no undobuffer is present. + + Example (for a Turtle instance named turtle): + >>> turtle.setundobuffer(42) + """ + if size is None or size <= 0: + self.undobuffer = None + else: + self.undobuffer = Tbuffer(size) + + def undobufferentries(self): + """Return count of entries in the undobuffer. + + No argument. + + Example (for a Turtle instance named turtle): + >>> while undobufferentries(): + ... undo() + """ + if self.undobuffer is None: + return 0 + return self.undobuffer.nr_of_items() + + def _clear(self): + """Delete all of pen's drawings""" + self._fillitem = self._fillpath = None + for item in self.items: + self.screen._delete(item) + self.currentLineItem = self.screen._createline() + self.currentLine = [] + if self._drawing: + self.currentLine.append(self._position) + self.items = [self.currentLineItem] + self.clearstamps() + self.setundobuffer(self._undobuffersize) + + + def clear(self): + """Delete the turtle's drawings from the screen. Do not move turtle. + + No arguments. + + Delete the turtle's drawings from the screen. Do not move turtle. + State and position of the turtle as well as drawings of other + turtles are not affected. + + Examples (for a Turtle instance named turtle): + >>> turtle.clear() + """ + self._clear() + self._update() + + def _update_data(self): + self.screen._incrementudc() + if self.screen._updatecounter != 0: + return + if len(self.currentLine)>1: + self.screen._drawline(self.currentLineItem, self.currentLine, + self._pencolor, self._pensize) + + def _update(self): + """Perform a Turtle-data update. + """ + screen = self.screen + if screen._tracing == 0: + return + elif screen._tracing == 1: + self._update_data() + self._drawturtle() + screen._update() # TurtleScreenBase + screen._delay(screen._delayvalue) # TurtleScreenBase + else: + self._update_data() + if screen._updatecounter == 0: + for t in screen.turtles(): + t._drawturtle() + screen._update() + + def _tracer(self, flag=None, delay=None): + """Turns turtle animation on/off and set delay for update drawings. + + Optional arguments: + n -- nonnegative integer + delay -- nonnegative integer + + If n is given, only each n-th regular screen update is really performed. + (Can be used to accelerate the drawing of complex graphics.) + Second arguments sets delay value (see RawTurtle.delay()) + + Example (for a Turtle instance named turtle): + >>> turtle.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + ... turtle.fd(dist) + ... turtle.rt(90) + ... dist += 2 + """ + return self.screen.tracer(flag, delay) + + def _color(self, args): + return self.screen._color(args) + + def _colorstr(self, args): + return self.screen._colorstr(args) + + def _cc(self, args): + """Convert colortriples to hexstrings. + """ + if isinstance(args, str): + return args + try: + r, g, b = args + except (TypeError, ValueError): + raise TurtleGraphicsError("bad color arguments: %s" % str(args)) + if self.screen._colormode == 1.0: + r, g, b = [round(255.0*x) for x in (r, g, b)] + if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): + raise TurtleGraphicsError("bad color sequence: %s" % str(args)) + return "#%02x%02x%02x" % (r, g, b) + + def clone(self): + """Create and return a clone of the turtle. + + No argument. + + Create and return a clone of the turtle with same position, heading + and turtle properties. + + Example (for a Turtle instance named mick): + mick = Turtle() + joe = mick.clone() + """ + screen = self.screen + self._newLine(self._drawing) + + turtle = self.turtle + self.screen = None + self.turtle = None # too make self deepcopy-able + + q = deepcopy(self) + + self.screen = screen + self.turtle = turtle + + q.screen = screen + q.turtle = _TurtleImage(screen, self.turtle.shapeIndex) + + screen._turtles.append(q) + ttype = screen._shapes[self.turtle.shapeIndex]._type + if ttype == "polygon": + q.turtle._item = screen._createpoly() + elif ttype == "image": + q.turtle._item = screen._createimage(screen._shapes["blank"]._data) + elif ttype == "compound": + q.turtle._item = [screen._createpoly() for item in + screen._shapes[self.turtle.shapeIndex]._data] + q.currentLineItem = screen._createline() + q._update() + return q + + def shape(self, name=None): + """Set turtle shape to shape with given name / return current shapename. + + Optional argument: + name -- a string, which is a valid shapename + + Set turtle shape to shape with given name or, if name is not given, + return name of current shape. + Shape with name must exist in the TurtleScreen's shape dictionary. + Initially there are the following polygon shapes: + 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'. + To learn about how to deal with shapes see Screen-method register_shape. + + Example (for a Turtle instance named turtle): + >>> turtle.shape() + 'arrow' + >>> turtle.shape("turtle") + >>> turtle.shape() + 'turtle' + """ + if name is None: + return self.turtle.shapeIndex + if not name in self.screen.getshapes(): + raise TurtleGraphicsError("There is no shape named %s" % name) + self.turtle._setshape(name) + self._update() + + def shapesize(self, stretch_wid=None, stretch_len=None, outline=None): + """Set/return turtle's stretchfactors/outline. Set resizemode to "user". + + Optional arguments: + stretch_wid : positive number + stretch_len : positive number + outline : positive number + + Return or set the pen's attributes x/y-stretchfactors and/or outline. + Set resizemode to "user". + If and only if resizemode is set to "user", the turtle will be displayed + stretched according to its stretchfactors: + stretch_wid is stretchfactor perpendicular to orientation + stretch_len is stretchfactor in direction of turtles orientation. + outline determines the width of the shapes's outline. + + Examples (for a Turtle instance named turtle): + >>> turtle.resizemode("user") + >>> turtle.shapesize(5, 5, 12) + >>> turtle.shapesize(outline=8) + """ + if stretch_wid is stretch_len is outline is None: + stretch_wid, stretch_len = self._stretchfactor + return stretch_wid, stretch_len, self._outlinewidth + if stretch_wid == 0 or stretch_len == 0: + raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero") + if stretch_wid is not None: + if stretch_len is None: + stretchfactor = stretch_wid, stretch_wid + else: + stretchfactor = stretch_wid, stretch_len + elif stretch_len is not None: + stretchfactor = self._stretchfactor[0], stretch_len + else: + stretchfactor = self._stretchfactor + if outline is None: + outline = self._outlinewidth + self.pen(resizemode="user", + stretchfactor=stretchfactor, outline=outline) + + def shearfactor(self, shear=None): + """Set or return the current shearfactor. + + Optional argument: shear -- number, tangent of the shear angle + + Shear the turtleshape according to the given shearfactor shear, + which is the tangent of the shear angle. DO NOT change the + turtle's heading (direction of movement). + If shear is not given: return the current shearfactor, i. e. the + tangent of the shear angle, by which lines parallel to the + heading of the turtle are sheared. + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.shearfactor(0.5) + >>> turtle.shearfactor() + >>> 0.5 + """ + if shear is None: + return self._shearfactor + self.pen(resizemode="user", shearfactor=shear) + + def settiltangle(self, angle): + """Rotate the turtleshape to point in the specified direction + + Argument: angle -- number + + Rotate the turtleshape to point in the direction specified by angle, + regardless of its current tilt-angle. DO NOT change the turtle's + heading (direction of movement). + + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.settiltangle(45) + >>> stamp() + >>> turtle.fd(50) + >>> turtle.settiltangle(-45) + >>> stamp() + >>> turtle.fd(50) + """ + tilt = -angle * self._degreesPerAU * self._angleOrient + tilt = (tilt * math.pi / 180.0) % (2*math.pi) + self.pen(resizemode="user", tilt=tilt) + + def tiltangle(self, angle=None): + """Set or return the current tilt-angle. + + Optional argument: angle -- number + + Rotate the turtleshape to point in the direction specified by angle, + regardless of its current tilt-angle. DO NOT change the turtle's + heading (direction of movement). + If angle is not given: return the current tilt-angle, i. e. the angle + between the orientation of the turtleshape and the heading of the + turtle (its direction of movement). + (Incorrectly marked as deprecated since Python 3.1, it is really settiltangle that is deprecated.) - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("circle") - >>> turtle.shapesize(5,2) - >>> turtle.tilt(45) - >>> turtle.tiltangle() - """ - if angle is None: - tilt = -self._tilt * (180.0/math.pi) * self._angleOrient - return (tilt / self._degreesPerAU) % self._fullcircle - else: - self.settiltangle(angle) - - def tilt(self, angle): - """Rotate the turtleshape by angle. - - Argument: - angle - a number - - Rotate the turtleshape by angle from its current tilt-angle, - but do NOT change the turtle's heading (direction of movement). - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("circle") - >>> turtle.shapesize(5,2) - >>> turtle.tilt(30) - >>> turtle.fd(50) - >>> turtle.tilt(30) - >>> turtle.fd(50) - """ - self.settiltangle(angle + self.tiltangle()) - - def shapetransform(self, t11=None, t12=None, t21=None, t22=None): - """Set or return the current transformation matrix of the turtle shape. - - Optional arguments: t11, t12, t21, t22 -- numbers. - - If none of the matrix elements are given, return the transformation - matrix. - Otherwise set the given elements and transform the turtleshape - according to the matrix consisting of first row t11, t12 and - second row t21, 22. - Modify stretchfactor, shearfactor and tiltangle according to the - given matrix. - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("square") - >>> turtle.shapesize(4,2) - >>> turtle.shearfactor(-0.5) - >>> turtle.shapetransform() - (4.0, -1.0, -0.0, 2.0) - """ - if t11 is t12 is t21 is t22 is None: - return self._shapetrafo - m11, m12, m21, m22 = self._shapetrafo - if t11 is not None: m11 = t11 - if t12 is not None: m12 = t12 - if t21 is not None: m21 = t21 - if t22 is not None: m22 = t22 - if t11 * t22 - t12 * t21 == 0: - raise TurtleGraphicsError("Bad shape transform matrix: must not be singular") - self._shapetrafo = (m11, m12, m21, m22) - alfa = math.atan2(-m21, m11) % (2 * math.pi) - sa, ca = math.sin(alfa), math.cos(alfa) - a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22, - sa*m11 + ca*m21, sa*m12 + ca*m22) - self._stretchfactor = a11, a22 - self._shearfactor = a12/a22 - self._tilt = alfa - self.pen(resizemode="user") - - - def _polytrafo(self, poly): - """Computes transformed polygon shapes from a shape - according to current position and heading. - """ - screen = self.screen - p0, p1 = self._position - e0, e1 = self._orient - e = Vec2D(e0, e1 * screen.yscale / screen.xscale) - e0, e1 = (1.0 / abs(e)) * e - return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale) - for (x, y) in poly] - - def get_shapepoly(self): - """Return the current shape polygon as tuple of coordinate pairs. - - No argument. - - Examples (for a Turtle instance named turtle): - >>> turtle.shape("square") - >>> turtle.shapetransform(4, -1, 0, 2) - >>> turtle.get_shapepoly() - ((50, -20), (30, 20), (-50, 20), (-30, -20)) - - """ - shape = self.screen._shapes[self.turtle.shapeIndex] - if shape._type == "polygon": - return self._getshapepoly(shape._data, shape._type == "compound") - # else return None - - def _getshapepoly(self, polygon, compound=False): - """Calculate transformed shape polygon according to resizemode - and shapetransform. - """ - if self._resizemode == "user" or compound: - t11, t12, t21, t22 = self._shapetrafo - elif self._resizemode == "auto": - l = max(1, self._pensize/5.0) - t11, t12, t21, t22 = l, 0, 0, l - elif self._resizemode == "noresize": - return polygon - return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon) - - def _drawturtle(self): - """Manages the correct rendering of the turtle with respect to - its shape, resizemode, stretch and tilt etc.""" - screen = self.screen - shape = screen._shapes[self.turtle.shapeIndex] - ttype = shape._type - titem = self.turtle._item - if self._shown and screen._updatecounter == 0 and screen._tracing > 0: - self._hidden_from_screen = False - tshape = shape._data - if ttype == "polygon": - if self._resizemode == "noresize": w = 1 - elif self._resizemode == "auto": w = self._pensize - else: w =self._outlinewidth - shape = self._polytrafo(self._getshapepoly(tshape)) - fc, oc = self._fillcolor, self._pencolor - screen._drawpoly(titem, shape, fill=fc, outline=oc, - width=w, top=True) - elif ttype == "image": - screen._drawimage(titem, self._position, tshape) - elif ttype == "compound": - for item, (poly, fc, oc) in zip(titem, tshape): - poly = self._polytrafo(self._getshapepoly(poly, True)) - screen._drawpoly(item, poly, fill=self._cc(fc), - outline=self._cc(oc), width=self._outlinewidth, top=True) - else: - if self._hidden_from_screen: - return - if ttype == "polygon": - screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "") - elif ttype == "image": - screen._drawimage(titem, self._position, - screen._shapes["blank"]._data) - elif ttype == "compound": - for item in titem: - screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "") - self._hidden_from_screen = True - -############################## stamp stuff ############################### - - def stamp(self): - """Stamp a copy of the turtleshape onto the canvas and return its id. - - No argument. - - Stamp a copy of the turtle shape onto the canvas at the current - turtle position. Return a stamp_id for that stamp, which can be - used to delete it by calling clearstamp(stamp_id). - - Example (for a Turtle instance named turtle): - >>> turtle.color("blue") - >>> turtle.stamp() - 13 - >>> turtle.fd(50) - """ - screen = self.screen - shape = screen._shapes[self.turtle.shapeIndex] - ttype = shape._type - tshape = shape._data - if ttype == "polygon": - stitem = screen._createpoly() - if self._resizemode == "noresize": w = 1 - elif self._resizemode == "auto": w = self._pensize - else: w =self._outlinewidth - shape = self._polytrafo(self._getshapepoly(tshape)) - fc, oc = self._fillcolor, self._pencolor - screen._drawpoly(stitem, shape, fill=fc, outline=oc, - width=w, top=True) - elif ttype == "image": - stitem = screen._createimage("") - screen._drawimage(stitem, self._position, tshape) - elif ttype == "compound": - stitem = [] - for element in tshape: - item = screen._createpoly() - stitem.append(item) - stitem = tuple(stitem) - for item, (poly, fc, oc) in zip(stitem, tshape): - poly = self._polytrafo(self._getshapepoly(poly, True)) - screen._drawpoly(item, poly, fill=self._cc(fc), - outline=self._cc(oc), width=self._outlinewidth, top=True) - self.stampItems.append(stitem) - self.undobuffer.push(("stamp", stitem)) - return stitem - - def _clearstamp(self, stampid): - """does the work for clearstamp() and clearstamps() - """ - if stampid in self.stampItems: - if isinstance(stampid, tuple): - for subitem in stampid: - self.screen._delete(subitem) - else: - self.screen._delete(stampid) - self.stampItems.remove(stampid) - # Delete stampitem from undobuffer if necessary - # if clearstamp is called directly. - item = ("stamp", stampid) - buf = self.undobuffer - if item not in buf.buffer: - return - index = buf.buffer.index(item) - buf.buffer.remove(item) - if index <= buf.ptr: - buf.ptr = (buf.ptr - 1) % buf.bufsize - buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None]) - - def clearstamp(self, stampid): - """Delete stamp with given stampid - - Argument: - stampid - an integer, must be return value of previous stamp() call. - - Example (for a Turtle instance named turtle): - >>> turtle.color("blue") - >>> astamp = turtle.stamp() - >>> turtle.fd(50) - >>> turtle.clearstamp(astamp) - """ - self._clearstamp(stampid) - self._update() - - def clearstamps(self, n=None): - """Delete all or first/last n of turtle's stamps. - - Optional argument: - n -- an integer - - If n is None, delete all of pen's stamps, - else if n > 0 delete first n stamps - else if n < 0 delete last n stamps. - - Example (for a Turtle instance named turtle): - >>> for i in range(8): - ... turtle.stamp(); turtle.fd(30) - ... - >>> turtle.clearstamps(2) - >>> turtle.clearstamps(-2) - >>> turtle.clearstamps() - """ - if n is None: - toDelete = self.stampItems[:] - elif n >= 0: - toDelete = self.stampItems[:n] - else: - toDelete = self.stampItems[n:] - for item in toDelete: - self._clearstamp(item) - self._update() - - def _goto(self, end): - """Move the pen to the point end, thereby drawing a line - if pen is down. All other methods for turtle movement depend - on this one. - """ - ## Version with undo-stuff - go_modes = ( self._drawing, - self._pencolor, - self._pensize, - isinstance(self._fillpath, list)) - screen = self.screen - undo_entry = ("go", self._position, end, go_modes, - (self.currentLineItem, - self.currentLine[:], - screen._pointlist(self.currentLineItem), - self.items[:]) - ) - if self.undobuffer: - self.undobuffer.push(undo_entry) - start = self._position - if self._speed and screen._tracing == 1: - diff = (end-start) - diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 - nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) - delta = diff * (1.0/nhops) - for n in range(1, nhops): - if n == 1: - top = True - else: - top = False - self._position = start + delta * n - if self._drawing: - screen._drawline(self.drawingLineItem, - (start, self._position), - self._pencolor, self._pensize, top) - self._update() - if self._drawing: - screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), - fill="", width=self._pensize) - # Turtle now at end, - if self._drawing: # now update currentLine - self.currentLine.append(end) - if isinstance(self._fillpath, list): - self._fillpath.append(end) - ###### vererbung!!!!!!!!!!!!!!!!!!!!!! - self._position = end - if self._creatingPoly: - self._poly.append(end) - if len(self.currentLine) > 42: # 42! answer to the ultimate question - # of life, the universe and everything - self._newLine() - self._update() #count=True) - - def _undogoto(self, entry): - """Reverse a _goto. Used for undo() - """ - old, new, go_modes, coodata = entry - drawing, pc, ps, filling = go_modes - cLI, cL, pl, items = coodata - screen = self.screen - if abs(self._position - new) > 0.5: - print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!") - # restore former situation - self.currentLineItem = cLI - self.currentLine = cL - - if pl == [(0, 0), (0, 0)]: - usepc = "" - else: - usepc = pc - screen._drawline(cLI, pl, fill=usepc, width=ps) - - todelete = [i for i in self.items if (i not in items) and - (screen._type(i) == "line")] - for i in todelete: - screen._delete(i) - self.items.remove(i) - - start = old - if self._speed and screen._tracing == 1: - diff = old - new - diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 - nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) - delta = diff * (1.0/nhops) - for n in range(1, nhops): - if n == 1: - top = True - else: - top = False - self._position = new + delta * n - if drawing: - screen._drawline(self.drawingLineItem, - (start, self._position), - pc, ps, top) - self._update() - if drawing: - screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), - fill="", width=ps) - # Turtle now at position old, - self._position = old - ## if undo is done during creating a polygon, the last vertex - ## will be deleted. if the polygon is entirely deleted, - ## creatingPoly will be set to False. - ## Polygons created before the last one will not be affected by undo() - if self._creatingPoly: - if len(self._poly) > 0: - self._poly.pop() - if self._poly == []: - self._creatingPoly = False - self._poly = None - if filling: - if self._fillpath == []: - self._fillpath = None - print("Unwahrscheinlich in _undogoto!") - elif self._fillpath is not None: - self._fillpath.pop() - self._update() #count=True) - - def _rotate(self, angle): - """Turns pen clockwise by angle. - """ - if self.undobuffer: - self.undobuffer.push(("rot", angle, self._degreesPerAU)) - angle *= self._degreesPerAU - neworient = self._orient.rotate(angle) - tracing = self.screen._tracing - if tracing == 1 and self._speed > 0: - anglevel = 3.0 * self._speed - steps = 1 + int(abs(angle)/anglevel) - delta = 1.0*angle/steps - for _ in range(steps): - self._orient = self._orient.rotate(delta) - self._update() - self._orient = neworient - self._update() - - def _newLine(self, usePos=True): - """Closes current line item and starts a new one. - Remark: if current line became too long, animation - performance (via _drawline) slowed down considerably. - """ - if len(self.currentLine) > 1: - self.screen._drawline(self.currentLineItem, self.currentLine, - self._pencolor, self._pensize) - self.currentLineItem = self.screen._createline() - self.items.append(self.currentLineItem) - else: - self.screen._drawline(self.currentLineItem, top=True) - self.currentLine = [] - if usePos: - self.currentLine = [self._position] - - def filling(self): - """Return fillstate (True if filling, False else). - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.begin_fill() - >>> if turtle.filling(): - ... turtle.pensize(5) - ... else: - ... turtle.pensize(3) - """ - return isinstance(self._fillpath, list) - - def begin_fill(self): - """Called just before drawing a shape to be filled. - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.color("black", "red") - >>> turtle.begin_fill() - >>> turtle.circle(60) - >>> turtle.end_fill() - """ - if not self.filling(): - self._fillitem = self.screen._createpoly() - self.items.append(self._fillitem) - self._fillpath = [self._position] - self._newLine() - if self.undobuffer: - self.undobuffer.push(("beginfill", self._fillitem)) - self._update() - - - def end_fill(self): - """Fill the shape drawn after the call begin_fill(). - - No argument. - - Example (for a Turtle instance named turtle): - >>> turtle.color("black", "red") - >>> turtle.begin_fill() - >>> turtle.circle(60) - >>> turtle.end_fill() - """ - if self.filling(): - if len(self._fillpath) > 2: - self.screen._drawpoly(self._fillitem, self._fillpath, - fill=self._fillcolor) - if self.undobuffer: - self.undobuffer.push(("dofill", self._fillitem)) - self._fillitem = self._fillpath = None - self._update() - - def dot(self, size=None, *color): - """Draw a dot with diameter size, using color. - - Optional arguments: - size -- an integer >= 1 (if given) - color -- a colorstring or a numeric color tuple - - Draw a circular dot with diameter size, using color. - If size is not given, the maximum of pensize+4 and 2*pensize is used. - - Example (for a Turtle instance named turtle): - >>> turtle.dot() - >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50) - """ - if not color: - if isinstance(size, (str, tuple)): - color = self._colorstr(size) - size = self._pensize + max(self._pensize, 4) - else: - color = self._pencolor - if not size: - size = self._pensize + max(self._pensize, 4) - else: - if size is None: - size = self._pensize + max(self._pensize, 4) - color = self._colorstr(color) - if hasattr(self.screen, "_dot"): - item = self.screen._dot(self._position, size, color) - self.items.append(item) - if self.undobuffer: - self.undobuffer.push(("dot", item)) - else: - pen = self.pen() - if self.undobuffer: - self.undobuffer.push(["seq"]) - self.undobuffer.cumulate = True - try: - if self.resizemode() == 'auto': - self.ht() - self.pendown() - self.pensize(size) - self.pencolor(color) - self.forward(0) - finally: - self.pen(pen) - if self.undobuffer: - self.undobuffer.cumulate = False - - def _write(self, txt, align, font): - """Performs the writing for write() - """ - item, end = self.screen._write(self._position, txt, align, font, - self._pencolor) - self.items.append(item) - if self.undobuffer: - self.undobuffer.push(("wri", item)) - return end - - def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")): - """Write text at the current turtle position. - - Arguments: - arg -- info, which is to be written to the TurtleScreen - move (optional) -- True/False - align (optional) -- one of the strings "left", "center" or right" - font (optional) -- a triple (fontname, fontsize, fonttype) - - Write text - the string representation of arg - at the current - turtle position according to align ("left", "center" or right") - and with the given font. - If move is True, the pen is moved to the bottom-right corner - of the text. By default, move is False. - - Example (for a Turtle instance named turtle): - >>> turtle.write('Home = ', True, align="center") - >>> turtle.write((0,0), True) - """ - if self.undobuffer: - self.undobuffer.push(["seq"]) - self.undobuffer.cumulate = True - end = self._write(str(arg), align.lower(), font) - if move: - x, y = self.pos() - self.setpos(end, y) - if self.undobuffer: - self.undobuffer.cumulate = False - - def begin_poly(self): - """Start recording the vertices of a polygon. - - No argument. - - Start recording the vertices of a polygon. Current turtle position - is first point of polygon. - - Example (for a Turtle instance named turtle): - >>> turtle.begin_poly() - """ - self._poly = [self._position] - self._creatingPoly = True - - def end_poly(self): - """Stop recording the vertices of a polygon. - - No argument. - - Stop recording the vertices of a polygon. Current turtle position is - last point of polygon. This will be connected with the first point. - - Example (for a Turtle instance named turtle): - >>> turtle.end_poly() - """ - self._creatingPoly = False - - def get_poly(self): - """Return the lastly recorded polygon. - - No argument. - - Example (for a Turtle instance named turtle): - >>> p = turtle.get_poly() - >>> turtle.register_shape("myFavouriteShape", p) - """ - ## check if there is any poly? - if self._poly is not None: - return tuple(self._poly) - - def getscreen(self): - """Return the TurtleScreen object, the turtle is drawing on. - - No argument. - - Return the TurtleScreen object, the turtle is drawing on. - So TurtleScreen-methods can be called for that object. - - Example (for a Turtle instance named turtle): - >>> ts = turtle.getscreen() - >>> ts - <turtle.TurtleScreen object at 0x0106B770> - >>> ts.bgcolor("pink") - """ - return self.screen - - def getturtle(self): - """Return the Turtleobject itself. - - No argument. - - Only reasonable use: as a function to return the 'anonymous turtle': - - Example: - >>> pet = getturtle() - >>> pet.fd(50) - >>> pet - <turtle.Turtle object at 0x0187D810> - >>> turtles() - [<turtle.Turtle object at 0x0187D810>] - """ - return self - - getpen = getturtle - - - ################################################################ - ### screen oriented methods recurring to methods of TurtleScreen - ################################################################ - - def _delay(self, delay=None): - """Set delay value which determines speed of turtle animation. - """ - return self.screen.delay(delay) - - def onclick(self, fun, btn=1, add=None): - """Bind fun to mouse-click event on this turtle on canvas. - - Arguments: - fun -- a function with two arguments, to which will be assigned - the coordinates of the clicked point on the canvas. - btn -- number of the mouse-button defaults to 1 (left mouse button). - add -- True or False. If True, new binding will be added, otherwise - it will replace a former binding. - - Example for the anonymous turtle, i. e. the procedural way: - - >>> def turn(x, y): - ... left(360) - ... - >>> onclick(turn) # Now clicking into the turtle will turn it. - >>> onclick(None) # event-binding will be removed - """ - self.screen._onclick(self.turtle._item, fun, btn, add) - self._update() - - def onrelease(self, fun, btn=1, add=None): - """Bind fun to mouse-button-release event on this turtle on canvas. - - Arguments: - fun -- a function with two arguments, to which will be assigned - the coordinates of the clicked point on the canvas. - btn -- number of the mouse-button defaults to 1 (left mouse button). - - Example (for a MyTurtle instance named joe): - >>> class MyTurtle(Turtle): - ... def glow(self,x,y): - ... self.fillcolor("red") - ... def unglow(self,x,y): - ... self.fillcolor("") - ... - >>> joe = MyTurtle() - >>> joe.onclick(joe.glow) - >>> joe.onrelease(joe.unglow) - - Clicking on joe turns fillcolor red, unclicking turns it to - transparent. - """ - self.screen._onrelease(self.turtle._item, fun, btn, add) - self._update() - - def ondrag(self, fun, btn=1, add=None): - """Bind fun to mouse-move event on this turtle on canvas. - - Arguments: - fun -- a function with two arguments, to which will be assigned - the coordinates of the clicked point on the canvas. - btn -- number of the mouse-button defaults to 1 (left mouse button). - - Every sequence of mouse-move-events on a turtle is preceded by a - mouse-click event on that turtle. - - Example (for a Turtle instance named turtle): - >>> turtle.ondrag(turtle.goto) - - Subsequently clicking and dragging a Turtle will move it - across the screen thereby producing handdrawings (if pen is - down). - """ - self.screen._ondrag(self.turtle._item, fun, btn, add) - - - def _undo(self, action, data): - """Does the main part of the work for undo() - """ - if self.undobuffer is None: - return - if action == "rot": - angle, degPAU = data - self._rotate(-angle*degPAU/self._degreesPerAU) - dummy = self.undobuffer.pop() - elif action == "stamp": - stitem = data[0] - self.clearstamp(stitem) - elif action == "go": - self._undogoto(data) - elif action in ["wri", "dot"]: - item = data[0] - self.screen._delete(item) - self.items.remove(item) - elif action == "dofill": - item = data[0] - self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)), - fill="", outline="") - elif action == "beginfill": - item = data[0] - self._fillitem = self._fillpath = None - if item in self.items: - self.screen._delete(item) - self.items.remove(item) - elif action == "pen": - TPen.pen(self, data[0]) - self.undobuffer.pop() - - def undo(self): - """undo (repeatedly) the last turtle action. - - No argument. - - undo (repeatedly) the last turtle action. - Number of available undo actions is determined by the size of - the undobuffer. - - Example (for a Turtle instance named turtle): - >>> for i in range(4): - ... turtle.fd(50); turtle.lt(80) - ... - >>> for i in range(8): - ... turtle.undo() - ... - """ - if self.undobuffer is None: - return - item = self.undobuffer.pop() - action = item[0] - data = item[1:] - if action == "seq": - while data: - item = data.pop() - self._undo(item[0], item[1:]) - else: - self._undo(action, data) - - turtlesize = shapesize - -RawPen = RawTurtle - -### Screen - Singleton ######################## - -def Screen(): - """Return the singleton screen object. - If none exists at the moment, create a new one and return it, - else return the existing one.""" - if Turtle._screen is None: - Turtle._screen = _Screen() - return Turtle._screen - -class _Screen(TurtleScreen): - - _root = None - _canvas = None - _title = _CFG["title"] - - def __init__(self): - # XXX there is no need for this code to be conditional, - # as there will be only a single _Screen instance, anyway - # XXX actually, the turtle demo is injecting root window, - # so perhaps the conditional creation of a root should be - # preserved (perhaps by passing it as an optional parameter) - if _Screen._root is None: - _Screen._root = self._root = _Root() - self._root.title(_Screen._title) - self._root.ondestroy(self._destroy) - if _Screen._canvas is None: - width = _CFG["width"] - height = _CFG["height"] - canvwidth = _CFG["canvwidth"] - canvheight = _CFG["canvheight"] - leftright = _CFG["leftright"] - topbottom = _CFG["topbottom"] - self._root.setupcanvas(width, height, canvwidth, canvheight) - _Screen._canvas = self._root._getcanvas() - TurtleScreen.__init__(self, _Screen._canvas) - self.setup(width, height, leftright, topbottom) - - def setup(self, width=_CFG["width"], height=_CFG["height"], - startx=_CFG["leftright"], starty=_CFG["topbottom"]): - """ Set the size and position of the main window. - - Arguments: - width: as integer a size in pixels, as float a fraction of the screen. - Default is 50% of screen. - height: as integer the height in pixels, as float a fraction of the - screen. Default is 75% of screen. - startx: if positive, starting position in pixels from the left - edge of the screen, if negative from the right edge - Default, startx=None is to center window horizontally. - starty: if positive, starting position in pixels from the top - edge of the screen, if negative from the bottom edge - Default, starty=None is to center window vertically. - - Examples (for a Screen instance named screen): - >>> screen.setup (width=200, height=200, startx=0, starty=0) - - sets window to 200x200 pixels, in upper left of screen - - >>> screen.setup(width=.75, height=0.5, startx=None, starty=None) - - sets window to 75% of screen by 50% of screen and centers - """ - if not hasattr(self._root, "set_geometry"): - return - sw = self._root.win_width() - sh = self._root.win_height() - if isinstance(width, float) and 0 <= width <= 1: - width = sw*width - if startx is None: - startx = (sw - width) / 2 - if isinstance(height, float) and 0 <= height <= 1: - height = sh*height - if starty is None: - starty = (sh - height) / 2 - self._root.set_geometry(width, height, startx, starty) - self.update() - - def title(self, titlestring): - """Set title of turtle-window - - Argument: - titlestring -- a string, to appear in the titlebar of the - turtle graphics window. - - This is a method of Screen-class. Not available for TurtleScreen- - objects. - - Example (for a Screen instance named screen): - >>> screen.title("Welcome to the turtle-zoo!") - """ - if _Screen._root is not None: - _Screen._root.title(titlestring) - _Screen._title = titlestring - - def _destroy(self): - root = self._root - if root is _Screen._root: - Turtle._pen = None - Turtle._screen = None - _Screen._root = None - _Screen._canvas = None - TurtleScreen._RUNNING = False - root.destroy() - - def bye(self): - """Shut the turtlegraphics window. - - Example (for a TurtleScreen instance named screen): - >>> screen.bye() - """ - self._destroy() - - def exitonclick(self): - """Go into mainloop until the mouse is clicked. - - No arguments. - - Bind bye() method to mouseclick on TurtleScreen. - If "using_IDLE" - value in configuration dictionary is False - (default value), enter mainloop. - If IDLE with -n switch (no subprocess) is used, this value should be - set to True in turtle.cfg. In this case IDLE's mainloop - is active also for the client script. - - This is a method of the Screen-class and not available for - TurtleScreen instances. - - Example (for a Screen instance named screen): - >>> screen.exitonclick() - - """ - def exitGracefully(x, y): - """Screen.bye() with two dummy-parameters""" - self.bye() - self.onclick(exitGracefully) - if _CFG["using_IDLE"]: - return - try: - mainloop() - except AttributeError: - exit(0) - -class Turtle(RawTurtle): - """RawTurtle auto-creating (scrolled) canvas. - - When a Turtle object is created or a function derived from some - Turtle method is called a TurtleScreen object is automatically created. - """ - _pen = None - _screen = None - - def __init__(self, - shape=_CFG["shape"], - undobuffersize=_CFG["undobuffersize"], - visible=_CFG["visible"]): - if Turtle._screen is None: - Turtle._screen = Screen() - RawTurtle.__init__(self, Turtle._screen, - shape=shape, - undobuffersize=undobuffersize, - visible=visible) - -Pen = Turtle - -def write_docstringdict(filename="turtle_docstringdict"): - """Create and write docstring-dictionary to file. - - Optional argument: - filename -- a string, used as filename - default value is turtle_docstringdict - - Has to be called explicitly, (not used by the turtle-graphics classes) - The docstring dictionary will be written to the Python script <filname>.py - It is intended to serve as a template for translation of the docstrings - into different languages. - """ - docsdict = {} - - for methodname in _tg_screen_functions: - key = "_Screen."+methodname - docsdict[key] = eval(key).__doc__ - for methodname in _tg_turtle_functions: - key = "Turtle."+methodname - docsdict[key] = eval(key).__doc__ - - with open("%s.py" % filename,"w") as f: - keys = sorted(x for x in docsdict - if x.split('.')[1] not in _alias_list) - f.write('docsdict = {\n\n') - for key in keys[:-1]: - f.write('%s :\n' % repr(key)) - f.write(' """%s\n""",\n\n' % docsdict[key]) - key = keys[-1] - f.write('%s :\n' % repr(key)) - f.write(' """%s\n"""\n\n' % docsdict[key]) - f.write("}\n") - f.close() - -def read_docstrings(lang): - """Read in docstrings from lang-specific docstring dictionary. - - Transfer docstrings, translated to lang, from a dictionary-file - to the methods of classes Screen and Turtle and - in revised form - - to the corresponding functions. - """ - modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()} - module = __import__(modname) - docsdict = module.docsdict - for key in docsdict: - try: -# eval(key).im_func.__doc__ = docsdict[key] - eval(key).__doc__ = docsdict[key] - except Exception: - print("Bad docstring-entry: %s" % key) - -_LANGUAGE = _CFG["language"] - -try: - if _LANGUAGE != "english": - read_docstrings(_LANGUAGE) -except ImportError: - print("Cannot find docsdict for", _LANGUAGE) -except Exception: - print ("Unknown Error when trying to import %s-docstring-dictionary" % - _LANGUAGE) - - -def getmethparlist(ob): - """Get strings describing the arguments for the given object - - Returns a pair of strings representing function parameter lists - including parenthesis. The first string is suitable for use in - function definition and the second is suitable for use in function - call. The "self" parameter is not included. - """ - defText = callText = "" - # bit of a hack for methods - turn it into a function - # but we drop the "self" param. - # Try and build one for Python defined functions - args, varargs, varkw = inspect.getargs(ob.__code__) - items2 = args[1:] - realArgs = args[1:] - defaults = ob.__defaults__ or [] - defaults = ["=%r" % (value,) for value in defaults] - defaults = [""] * (len(realArgs)-len(defaults)) + defaults - items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)] - if varargs is not None: - items1.append("*" + varargs) - items2.append("*" + varargs) - if varkw is not None: - items1.append("**" + varkw) - items2.append("**" + varkw) - defText = ", ".join(items1) - defText = "(%s)" % defText - callText = ", ".join(items2) - callText = "(%s)" % callText - return defText, callText - -def _turtle_docrevise(docstr): - """To reduce docstrings from RawTurtle class for functions - """ - import re - if docstr is None: - return None - turtlename = _CFG["exampleturtle"] - newdocstr = docstr.replace("%s." % turtlename,"") - parexp = re.compile(r' \(.+ %s\):' % turtlename) - newdocstr = parexp.sub(":", newdocstr) - return newdocstr - -def _screen_docrevise(docstr): - """To reduce docstrings from TurtleScreen class for functions - """ - import re - if docstr is None: - return None - screenname = _CFG["examplescreen"] - newdocstr = docstr.replace("%s." % screenname,"") - parexp = re.compile(r' \(.+ %s\):' % screenname) - newdocstr = parexp.sub(":", newdocstr) - return newdocstr - -## The following mechanism makes all methods of RawTurtle and Turtle available -## as functions. So we can enhance, change, add, delete methods to these -## classes and do not need to change anything here. - -__func_body = """\ -def {name}{paramslist}: - if {obj} is None: - if not TurtleScreen._RUNNING: - TurtleScreen._RUNNING = True - raise Terminator - {obj} = {init} - try: - return {obj}.{name}{argslist} - except TK.TclError: - if not TurtleScreen._RUNNING: - TurtleScreen._RUNNING = True - raise Terminator - raise -""" - -def _make_global_funcs(functions, cls, obj, init, docrevise): - for methodname in functions: - method = getattr(cls, methodname) - pl1, pl2 = getmethparlist(method) - if pl1 == "": - print(">>>>>>", pl1, pl2) - continue - defstr = __func_body.format(obj=obj, init=init, name=methodname, - paramslist=pl1, argslist=pl2) - exec(defstr, globals()) - globals()[methodname].__doc__ = docrevise(method.__doc__) - -_make_global_funcs(_tg_screen_functions, _Screen, - 'Turtle._screen', 'Screen()', _screen_docrevise) -_make_global_funcs(_tg_turtle_functions, Turtle, - 'Turtle._pen', 'Turtle()', _turtle_docrevise) - - -done = mainloop - -if __name__ == "__main__": - def switchpen(): - if isdown(): - pu() - else: - pd() - - def demo1(): - """Demo of old turtle.py - module""" - reset() - tracer(True) - up() - backward(100) - down() - # draw 3 squares; the last filled - width(3) - for i in range(3): - if i == 2: - begin_fill() - for _ in range(4): - forward(20) - left(90) - if i == 2: - color("maroon") - end_fill() - up() - forward(30) - down() - width(1) - color("black") - # move out of the way - tracer(False) - up() - right(90) - forward(100) - right(90) - forward(100) - right(180) - down() - # some text - write("startstart", 1) - write("start", 1) - color("red") - # staircase - for i in range(5): - forward(20) - left(90) - forward(20) - right(90) - # filled staircase - tracer(True) - begin_fill() - for i in range(5): - forward(20) - left(90) - forward(20) - right(90) - end_fill() - # more text - - def demo2(): - """Demo of some new features.""" - speed(1) - st() - pensize(3) - setheading(towards(0, 0)) - radius = distance(0, 0)/2.0 - rt(90) - for _ in range(18): - switchpen() - circle(radius, 10) - write("wait a moment...") - while undobufferentries(): - undo() - reset() - lt(90) - colormode(255) - laenge = 10 - pencolor("green") - pensize(3) - lt(180) - for i in range(-2, 16): - if i > 0: - begin_fill() - fillcolor(255-15*i, 0, 15*i) - for _ in range(3): - fd(laenge) - lt(120) - end_fill() - laenge += 10 - lt(15) - speed((speed()+1)%12) - #end_fill() - - lt(120) - pu() - fd(70) - rt(30) - pd() - color("red","yellow") - speed(0) - begin_fill() - for _ in range(4): - circle(50, 90) - rt(90) - fd(30) - rt(90) - end_fill() - lt(90) - pu() - fd(30) - pd() - shape("turtle") - - tri = getturtle() - tri.resizemode("auto") - turtle = Turtle() - turtle.resizemode("auto") - turtle.shape("turtle") - turtle.reset() - turtle.left(90) - turtle.speed(0) - turtle.up() - turtle.goto(280, 40) - turtle.lt(30) - turtle.down() - turtle.speed(6) - turtle.color("blue","orange") - turtle.pensize(2) - tri.speed(6) - setheading(towards(turtle)) - count = 1 - while tri.distance(turtle) > 4: - turtle.fd(3.5) - turtle.lt(0.6) - tri.setheading(tri.towards(turtle)) - tri.fd(4) - if count % 20 == 0: - turtle.stamp() - tri.stamp() - switchpen() - count += 1 - tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right") - tri.pencolor("black") - tri.pencolor("red") - - def baba(xdummy, ydummy): - clearscreen() - bye() - - time.sleep(2) - - while undobufferentries(): - tri.undo() - turtle.undo() - tri.fd(50) - tri.write(" Click me!", font = ("Courier", 12, "bold") ) - tri.onclick(baba, 1) - - demo1() - demo2() - exitonclick() + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(45) + >>> turtle.tiltangle() + """ + if angle is None: + tilt = -self._tilt * (180.0/math.pi) * self._angleOrient + return (tilt / self._degreesPerAU) % self._fullcircle + else: + self.settiltangle(angle) + + def tilt(self, angle): + """Rotate the turtleshape by angle. + + Argument: + angle - a number + + Rotate the turtleshape by angle from its current tilt-angle, + but do NOT change the turtle's heading (direction of movement). + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(30) + >>> turtle.fd(50) + >>> turtle.tilt(30) + >>> turtle.fd(50) + """ + self.settiltangle(angle + self.tiltangle()) + + def shapetransform(self, t11=None, t12=None, t21=None, t22=None): + """Set or return the current transformation matrix of the turtle shape. + + Optional arguments: t11, t12, t21, t22 -- numbers. + + If none of the matrix elements are given, return the transformation + matrix. + Otherwise set the given elements and transform the turtleshape + according to the matrix consisting of first row t11, t12 and + second row t21, 22. + Modify stretchfactor, shearfactor and tiltangle according to the + given matrix. + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("square") + >>> turtle.shapesize(4,2) + >>> turtle.shearfactor(-0.5) + >>> turtle.shapetransform() + (4.0, -1.0, -0.0, 2.0) + """ + if t11 is t12 is t21 is t22 is None: + return self._shapetrafo + m11, m12, m21, m22 = self._shapetrafo + if t11 is not None: m11 = t11 + if t12 is not None: m12 = t12 + if t21 is not None: m21 = t21 + if t22 is not None: m22 = t22 + if t11 * t22 - t12 * t21 == 0: + raise TurtleGraphicsError("Bad shape transform matrix: must not be singular") + self._shapetrafo = (m11, m12, m21, m22) + alfa = math.atan2(-m21, m11) % (2 * math.pi) + sa, ca = math.sin(alfa), math.cos(alfa) + a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22, + sa*m11 + ca*m21, sa*m12 + ca*m22) + self._stretchfactor = a11, a22 + self._shearfactor = a12/a22 + self._tilt = alfa + self.pen(resizemode="user") + + + def _polytrafo(self, poly): + """Computes transformed polygon shapes from a shape + according to current position and heading. + """ + screen = self.screen + p0, p1 = self._position + e0, e1 = self._orient + e = Vec2D(e0, e1 * screen.yscale / screen.xscale) + e0, e1 = (1.0 / abs(e)) * e + return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale) + for (x, y) in poly] + + def get_shapepoly(self): + """Return the current shape polygon as tuple of coordinate pairs. + + No argument. + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("square") + >>> turtle.shapetransform(4, -1, 0, 2) + >>> turtle.get_shapepoly() + ((50, -20), (30, 20), (-50, 20), (-30, -20)) + + """ + shape = self.screen._shapes[self.turtle.shapeIndex] + if shape._type == "polygon": + return self._getshapepoly(shape._data, shape._type == "compound") + # else return None + + def _getshapepoly(self, polygon, compound=False): + """Calculate transformed shape polygon according to resizemode + and shapetransform. + """ + if self._resizemode == "user" or compound: + t11, t12, t21, t22 = self._shapetrafo + elif self._resizemode == "auto": + l = max(1, self._pensize/5.0) + t11, t12, t21, t22 = l, 0, 0, l + elif self._resizemode == "noresize": + return polygon + return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon) + + def _drawturtle(self): + """Manages the correct rendering of the turtle with respect to + its shape, resizemode, stretch and tilt etc.""" + screen = self.screen + shape = screen._shapes[self.turtle.shapeIndex] + ttype = shape._type + titem = self.turtle._item + if self._shown and screen._updatecounter == 0 and screen._tracing > 0: + self._hidden_from_screen = False + tshape = shape._data + if ttype == "polygon": + if self._resizemode == "noresize": w = 1 + elif self._resizemode == "auto": w = self._pensize + else: w =self._outlinewidth + shape = self._polytrafo(self._getshapepoly(tshape)) + fc, oc = self._fillcolor, self._pencolor + screen._drawpoly(titem, shape, fill=fc, outline=oc, + width=w, top=True) + elif ttype == "image": + screen._drawimage(titem, self._position, tshape) + elif ttype == "compound": + for item, (poly, fc, oc) in zip(titem, tshape): + poly = self._polytrafo(self._getshapepoly(poly, True)) + screen._drawpoly(item, poly, fill=self._cc(fc), + outline=self._cc(oc), width=self._outlinewidth, top=True) + else: + if self._hidden_from_screen: + return + if ttype == "polygon": + screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "") + elif ttype == "image": + screen._drawimage(titem, self._position, + screen._shapes["blank"]._data) + elif ttype == "compound": + for item in titem: + screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "") + self._hidden_from_screen = True + +############################## stamp stuff ############################### + + def stamp(self): + """Stamp a copy of the turtleshape onto the canvas and return its id. + + No argument. + + Stamp a copy of the turtle shape onto the canvas at the current + turtle position. Return a stamp_id for that stamp, which can be + used to delete it by calling clearstamp(stamp_id). + + Example (for a Turtle instance named turtle): + >>> turtle.color("blue") + >>> turtle.stamp() + 13 + >>> turtle.fd(50) + """ + screen = self.screen + shape = screen._shapes[self.turtle.shapeIndex] + ttype = shape._type + tshape = shape._data + if ttype == "polygon": + stitem = screen._createpoly() + if self._resizemode == "noresize": w = 1 + elif self._resizemode == "auto": w = self._pensize + else: w =self._outlinewidth + shape = self._polytrafo(self._getshapepoly(tshape)) + fc, oc = self._fillcolor, self._pencolor + screen._drawpoly(stitem, shape, fill=fc, outline=oc, + width=w, top=True) + elif ttype == "image": + stitem = screen._createimage("") + screen._drawimage(stitem, self._position, tshape) + elif ttype == "compound": + stitem = [] + for element in tshape: + item = screen._createpoly() + stitem.append(item) + stitem = tuple(stitem) + for item, (poly, fc, oc) in zip(stitem, tshape): + poly = self._polytrafo(self._getshapepoly(poly, True)) + screen._drawpoly(item, poly, fill=self._cc(fc), + outline=self._cc(oc), width=self._outlinewidth, top=True) + self.stampItems.append(stitem) + self.undobuffer.push(("stamp", stitem)) + return stitem + + def _clearstamp(self, stampid): + """does the work for clearstamp() and clearstamps() + """ + if stampid in self.stampItems: + if isinstance(stampid, tuple): + for subitem in stampid: + self.screen._delete(subitem) + else: + self.screen._delete(stampid) + self.stampItems.remove(stampid) + # Delete stampitem from undobuffer if necessary + # if clearstamp is called directly. + item = ("stamp", stampid) + buf = self.undobuffer + if item not in buf.buffer: + return + index = buf.buffer.index(item) + buf.buffer.remove(item) + if index <= buf.ptr: + buf.ptr = (buf.ptr - 1) % buf.bufsize + buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None]) + + def clearstamp(self, stampid): + """Delete stamp with given stampid + + Argument: + stampid - an integer, must be return value of previous stamp() call. + + Example (for a Turtle instance named turtle): + >>> turtle.color("blue") + >>> astamp = turtle.stamp() + >>> turtle.fd(50) + >>> turtle.clearstamp(astamp) + """ + self._clearstamp(stampid) + self._update() + + def clearstamps(self, n=None): + """Delete all or first/last n of turtle's stamps. + + Optional argument: + n -- an integer + + If n is None, delete all of pen's stamps, + else if n > 0 delete first n stamps + else if n < 0 delete last n stamps. + + Example (for a Turtle instance named turtle): + >>> for i in range(8): + ... turtle.stamp(); turtle.fd(30) + ... + >>> turtle.clearstamps(2) + >>> turtle.clearstamps(-2) + >>> turtle.clearstamps() + """ + if n is None: + toDelete = self.stampItems[:] + elif n >= 0: + toDelete = self.stampItems[:n] + else: + toDelete = self.stampItems[n:] + for item in toDelete: + self._clearstamp(item) + self._update() + + def _goto(self, end): + """Move the pen to the point end, thereby drawing a line + if pen is down. All other methods for turtle movement depend + on this one. + """ + ## Version with undo-stuff + go_modes = ( self._drawing, + self._pencolor, + self._pensize, + isinstance(self._fillpath, list)) + screen = self.screen + undo_entry = ("go", self._position, end, go_modes, + (self.currentLineItem, + self.currentLine[:], + screen._pointlist(self.currentLineItem), + self.items[:]) + ) + if self.undobuffer: + self.undobuffer.push(undo_entry) + start = self._position + if self._speed and screen._tracing == 1: + diff = (end-start) + diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 + nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) + delta = diff * (1.0/nhops) + for n in range(1, nhops): + if n == 1: + top = True + else: + top = False + self._position = start + delta * n + if self._drawing: + screen._drawline(self.drawingLineItem, + (start, self._position), + self._pencolor, self._pensize, top) + self._update() + if self._drawing: + screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), + fill="", width=self._pensize) + # Turtle now at end, + if self._drawing: # now update currentLine + self.currentLine.append(end) + if isinstance(self._fillpath, list): + self._fillpath.append(end) + ###### vererbung!!!!!!!!!!!!!!!!!!!!!! + self._position = end + if self._creatingPoly: + self._poly.append(end) + if len(self.currentLine) > 42: # 42! answer to the ultimate question + # of life, the universe and everything + self._newLine() + self._update() #count=True) + + def _undogoto(self, entry): + """Reverse a _goto. Used for undo() + """ + old, new, go_modes, coodata = entry + drawing, pc, ps, filling = go_modes + cLI, cL, pl, items = coodata + screen = self.screen + if abs(self._position - new) > 0.5: + print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!") + # restore former situation + self.currentLineItem = cLI + self.currentLine = cL + + if pl == [(0, 0), (0, 0)]: + usepc = "" + else: + usepc = pc + screen._drawline(cLI, pl, fill=usepc, width=ps) + + todelete = [i for i in self.items if (i not in items) and + (screen._type(i) == "line")] + for i in todelete: + screen._delete(i) + self.items.remove(i) + + start = old + if self._speed and screen._tracing == 1: + diff = old - new + diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 + nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) + delta = diff * (1.0/nhops) + for n in range(1, nhops): + if n == 1: + top = True + else: + top = False + self._position = new + delta * n + if drawing: + screen._drawline(self.drawingLineItem, + (start, self._position), + pc, ps, top) + self._update() + if drawing: + screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), + fill="", width=ps) + # Turtle now at position old, + self._position = old + ## if undo is done during creating a polygon, the last vertex + ## will be deleted. if the polygon is entirely deleted, + ## creatingPoly will be set to False. + ## Polygons created before the last one will not be affected by undo() + if self._creatingPoly: + if len(self._poly) > 0: + self._poly.pop() + if self._poly == []: + self._creatingPoly = False + self._poly = None + if filling: + if self._fillpath == []: + self._fillpath = None + print("Unwahrscheinlich in _undogoto!") + elif self._fillpath is not None: + self._fillpath.pop() + self._update() #count=True) + + def _rotate(self, angle): + """Turns pen clockwise by angle. + """ + if self.undobuffer: + self.undobuffer.push(("rot", angle, self._degreesPerAU)) + angle *= self._degreesPerAU + neworient = self._orient.rotate(angle) + tracing = self.screen._tracing + if tracing == 1 and self._speed > 0: + anglevel = 3.0 * self._speed + steps = 1 + int(abs(angle)/anglevel) + delta = 1.0*angle/steps + for _ in range(steps): + self._orient = self._orient.rotate(delta) + self._update() + self._orient = neworient + self._update() + + def _newLine(self, usePos=True): + """Closes current line item and starts a new one. + Remark: if current line became too long, animation + performance (via _drawline) slowed down considerably. + """ + if len(self.currentLine) > 1: + self.screen._drawline(self.currentLineItem, self.currentLine, + self._pencolor, self._pensize) + self.currentLineItem = self.screen._createline() + self.items.append(self.currentLineItem) + else: + self.screen._drawline(self.currentLineItem, top=True) + self.currentLine = [] + if usePos: + self.currentLine = [self._position] + + def filling(self): + """Return fillstate (True if filling, False else). + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.begin_fill() + >>> if turtle.filling(): + ... turtle.pensize(5) + ... else: + ... turtle.pensize(3) + """ + return isinstance(self._fillpath, list) + + def begin_fill(self): + """Called just before drawing a shape to be filled. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") + >>> turtle.begin_fill() + >>> turtle.circle(60) + >>> turtle.end_fill() + """ + if not self.filling(): + self._fillitem = self.screen._createpoly() + self.items.append(self._fillitem) + self._fillpath = [self._position] + self._newLine() + if self.undobuffer: + self.undobuffer.push(("beginfill", self._fillitem)) + self._update() + + + def end_fill(self): + """Fill the shape drawn after the call begin_fill(). + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") + >>> turtle.begin_fill() + >>> turtle.circle(60) + >>> turtle.end_fill() + """ + if self.filling(): + if len(self._fillpath) > 2: + self.screen._drawpoly(self._fillitem, self._fillpath, + fill=self._fillcolor) + if self.undobuffer: + self.undobuffer.push(("dofill", self._fillitem)) + self._fillitem = self._fillpath = None + self._update() + + def dot(self, size=None, *color): + """Draw a dot with diameter size, using color. + + Optional arguments: + size -- an integer >= 1 (if given) + color -- a colorstring or a numeric color tuple + + Draw a circular dot with diameter size, using color. + If size is not given, the maximum of pensize+4 and 2*pensize is used. + + Example (for a Turtle instance named turtle): + >>> turtle.dot() + >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50) + """ + if not color: + if isinstance(size, (str, tuple)): + color = self._colorstr(size) + size = self._pensize + max(self._pensize, 4) + else: + color = self._pencolor + if not size: + size = self._pensize + max(self._pensize, 4) + else: + if size is None: + size = self._pensize + max(self._pensize, 4) + color = self._colorstr(color) + if hasattr(self.screen, "_dot"): + item = self.screen._dot(self._position, size, color) + self.items.append(item) + if self.undobuffer: + self.undobuffer.push(("dot", item)) + else: + pen = self.pen() + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + try: + if self.resizemode() == 'auto': + self.ht() + self.pendown() + self.pensize(size) + self.pencolor(color) + self.forward(0) + finally: + self.pen(pen) + if self.undobuffer: + self.undobuffer.cumulate = False + + def _write(self, txt, align, font): + """Performs the writing for write() + """ + item, end = self.screen._write(self._position, txt, align, font, + self._pencolor) + self.items.append(item) + if self.undobuffer: + self.undobuffer.push(("wri", item)) + return end + + def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")): + """Write text at the current turtle position. + + Arguments: + arg -- info, which is to be written to the TurtleScreen + move (optional) -- True/False + align (optional) -- one of the strings "left", "center" or right" + font (optional) -- a triple (fontname, fontsize, fonttype) + + Write text - the string representation of arg - at the current + turtle position according to align ("left", "center" or right") + and with the given font. + If move is True, the pen is moved to the bottom-right corner + of the text. By default, move is False. + + Example (for a Turtle instance named turtle): + >>> turtle.write('Home = ', True, align="center") + >>> turtle.write((0,0), True) + """ + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + end = self._write(str(arg), align.lower(), font) + if move: + x, y = self.pos() + self.setpos(end, y) + if self.undobuffer: + self.undobuffer.cumulate = False + + def begin_poly(self): + """Start recording the vertices of a polygon. + + No argument. + + Start recording the vertices of a polygon. Current turtle position + is first point of polygon. + + Example (for a Turtle instance named turtle): + >>> turtle.begin_poly() + """ + self._poly = [self._position] + self._creatingPoly = True + + def end_poly(self): + """Stop recording the vertices of a polygon. + + No argument. + + Stop recording the vertices of a polygon. Current turtle position is + last point of polygon. This will be connected with the first point. + + Example (for a Turtle instance named turtle): + >>> turtle.end_poly() + """ + self._creatingPoly = False + + def get_poly(self): + """Return the lastly recorded polygon. + + No argument. + + Example (for a Turtle instance named turtle): + >>> p = turtle.get_poly() + >>> turtle.register_shape("myFavouriteShape", p) + """ + ## check if there is any poly? + if self._poly is not None: + return tuple(self._poly) + + def getscreen(self): + """Return the TurtleScreen object, the turtle is drawing on. + + No argument. + + Return the TurtleScreen object, the turtle is drawing on. + So TurtleScreen-methods can be called for that object. + + Example (for a Turtle instance named turtle): + >>> ts = turtle.getscreen() + >>> ts + <turtle.TurtleScreen object at 0x0106B770> + >>> ts.bgcolor("pink") + """ + return self.screen + + def getturtle(self): + """Return the Turtleobject itself. + + No argument. + + Only reasonable use: as a function to return the 'anonymous turtle': + + Example: + >>> pet = getturtle() + >>> pet.fd(50) + >>> pet + <turtle.Turtle object at 0x0187D810> + >>> turtles() + [<turtle.Turtle object at 0x0187D810>] + """ + return self + + getpen = getturtle + + + ################################################################ + ### screen oriented methods recurring to methods of TurtleScreen + ################################################################ + + def _delay(self, delay=None): + """Set delay value which determines speed of turtle animation. + """ + return self.screen.delay(delay) + + def onclick(self, fun, btn=1, add=None): + """Bind fun to mouse-click event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + btn -- number of the mouse-button defaults to 1 (left mouse button). + add -- True or False. If True, new binding will be added, otherwise + it will replace a former binding. + + Example for the anonymous turtle, i. e. the procedural way: + + >>> def turn(x, y): + ... left(360) + ... + >>> onclick(turn) # Now clicking into the turtle will turn it. + >>> onclick(None) # event-binding will be removed + """ + self.screen._onclick(self.turtle._item, fun, btn, add) + self._update() + + def onrelease(self, fun, btn=1, add=None): + """Bind fun to mouse-button-release event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + btn -- number of the mouse-button defaults to 1 (left mouse button). + + Example (for a MyTurtle instance named joe): + >>> class MyTurtle(Turtle): + ... def glow(self,x,y): + ... self.fillcolor("red") + ... def unglow(self,x,y): + ... self.fillcolor("") + ... + >>> joe = MyTurtle() + >>> joe.onclick(joe.glow) + >>> joe.onrelease(joe.unglow) + + Clicking on joe turns fillcolor red, unclicking turns it to + transparent. + """ + self.screen._onrelease(self.turtle._item, fun, btn, add) + self._update() + + def ondrag(self, fun, btn=1, add=None): + """Bind fun to mouse-move event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + btn -- number of the mouse-button defaults to 1 (left mouse button). + + Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + + Example (for a Turtle instance named turtle): + >>> turtle.ondrag(turtle.goto) + + Subsequently clicking and dragging a Turtle will move it + across the screen thereby producing handdrawings (if pen is + down). + """ + self.screen._ondrag(self.turtle._item, fun, btn, add) + + + def _undo(self, action, data): + """Does the main part of the work for undo() + """ + if self.undobuffer is None: + return + if action == "rot": + angle, degPAU = data + self._rotate(-angle*degPAU/self._degreesPerAU) + dummy = self.undobuffer.pop() + elif action == "stamp": + stitem = data[0] + self.clearstamp(stitem) + elif action == "go": + self._undogoto(data) + elif action in ["wri", "dot"]: + item = data[0] + self.screen._delete(item) + self.items.remove(item) + elif action == "dofill": + item = data[0] + self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)), + fill="", outline="") + elif action == "beginfill": + item = data[0] + self._fillitem = self._fillpath = None + if item in self.items: + self.screen._delete(item) + self.items.remove(item) + elif action == "pen": + TPen.pen(self, data[0]) + self.undobuffer.pop() + + def undo(self): + """undo (repeatedly) the last turtle action. + + No argument. + + undo (repeatedly) the last turtle action. + Number of available undo actions is determined by the size of + the undobuffer. + + Example (for a Turtle instance named turtle): + >>> for i in range(4): + ... turtle.fd(50); turtle.lt(80) + ... + >>> for i in range(8): + ... turtle.undo() + ... + """ + if self.undobuffer is None: + return + item = self.undobuffer.pop() + action = item[0] + data = item[1:] + if action == "seq": + while data: + item = data.pop() + self._undo(item[0], item[1:]) + else: + self._undo(action, data) + + turtlesize = shapesize + +RawPen = RawTurtle + +### Screen - Singleton ######################## + +def Screen(): + """Return the singleton screen object. + If none exists at the moment, create a new one and return it, + else return the existing one.""" + if Turtle._screen is None: + Turtle._screen = _Screen() + return Turtle._screen + +class _Screen(TurtleScreen): + + _root = None + _canvas = None + _title = _CFG["title"] + + def __init__(self): + # XXX there is no need for this code to be conditional, + # as there will be only a single _Screen instance, anyway + # XXX actually, the turtle demo is injecting root window, + # so perhaps the conditional creation of a root should be + # preserved (perhaps by passing it as an optional parameter) + if _Screen._root is None: + _Screen._root = self._root = _Root() + self._root.title(_Screen._title) + self._root.ondestroy(self._destroy) + if _Screen._canvas is None: + width = _CFG["width"] + height = _CFG["height"] + canvwidth = _CFG["canvwidth"] + canvheight = _CFG["canvheight"] + leftright = _CFG["leftright"] + topbottom = _CFG["topbottom"] + self._root.setupcanvas(width, height, canvwidth, canvheight) + _Screen._canvas = self._root._getcanvas() + TurtleScreen.__init__(self, _Screen._canvas) + self.setup(width, height, leftright, topbottom) + + def setup(self, width=_CFG["width"], height=_CFG["height"], + startx=_CFG["leftright"], starty=_CFG["topbottom"]): + """ Set the size and position of the main window. + + Arguments: + width: as integer a size in pixels, as float a fraction of the screen. + Default is 50% of screen. + height: as integer the height in pixels, as float a fraction of the + screen. Default is 75% of screen. + startx: if positive, starting position in pixels from the left + edge of the screen, if negative from the right edge + Default, startx=None is to center window horizontally. + starty: if positive, starting position in pixels from the top + edge of the screen, if negative from the bottom edge + Default, starty=None is to center window vertically. + + Examples (for a Screen instance named screen): + >>> screen.setup (width=200, height=200, startx=0, starty=0) + + sets window to 200x200 pixels, in upper left of screen + + >>> screen.setup(width=.75, height=0.5, startx=None, starty=None) + + sets window to 75% of screen by 50% of screen and centers + """ + if not hasattr(self._root, "set_geometry"): + return + sw = self._root.win_width() + sh = self._root.win_height() + if isinstance(width, float) and 0 <= width <= 1: + width = sw*width + if startx is None: + startx = (sw - width) / 2 + if isinstance(height, float) and 0 <= height <= 1: + height = sh*height + if starty is None: + starty = (sh - height) / 2 + self._root.set_geometry(width, height, startx, starty) + self.update() + + def title(self, titlestring): + """Set title of turtle-window + + Argument: + titlestring -- a string, to appear in the titlebar of the + turtle graphics window. + + This is a method of Screen-class. Not available for TurtleScreen- + objects. + + Example (for a Screen instance named screen): + >>> screen.title("Welcome to the turtle-zoo!") + """ + if _Screen._root is not None: + _Screen._root.title(titlestring) + _Screen._title = titlestring + + def _destroy(self): + root = self._root + if root is _Screen._root: + Turtle._pen = None + Turtle._screen = None + _Screen._root = None + _Screen._canvas = None + TurtleScreen._RUNNING = False + root.destroy() + + def bye(self): + """Shut the turtlegraphics window. + + Example (for a TurtleScreen instance named screen): + >>> screen.bye() + """ + self._destroy() + + def exitonclick(self): + """Go into mainloop until the mouse is clicked. + + No arguments. + + Bind bye() method to mouseclick on TurtleScreen. + If "using_IDLE" - value in configuration dictionary is False + (default value), enter mainloop. + If IDLE with -n switch (no subprocess) is used, this value should be + set to True in turtle.cfg. In this case IDLE's mainloop + is active also for the client script. + + This is a method of the Screen-class and not available for + TurtleScreen instances. + + Example (for a Screen instance named screen): + >>> screen.exitonclick() + + """ + def exitGracefully(x, y): + """Screen.bye() with two dummy-parameters""" + self.bye() + self.onclick(exitGracefully) + if _CFG["using_IDLE"]: + return + try: + mainloop() + except AttributeError: + exit(0) + +class Turtle(RawTurtle): + """RawTurtle auto-creating (scrolled) canvas. + + When a Turtle object is created or a function derived from some + Turtle method is called a TurtleScreen object is automatically created. + """ + _pen = None + _screen = None + + def __init__(self, + shape=_CFG["shape"], + undobuffersize=_CFG["undobuffersize"], + visible=_CFG["visible"]): + if Turtle._screen is None: + Turtle._screen = Screen() + RawTurtle.__init__(self, Turtle._screen, + shape=shape, + undobuffersize=undobuffersize, + visible=visible) + +Pen = Turtle + +def write_docstringdict(filename="turtle_docstringdict"): + """Create and write docstring-dictionary to file. + + Optional argument: + filename -- a string, used as filename + default value is turtle_docstringdict + + Has to be called explicitly, (not used by the turtle-graphics classes) + The docstring dictionary will be written to the Python script <filname>.py + It is intended to serve as a template for translation of the docstrings + into different languages. + """ + docsdict = {} + + for methodname in _tg_screen_functions: + key = "_Screen."+methodname + docsdict[key] = eval(key).__doc__ + for methodname in _tg_turtle_functions: + key = "Turtle."+methodname + docsdict[key] = eval(key).__doc__ + + with open("%s.py" % filename,"w") as f: + keys = sorted(x for x in docsdict + if x.split('.')[1] not in _alias_list) + f.write('docsdict = {\n\n') + for key in keys[:-1]: + f.write('%s :\n' % repr(key)) + f.write(' """%s\n""",\n\n' % docsdict[key]) + key = keys[-1] + f.write('%s :\n' % repr(key)) + f.write(' """%s\n"""\n\n' % docsdict[key]) + f.write("}\n") + f.close() + +def read_docstrings(lang): + """Read in docstrings from lang-specific docstring dictionary. + + Transfer docstrings, translated to lang, from a dictionary-file + to the methods of classes Screen and Turtle and - in revised form - + to the corresponding functions. + """ + modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()} + module = __import__(modname) + docsdict = module.docsdict + for key in docsdict: + try: +# eval(key).im_func.__doc__ = docsdict[key] + eval(key).__doc__ = docsdict[key] + except Exception: + print("Bad docstring-entry: %s" % key) + +_LANGUAGE = _CFG["language"] + +try: + if _LANGUAGE != "english": + read_docstrings(_LANGUAGE) +except ImportError: + print("Cannot find docsdict for", _LANGUAGE) +except Exception: + print ("Unknown Error when trying to import %s-docstring-dictionary" % + _LANGUAGE) + + +def getmethparlist(ob): + """Get strings describing the arguments for the given object + + Returns a pair of strings representing function parameter lists + including parenthesis. The first string is suitable for use in + function definition and the second is suitable for use in function + call. The "self" parameter is not included. + """ + defText = callText = "" + # bit of a hack for methods - turn it into a function + # but we drop the "self" param. + # Try and build one for Python defined functions + args, varargs, varkw = inspect.getargs(ob.__code__) + items2 = args[1:] + realArgs = args[1:] + defaults = ob.__defaults__ or [] + defaults = ["=%r" % (value,) for value in defaults] + defaults = [""] * (len(realArgs)-len(defaults)) + defaults + items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)] + if varargs is not None: + items1.append("*" + varargs) + items2.append("*" + varargs) + if varkw is not None: + items1.append("**" + varkw) + items2.append("**" + varkw) + defText = ", ".join(items1) + defText = "(%s)" % defText + callText = ", ".join(items2) + callText = "(%s)" % callText + return defText, callText + +def _turtle_docrevise(docstr): + """To reduce docstrings from RawTurtle class for functions + """ + import re + if docstr is None: + return None + turtlename = _CFG["exampleturtle"] + newdocstr = docstr.replace("%s." % turtlename,"") + parexp = re.compile(r' \(.+ %s\):' % turtlename) + newdocstr = parexp.sub(":", newdocstr) + return newdocstr + +def _screen_docrevise(docstr): + """To reduce docstrings from TurtleScreen class for functions + """ + import re + if docstr is None: + return None + screenname = _CFG["examplescreen"] + newdocstr = docstr.replace("%s." % screenname,"") + parexp = re.compile(r' \(.+ %s\):' % screenname) + newdocstr = parexp.sub(":", newdocstr) + return newdocstr + +## The following mechanism makes all methods of RawTurtle and Turtle available +## as functions. So we can enhance, change, add, delete methods to these +## classes and do not need to change anything here. + +__func_body = """\ +def {name}{paramslist}: + if {obj} is None: + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNING = True + raise Terminator + {obj} = {init} + try: + return {obj}.{name}{argslist} + except TK.TclError: + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNING = True + raise Terminator + raise +""" + +def _make_global_funcs(functions, cls, obj, init, docrevise): + for methodname in functions: + method = getattr(cls, methodname) + pl1, pl2 = getmethparlist(method) + if pl1 == "": + print(">>>>>>", pl1, pl2) + continue + defstr = __func_body.format(obj=obj, init=init, name=methodname, + paramslist=pl1, argslist=pl2) + exec(defstr, globals()) + globals()[methodname].__doc__ = docrevise(method.__doc__) + +_make_global_funcs(_tg_screen_functions, _Screen, + 'Turtle._screen', 'Screen()', _screen_docrevise) +_make_global_funcs(_tg_turtle_functions, Turtle, + 'Turtle._pen', 'Turtle()', _turtle_docrevise) + + +done = mainloop + +if __name__ == "__main__": + def switchpen(): + if isdown(): + pu() + else: + pd() + + def demo1(): + """Demo of old turtle.py - module""" + reset() + tracer(True) + up() + backward(100) + down() + # draw 3 squares; the last filled + width(3) + for i in range(3): + if i == 2: + begin_fill() + for _ in range(4): + forward(20) + left(90) + if i == 2: + color("maroon") + end_fill() + up() + forward(30) + down() + width(1) + color("black") + # move out of the way + tracer(False) + up() + right(90) + forward(100) + right(90) + forward(100) + right(180) + down() + # some text + write("startstart", 1) + write("start", 1) + color("red") + # staircase + for i in range(5): + forward(20) + left(90) + forward(20) + right(90) + # filled staircase + tracer(True) + begin_fill() + for i in range(5): + forward(20) + left(90) + forward(20) + right(90) + end_fill() + # more text + + def demo2(): + """Demo of some new features.""" + speed(1) + st() + pensize(3) + setheading(towards(0, 0)) + radius = distance(0, 0)/2.0 + rt(90) + for _ in range(18): + switchpen() + circle(radius, 10) + write("wait a moment...") + while undobufferentries(): + undo() + reset() + lt(90) + colormode(255) + laenge = 10 + pencolor("green") + pensize(3) + lt(180) + for i in range(-2, 16): + if i > 0: + begin_fill() + fillcolor(255-15*i, 0, 15*i) + for _ in range(3): + fd(laenge) + lt(120) + end_fill() + laenge += 10 + lt(15) + speed((speed()+1)%12) + #end_fill() + + lt(120) + pu() + fd(70) + rt(30) + pd() + color("red","yellow") + speed(0) + begin_fill() + for _ in range(4): + circle(50, 90) + rt(90) + fd(30) + rt(90) + end_fill() + lt(90) + pu() + fd(30) + pd() + shape("turtle") + + tri = getturtle() + tri.resizemode("auto") + turtle = Turtle() + turtle.resizemode("auto") + turtle.shape("turtle") + turtle.reset() + turtle.left(90) + turtle.speed(0) + turtle.up() + turtle.goto(280, 40) + turtle.lt(30) + turtle.down() + turtle.speed(6) + turtle.color("blue","orange") + turtle.pensize(2) + tri.speed(6) + setheading(towards(turtle)) + count = 1 + while tri.distance(turtle) > 4: + turtle.fd(3.5) + turtle.lt(0.6) + tri.setheading(tri.towards(turtle)) + tri.fd(4) + if count % 20 == 0: + turtle.stamp() + tri.stamp() + switchpen() + count += 1 + tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right") + tri.pencolor("black") + tri.pencolor("red") + + def baba(xdummy, ydummy): + clearscreen() + bye() + + time.sleep(2) + + while undobufferentries(): + tri.undo() + turtle.undo() + tri.fd(50) + tri.write(" Click me!", font = ("Courier", 12, "bold") ) + tri.onclick(baba, 1) + + demo1() + demo2() + exitonclick() |