Re: [Edu-sig] suggestions about turtle.py
Hi all, some years ago I had to teach using a macintosh lab: due to some problems with Tk on mac there was no Idle (and no turtle.py IIRC). That was when I realized how useful the "batteries included" Idle (-n) together with the old turle.py really was for me. It is my standard "graphics package". I once used it to demonstrate the adding up of arrows for physics (arrows representing sine-waves with different phases like from a grating): you could script this from idle and look at the different resulting amplitudes for different angles (arrow spirals curling up or not). Physicists will know what I mean. A Turtle was exactly the right tool to do this. As a quick way to introduce loops etc. with visual feedback the turtle has always worked out well in my (school) classes: ages 15-17. I also like it that the old turtle.py lets you decide if you want to do it procedural (module-level functions) or OO-like. And if you want to hide the things you don't need or rename methods etc. just implement an appropriate intermediate module with your favorite settings and put it in the pythonpath for the class to import: # ooTurtle.py from turtle import Pen class Turtle: def __init__(self, mycolor='black'): self._pen=Pen() self.color(mycolor) def color(self,*args): self._pen.color(*args) ... So dir(Turtle) will only show the methods you explicitly put in there. And you can have the names that you want. Gregor Lingl wrote:
In the meantime (and looking forward to preparing a second edition of my book) I've been working on a new turtle graphics module - I named it Xturtle.py.
BTW, I like your book.
1) I submit my Xturtle.py module along with a set of 10+ demo samples. I've zipped them into a package and I'll try to submit them as an attachment in a separate follow up posting.
Great stuff.
Moreover let's remember, that turtle graphics was invented to teach programming to very young people. So let's do it in a way, that interesting things can easily be done with it.
Still: "young" could be quite old. I still like to play around with turtles...
(a) How about compatibility? Should every old turtle program be executable with the amended module? Remind that there is some strange behaviour in the old module concerning fill. IMO it should be discussed if the "radians"-mode in turtle.py should be retained. (Is it really useful?)
As I could rename methods (i.e. to German) anyway, backwards compatibility isn't that important. I just need to find the things I need. Fixing things to go with a new turtle-module shouldn't be difficult. But there should always be an Out-of-the-Box turtle in the standard installation.
(c) I'd propose to add a sort of TurtleCanvas class to the turtle module.
Seems reasonable to me. Just my 2c. Cheers Christian
Just for fun I stole some of the turtle code, added docstrings, used complex as position format, and made a small (but not entirely minimal) turtle class. I figured this might do as a straw-man for Myrtle the Minimal Turtle. I do like Christian Mascher's idea of making a turtle with a contained Pen being how to control all of the "dir()" results. --Scott David Daniels Scott.Daniels@Acm.Org # $Id: myrtle.py 1.3 2006/03/04 22:54:25 daniels Exp daniels $ '''Myrtle the minimally complex turtle''' from math import sin, cos, tan, pi, atan2 import Tkinter __version__ = '0.3' __all__ = 'Turtle pi'.split() class Turtle(object): _root = None _canvas = None def destroy(self): '''Wipe out evidence of where the root and canvas are''' root = self.canvas._root() if root is self._root: self.__class__._root = None self.__class__._canvas = self.canvas = None root.destroy() def __init__(self, canvas=None, root=None): '''Make a turtle, and a canvas pen for it to play in (if necessary)''' # Make a canvas (and a root) if necessary, record root. if canvas is None: if root is None: if self.__class__._root is None: self.__class__._root = root = Tkinter.Tk() root.wm_protocol("WM_DELETE_WINDOW", self.destroy) root = self.__class__._root if self.__class__._canvas is None: canvas = Tkinter.Canvas(root, background="white") canvas.pack(expand=1, fill="both") self.__class__._canvas = canvas canvas = self.__class__._canvas elif self.__class__._root is None: if root is None: root = canvas._root() self.__class__._root = root self.canvas = canvas # The canvas playpen for our turtle self.items = [] # things showing on canvas self.tracing = True # moving in little increments self.arrow = 0 # The visible manifextation of the Turtle self.reset() # Set other things up def reset(self): '''Erase the canvas and place the turtle in the center''' self.canvas.update() # Make sure canvas is up-to-date # Find current window sizes, and set origin and position at center self.position = self.origin = self.window_dimension() * .5 # Set step-scaling and direction to right, 8 pixels self.heading = complex(8.) self.drawing = True # False for Pen-Up operation self.width = 1 # width of the line the turtle leaves behind self.ink = "black" # color of ink Turtle currently wielding self.filling = 0 # polygon (1), smooth (-1), or non-fill modes self.path = [] # Track of positions for use by fill modes self.clear() self.canvas._root().tkraise() # Pop turtle's canvas to top-o-screen def window_dimension(self): '''Get the width and height of the canvas''' width = self.canvas.winfo_width() if width <= 1: # the window isn't managed by a geometry manager width = self.canvas['width'] height = self.canvas.winfo_height() if height <= 1: # the window isn't managed by a geometry manager height = self.canvas['height'] return complex(width, height) def clear(self): '''Drop all known elements from the canvas''' self.fill(False) for item in self.items: self.canvas.delete(item) self.items = [] self.hide() self.show() def fill(self, flag): '''Fill path so far, then 0: no fill, 1: polygon fill, -1: smooth fill''' if self.filling: if len(self.path) > 2: # can't create filled anything with less than 2 points path = [(p.real, p.imag) for p in self.path] item = self.canvas._create('polygon', path, dict(fill=self.ink, smooth=self.filling < 0)) self.items.append(item) self.canvas.lower(item) self.path = [] self.filling = flag if flag: self.path.append(self.position) self.forward(0) def show(self, position=None): '''Make the turtle visible (if tracing)''' if self.tracing: if position is None: position = self.position back = position - 2 * self.heading self.hide() self.arrow = self.canvas.create_line(back.real, back.imag, position.real, position.imag, width=self.width, arrow="last", capstyle="round", fill=self.ink) self.canvas.update() def hide(self): '''Make the turtle invisible''' if self.arrow: self.canvas.delete(self.arrow) self.arrow = 0 def forward(self, distance=1): '''Move forward''' self.goto(self.position + distance * self.heading) def left(self, angle=pi/2): '''Rotate left angle radians.''' dist = abs(self.heading) angle += atan2(self.heading.real, self.heading.imag) self.heading = abs(self.heading) * complex(sin(angle), cos(angle)) if self.tracing: self.show() def goto(self, destination): '''Move the turtle to the destination.''' start = self.position self.position = destination if self.filling: self.path.append(self.position) if self.drawing: if self.tracing: delta = destination - start hops = int(abs(delta)) item = self.canvas.create_line(start.real, start.imag, start.real, start.imag, width=self.width, capstyle="round", fill=self.ink) try: for i in range(1, hops): pos = start + delta * i / hops self.canvas.coords(item, start.real, start.imag, pos.real, pos.imag) self.show(pos) self.canvas.update() self.canvas.after(10) self.canvas.coords(item, start.real, start.imag, destination.real, destination.imag) self.canvas.itemconfigure(item, arrow="none") except Tkinter.TclError: # Probably the window was closed! return else: item = self.canvas.create_line(start.real, start.imag, destination.real, destination.imag, width=self.width, capstyle="round", fill=self.ink) self.items.append(item) self.show() def write(self, text, move=False): '''Write text at the current position. If move True, position past it.''' x = self.position.real - 1 # correction -- calibrated for Windows y = self.position.imag if not isinstance(text, basestring): text = str(text) item = self.canvas.create_text(x, y, text=text, anchor="sw", fill=self.ink) self.items.append(item) if move: x0, y0, x1, y1 = self.canvas.bbox(item) self.goto(complex(x1, y1)) self.show() def color(self, *args): '''Set my ink color (name or r,g,b where 1,1,1 is pure white)''' if not args: raise Error, "no color arguments" if len(args) == 1: color = args[0] if isinstance(color, basestring): # Test the color first try: id = self._canvas.create_line(0, 0, 0, 0, fill=color) except Tkinter.TclError: raise Error, "bad color string: %r" % (color,) self._set_color(color) return try: r, g, b = color except: raise Error, "bad color sequence: %r" % (color,) else: try: r, g, b = args except: raise Error, "bad color arguments: %r" % (args,) assert 0 <= r <= 1 and 0 <= g <= 1 and 0 <= b <= 1 x = 255.0 y = 0.5 self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y))) def _set_color(self, color): '''Set ink color by name only (for internal use)''' self.ink = color self.show()
participants (2)
-
Christian Mascher -
Scott David Daniels