# xturtle.py: a Tkinter based turtle graphics module for # # Copyright (C) 2006 Gregor Lingl, # # 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. 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.left(25), and it rotates in-place 25 degrees clockwise. By combining together these and similar commands, intricate shapes and pictures can easily be drawn. ----- xturtle.py This module is an extended reimplementation of turtle.py from the Python standard distribution. (See: http:\\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. - 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 graphcis. - 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). - commands for controlling background color or backgorund image. - The implementation uses a 2-vector class named _Vec, derived from tuple. This class can also be imported and used by the programmer, which makes certain types of computations very natural and compact. Behind the scenes there are some features included with possible extensionsin in mind. These will be commented and documented elsewhere. - The module comes with a set of examples of different characters and complexity and a very simple q&d assembled demo viewer. """ # xturtle.py # Version: 0.95-a0 ## + 1 bugfix # Date: 2008-03-13 # Author: Gregor Lingl, # Vienna, Austria # email: glingl@aon.at _ver = "xturtle0.95a0 - bugfixes: 1" # bugfix nr 1, (reported by john Posner, 15. 3. 2008): # weird behavour of undo() with fill and redo fill # print _ver import Tkinter as TK import types import math, sys, time, os ## for compatibility: from math import * ??? from os.path import isfile, split, join from copy import deepcopy _CFG = {"width" : 640, "height" : 480, "canvwidth" : 800, "canvheight": 600, "leftright": -20, "topbottom": -50, "undobuffersize": 1000, "language": "english", "shape": "arrow", "mode": "standard", "importvec" : True } def _readconfig(cfgdict = _CFG): default_cfg = "xturtle.cfg" if isfile(default_cfg): cfg_file = default_cfg print "Loading config-file from cwd:", os.getcwd() else: head, tail = split(__file__) cfg_file = join(head, default_cfg) if not isfile(cfg_file): print "No config file!" return print "Loading config-file from:", head f = open(cfg_file, "r") cfglines = f.readlines() for line in cfglines: if line.startswith("#"): continue try: key, value = line.split("=") key = key.strip().lower() value = value.strip().lower() try: if value == "true": value = True elif value == "false": value = False elif value == "none": value = None elif "." in value: value = float(value) else: value = int(value) except: pass cfgdict[key] = value except: pass _readconfig(_CFG) def __methodDict(cls, _dict): 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): _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 = ()): _dict = {} __methodDict(toClass, _dict) for ex in _dict.keys(): if ex[:1] == '_' or ex[-1:] == '_': del _dict[ex] for ex in exclude: if _dict.has_key(ex): del _dict[ex] for ex in __methods(fromClass): if _dict.has_key(ex): del _dict[ex] for method, func in _dict.items(): d = {'method': method, 'func': func} if type(toPart) == types.StringType: execString = \ __stringBody % {'method' : method, 'attribute' : toPart} exec execString in d fromClass.__dict__[method] = d[method] class ScrolledCanvas(TK.Frame): """Modeled after the scrolled canvas class from Grayons's book Python and Tkinter Programming. 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._root = 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._root.bind('', self.onResize) def reset(self, canvwidth=None, canvheight=None, bg = None): 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.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 _Vec(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 _Vec(self[0]+other[0], self[1]+other[1]) def __mul__(self, other): if isinstance(other, _Vec): return self[0]*other[0]+self[1]*other[1] return _Vec(self[0]*other, self[1]*other) def __rmul__(self, other): if isinstance(other, int) or isinstance(other, float): return _Vec(self[0]*other, self[1]*other) def __sub__(self, other): return _Vec(self[0]-other[0], self[1]-other[1]) def __neg__(self): return _Vec(-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 = _Vec(-self[1], self[0]) angle = angle * math.pi / 180.0 c, s = math.cos(angle), math.sin(angle) return _Vec(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 if _CFG["importvec"]: Vec = _Vec class TurtleScreenBase(object): """Provides the basic graphics functionality. Interface between Tkinter and xturtle.py. To port xturtle.py to some other graphics toolkit a corresponding TurtleScreenBase class has to be implemented. """ @staticmethod def _blankimage(): """returns a blank image object """ img = TK.PhotoImage(width=1, height=1) img.blank() return img @staticmethod def _image(filename): """returns an image object containing the imagedata from a gif-file named filename. """ return TK.PhotoImage(file=filename) 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 def _createpoly(self): """Creates 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): """configures 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) cl.append(-y) 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): """Creates 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): """configures 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) cl.append(-y) 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): """deletes graphics item from canvas """ self.cv.delete(item) def _update(self): """readraws 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): """Checks 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._canvas.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 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 _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, "" % num) else: def eventfun(event): x, y = self.cv.canvasx(event.x), -self.cv.canvasy(event.y) fun(x, y) self.cv.tag_bind(item, "" % 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, "" % num) else: def eventfun(event): x, y = self.cv.canvasx(event.x), -self.cv.canvasy(event.y) fun(x, y) self.cv.tag_bind(item, "" % 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, "" % num) else: def eventfun(event): try: x, y = self.cv.canvasx(event.x), -self.cv.canvasy(event.y) fun(x, y) except: pass self.cv.tag_bind(item, "" % 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("" % num) else: def eventfun(event): x, y = self.cv.canvasx(event.x), -self.cv.canvasy(event.y) fun(x, y) self.cv.bind("" % num, eventfun, add) def _onkey(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("" % key, None) else: def eventfun(event): fun() self.cv.bind("" % 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, (x, y), image): """Configure image item as to draw image object at position (x,y) on canvas) """ self.cv.coords(item, (x, -y)) 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 xturtle 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 #### Rudiments of Error handling with these classes #### Urgently need to be amended. _DEBUG = 0 _NONE = types.NoneType def debug(level=1): global _DEBUG _DEBUG = level class Terminator (Exception): """Will be raised in TurtleScreen.update, if _RUNNING becomes False. Thus stops execution of turtle graphics script. (Main purpose: use in in the Demo-Viewer xturtle.Demo.py. """ pass class TG_Error(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 TG_Error, "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 addshape() """ if self._type != "compound": raise TG_Error, "Cannot add component to %s Shape" % self._type if outline is None: outline = fill self._data.append([poly, fill, outline]) class Tbuffer(object): 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 get(self): if self.bufsize > 0: return self.buffer[self.ptr] def set(self, item): if self.bufsize > 0: self.buffer[self.ptr] = item def __repr__(self): return str(self.buffer) + " " + str(self.ptr) class TurtleScreen(TurtleScreenBase): """Provides screen oriented methods like setbg etc. All those methods are also transferred to the RawTurtle class, so theys can be called as RawTurtle methods or - consequently - functions. Only relies upon the methods of TurtleScreenBase and NOT upon components of the underlying graphics toolkit - which is Tkinter in this case. """ _STANDARD_DELAY = 5 _RUNNING = True def __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),(-3,-8),(0,-6),(3,-8))), "blank" : Shape("image", self._blankimage()) } self._bgpics = {"nopic" : ""} TurtleScreenBase.__init__(self, cv) self._colormode = 1.0 self._bgpic = self._createimage("") self._bgpicname = "nopic" self._tracing = 1 self._delayvalue = 10 self._updatecounter = 0 self._turtles = [] def addshape(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: addshape("turtle.gif") --or: addshape("tri", ((0,0), (10,10), (-10,10))) Example (for a TurtleScreen instance named screen): >>> screen.addshape("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 elif isinstance(shape, tuple): shape = Shape("polygon", shape) ## else shape assumed to be Shape-instance self._shapes[name] = shape def _color(self, args): """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(args) == 1: color = args[0] if color == "": return color if isinstance(color, str) and self._iscolorstring(color): return color else: args = color if isinstance(args, str): raise TG_Error, "bad color sequence: %s" % `args` try: r, g, b = args except: raise TG_Error, "bad color arguments: %s" % `args` if self._colormode == 1.0: # r, g, b = tuple(map(lambda x: round(255.0*x), (r, g, b))) 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 TG_Error, "bad color sequence: %s" % `args` return "#%02x%02x%02x" % (r, g, b) def colormode(self, cmode=None): """ Return the colormode or set it to 1.0 or 255. --- Argument: one of the values 1.0 or 255 call: colormode(255) # for example --or: colormode() Example (for a Turtle instance named turtle): >>> screen = turtle.getscreen() >>> screen.colormode() 1.0 >>> screen.colormode(255) >>> turtle.pencolor(240,160,80) """ if cmode is None: return self._colormode if cmode in [1.0, 255]: self._colormode = cmode def resetscreen(self): """Reset all Turtles on the Screen to their initial state. Example (for a TurtleScreen instance named screen): >>> screen.reset() """ for turtle in self._turtles: turtle.reset() def turtles(self): """Return the list of turtles on the screen. Example (for a TurtleScreen instance named screen): >>> screen.turtles() [] """ 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._color(args) else: color = None return self._bgcolor(color) def tracer(self, n=None, delay=None): """tracer(True/False) turns turtle animation on/off. --- tracer accepts positive integers as first argument: tracer(n) has the effect, that only each n-th update is really performed. Can be used to accelerate the drawing of complex graphics.) Second arguments sets delay value (see RawTurtle.delay()) Optional arguments: two nonnegative integers call: tracer() --or: tracer(, ) Example (for a Turtle instance named turtle): >>> turtle.tracer(8, 25) >>> l=2 >>> for i in range(200): turtle.fd(l) turtle.rt(90) l+=2 """ if n is None: return self._tracing self._tracing = int(n) self._updatecounter = 0 if delay is None: pass else: self._delayvalue = int(delay) # correction added 2006-08-31 if n: self.update() def delay(self, delay=None): """ Return or set the drawing delay in milliseconds, which determines the speed of the turtle-animation. --- Argument: None or positive integer call: delay() --or: delay() 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): if self._tracing > 0: self._updatecounter += 1 self._updatecounter %= self._tracing def update(self, forced=False): """Perform a TurtleScreen update. """ if not TurtleScreen._RUNNING: TurtleScreen._RUNNNING = True raise Terminator if self._tracing == 0 and not forced: return if self._updatecounter == 0 or forced: self._update() self._delay(self._delayvalue) def resize(self, canvwidth=None, canvheight=None, bg=None): """Resize the canvas, the turtles are drawing on. Does not alter the drawing window. To observe hidden parts of the canvas use the scrollbars. (Can make visible parts of a drawing, which were outside the canvas before!) Arguments: two positive integers Example (for a TurtleScreen instance named screen): >>> screen.resize(2000,1500) ### e. g. to search for an erroneously escaped turtle ;-) """ # needs amendment if not isinstance(self.cv, ScrolledCanvas): return self.canvwidth, self.canvheight if canvwidth is None or canvheight is None: return self.cv.canvwidth, self.cv.canvheight self.cv.reset(canvwidth, canvheight, bg) self.canvwidth, self.canvheight = canvwidth, canvheight def window_width(self): """ Returns the width of the turtle window. Example (for a TurtleScreen instance named screen): >>> screen.window_width() 640 """ width = self.cv.winfo_width() if width <= 1: # the window isn't managed by a geometry manager width = self.cv['width'] return width def window_height(self): """ Return the height of the turtle window. Example (for a TurtleScreen instance named screen): >>> screen.window_height() 480 """ height = self.cv.winfo_height() if height <= 1: # the window isn't managed by a geometry manager height = self.cv['height'] return height def screen_width(self): """ Returns the width of the turtle screen, that is of the rectangular area, which can be viewed by using the scrollbars. Example (for a TurtleScreen instance named screen): >>> screen.screen_width() 800 """ return self.canvwidth def screen_height(self): """ Return the height of the turtle screen, that is of the rectangular area, which can be viewed by using the scrollbars. Example (for a TurtleScreen instance named screen): >>> screen.screen_height() 600 """ return self.canvheight def getshapes(self): """Return a list of names of all currently available shapes. Example (for a TurtleScreen instance named screen): >>> screen.getshapes() ['turtle', 'circle', 'arrow', 'blank'] """ return sorted(self._shapes.keys()) def onclick(self, fun, btn=1, add=None): """Bind fun to mouse-click event on canvas. Arguments: 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 Example (for a TurtleScreen instance named screen and a Turtle instance named turtle): >>> screen.onclick(turtle.goto) ### Subsequently clicking into the TurtleScreen will ### make the turtle move to the clicked point. >>> screen.onclick(None) ### event-binding will be removed """ self._onscreenclick(fun, btn, add) def onkey(self, fun, key = None): """Bind fun to key-release event of key. Canvas must have focus. (See method listen.) Example (for a TurtleScreen instance named screen and a Turtle instance named turtle): >>> def f(): turtle.fd(50) turtle.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 """ self._onkey(fun, key) def listen(self, xdummy=None, ydummy=None): """Set focus on TurtleScreen (in order to collect key-events) No arguments. Dummy argumetns 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 is a function with no arguments. t is a number >= 0 Example (for a TurtleScreen instance named screen and a Turtle instance named turtle): >>> running = True >>> def f(): if running: turtle.fd(50) turtle.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 to image contained in gif-file picname, or return name of current backgroundimage. If picname is "nopic", backgroundimage is deleted. Argument: string, which is the name of a gif imagefile or "nopic". Example (for a TurtleScreen instance named screen): >>> screen.bgpic() 'nopic' >>> screen.bgpic("landscape.gif") >>> screen.bgpic() 'landscape.gif' >>> screen.bgpic("nopic") """ 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 class TNavigator(object): """Navigation part of the RawTurtle. Implements methods for turtle movement. """ START_ORIENTATION = { "standard": _Vec(1.0, 0.0), "logo" : _Vec(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() if mode == "standard": self._angleOffset = 0 self._angleOrient = 1 else: # mode == "logo": self._angleOffset = self._fullcircle/4. self._angleOrient = -1 TNavigator.reset(self) def reset(self): """reset turtle to its initial values Will be overwritten by parent class """ self._position = _Vec(0.0, 0.0) self._orient = TNavigator.START_ORIENTATION[self._mode] self._odometer = 0.0 self._odometerOn = False def mode(self, mode=None): """Sets turtle-mode to 'standard' or 'logo' and resets turtle. Mode 'standard' is compatible with turtle.py. Mode 'logo' is compatible with most Logo-Turtle-Graphics'. If mode is not given, return the current mode. call: mode('standard') --or: mode('logo') --or: 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 == None: return self._mode if mode not in ["standard", "logo"]: return self._mode = mode if mode == "standard": self._angleOffset = 0 self._angleOrient = 1 else: # mode == "logo": self._angleOffset = self._fullcircle/4. self._angleOrient = -1 self.reset() def _setDegreesPerAU(self, fullcircle): 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: number (set number of 'degrees' for a full circle). call: degrees() # fullcircle defaults to 360 - # learn more with help(degrees) Example (for a Turtle instance named turtle): >>> turtle.left(90) >>> turtle.heading() 90 >>> turtle.degrees(400.0) # angle measurement in gon >>> turtle.heading() 100 >>> turtle.degrees() >>> turtle.heading() 90 """ 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): """Bewegt die Turtle um distance nach vorne""" ende = self._position + self._orient * distance self._goto(ende) def _rotate(self, angle): """Dreht turtle um angle Grad nach links""" angle *= self._degreesPerAU self._orient = self._orient.rotate(angle) def _goto(self, end): """Bewegt die Turtle nach end""" if self._odometerOn: self._odometer += self.distance(end) self._position = end def forward(self, distance): """forward | fd: Move the turtle forward by the specified distance, in the direction the turtle is headed. --- Argument: a number (integer or float) call: forward() --or: fd() 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): """back | backward | bk: Move the turtle backward by distance, opposite to the direction the turtle is headed The turtle's heading does not change. --- Argument: a number (integer or float) call: back() --or: bk() --or: backward() 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): """right | rt: Turn turtle right angle units (units are by default degrees, but can be set via the degrees() and radians() functions.) --- Argument: a number (integer or float) call: right() --or: rt() 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): """ left | lt: Turn turtle left angle units (units are by default degrees, but can be set via the degrees() and radians() functions.) --- Argument: a number (integer or float) call: left() --or: lt() Example (for a Turtle instance named turtle): >>> turtle.heading() 22.0 >>> turtle.left(45) >>> turtle.heading() 67.0 """ self._rotate(angle) def pos(self): """ pos | position: Return the turtle's current location (x,y) (as a vector) 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 setpos(self, x, y=None): """setpos | setposition | goto: Move turtle to an absolute position. If the pen is down, then a line will be drawn. The turtle's orientation does not change. --- Arguments: two numbers or a pair/vector of numbers call: goto(,) # two coordinates --or: goto((,)) # a pair (tuple) of coordinates --or: goto(<2-vector>) # e.g. as returned by pos() >>> 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(_Vec(*x)) else: self._goto(_Vec(x, y)) def home(self): """Move turtle to the origin - coordinates (0,0) --- No arguments. 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 Second coordinate remains unchanged. --- Argument: a number (integer or float) call: setx() Example (for a Turtle instance named turtle): >>> turtle.position() (0.00, 240.00) >>> turtle.setx(10) >>> turtle.position() (10.00, 240.00) """ self._goto(_Vec(x, self._position[1])) def sety(self, y): """ Sets the turtle's second coordinate to y First coordinate remains unchanged. --- Argument: a number (integer or float) call: sety() Example (for a Turtle instance named turtle): >>> turtle.position() (0.00, 40.00) >>> turtle.sety(-10) >>> turtle.position() (0.00, -10.00) """ self._goto(_Vec(self._position[0], y)) def distance(self, x, y=None): """Return the distance from the turtle to (x,y), in turtle step units. --- Arguments: two numbers or a pair/vector of numbers or a Turtle instance call: distance(,) # two coordinates --or: distance((,)) # a pair (tuple) of coordinates --or: distance(<2-vector>) # 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 = _Vec(x, y) if isinstance(x, _Vec): pos = x elif isinstance(x, tuple): pos = _Vec(*x) elif isinstance(x, TNavigator): pos = x._position return abs(pos - self._position) def towards(self, x, y=None): """Return the angle, between the line from turtle-position to pos and the turtle's start orientation. (Depends on modes - "standard" or "logo") --- Arguments: two numbers or a pair/vector of numbers or a Turtle instance call: towards(,) # two coordinates --or: towards((,)) # a pair (tuple) of coordinates --or: towards(<2-vector>) # e.g. as returned by pos() --or: towards(mypen) # where mypen is another turtle 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 = _Vec(x, y) if isinstance(x, _Vec): pos = x elif isinstance(x, tuple): pos = _Vec(*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): """ setheading | seth: Set the turtle facing the given 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 --- Argument: a number (integer or float) call: setheading() 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. The center is radius units left of the turtle; extent - an angle - determines which part of the circle is drawn. If not given, the entire circle is drawn. - If extent is not a full circle, one endpoint of the arc is the current pen position. The arc is drawn in a counter clockwise direction if radius is positive, otherwise in a clockwise direction. In the process, the direction of the turtle is changed by the amount of the 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. --- Arguments: number, Optional arguments: number, integer call: circle() # full circle --or: circle(, ) # arc --or: circle(, , ) Example (for a Turtle instance named turtle): >>> turtle.circle(50) >>> turtle.circle(120, 180) # semicircle """ def circle(self, radius, extent = None, steps = None): 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 def reset_odometer(self): """Reset the turtle odometer to 0.0""" self._odometer = 0.0 def odometer_on(self): """Resume the turtle odometer, i. e. the distance the turtle moves will be recorded. """ self._odometerOn = True def odometer_off(self): """Suspend the turtle odometer. """ self._odometerOn = False def get_odometer(self): """Return the turtle odometer value""" return self._odometer ## 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 goto = setpos setposition = setpos seth = setheading class TPen(object): """Drawing part of the RawTurtle. Implements drawing properties. """ def __init__(self): self._resizemode = "auto" # or "user" or "noresize" self.undobuffer = None TPen._reset(self) def _reset(self): self._pensize = 1 self._shown = True self._pencolor = "black" self._fillcolor = "" self._drawing = True self._speed = 3 self._stretchfactor = (1, 1) self._tilt = 0 self._outlinewidth = 1 def resizemode(self, rmode=None): """Set resizemode to one of the values: "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 turtlesize() - "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 turtlesize with arguments. --- (Optional) Argument: one of the strings "auto", "user", "noresize" call: resizemode("user") # for example --or: resizemode() 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._resizemode = rmode self._update() def pensize(self, width=None): """ pensize | width: Set (or return) the line thickness to width. If resizemode is set to "auto" and turtleshape is a polygon, that polygon is drawn with the same line thickness. IF argument --- Argument: positive number call: pensize() 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 if self.undobuffer: self.undobuffer.push(("pens", self._pensize)) self._newLine() self._pensize = width self._update() def penup(self): """penup | pu | up: Pull the pen up -- no drawing when moving. Aliases: penup, pu, up No argument Example: >>> turtle.penup() """ if not self._drawing: return self._drawing = False self._newLine(False) def pendown(self): """pendown | pd | down: Pull the pen down -- no drawing when moving. Aliases: pendown, pd, down No argument. Example (for a Turtle instance named turtle): >>> turtle.pendown() """ if self._drawing: return self._newLine() self._drawing = True def isdown(self): """Return True if pen is down, False if it's up. Aliases: isdown | pendownp --- 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 the turtle's speed or set it to an integer value in the range 0 .. 10. If no argument is given: current speed is returned. If input is a number greater than 10 or smaller than 0.5, speed is set to 0. Speedstrings correspond to speedvalues in the following way: 'fastest' : 0 'fast' : 10 'normal' : 6 'slow' : 3 'slowest' : 1 speeds from 1 to 10 enforces increasingly faster animation of line drawing and turtle turning. !!! speed = 0 : *no* animation takes place. forward/back makes turtle jump and likewise left/right make the turtle turn instantly Optional Argument: number or one of the strings 'fastest', 'fast', 'normal', 'slow', 'slowest' (for compatibility with turtle.py) !!! Optional Argument: number in range 0..10, or 'speedstring' as described above. call: speed(1) # slow --or: speed(10) # fastest --or: speed('fastest') --or: speed(0) 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._speed = speed def color(self, *args): """Return or set the pencolor and fillcolor. If turtleshape is a polygon, outline and interior of that polygon is drawn with the newly set colors. - Arguments: Several input formats are allowed. They use 0, 1, 2, 3, or 6 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(s), color((r,g,b)), color(r,g,b) inputs as in pencolor, set both, fillcolor and pencolor, to the given value. - color(s1, s2), color((r1,g1,b1), (r2,g2,b2)) color(r1, g1, b1, r2, g2, b2) equivalent to pencolor(s1) fillcolor(s2) and analog, if the other input formats are used. call: color() --or: color(, ) --or with s in range 0 .. colormode (i.e. 1.0 or 255) - color((, , )) - color((, , ), (, , )) - color((, , , , , )) - color() 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: if self.undobuffer: self.undobuffer.push(["seq"]) self.undobuffer.cumulate = True l = len(args) if l == 2 or l == 6: pcolor = args[:(l//2)] fcolor = args[(l//2):] else: pcolor = fcolor = args self.pencolor(*pcolor) self.fillcolor(*fcolor) if self.undobuffer: self.undobuffer.cumulate = False else: return self._pencolor, self._fillcolor def pencolor(self, *args): """ Return or set the pen color. If turtleshape is a polygon, the outline of that polygon is drawn with the newly set 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(s) s is a Tk 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 call: pencolor() --or with s in range 0 .. colormode (i.e. 1.0 or 255) - pencolor(, , ) - pencolor((, , )) - 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: if self.undobuffer: self.undobuffer.push(("pcol", self.pencolor())) color = self._color(args) if color == self._pencolor: return self._newLine() self._pencolor = color self._update() else: return self._pencolor def fillcolor(self, *args): """ Return or set the fill color. If turtleshape is a polygon, that polygon is filled with the newly set fillcolor. Four input formats are accepted: - fillcolor() 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. - fillcolor(s) s is a Tk 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 call: fillcolor() --or with s in range 0 .. colormode (i.e. 1.0 or 255) - fillcolor(, , ) - fillcolor((, , )) - fillcolor() Example (for a Turtle instance named turtle): >>> turtle.fillcolor('violet') >>> tup = turtle.pencolor() >>> turtle.fillcolor(tup) >>> turtle.fillcolor(0, .5, 0) """ if args: if self.undobuffer: self.undobuffer.push(("fcol", self.fillcolor())) color = self._color(args) if color == self._fillcolor: return self._fillcolor = color self._update() else: return self._fillcolor def showturtle(self): """showturtle | st: Makes the turtle visible. No argument. Example (for a Turtle instance named turtle): >>> turtle.hideturtle() >>> turtle.showturtle() """ self._shown = True self._update() def hideturtle(self): """hideturtle | ht: Makes the turtle invisible. 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. No argument. Example (for a Turtle instance named turtle): >>> turtle.hideturtle() """ self._shown = False self._update() def isvisible(self): """Return True if the Turtle is shown, False if it's hidden. Aliases: isvisible | shownp --- No argument. Example (for a Turtle instance named turtle): >>> turtle.hideturtle() >>> if not turtle.isvisible(): print "Turtle is hidden!" Turtle is hidden! """ return self._shown def pen(self, pen=None, **pendict): """return (with no argument) 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) "outline" : positive number This dicionary 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. Arguments: pen : a dictionary with some or all of the above listed keys. **pendict : one or more keyword-arguments with the above listed keys as keywords. 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} >>> 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} >>> 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} """ if not (pen or pendict): return {"shown" : self._shown, "pendown" : self._drawing, "pencolor" : self._pencolor, "fillcolor" : self._fillcolor, "pensize" : self._pensize, "speed" : self._speed, "resizemode" : self._resizemode, "stretchfactor" : self._stretchfactor, "outline" : self._outlinewidth } if isinstance(pen, dict): p = pen else: p = {} p.update(pendict) 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._color((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._color((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 "outline" in p: self._outlinewidth = p["outline"] if "shown" in p: self._shown = p["shown"] self._update() def turtlesize(self, stretchx=None, stretchy=None, outline=None): """Return or set the pen's attributes x/y-stretchfactors and/or outline. Sets resizemode to "user". If and only if resizemode is set to "user", the turtle will be displayed stretchfactor times as big as the original shape with an outline of width outline. Optinonal arguments: stretchx : positive number stretchy : positive number outline : positive number Examples (for a Turtle instance named turtle): >>> turtle.resizemode("user") >>> turtlesize(5, 5, 12) >>> turtlesize(outline=8) """ # print "turtlesize-args", stretchx, stretchy, outline if stretchx is None and stretchy is None and outline == None: stretchx, stretchy = self._stretchfactor return stretchx, stretchy, self._outlinewidth if stretchx is not None: if stretchy is None: self._stretchfactor = stretchx, stretchx else: self._stretchfactor = stretchx, stretchy elif stretchy is not None: stretchx = self._stretchfactor[0] self._stretchfactor = stretchx, stretchy if outline is not None: self._outlinewidth = outline self.resizemode("user") self._update() def tilt(self, angle): """Turns the shape of the turtle by angle clockwise with respect to it's current tilt-angle. Does not affect it's orientation, i. e. it's direction of movement. Argument: angle : number Examples (for a Turtle instance named turtle): >>> turtle.shape("circle") >>> turtle.turtlesize(5,2) >>> turtle.tilt(30) >>> turtle.fd(50) >>> turtle.tilt(30) >>> turtle.fd(50) """ if angle == None: return self._tilt * (180.0/math.pi) / self._degreesPerAU w = angle * self._degreesPerAU * math.pi / 180.0 self._tilt = (self._tilt + w) % (2*math.pi) self._update() def settilt(self, angle=None): """Turns the shape of the turtle to angle with respect to it's current orientation, but does not affect it's orientation, i. e. it's direction of movement. If no argument is given, the current tilt-angle is returned. Optional argument: angle : number Examples (for a Turtle instance named turtle): >>> turtle.shape("circle") >>> turtle.turtlesize(5,2) >>> turtle.tilt(45) >>> turtle.fd(50) >>> turtle.tilt(-45) >>> turtle.fd(50) """ self._tilt = (angle * self._degreesPerAU * math.pi / 180.0) % (2*math.pi) 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""" width = pensize up = penup pu = penup pd = pendown pendownp = isdown down = pendown st = showturtle ht = hideturtle shownp = isvisible class _TurtleImage(object): """Helper class: Datatype to store Turtle attributes """ def __init__(self, screenIndex, shapeIndex): self.screenIndex = screenIndex self._type = None self._setshape(shapeIndex) def _setshape(self, shapeIndex): screen = RawTurtle.screens[self.screenIndex] 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 it's animation. """ canvases = [] screens = [] def __init__(self, canvas, shape="arrow", mode="standard", visible=True, undobufsize=1000): if canvas not in RawTurtle.canvases: RawTurtle.canvases.append(canvas) RawTurtle.screens.append(TurtleScreen(canvas)) self.screenIndex = RawTurtle.canvases.index(canvas) TNavigator.__init__(self, mode) TPen.__init__(self) screen = self.screens[self.screenIndex] screen._turtles.append(self) self.drawingLineItem = screen._createline() self.turtle = _TurtleImage(self.screenIndex, 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.undobuffer = Tbuffer(undobufsize) self._update() def reset(self): """Delete the pen's drawing from the screen, re-center the pen and set variables to the default values. Example (for a Turtle instance named turtle): >>> turtle.position() (0.00,-22.00) >>> turtle.seth(100) >>> 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 _clear(self): """Delete all of pen's drawings""" screen = self.screens[self.screenIndex] items = self.items self._fillitem = self._fillpath = None for item in items: screen._delete(item) self.currentLineItem = screen._createline() self.currentLine = [] if self._drawing: self.currentLine.append(self._position) self.items = [self.currentLineItem] self.clearstamps() self.undobuffer.reset() def clear(self): """Delete the pen's drawings from the screen. Does not move the pen. Drawings of other pens are not affected. Examples (for a Turtle instance named turtle): >>> turtle.clear() """ self._clear() self._update() def _update(self, count=True, forced=False): """Perform a TurtleScreen update. """ screen = self.screens[self.screenIndex] if count: screen._incrementudc() ## ## Bug fix contributed by Brad Miller ## ## if forced: if forced or (screen._tracing != 0 and screen._updatecounter==0): # bnm rather than self._drawturtle() this seems to fix the problem of # turtles coming and going at odd times when tracer is set to values != 1 for t in screen._turtles: t._drawturtle() ## if forced or (screen._tracing != 0 and screen._updatecounter==0): ## self._drawturtle() ## if len(self.currentLine)>1: ## screen._drawline(self.currentLineItem, self.currentLine, ## self._pencolor, self._pensize) if len(t.currentLine)>1: screen._drawline(t.currentLineItem, t.currentLine, t._pencolor, t._pensize) screen.update(forced) def update(self): """Perform a TurtleScreen update. Especially useful to control screen updates when tracer(False) is set. Example (for a Turtle instance named turtle): >>> turtle.tracer(0) >>> for i in range(36): for j in range(5): turtle.fd(100) turtle.lt(72) turtle.update() # draws a pentagon at once turtle.lt(10) """ self._update(forced=True) def tracer(self, flag=None, delay=None): """tracer(True/False) turns turtle animation on/off. tracer accepts positive integers as first argument: tracer(n) has the effect, that only each n-th 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) >>> l=2 >>> for i in range(200): turtle.fd(l) turtle.rt(90) l+=2 """ screen = self.screens[self.screenIndex] if flag is None: return screen._tracing if screen._tracing != 1: for t in screen._turtles: if len(t.currentLine)>1: screen._drawline(t.currentLineItem, t.currentLine, t._pencolor, t._pensize) screen.tracer(flag, delay) def _color(self, args): return self.getscreen()._color(args) def _cc(self, args): """Convert colortriples to hexstrings. """ if isinstance(args, str): return args try: r, g, b = args except: raise TG_Error, "bad color arguments: %s" % `args` if self.getscreen()._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 TG_Error, "bad color sequence: %s" % `args` return "#%02x%02x%02x" % (r, g, b) def clone(self): """Create and return an clone of the pen, with same position, heading and pen properties. Example (for a Turtle instance named turtle): turtle = Turtle() tortoise = turtle.clone() """ screen = self.getscreen() self._newLine(self._drawing) q = deepcopy(self) 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 pen shape to shape with given name. Shape must exist in the TurtleScreen's shape dictionary. Initially there are three polygon shapes: 'arrow', 'turtle', 'circle'. To learn about different types of shapes see method addshape! Without argument return name of current shape. Example (for a Turtle instance named turtle): >>> turtle.shape() 'arrow' >>> turtle.shape("turtle") >>> shape() 'turtle' """ if name is None: return self.turtle.shapeIndex if not name in self.getshapes(): raise TG_Error, "There is no shape named %s" % name self.turtle._setshape(name) self._update() def _polytrafo(self, poly): """Computes transformed polygon shapes from a shape according to current position and heading. """ p0, p1 = self._position e0, e1 = self._orient return [(p0+e1*x+e0*y, p1-e0*x+e1*y) for (x, y) in poly] def _drawturtle(self): """Manages the correct rendering of the turtle with respect to it's shape, resizemode, strech and tilt etc.""" screen = self.screens[self.screenIndex] 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 shape = tshape else: if self._resizemode == "auto": lx = ly = max(1, self._pensize/5.0) w = self._pensize tiltangle = 0 elif self._resizemode == "user": lx, ly = self._stretchfactor w = self._outlinewidth tiltangle = self._tilt shape = [(lx*x, ly*y) for (x, y) in tshape] t0, t1 = math.sin(tiltangle), math.cos(tiltangle) shape = [(t1*x+t0*y, -t0*x+t1*y) for (x,y) in shape] shape = self._polytrafo(shape) 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": lx, ly = self._stretchfactor w = self._outlinewidth for item, (poly, fc, oc) in zip(titem, tshape): poly = [(lx*x, ly*y) for (x, y) in poly] poly = self._polytrafo(poly) screen._drawpoly(item, poly, fill=self._cc(fc), outline=self._cc(oc), width=w, 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): """Stamps a copy of the turtle shape onto the canvas at the current turtle position. Returns an 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.screens[self.screenIndex] shape = screen._shapes[self.turtle.shapeIndex] ttype = shape._type tshape = shape._data if ttype == "polygon": stitem = screen._createpoly() if self._resizemode == "noresize": w = 1 shape = tshape else: if self._resizemode == "auto": lx = ly = max(1, self._pensize/5.0) w = self._pensize tiltangle = 0 elif self._resizemode == "user": lx, ly = self._stretchfactor w = self._outlinewidth tiltangle = self._tilt shape = [(lx*x, ly*y) for (x, y) in tshape] t0, t1 = math.sin(tiltangle), math.cos(tiltangle) shape = [(t1*x+t0*y, -t0*x+t1*y) for (x,y) in shape] shape = self._polytrafo(shape) 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) lx, ly = self._stretchfactor w = self._outlinewidth for item, (poly, fc, oc) in zip(stitem, tshape): poly = [(lx*x, ly*y) for (x, y) in poly] poly = self._polytrafo(poly) screen._drawpoly(item, poly, fill=self._cc(fc), outline=self._cc(oc), width=w, top=True) self.stampItems.append(stitem) self.undobuffer.push(("stamp", stitem)) return stitem def _clearstamp(self, stampid): """does the work for clearstamp() and clearstamps() """ screen = self.screens[self.screenIndex] if stampid in self.stampItems: if isinstance(stampid, tuple): for subitem in stampid: screen._delete(subitem) else: 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 : return value of previous stamp() call Example (for a Turtle instance named turtle): >>> turtle.color("blue") >>> turtle.s = stamp() >>> turtle.fd(50) >>> turtle.clearstamp() """ self._clearstamp(stampid) self._update() def clearstamps(self, n=None): """If n is None, delete all of pen's stamps, else (n>0) delete first n stamps or (n<0) last n stamps. Optional argument: n : integer 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() """ screen = self.screens[self.screenIndex] 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 methodes for turtle movement depend on this one. """ ## Version mit undo-stuff go_modes = ( self._odometer, self._drawing, self._pencolor, self._pensize, isinstance(self._fillpath, list)) if self._odometerOn: self._odometer += self.distance(end) screen = self.screens[self.screenIndex] undo_entry = ("go", self._position, end, go_modes, (self.currentLineItem, self.currentLine[:], screen._pointlist(self.currentLineItem), self.items[:]) ) self.undobuffer.push(undo_entry) start = self._position if self._speed and screen._tracing == 1: diff = end-start nhops = 1+int(abs(diff)/(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 odometer, drawing, pc, ps, filling = go_modes cLI, cL, pl, items = coodata self._odometer = odometer screen = self.screens[self.screenIndex] if abs(self._position - new) > 0.5: print "undogoto: HALLO-DA-STIMMT-WAS-NICHT!" ### restore former situation self.currentLineItem = cLI self.currentLine = cL screen._drawline(cLI, pl, fill=pc, 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 nhops = 1+int(abs(diff)/(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 crating a polygon, the last vertex ## will be deleted. if the polygon is entirel deleted, ## creatigPoly 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. """ self.undobuffer.push(("rot", angle, self._degreesPerAU)) screen = self.screens[self.screenIndex] angle *= self._degreesPerAU neworient = self._orient.rotate(angle) tracing = 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 i 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. """ screen = self.screens[self.screenIndex] if len(self.currentLine) > 1: screen._drawline(self.currentLineItem, self.currentLine, self._pencolor, self._pensize) self.currentLineItem = screen._createline() self.items.append(self.currentLineItem) else: screen._drawline(self.currentLineItem, top=True) self.currentLine = [] if usePos: self.currentLine = [self._position] def fill(self, flag): """ Call fill(True) before drawing the shape you want to fill, and fill(False) when done. Argument: True/False (or 1/0 respectively) Example (for a Turtle instance named turtle): >>> turtle.fill(True) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.fill(False) """ screen = self.screens[self.screenIndex] entry1 = entry2 = () filling = isinstance(self._fillpath, list) if filling: if len(self._fillpath) > 2: screen._drawpoly(self._fillitem, self._fillpath, fill=self._fillcolor) entry1 = ("dofill", self._fillitem) if flag: self._fillitem = screen._createpoly() self.items.append(self._fillitem) self._fillpath = [self._position] entry2 = ("beginfill", self._fillitem) # , self._fillpath) self._newLine() else: self._fillitem = self._fillpath = None if entry1 == (): if entry2 != (): self.undobuffer.push(entry2) else: if entry2 == (): self.undobuffer.push(entry1) else: self.undobuffer.push(["seq", entry1, entry2]) self._update(count=True) def begin_fill(self): """Called just before drawing a shape to be filled. No argument. Example (for a Turtle instance named turtle): >>> turtle.begin_fill() >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.end_fill() """ self.fill(True) def end_fill(self): """Fill the shape drawn after the call begin_fill(). No argument. Example (for a Turtle instance named turtle): >>> turtle.begin_fill() >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.left(90) >>> turtle.forward(100) >>> turtle.end_fill() """ self.fill(False) def dot(self, size=None, *color): """Draw a dot with diameter size, using pencolor. If size is not given, pensize()+4 is used. Argument: size is number >= 1 (if given) color is colorstring or numeric color tuple Example (for a Turtle instance named turtle): >>> turtle.dot() >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50) """ pen = self.pen() if self.resizemode() == 'auto': self.ht() self.pendown() if size: self.pensize(size) else: self.pensize(self._pensize+4) if color: self.pencolor(*color) self.forward(0) self.pen(pen) def _write(self, txt, align, font): """Performs the writing for write() """ screen = self.screens[self.screenIndex] item, end = screen._write(self._position, txt, align, font, self._pencolor) self.items.append(item) self.undobuffer.push(("wri", item)) return end def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")): """ Write text at the current pen 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('The race is on!') >>> turtle.write('Home = (0, 0)', True, align="center") """ 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) self.undobuffer.cumulate = False def begin_poly(self): """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. Current turtle position is first 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. Example (for a Turtle instance named turtle): >>> p = turtle.get_poly() >>> turtle.addshape("myFavouriteShape", p) """ ## check if there is any poly? -- 1st solution: if self._poly is not None: return tuple(self._poly) def getcanvas(self): """Return the Canvas, the turtle is drawing on. Just for those who like it tu tinker around with (Scrolled or not) Canvas objects' Example (for a Turtle instance named turtle): >>> cv = turtle.getcanvas() >>> cv """ return self.canvases[self.screenIndex] def getscreen(self): """Return the TurtleScreen object, the turtle is drawing on. Some of the RawTurtle methods can also be called as TurtleScreen methods. Example (for a Turtle instance named turtle): >>> ts = turtle.getscreen() >>> ts """ return self.screens[self.screenIndex] def getturtle(self): """Return the Turtleobject itself. Only reasonable use: as a function to return the 'anonymous turtle': Example: >>> turtle = getturtle() >>> turtle.fd(50) >>> turtle >>> turtles() [] """ return self ################################################################ ### screen oriented methods recurring to methods of TurtleScreen ################################################################ def addshape(self, name, shape=None): """Adds a turtle shape to TurtleScreen's shape dictionary. Arguments: (1) name is the name of a gif-file and shape is None. Installs the corresponding image shape. (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). Examples (for a Turtle instance named turtle): >>> turtle.addshape("triangle", ((5,-3),(0,5),(-5,-3))) """ self.getscreen().addshape(name, shape) 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. Examples (for a Turtle instance named turtle): >>> turtle.bgcolor("orange") >>> turtle.bgcolor() 'orange' >>> turtle.bgcolor(0.5,0,0.5) >>> turtle.bgcolor() '#800080' """ return self.getscreen().bgcolor(*args) def bgpic(self, picname=None): """Set background image to image contained in gif-file picname, or return name of current backgroundimage. If picname is "nopic", backgroundimage is deleted. Argument: string, which is the name of a gif imagefile or "nopic". Examples (for a Turtle instance named turtle): >>> turtle.bgpic() 'nopic' >>> turtle.bgpic("landscape.gif") >>> turtle.bgpic() 'landscape.gif' >>> turtle.bgpic("nopic") """ return self.getscreen().bgpic(picname) def colormode(self, cmode=None): """ Return the colormode or set it to 1.0 or 255. --- Argument: None or one of the values 1.0 or 255 call: colormode(255) # for example --or: colormode() Example (for a Turtle instance named turtle): >>> turtle.colormode(255) >>> turtle.pencolor(240,160,80) """ return self.getscreen().colormode(cmode) def delay(self, delay=None): """ Return or set the drawing delay in milliseconds. Screen oriented method, i.e. affects all Pens on the Screen. Argument: None or number >= 0. Example (for a Turtle instance named turtle): >>> turtle.delay(15) >>> turtle.delay() 15 """ return self.getscreen().delay(delay) def getshapes(self): """Return a list of names of all currently available shapes. Example (for a Turtle instance named turtle): >>> turtle.getshapes() ['arrow', 'blank', 'circle', 'turtle'] """ return self.getscreen().getshapes() def clearscreen(self): """resetscreen | clearscreen: Reset all Pens on the Screen to their initial state. Example (for a Turtle instance named turtle): >>> turtle.resetscreen() """ self.getscreen().resetscreen() def turtles(self): """Return the list of turtles on the screen. Example (for a Turtle instance named turtle): >>> turtle.turtles() [] """ return self.getscreen().turtles() def screensize(self, canvwidth=None, canvheight=None, bg=None): """Resize the canvas, the turtles are drawing on. Does not alter the drawing window. To observe hidden parts of the canvas use the scrollbars. (Can make visible parts of a drawing, which were outside the canvas before!) Arguments: two positive integers Example (for a Turtle instance named turtle): >>> turtle.screensize(2000,1500) ### e. g. to search for an erroneously escaped turtle ;-) """ return self.getscreen().resize(canvwidth, canvheight, bg) resetscreen = clearscreen # resize = screensize # for historical reasons, say: 'deprecated' # so not documented getpen = getturtle ##### event binding methods ##### def onscreenclick(self, fun, btn=1, add=None): """Bind fun to mouse-click event on canvas. Arguments: 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 Example (for a Turtle instance named turtle): >>> turtle.onscreenclick(turtle.goto) ### Subsequently clicking into the TurtleScreen will ### make the turtle move to the clicked point. >>> turtle.onclick(None) ### event-binding will be removed """ self.getscreen().onclick(fun, btn, add) def onclick(self, fun, btn=1, add=None): """Bind fun to mouse-click event on this turtle on canvas. Arguments: 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 Example (for a Turtle instance named turtle): >>> class MyTurtle(Turtle): def glow(self,x,y): self.fillcolor("red") >>> t = MyTurtle() >>> t.onclick(t.glow) ### Subsequently clickinfg into the turtle will turn it'S ### fillcolor red. >>> turtle.onclick(None) ###event-binding will be removed """ self.getscreen()._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 canvas. Arguments: 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 Example (for a Turtle instance named turtle): >>> class MyTurtle(Turtle): def glow(self,x,y): self.fillcolor("red") def unglow(self,x,y): self.fillcolor("") >>> turtle = MyTurtle() >>> turtle.onclick(turtle.glow) >>> turtle.onrelease(turtle.unglow) ### clicking on turtle turns fillcolor red, ### unclicking turns it to transparent. """ self.getscreen()._onrelease(self.turtle._item, fun, btn, add) self._update() def ondrag(self, fun, btn=1, add=None): """Bind fun to mouse-move event on canvas. Arguments: fun must be a function with two arguments, the coordinates of the current point of the mouse 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. Example (for a Turtle instance named turtle): >>> turtle.onclick(turtle.goto) >>> turtle.ondrag(turtle.goto) ### Subsequently clicking and dragging a Turtle will ### move it across the screen thereby producing handdrawings ### (if pen is down). """ self.getscreen()._ondrag(self.turtle._item, fun, btn, add) def onkey(self, fun, key = None): """Bind fun to key-release event of key. Canvas must have focus. (See method listen.) Example (for a Turtle instance named turtle): >>> def f(): turtle.fd(50) turtle.lt(60) >>> turtle.onkey(f, "Up") >>> turtle.listen() ### Subsequently the turtle can be moved by ### repeatedly pressing the up-arrow key, ### consequently drawing a hexagon """ self.getscreen().onkey(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, so listen can be passed as function argument to the onclick method.) Example (for a Turtle instance named turtle): >>> turtle.listen() """ self.getscreen().listen() def ontimer(self, fun, t=0): """Install a timer, which calls fun after t milliseconds. Arguments: fun is a function with no arguments. t is a number >= 0 Example (for a Turtle instance named turtle): >>> running = True >>> def f(): if running: turtle.fd(50) turtle.lt(60) turtle.ontimer(f, 250) >>> f() ### makes the turtle marching around >>> running = False """ self.getscreen().ontimer(fun, t) def window_width(self): """ Returns the width of the turtle window. Example (for a Turtle instance named turtle): >>> turtle.window_width() 640 """ return self.getscreen().window_width() def window_height(self): """ Return the height of the turtle window. Example (for a Turtle instance named turtle): >>> turtle.window_height() 480 """ return self.getscreen().window_height() def screen_width(self): """ Returns the width of the turtle screen. Example (for a Turtle instance named turtle): >>> turtle.screen_width() 800 """ return self.getscreen().screen_width() def screen_height(self): """ Return the height of the turtle screen. Example (for a Turtle instance named turtle): >>> turtle.screen_height() 600 """ return self.getscreen().screen_height() def _undo(self, action, data): """Does the main part of the work for undo() """ 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 == "wri": screen = self.screens[self.screenIndex] item = data[0] screen._delete(item) self.items.remove(item) elif action == "dofill": screen = self.screens[self.screenIndex] item = data[0] screen._drawpoly(item, ((0,0),(0,0),(0,0)), fill="", outline="") elif action == "beginfill": item = data[0] self._fillitem = self._fillpath = None screen = self.screens[self.screenIndex] screen._delete(item) self.items.remove(item) elif action == "pcol": TPen.pencolor(self,data[0]) self.undobuffer.pop() elif action == "fcol": TPen.fillcolor(self,data[0]) self.undobuffer.pop() elif action == "pens": TPen.pensize(self,data[0]) self.undobuffer.pop() def undo(self): """undo (repeatedly) the last turtle action. Number of available undo actions is determined by 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() """ 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) # consider the following three aliases as deprecated polystart = begin_poly polyend = end_poly getpoly = get_poly RawPen = RawTurtle ### Turtle - Klasse ######################## _root = None _canvas = None _pen = None _title = "Extended Turtle Graphics" screen = None # DEFAULT WINDOW CONFIGURATION class Turtle(RawTurtle): def __init__(self, shape=_CFG["shape"], mode=_CFG["mode"], width=_CFG["width"], height=_CFG["height"], canvwidth=_CFG["canvwidth"], canvheight=_CFG["canvheight"], leftright=_CFG["leftright"], topbottom=_CFG["topbottom"], undobuffersize=_CFG["undobuffersize"], visible=True): """Erzeugt Turtle auf einem Standard-ScrolledCanvas """ global _root, _canvas if _root is None: _root = TK.Tk() _root.title(_title) _root.wm_protocol("WM_DELETE_WINDOW", self._destroy) if _canvas is None: _canvas = ScrolledCanvas(_root, width=width, height=height, canvwidth = canvwidth, canvheight = canvheight) ## self.winsize(width, height, leftright, topbottom) self.setup(width, height, leftright, topbottom) _canvas.pack(expand=1, fill="both") RawTurtle.__init__(self, _canvas, shape=shape, mode=mode, visible=visible, undobufsize=undobuffersize) def winsize(self, w, h, lr=_CFG["leftright"], tb=_CFG["topbottom"]): """Reset the geometry of the turtle graphics window. --- Arguments: w and h are new width and height of the window, lr ('left/right') and tb ('top/bottom') are coordinates of the edges of the graphics window on the monitor-screen. + is used for left/upper edge, - is used for right/bottom edge. Example (for a Turtle instance named turtle): >>> turtle.winsize(800, 600, -10, 10) ### Resize the window to 800x600 with position ### 10 pixels from the left screen-edge and ### 10 pixels from the upper screen-edge. """ _root.geometry("%dx%d%+d%+d"%(w, h, lr, tb)) def setup(self, width=0.5, height=0.75, startx=None, starty=None): """ 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 (when used as function): >>> setup (width=200, height=200, startx=0, starty=0) sets window to 200x200 pixels, in upper left of screen >>> setup(width=.75, height=0.5, startx=None, starty=None) sets window to 75% of screen by 50% of screen and centers """ sw = _root.winfo_screenwidth() sh = _root.winfo_screenheight() 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.winsize(width, height, startx, starty) def _destroy(self): global _root, _canvas, _pen, screen screen = self.screens[self.screenIndex] root = screen.cv._root if root is _root: _pen = None _root = None _canvas = None screen = None TurtleScreen._RUNNING = True root.destroy() def bye(self): """Shut the turtlegraphics window. Example (for a Turtle instance named turtle): >>> turtle.bye() """ self._destroy() Pen = Turtle def _getpen(): global _pen if not _pen: _pen = Turtle() return _pen ## Provisorium - durch "Wiener Desktop Fassung" ersetzen def title(titlestring): """Set title of xturtle-window """ global _root, _title if _root is not None: _root.title(titlestring) _title = titlestring ### stuff for providing translation of docstrings ### for methods of RawTurtle def write_docstrings(cls): """Helper-Method to create doc-dictionary, has to called explicitely, (not used by the turtle-graphics classes) """ f = open(cls.__name__+"_docsdict.py","w") f.write('docsdict = {\n\n') for key in cls.__dict__: if key.startswith('_'): continue f.write('%s :\n' % repr(key)) f.write('"""%s\n""",\n\n' % eval('%s.%s.__doc__'%(cls.__name__,key))) f.write("}\n") f.close() def read_docstrings(lang): """Transfer docstrings, translated to lang, from a dictionary-file to the methods of the above classes. """ modname = "docsdict_%(language)s" % {'language':lang.lower() } module = __import__(modname) docsdict = module.docsdict for key in docsdict: for cls in [Turtle, RawTurtle, TNavigator, TPen]: if key not in cls.__dict__: continue eval('%s.%s' % (cls.__name__, key)).im_func.__doc__ = docsdict[key] _LANGUAGE = _CFG["language"] try: if _LANGUAGE != "english": read_docstrings(_LANGUAGE) except ImportError: print "Cannot find docsdict for", _LANGUAGE except: print ("Unknown Error when trying to import %s-docstring-dictionary" % _LANGUAGE) def getmethparlist(ob): "Get strings describing the arguments for the given object" argText1 = argText2 = "" # bit of a hack for methods - turn it into a function # but we drop the "self" param. if type(ob)==types.MethodType: fob = ob.im_func argOffset = 1 else: fob = ob argOffset = 0 # Try and build one for Python defined functions if type(fob) in [types.FunctionType, types.LambdaType]: try: counter = fob.func_code.co_argcount items2 = list(fob.func_code.co_varnames[argOffset:counter]) realArgs = fob.func_code.co_varnames[argOffset:counter] defaults = fob.func_defaults or [] defaults = list(map(lambda name: "=%s" % repr(name), defaults)) defaults = [""] * (len(realArgs)-len(defaults)) + defaults items1 = map(lambda arg, dflt: arg+dflt, realArgs, defaults) if fob.func_code.co_flags & 0x4: items1.append("*"+fob.func_code.co_varnames[counter]) items2.append("*"+fob.func_code.co_varnames[counter]) counter += 1 if fob.func_code.co_flags & 0x8: items1.append("**"+fob.func_code.co_varnames[counter]) items2.append("**"+fob.func_code.co_varnames[counter]) argText1 = ", ".join(items1) argText1 = "(%s)" % argText1 argText2 = ", ".join(items2) argText2 = "(%s)" % argText2 except: pass return argText1, argText2 def _docrevision(docstr): """To reduce docstrings from RawTurtle class for functions """ import re if docstr is None: return None newdocstr = docstr.replace("turtle.","") parexp = re.compile(r' \(.+ turtle\):') newdocstr = parexp.sub(":", newdocstr) ## newdocstr = newdocstr.replace(" (for a Turtle instance named turtle)","") 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. for _cls in TNavigator, TPen, RawTurtle, Turtle: for _key in _cls.__dict__.keys(): if _key.startswith("_"): continue pl1, pl2 = getmethparlist(eval(_cls.__name__+'.'+_key)) if pl1 == "": continue defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" % {'key':_key, 'pl1':pl1, 'pl2':pl2}) exec defstr eval(_key).__doc__ = _docrevision(_cls.__dict__[_key].__doc__) mainloop = TK.mainloop del pl1, pl2, defstr if __name__ == "__main__": def demo1(): # demo des alten turtle-Moduls reset() tracer(1) up() backward(100) down() # draw 3 squares; the last filled width(3) for i in range(3): if i == 2: fill(1) for j in range(4): forward(20) left(90) if i == 2: color("maroon") fill(0) up() forward(30) down() width(1) color("black") # move out of the way tracer(0) 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 fill(1) for i in range(5): forward(20) left(90) forward(20) right(90) fill(0) # more text write("wait a moment...") tracer(1) def demo2(): # einige weitere und einige neue features speed(10) st() pensize(3) setheading(towards(0,0)) r = distance(0,0)/2.0 rt(90) for i in range(18): if pen()["pendown"]: pu() else: pd() circle(r,10) for i in range(500): undo() reset() lt(90) colormode(255) l = 10 pencolor("green") pensize(3) lt(180) for i in range(-2,16): if i > 0: fill(1) fillcolor(255-15*i,0,15*i) for j in range(3): fd(l) lt(120) l += 10 lt(15) speed((speed()+1)%12) fill(0) lt(120) pu() fd(70) rt(30) pd() color("red","yellow") speed(0) fill(1) for i in range(4): circle(50,90) rt(90) fd(30) rt(90) fill(0) lt(90) pu(); fd(30); pd(); shape("turtle") tri = turtles()[0] turtle=Pen() turtle.shape("turtle") turtle.mode("logo") turtle.reset() 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() count += 1 tri.write("CAUGHT! ",font=("Arial",16,"bold"), align="right") tri.pencolor("black") tri.pencolor("red") def baba(x,y): if tri.distance(x,y) < 10: resetscreen() turtle.ht() tri.ht() tri.up() tri.bk(130) tri.pencolor("red") tri.bye() from time import sleep sleep(2) for i in range(1000): tri.undo() turtle.undo() tri.fd(50) tri.write(" Click me!", font = ("Courier", 12, "bold") ) tri.onclick(baba, 1) demo1() demo2() #mainloop()