Untitled diff

Created Diff never expires
#
#
# turtle.py: a Tkinter based turtle graphics module for Python
# turtle.py: a Tkinter based turtle graphics module for Python
# Version 1.1b - 4. 5. 2009
# Version 1.1b - 4. 5. 2009
#
#
# Copyright (C) 2006 - 2010 Gregor Lingl
# Copyright (C) 2006 - 2010 Gregor Lingl
# email: glingl@aon.at
# email: glingl@aon.at
#
#
# This software is provided 'as-is', without any express or implied
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
# arising from the use of this software.
#
#
# Permission is granted to anyone to use this software for any purpose,
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
# freely, subject to the following restrictions:
#
#
# 1. The origin of this software must not be misrepresented; you must not
# 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
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
# 3. This notice may not be removed or altered from any source distribution.




"""
"""
Turtle graphics is a popular way for introducing programming to
Turtle graphics is a popular way for introducing programming to
kids. It was part of the original Logo programming language developed
kids. It was part of the original Logo programming language developed
by Wally Feurzig and Seymour Papert in 1966.
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
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 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
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.
command turtle.right(25), and it rotates in-place 25 degrees clockwise.


By combining together these and similar commands, intricate shapes and
By combining together these and similar commands, intricate shapes and
pictures can easily be drawn.
pictures can easily be drawn.


----- turtle.py
----- turtle.py


This module is an extended reimplementation of turtle.py from the
This module is an extended reimplementation of turtle.py from the
Python standard distribution up to Python 2.5. (See: http://www.python.org)
Python standard distribution up to Python 2.5. (See: http://www.python.org)


It tries to keep the merits of turtle.py and to be (nearly) 100%
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
compatible with it. This means in the first place to enable the
learning programmer to use all the commands, classes and methods
learning programmer to use all the commands, classes and methods
interactively when using the module from within IDLE run with
interactively when using the module from within IDLE run with
the -n switch.
the -n switch.


Roughly it has the following features added:
Roughly it has the following features added:


- Better animation of the turtle movements, especially of turning the
- Better animation of the turtle movements, especially of turning the
turtle. So the turtles can more easily be used as a visual feedback
turtle. So the turtles can more easily be used as a visual feedback
instrument by the (beginning) programmer.
instrument by the (beginning) programmer.


- Different turtle shapes, gif-images as turtle shapes, user defined
- Different turtle shapes, gif-images as turtle shapes, user defined
and user controllable turtle shapes, among them compound
and user controllable turtle shapes, among them compound
(multicolored) shapes. Turtle shapes can be stretched and tilted, which
(multicolored) shapes. Turtle shapes can be stretched and tilted, which
makes turtles very versatile geometrical objects.
makes turtles very versatile geometrical objects.


- Fine control over turtle movement and screen updates via delay(),
- Fine control over turtle movement and screen updates via delay(),
and enhanced tracer() and speed() methods.
and enhanced tracer() and speed() methods.


- Aliases for the most commonly used commands, like fd for forward etc.,
- Aliases for the most commonly used commands, like fd for forward etc.,
following the early Logo traditions. This reduces the boring work of
following the early Logo traditions. This reduces the boring work of
typing long sequences of commands, which often occur in a natural way
typing long sequences of commands, which often occur in a natural way
when kids try to program fancy pictures on their first encounter with
when kids try to program fancy pictures on their first encounter with
turtle graphics.
turtle graphics.


- Turtles now have an undo()-method with configurable undo-buffer.
- Turtles now have an undo()-method with configurable undo-buffer.


- Some simple commands/methods for creating event driven programs
- Some simple commands/methods for creating event driven programs
(mouse-, key-, timer-events). Especially useful for programming games.
(mouse-, key-, timer-events). Especially useful for programming games.


- A scrollable Canvas class. The default scrollable Canvas can be
- A scrollable Canvas class. The default scrollable Canvas can be
extended interactively as needed while playing around with the turtle(s).
extended interactively as needed while playing around with the turtle(s).


- A TurtleScreen class with methods controlling background color or
- A TurtleScreen class with methods controlling background color or
background image, window and canvas size and other properties of the
background image, window and canvas size and other properties of the
TurtleScreen.
TurtleScreen.


- There is a method, setworldcoordinates(), to install a user defined
- There is a method, setworldcoordinates(), to install a user defined
coordinate-system for the TurtleScreen.
coordinate-system for the TurtleScreen.


- The implementation uses a 2-vector class named Vec2D, derived from tuple.
- 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,
This class is public, so it can be imported by the application programmer,
which makes certain types of computations very natural and compact.
which makes certain types of computations very natural and compact.


- Appearance of the TurtleScreen and the Turtles at startup/import can be
- Appearance of the TurtleScreen and the Turtles at startup/import can be
configured by means of a turtle.cfg configuration file.
configured by means of a turtle.cfg configuration file.
The default configuration mimics the appearance of the old turtle module.
The default configuration mimics the appearance of the old turtle module.


- If configured appropriately the module reads in docstrings from a docstring
- If configured appropriately the module reads in docstrings from a docstring
dictionary in some different language, supplied separately and replaces
dictionary in some different language, supplied separately and replaces
the English ones by those read in. There is a utility function
the English ones by those read in. There is a utility function
write_docstringdict() to write a dictionary with the original (English)
write_docstringdict() to write a dictionary with the original (English)
docstrings to disc, so it can serve as a template for translations.
docstrings to disc, so it can serve as a template for translations.


Behind the scenes there are some features included with possible
Behind the scenes there are some features included with possible
extensions in mind. These will be commented and documented elsewhere.
extensions in mind. These will be commented and documented elsewhere.


"""
"""


_ver = "turtle 1.1b- - for Python 3.1 - 4. 5. 2009"
_ver = "turtle 1.1b- - for Python 3.1 - 4. 5. 2009"


# print(_ver)
# print(_ver)


import tkinter as TK
import sys
if sys.version_info[0] == 2:
import Tkinter as TK
else:
import tkinter as TK
from tkinter import simpledialog
import types
import types
import math
import math
import time
import time
import inspect
import inspect
import sys


from os.path import isfile, split, join
from os.path import isfile, split, join
from copy import deepcopy
from copy import deepcopy
from tkinter import simpledialog


_SPANISH = {'TurtleScreen': 'PantallaTortuga',
'RawTurtle': 'TortugaBruta',
'RawPen': 'LapizBruto',
'Turtle': 'Tortuga',
'Pen': 'Lapiz',
'forward': 'adelante',
'back': 'atras',
'right': 'derecho',
'left': 'izquierda',
'goto': 'ir_a',
'setx': 'fijar_x',
'sety': 'fijar_y',
'setheading': 'fijar_direccion',
'home': 'origen',
'circle': 'circulo',
'dot': 'punto',
'stamp': 'sello',
'clearstamp': 'borrar_sello',
'clearstamps': 'borrar_sellos',
'undo': 'deshacer',
'speed': 'velocidad',
'position': 'posicion',
'towards': 'hacia',
'xcor': 'posicion_x',
'ycor': 'posicion_y',
'heading': 'direccion',
'distance': 'distancia',
'degrees': 'grados',
'radians': 'radianes',
'pendown': 'bajar_lapiz',
'penup': 'subir_lapiz',
'pensize': 'tamano_lapiz',
'pen': 'lapiz',
'isdown': 'esta_presionada',
'color': 'color',
'pencolor': 'color_de_lapiz',
'fillcolor': 'color_de_relleno',
'reset': 'reiniciar',
'clear': 'borrar',
'write': 'escribir',
'showturtle': 'mostrar',
'hideturtle': 'esconder',
'isvisible': 'esta_visible',
'shape': 'figura',
'resizemode': 'modo_cambio_tamano',
'shapesize': 'tamano_de_figura',
'shearfactor': 'factor_de_inclinacion',
'settiltangle': 'fijar_angulo_rotacion',
'tiltangle': 'angulo_de_rotacion',
'tilt': 'rotar',
'shapetransform': 'transformar_figura',
'get_shapepoly': 'obtener_poligono',
'onclick': 'al_hacer_clic',
'onrelease': 'al_liberar',
'ondrag': 'al_arrastrar',
'begin_poly': 'comienzar_poligono',
'end_poly': 'terminar_poligono',
'get_poly': 'obtener_poligono',
'filling': 'relleno',
'begin_fill': 'comienzar_relleno',
'end_fill': 'cterminar_relleno',
'clone': 'clonar',
'getturtle': 'obtener_tortuga',
'getpen': 'obtener_lapiz',
'getscreen': 'obtener_pantalla',
'setundobuffer': 'establecer_bufer_deshacer',
'undobufferentries': 'tamano_bufer_deshacer',
'bgcolor': 'color_fondo',
'bgpic': 'imagen_fondo',
'clear': 'borrar',
'reset': 'reiniciar',
'screensize': 'tamano_de_pantalla',
'setworldcoordinates': 'fijar_coordenadas_mundo',
'delay': 'retraso',
'tracer': 'animacion',
'update': 'redibujar',
'listen': 'escuchar',
'onkey': 'al_soltar_la_tecla',
'onkeypress': 'al_pulsar_la_tecla',
'ontimer': 'temporizador',
'mainloop': 'bucle_principal',
'done': 'hecho',
'mode': 'modo',
'colormode': 'modo_de_color',
'getcanvas': 'obtener_el_lienzo',
'getshapes': 'conseguir_figuras',
'register_shape': 'registrar_figura',
'turtles': 'tortugas',
'window_height': 'altura_de_ventana',
'window_width': 'ancho_de_ventana',
'textinput': 'entrada_texto',
'numinput': 'entrada_numero',
'bye': 'adios',
'exitonclick': 'salida_en_clic',
'setup': 'configurar',
'title': 'titulo',
}

_SPANISH_SETTING_WORDS = {'negro': 'black',
'azul': 'blue',
'marron': 'brown',
'naranja': 'orange',
'gris': 'gray',
'verde': 'green',
'morado': 'purple',
'rosa': 'pink',
'amarillo': 'yellow',
'blanco': 'white',
'rojo': 'red',
'auto': 'auto',
'usuario': 'user',
'sin_cambio_de_tamano': 'noresize',
'derecho': 'right',
'izquierda': 'left',
'centro': 'center',
'tortuga': 'turtle',
'cuadrado': 'square',
'triangulo': 'triangle',
'clasico': 'classic',
'flecha': 'arrow',
'nada': 'blank',
'circulo': 'circle',
'se_muestra': 'shown',
'bajar_lapiz': 'pendown',
'color_de_lapiz': 'pencolor',
'color_de_relleno': 'fillcolor',
'tamano_lapiz': 'pensize',
'velocidad': 'speed',
'modo_cambio_tamano': 'resizemode',
'factor_inclinacion': 'stretchfactor',
'ancho_contorno': 'outline',
'rotar': 'tilt'
}


_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
'getshapes', 'listen', 'mainloop', 'mode', 'numinput',
'getshapes', 'listen', 'mainloop', 'mode', 'numinput',
'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer',
'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer',
'register_shape', 'resetscreen', 'screensize', 'setup',
'register_shape', 'resetscreen', 'screensize', 'setup',
'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update',
'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update',
'window_height', 'window_width']
'window_height', 'window_width']
_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly',
'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly',
'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle',
'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle',
'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards',
'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards',
'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
'write', 'xcor', 'ycor']
'write', 'xcor', 'ycor']
_tg_utilities = ['write_docstringdict', 'done']
_tg_utilities = ['write_docstringdict', 'done']


__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
_tg_utilities + ['Terminator']) # + _math_functions)
_tg_utilities + ['Terminator'] + list(_SPANISH.values())) # + _math_functions)


_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
'turtlesize', 'up', 'width']
'turtlesize', 'up', 'width']


_CFG = {"width" : 0.5, # Screen
_CFG = {"width" : 0.5, # Screen
"height" : 0.75,
"height" : 0.75,
"canvwidth" : 400,
"canvwidth" : 400,
"canvheight": 300,
"canvheight": 300,
"leftright": None,
"leftright": None,
"topbottom": None,
"topbottom": None,
"mode": "standard", # TurtleScreen
"mode": "standard", # TurtleScreen
"colormode": 1.0,
"colormode": 1.0,
"delay": 10,
"delay": 10,
"undobuffersize": 1000, # RawTurtle
"undobuffersize": 1000, # RawTurtle
"shape": "classic",
"shape": "classic",
"pencolor" : "black",
"pencolor" : "black",
"fillcolor" : "black",
"fillcolor" : "black",
"resizemode" : "noresize",
"resizemode" : "noresize",
"visible" : True,
"visible" : True,
"language": "english", # docstrings
"language": "english", # docstrings
"exampleturtle": "turtle",
"exampleturtle": "turtle",
"examplescreen": "screen",
"examplescreen": "screen",
"title": "Python Turtle Graphics",
"title": "Python Turtle Graphics",
"using_IDLE": False
"using_IDLE": False
}
}


def config_dict(filename):
def config_dict(filename):
"""Convert content of config-file into dictionary."""
"""Convert content of config-file into dictionary."""
with open(filename, "r") as f:
with open(filename, "r") as f:
cfglines = f.readlines()
cfglines = f.readlines()
cfgdict = {}
cfgdict = {}
for line in cfglines:
for line in cfglines:
line = line.strip()
line = line.strip()
if not line or line.startswith("#"):
if not line or line.startswith("#"):
continue
continue
try:
try:
key, value = line.split("=")
key, value = line.split("=")
except:
except:
print("Bad line in config-file %s:\n%s" % (filename,line))
print("Bad line in config-file %s:\n%s" % (filename,line))
continue
continue
key = key.strip()
key = key.strip()
value = value.strip()
value = value.strip()
if value in ["True", "False", "None", "''", '""']:
if value in ["True", "False", "None", "''", '""']:
value = eval(value)
value = eval(value)
else:
else:
try:
try:
if "." in value:
if "." in value:
value = float(value)
value = float(value)
else:
else:
value = int(value)
value = int(value)
except:
except:
pass # value need not be converted
pass # value need not be converted
cfgdict[key] = value
cfgdict[key] = value
return cfgdict
return cfgdict


def readconfig(cfgdict):
def readconfig(cfgdict):
"""Read config-files, change configuration-dict accordingly.
"""Read config-files, change configuration-dict accordingly.


If there is a turtle.cfg file in the current working directory,
If there is a turtle.cfg file in the current working directory,
read it from there. If this contains an importconfig-value,
read it from there. If this contains an importconfig-value,
say 'myway', construct filename turtle_mayway.cfg else use
say 'myway', construct filename turtle_mayway.cfg else use
turtle.cfg and read it from the import-directory, where
turtle.cfg and read it from the import-directory, where
turtle.py is located.
turtle.py is located.
Update configuration dictionary first according to config-file,
Update configuration dictionary first according to config-file,
in the import directory, then according to config-file in the
in the import directory, then according to config-file in the
current working directory.
current working directory.
If no config-file is found, the default configuration is used.
If no config-file is found, the default configuration is used.
"""
"""
default_cfg = "turtle.cfg"
default_cfg = "turtle.cfg"
cfgdict1 = {}
cfgdict1 = {}
cfgdict2 = {}
cfgdict2 = {}
if isfile(default_cfg):
if isfile(default_cfg):
cfgdict1 = config_dict(default_cfg)
cfgdict1 = config_dict(default_cfg)
if "importconfig" in cfgdict1:
if "importconfig" in cfgdict1:
default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
try:
try:
head, tail = split(__file__)
head, tail = split(__file__)
cfg_file2 = join(head, default_cfg)
cfg_file2 = join(head, default_cfg)
except:
except:
cfg_file2 = ""
cfg_file2 = ""
if isfile(cfg_file2):
if isfile(cfg_file2):
cfgdict2 = config_dict(cfg_file2)
cfgdict2 = config_dict(cfg_file2)
_CFG.update(cfgdict2)
_CFG.update(cfgdict2)
_CFG.update(cfgdict1)
_CFG.update(cfgdict1)


try:
try:
readconfig(_CFG)
readconfig(_CFG)
except:
except:
print ("No configfile read, reason unknown")
print ("No configfile read, reason unknown")




class Vec2D(tuple):
class Vec2D(tuple):
"""A 2 dimensional vector class, used as a helper class
"""A 2 dimensional vector class, used as a helper class
for implementing turtle graphics.
for implementing turtle graphics.
May be useful for turtle graphics programs also.
May be useful for turtle graphics programs also.
Derived from tuple, so a vector is a tuple!
Derived from tuple, so a vector is a tuple!


Provides (for a, b vectors, k number):
Provides (for a, b vectors, k number):
a+b vector addition
a+b vector addition
a-b vector subtraction
a-b vector subtraction
a*b inner product
a*b inner product
k*a and a*k multiplication with scalar
k*a and a*k multiplication with scalar
|a| absolute value of a
|a| absolute value of a
a.rotate(angle) rotation
a.rotate(angle) rotation
"""
"""
def __new__(cls, x, y):
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
return tuple.__new__(cls, (x, y))
def __add__(self, other):
def __add__(self, other):
return Vec2D(self[0]+other[0], self[1]+other[1])
return Vec2D(self[0]+other[0], self[1]+other[1])
def __mul__(self, other):
def __mul__(self, other):
if isinstance(other, Vec2D):
if isinstance(other, Vec2D):
return self[0]*other[0]+self[1]*other[1]
return self[0]*other[0]+self[1]*other[1]
return Vec2D(self[0]*other, self[1]*other)
return Vec2D(self[0]*other, self[1]*other)
def __rmul__(self, other):
def __rmul__(self, other):
if isinstance(other, int) or isinstance(other, float):
if isinstance(other, int) or isinstance(other, float):
return Vec2D(self[0]*other, self[1]*other)
return Vec2D(self[0]*other, self[1]*other)
def __sub__(self, other):
def __sub__(self, other):
return Vec2D(self[0]-other[0], self[1]-other[1])
return Vec2D(self[0]-other[0], self[1]-other[1])
def __neg__(self):
def __neg__(self):
return Vec2D(-self[0], -self[1])
return Vec2D(-self[0], -self[1])
def __abs__(self):
def __abs__(self):
return (self[0]**2 + self[1]**2)**0.5
return (self[0]**2 + self[1]**2)**0.5
def rotate(self, angle):
def rotate(self, angle):
"""rotate self counterclockwise by angle
"""rotate self counterclockwise by angle
"""
"""
perp = Vec2D(-self[1], self[0])
perp = Vec2D(-self[1], self[0])
angle = angle * math.pi / 180.0
angle = angle * math.pi / 180.0
c, s = math.cos(angle), math.sin(angle)
c, s = math.cos(angle), math.sin(angle)
return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
def __getnewargs__(self):
def __getnewargs__(self):
return (self[0], self[1])
return (self[0], self[1])
def __repr__(self):
def __repr__(self):
return "(%.2f,%.2f)" % self
return "(%.2f,%.2f)" % self




##############################################################################
##############################################################################
### From here up to line : Tkinter - Interface for turtle.py ###
### From here up to line : Tkinter - Interface for turtle.py ###
### May be replaced by an interface to some different graphics toolkit ###
### May be replaced by an interface to some different graphics toolkit ###
##############################################################################
##############################################################################


## helper functions for Scrolled Canvas, to forward Canvas-methods
## helper functions for Scrolled Canvas, to forward Canvas-methods
## to ScrolledCanvas class
## to ScrolledCanvas class


def __methodDict(cls, _dict):
def __methodDict(cls, _dict):
"""helper function for Scrolled Canvas"""
"""helper function for Scrolled Canvas"""
baseList = list(cls.__bases__)
baseList = list(cls.__bases__)
baseList.reverse()
baseList.reverse()
for _super in baseList:
for _super in baseList:
__methodDict(_super, _dict)
__methodDict(_super, _dict)
for key, value in cls.__dict__.items():
for key, value in cls.__dict__.items():
if type(value) == types.FunctionType:
if type(value) == types.FunctionType:
_dict[key] = value
_dict[key] = value


def __methods(cls):
def __methods(cls):
"""helper function for Scrolled Canvas"""
"""helper function for Scrolled Canvas"""
_dict = {}
_dict = {}
__methodDict(cls, _dict)
__methodDict(cls, _dict)
return _dict.keys()
return _dict.keys()


__stringBody = (
__stringBody = (
'def %(method)s(self, *args, **kw): return ' +
'def %(method)s(self, *args, **kw): return ' +
'self.%(attribute)s.%(method)s(*args, **kw)')
'self.%(attribute)s.%(method)s(*args, **kw)')


def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
### MANY CHANGES ###
### MANY CHANGES ###
_dict_1 = {}
_dict_1 = {}
__methodDict(toClass, _dict_1)
__methodDict(toClass, _dict_1)
_dict = {}
_dict = {}
mfc = __methods(fromClass)
mfc = __methods(fromClass)
for ex in _dict_1.keys():
for ex in _dict_1.keys():
if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc:
pass
pass
else:
else:
_dict[ex] = _dict_1[ex]
_dict[ex] = _dict_1[ex]


for method, func in _dict.items():
for method, func in _dict.items():
d = {'method': method, 'func': func}
d = {'method': method, 'func': func}
if isinstance(toPart, str):
if isinstance(toPart, str):
execString = \
execString = \
__stringBody % {'method' : method, 'attribute' : toPart}
__stringBody % {'method' : method, 'attribute' : toPart}
exec(execString, d)
exec(execString, d)
setattr(fromClass, method, d[method]) ### NEWU!
setattr(fromClass, method, d[method]) ### NEWU!




class ScrolledCanvas(TK.Frame):
class ScrolledCanvas(TK.Frame):
"""Modeled after the scrolled canvas class from Grayons's Tkinter book.
"""Modeled after the scrolled canvas class from Grayons's Tkinter book.


Used as the default canvas, which pops up automatically when
Used as the default canvas, which pops up automatically when
using turtle graphics functions or the Turtle class.
using turtle graphics functions or the Turtle class.
"""
"""
def __init__(self, master, width=500, height=350,
def __init__(self, master, width=500, height=350,
canvwidth=600, canvheight=500):
canvwidth=600, canvheight=500):
TK.Frame.__init__(self, master, width=width, height=height)
TK.Frame.__init__(self, master, width=width, height=height)
self._rootwindow = self.winfo_toplevel()
self._rootwindow = self.winfo_toplevel()
self.width, self.height = width, height
self.width, self.height = width, height
self.canvwidth, self.canvheight = canvwidth, canvheight
self.canvwidth, self.canvheight = canvwidth, canvheight
self.bg = "white"
self.bg = "white"
self._canvas = TK.Canvas(master, width=width, height=height,
self._canvas = TK.Canvas(master, width=width, height=height,
bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
orient=TK.HORIZONTAL)
orient=TK.HORIZONTAL)
self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
self._canvas.configure(xscrollcommand=self.hscroll.set,
self._canvas.configure(xscrollcommand=self.hscroll.set,
yscrollcommand=self.vscroll.set)
yscrollcommand=self.vscroll.set)
self.rowconfigure(0, weight=1, minsize=0)
self.rowconfigure(0, weight=1, minsize=0)
self.columnconfigure(0, weight=1, minsize=0)
self.columnconfigure(0, weight=1, minsize=0)
self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
column=0, rowspan=1, columnspan=1, sticky='news')
column=0, rowspan=1, columnspan=1, sticky='news')
self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
column=1, rowspan=1, columnspan=1, sticky='news')
column=1, rowspan=1, columnspan=1, sticky='news')
self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
column=0, rowspan=1, columnspan=1, sticky='news')
column=0, rowspan=1, columnspan=1, sticky='news')
self.reset()
self.reset()
self._rootwindow.bind('<Configure>', self.onResize)
self._rootwindow.bind('<Configure>', self.onResize)


def reset(self, canvwidth=None, canvheight=None, bg = None):
def reset(self, canvwidth=None, canvheight=None, bg = None):
"""Adjust canvas and scrollbars according to given canvas size."""
"""Adjust canvas and scrollbars according to given canvas size."""
if canvwidth:
if canvwidth:
self.canvwidth = canvwidth
self.canvwidth = canvwidth
if canvheight:
if canvheight:
self.canvheight = canvheight
self.canvheight = canvheight
if bg:
if bg:
self.bg = bg
self.bg = bg
self._canvas.config(bg=bg,
self._canvas.config(bg=bg,
scrollregion=(-self.canvwidth//2, -self.canvheight//2,
scrollregion=(-self.canvwidth//2, -self.canvheight//2,
self.canvwidth//2, self.canvheight//2))
self.canvwidth//2, self.canvheight//2))
self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
self.canvwidth)
self.canvwidth)
self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
self.canvheight)
self.canvheight)
self.adjustScrolls()
self.adjustScrolls()




def adjustScrolls(self):
def adjustScrolls(self):
""" Adjust scrollbars according to window- and canvas-size.
""" Adjust scrollbars according to window- and canvas-size.
"""
"""
cwidth = self._canvas.winfo_width()
cwidth = self._canvas.winfo_width()
cheight = self._canvas.winfo_height()
cheight = self._canvas.winfo_height()
self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
if cwidth < self.canvwidth or cheight < self.canvheight:
if cwidth < self.canvwidth or cheight < self.canvheight:
self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
column=0, rowspan=1, columnspan=1, sticky='news')
column=0, rowspan=1, columnspan=1, sticky='news')
self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
column=1, rowspan=1, columnspan=1, sticky='news')
column=1, rowspan=1, columnspan=1, sticky='news')
else:
else:
self.hscroll.grid_forget()
self.hscroll.grid_forget()
self.vscroll.grid_forget()
self.vscroll.grid_forget()


def onResize(self, event):
def onResize(self, event):
"""self-explanatory"""
"""self-explanatory"""
self.adjustScrolls()
self.adjustScrolls()


def bbox(self, *args):
def bbox(self, *args):
""" 'forward' method, which canvas itself has inherited...
""" 'forward' method, which canvas itself has inherited...
"""
"""
return self._canvas.bbox(*args)
return self._canvas.bbox(*args)


def cget(self, *args, **kwargs):
def cget(self, *args, **kwargs):
""" 'forward' method, which canvas itself has inherited...
""" 'forward' method, which canvas itself has inherited...
"""
"""
return self._canvas.cget(*args, **kwargs)
return self._canvas.cget(*args, **kwargs)


def config(self, *args, **kwargs):
def config(self, *args, **kwargs):
""" 'forward' method, which canvas itself has inherited...
""" 'forward' method, which canvas itself has inherited...
"""
"""
self._canvas.config(*args, **kwargs)
self._canvas.config(*args, **kwargs)


def bind(self, *args, **kwargs):
def bind(self, *args, **kwargs):
""" 'forward' method, which canvas itself has inherited...
""" 'forward' method, which canvas itself has inherited...
"""
"""
self._canvas.bind(*args, **kwargs)
self._canvas.bind(*args, **kwargs)


def unbind(self, *args, **kwargs):
def unbind(self, *args, **kwargs):
""" 'forward' method, which canvas itself has inherited...
""" 'forward' method, which canvas itself has inherited...
"""
"""
self._canvas.unbind(*args, **kwargs)
self._canvas.unbind(*args, **kwargs)


def focus_force(self):
def focus_force(self):
""" 'forward' method, which canvas itself has inherited...
""" 'forward' method, which canvas itself has inherited...
"""
"""
self._canvas.focus_force()
self._canvas.focus_force()


__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')




class _Root(TK.Tk):
class _Root(TK.Tk):
"""Root class for Screen based on Tkinter."""
"""Root class for Screen based on Tkinter."""
def __init__(self):
def __init__(self):
TK.Tk.__init__(self)
TK.Tk.__init__(self)


def setupcanvas(self, width, height, cwidth, cheight):
def setupcanvas(self, width, height, cwidth, cheight):
self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
self._canvas.pack(expand=1, fill="both")
self._canvas.pack(expand=1, fill="both")


def _getcanvas(self):
def _getcanvas(self):
return self._canvas
return self._canvas


def set_geometry(self, width, height, startx, starty):
def set_geometry(self, width, height, startx, starty):
self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))


def ondestroy(self, destroy):
def ondestroy(self, destroy):
self.wm_protocol("WM_DELETE_WINDOW", destroy)
self.wm_protocol("WM_DELETE_WINDOW", destroy)


def win_width(self):
def win_width(self):
return self.winfo_screenwidth()
return self.winfo_screenwidth()


def win_height(self):
def win_height(self):
return self.winfo_screenheight()
return self.winfo_screenheight()


Canvas = TK.Canvas
Canvas = TK.Canvas




class TurtleScreenBase(object):
class TurtleScreenBase(object):
"""Provide the basic graphics functionality.
"""Provide the basic graphics functionality.
Interface between Tkinter and turtle.py.
Interface between Tkinter and turtle.py.


To port turtle.py to some different graphics toolkit
To port turtle.py to some different graphics toolkit
a corresponding TurtleScreenBase class has to be implemented.
a corresponding TurtleScreenBase class has to be implemented.
"""
"""


@staticmethod
@staticmethod
def _blankimage():
def _blankimage():
"""return a blank image object
"""return a blank image object
"""
"""
img = TK.PhotoImage(width=1, height=1)
img = TK.PhotoImage(width=1, h
img.blank()
return img

@staticmethod
def _image(filename):
"""return an image object containing the
imagedata from a gif-file named filename.
"""
return TK.PhotoImage(file=filename)

def __init__(self, cv):
self.cv = cv
if isinstance(cv, ScrolledCanvas):
w = self.cv.canvwidth
h = self.cv.canvheight
else: # expected: ordinary TK.Canvas
w = int(self.cv.cget("width"))
h = int(self.cv.cget("height"))
self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
self.canvwidth = w
self.canvheight = h
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(e