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
[EMAIL PROTECTED]
# $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()
_______________________________________________
Edu-sig mailing list
Edu-sig@python.org
http://mail.python.org/mailman/listinfo/edu-sig

Reply via email to