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