PageRenderTime 1197ms CodeModel.GetById 323ms app.highlight 583ms RepoModel.GetById 172ms app.codeStats 1ms

/Lib/lib-tk/turtle.py

http://unladen-swallow.googlecode.com/
Python | 4036 lines | 3832 code | 29 blank | 175 comment | 29 complexity | d65b7dc5dc1970fb0cb28e8624a43821 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1#
   2# turtle.py: a Tkinter based turtle graphics module for Python
   3# Version 1.0.1 - 24. 9. 2009
   4#
   5# Copyright (C) 2006 - 2009  Gregor Lingl
   6# email: glingl@aon.at
   7#
   8# This software is provided 'as-is', without any express or implied
   9# warranty.  In no event will the authors be held liable for any damages
  10# arising from the use of this software.
  11#
  12# Permission is granted to anyone to use this software for any purpose,
  13# including commercial applications, and to alter it and redistribute it
  14# freely, subject to the following restrictions:
  15#
  16# 1. The origin of this software must not be misrepresented; you must not
  17#    claim that you wrote the original software. If you use this software
  18#    in a product, an acknowledgment in the product documentation would be
  19#    appreciated but is not required.
  20# 2. Altered source versions must be plainly marked as such, and must not be
  21#    misrepresented as being the original software.
  22# 3. This notice may not be removed or altered from any source distribution.
  23
  24
  25"""
  26Turtle graphics is a popular way for introducing programming to
  27kids. It was part of the original Logo programming language developed
  28by Wally Feurzig and Seymour Papert in 1966.
  29
  30Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it
  31the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
  32the direction it is facing, drawing a line as it moves. Give it the
  33command turtle.left(25), and it rotates in-place 25 degrees clockwise.
  34
  35By combining together these and similar commands, intricate shapes and
  36pictures can easily be drawn.
  37
  38----- turtle.py
  39
  40This module is an extended reimplementation of turtle.py from the
  41Python standard distribution up to Python 2.5. (See: http://www.python.org)
  42
  43It tries to keep the merits of turtle.py and to be (nearly) 100%
  44compatible with it. This means in the first place to enable the
  45learning programmer to use all the commands, classes and methods
  46interactively when using the module from within IDLE run with
  47the -n switch.
  48
  49Roughly it has the following features added:
  50
  51- Better animation of the turtle movements, especially of turning the
  52  turtle. So the turtles can more easily be used as a visual feedback
  53  instrument by the (beginning) programmer.
  54
  55- Different turtle shapes, gif-images as turtle shapes, user defined
  56  and user controllable turtle shapes, among them compound
  57  (multicolored) shapes. Turtle shapes can be stretched and tilted, which
  58  makes turtles very versatile geometrical objects.
  59
  60- Fine control over turtle movement and screen updates via delay(),
  61  and enhanced tracer() and speed() methods.
  62
  63- Aliases for the most commonly used commands, like fd for forward etc.,
  64  following the early Logo traditions. This reduces the boring work of
  65  typing long sequences of commands, which often occur in a natural way
  66  when kids try to program fancy pictures on their first encounter with
  67  turtle graphics.
  68
  69- Turtles now have an undo()-method with configurable undo-buffer.
  70
  71- Some simple commands/methods for creating event driven programs
  72  (mouse-, key-, timer-events). Especially useful for programming games.
  73
  74- A scrollable Canvas class. The default scrollable Canvas can be
  75  extended interactively as needed while playing around with the turtle(s).
  76
  77- A TurtleScreen class with methods controlling background color or
  78  background image, window and canvas size and other properties of the
  79  TurtleScreen.
  80
  81- There is a method, setworldcoordinates(), to install a user defined
  82  coordinate-system for the TurtleScreen.
  83
  84- The implementation uses a 2-vector class named Vec2D, derived from tuple.
  85  This class is public, so it can be imported by the application programmer,
  86  which makes certain types of computations very natural and compact.
  87
  88- Appearance of the TurtleScreen and the Turtles at startup/import can be
  89  configured by means of a turtle.cfg configuration file.
  90  The default configuration mimics the appearance of the old turtle module.
  91
  92- If configured appropriately the module reads in docstrings from a docstring
  93  dictionary in some different language, supplied separately  and replaces
  94  the English ones by those read in. There is a utility function
  95  write_docstringdict() to write a dictionary with the original (English)
  96  docstrings to disc, so it can serve as a template for translations.
  97
  98Behind the scenes there are some features included with possible
  99extensions in in mind. These will be commented and documented elsewhere.
 100
 101"""
 102
 103_ver = "turtle 1.0b1 - for Python 2.6   -  30. 5. 2008, 18:08"
 104
 105#print _ver
 106
 107import Tkinter as TK
 108import types
 109import math
 110import time
 111import os
 112
 113from os.path import isfile, split, join
 114from copy import deepcopy
 115
 116from math import *    ## for compatibility with old turtle module
 117
 118_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen',
 119               'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D']
 120_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
 121        'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
 122        'getshapes', 'listen', 'mode', 'onkey', 'onscreenclick', 'ontimer',
 123        'register_shape', 'resetscreen', 'screensize', 'setup',
 124        'setworldcoordinates', 'title', 'tracer', 'turtles', 'update',
 125        'window_height', 'window_width']
 126_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
 127        'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color',
 128        'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd',
 129        'fill', 'fillcolor', 'forward', 'get_poly', 'getpen', 'getscreen',
 130        'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
 131        'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd',
 132        'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position',
 133        'pu', 'radians', 'right', 'reset', 'resizemode', 'rt',
 134        'seth', 'setheading', 'setpos', 'setposition', 'settiltangle',
 135        'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'showturtle',
 136        'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', 'tracer',
 137        'turtlesize', 'undo', 'undobufferentries', 'up', 'width',
 138        'window_height', 'window_width', 'write', 'xcor', 'ycor']
 139_tg_utilities = ['write_docstringdict', 'done', 'mainloop']
 140_math_functions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
 141        'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
 142        'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
 143
 144__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions +
 145           _tg_utilities + _math_functions)
 146
 147_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos',
 148               'pu', 'rt', 'seth', 'setpos', 'setposition', 'st',
 149               'turtlesize', 'up', 'width']
 150
 151_CFG = {"width" : 0.5,               # Screen
 152        "height" : 0.75,
 153        "canvwidth" : 400,
 154        "canvheight": 300,
 155        "leftright": None,
 156        "topbottom": None,
 157        "mode": "standard",          # TurtleScreen
 158        "colormode": 1.0,
 159        "delay": 10,
 160        "undobuffersize": 1000,      # RawTurtle
 161        "shape": "classic",
 162        "pencolor" : "black",
 163        "fillcolor" : "black",
 164        "resizemode" : "noresize",
 165        "visible" : True,
 166        "language": "english",        # docstrings
 167        "exampleturtle": "turtle",
 168        "examplescreen": "screen",
 169        "title": "Python Turtle Graphics",
 170        "using_IDLE": False
 171       }
 172
 173##print "cwd:", os.getcwd()
 174##print "__file__:", __file__
 175##
 176##def show(dictionary):
 177##    print "=========================="
 178##    for key in sorted(dictionary.keys()):
 179##        print key, ":", dictionary[key]
 180##    print "=========================="
 181##    print
 182
 183def config_dict(filename):
 184    """Convert content of config-file into dictionary."""
 185    f = open(filename, "r")
 186    cfglines = f.readlines()
 187    f.close()
 188    cfgdict = {}
 189    for line in cfglines:
 190        line = line.strip()
 191        if not line or line.startswith("#"):
 192            continue
 193        try:
 194            key, value = line.split("=")
 195        except:
 196            print "Bad line in config-file %s:\n%s" % (filename,line)
 197            continue
 198        key = key.strip()
 199        value = value.strip()
 200        if value in ["True", "False", "None", "''", '""']:
 201            value = eval(value)
 202        else:
 203            try:
 204                if "." in value:
 205                    value = float(value)
 206                else:
 207                    value = int(value)
 208            except:
 209                pass # value need not be converted
 210        cfgdict[key] = value
 211    return cfgdict
 212
 213def readconfig(cfgdict):
 214    """Read config-files, change configuration-dict accordingly.
 215
 216    If there is a turtle.cfg file in the current working directory,
 217    read it from there. If this contains an importconfig-value,
 218    say 'myway', construct filename turtle_mayway.cfg else use
 219    turtle.cfg and read it from the import-directory, where
 220    turtle.py is located.
 221    Update configuration dictionary first according to config-file,
 222    in the import directory, then according to config-file in the
 223    current working directory.
 224    If no config-file is found, the default configuration is used.
 225    """
 226    default_cfg = "turtle.cfg"
 227    cfgdict1 = {}
 228    cfgdict2 = {}
 229    if isfile(default_cfg):
 230        cfgdict1 = config_dict(default_cfg)
 231        #print "1. Loading config-file %s from: %s" % (default_cfg, os.getcwd())
 232    if "importconfig" in cfgdict1:
 233        default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"]
 234    try:
 235        head, tail = split(__file__)
 236        cfg_file2 = join(head, default_cfg)
 237    except:
 238        cfg_file2 = ""
 239    if isfile(cfg_file2):
 240        #print "2. Loading config-file %s:" % cfg_file2
 241        cfgdict2 = config_dict(cfg_file2)
 242##    show(_CFG)
 243##    show(cfgdict2)
 244    _CFG.update(cfgdict2)
 245##    show(_CFG)
 246##    show(cfgdict1)
 247    _CFG.update(cfgdict1)
 248##    show(_CFG)
 249
 250try:
 251    readconfig(_CFG)
 252except:
 253    print "No configfile read, reason unknown"
 254
 255
 256class Vec2D(tuple):
 257    """A 2 dimensional vector class, used as a helper class
 258    for implementing turtle graphics.
 259    May be useful for turtle graphics programs also.
 260    Derived from tuple, so a vector is a tuple!
 261
 262    Provides (for a, b vectors, k number):
 263       a+b vector addition
 264       a-b vector subtraction
 265       a*b inner product
 266       k*a and a*k multiplication with scalar
 267       |a| absolute value of a
 268       a.rotate(angle) rotation
 269    """
 270    def __new__(cls, x, y):
 271        return tuple.__new__(cls, (x, y))
 272    def __add__(self, other):
 273        return Vec2D(self[0]+other[0], self[1]+other[1])
 274    def __mul__(self, other):
 275        if isinstance(other, Vec2D):
 276            return self[0]*other[0]+self[1]*other[1]
 277        return Vec2D(self[0]*other, self[1]*other)
 278    def __rmul__(self, other):
 279        if isinstance(other, int) or isinstance(other, float):
 280            return Vec2D(self[0]*other, self[1]*other)
 281    def __sub__(self, other):
 282        return Vec2D(self[0]-other[0], self[1]-other[1])
 283    def __neg__(self):
 284        return Vec2D(-self[0], -self[1])
 285    def __abs__(self):
 286        return (self[0]**2 + self[1]**2)**0.5
 287    def rotate(self, angle):
 288        """rotate self counterclockwise by angle
 289        """
 290        perp = Vec2D(-self[1], self[0])
 291        angle = angle * math.pi / 180.0
 292        c, s = math.cos(angle), math.sin(angle)
 293        return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
 294    def __getnewargs__(self):
 295        return (self[0], self[1])
 296    def __repr__(self):
 297        return "(%.2f,%.2f)" % self
 298
 299
 300##############################################################################
 301### From here up to line    : Tkinter - Interface for turtle.py            ###
 302### May be replaced by an interface to some different graphics toolkit     ###
 303##############################################################################
 304
 305## helper functions for Scrolled Canvas, to forward Canvas-methods
 306## to ScrolledCanvas class
 307
 308def __methodDict(cls, _dict):
 309    """helper function for Scrolled Canvas"""
 310    baseList = list(cls.__bases__)
 311    baseList.reverse()
 312    for _super in baseList:
 313        __methodDict(_super, _dict)
 314    for key, value in cls.__dict__.items():
 315        if type(value) == types.FunctionType:
 316            _dict[key] = value
 317
 318def __methods(cls):
 319    """helper function for Scrolled Canvas"""
 320    _dict = {}
 321    __methodDict(cls, _dict)
 322    return _dict.keys()
 323
 324__stringBody = (
 325    'def %(method)s(self, *args, **kw): return ' +
 326    'self.%(attribute)s.%(method)s(*args, **kw)')
 327
 328def __forwardmethods(fromClass, toClass, toPart, exclude = ()):
 329    """Helper functions for Scrolled Canvas, used to forward
 330    ScrolledCanvas-methods to Tkinter.Canvas class.
 331    """
 332    _dict = {}
 333    __methodDict(toClass, _dict)
 334    for ex in _dict.keys():
 335        if ex[:1] == '_' or ex[-1:] == '_':
 336            del _dict[ex]
 337    for ex in exclude:
 338        if _dict.has_key(ex):
 339            del _dict[ex]
 340    for ex in __methods(fromClass):
 341        if _dict.has_key(ex):
 342            del _dict[ex]
 343
 344    for method, func in _dict.items():
 345        d = {'method': method, 'func': func}
 346        if type(toPart) == types.StringType:
 347            execString = \
 348                __stringBody % {'method' : method, 'attribute' : toPart}
 349        exec execString in d
 350        fromClass.__dict__[method] = d[method]
 351
 352
 353class ScrolledCanvas(TK.Frame):
 354    """Modeled after the scrolled canvas class from Grayons's Tkinter book.
 355
 356    Used as the default canvas, which pops up automatically when
 357    using turtle graphics functions or the Turtle class.
 358    """
 359    def __init__(self, master, width=500, height=350,
 360                                          canvwidth=600, canvheight=500):
 361        TK.Frame.__init__(self, master, width=width, height=height)
 362        self._rootwindow = self.winfo_toplevel()
 363        self.width, self.height = width, height
 364        self.canvwidth, self.canvheight = canvwidth, canvheight
 365        self.bg = "white"
 366        self._canvas = TK.Canvas(master, width=width, height=height,
 367                                 bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
 368        self.hscroll = TK.Scrollbar(master, command=self._canvas.xview,
 369                                    orient=TK.HORIZONTAL)
 370        self.vscroll = TK.Scrollbar(master, command=self._canvas.yview)
 371        self._canvas.configure(xscrollcommand=self.hscroll.set,
 372                               yscrollcommand=self.vscroll.set)
 373        self.rowconfigure(0, weight=1, minsize=0)
 374        self.columnconfigure(0, weight=1, minsize=0)
 375        self._canvas.grid(padx=1, in_ = self, pady=1, row=0,
 376                column=0, rowspan=1, columnspan=1, sticky='news')
 377        self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
 378                column=1, rowspan=1, columnspan=1, sticky='news')
 379        self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
 380                column=0, rowspan=1, columnspan=1, sticky='news')
 381        self.reset()
 382        self._rootwindow.bind('<Configure>', self.onResize)
 383
 384    def reset(self, canvwidth=None, canvheight=None, bg = None):
 385        """Adjust canvas and scrollbars according to given canvas size."""
 386        if canvwidth:
 387            self.canvwidth = canvwidth
 388        if canvheight:
 389            self.canvheight = canvheight
 390        if bg:
 391            self.bg = bg
 392        self._canvas.config(bg=bg,
 393                        scrollregion=(-self.canvwidth//2, -self.canvheight//2,
 394                                       self.canvwidth//2, self.canvheight//2))
 395        self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) /
 396                                                               self.canvwidth)
 397        self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) /
 398                                                              self.canvheight)
 399        self.adjustScrolls()
 400
 401
 402    def adjustScrolls(self):
 403        """ Adjust scrollbars according to window- and canvas-size.
 404        """
 405        cwidth = self._canvas.winfo_width()
 406        cheight = self._canvas.winfo_height()
 407        self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth)
 408        self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight)
 409        if cwidth < self.canvwidth or cheight < self.canvheight:
 410            self.hscroll.grid(padx=1, in_ = self, pady=1, row=1,
 411                              column=0, rowspan=1, columnspan=1, sticky='news')
 412            self.vscroll.grid(padx=1, in_ = self, pady=1, row=0,
 413                              column=1, rowspan=1, columnspan=1, sticky='news')
 414        else:
 415            self.hscroll.grid_forget()
 416            self.vscroll.grid_forget()
 417
 418    def onResize(self, event):
 419        """self-explanatory"""
 420        self.adjustScrolls()
 421
 422    def bbox(self, *args):
 423        """ 'forward' method, which canvas itself has inherited...
 424        """
 425        return self._canvas.bbox(*args)
 426
 427    def cget(self, *args, **kwargs):
 428        """ 'forward' method, which canvas itself has inherited...
 429        """
 430        return self._canvas.cget(*args, **kwargs)
 431
 432    def config(self, *args, **kwargs):
 433        """ 'forward' method, which canvas itself has inherited...
 434        """
 435        self._canvas.config(*args, **kwargs)
 436
 437    def bind(self, *args, **kwargs):
 438        """ 'forward' method, which canvas itself has inherited...
 439        """
 440        self._canvas.bind(*args, **kwargs)
 441
 442    def unbind(self, *args, **kwargs):
 443        """ 'forward' method, which canvas itself has inherited...
 444        """
 445        self._canvas.unbind(*args, **kwargs)
 446
 447    def focus_force(self):
 448        """ 'forward' method, which canvas itself has inherited...
 449        """
 450        self._canvas.focus_force()
 451
 452__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas')
 453
 454
 455class _Root(TK.Tk):
 456    """Root class for Screen based on Tkinter."""
 457    def __init__(self):
 458        TK.Tk.__init__(self)
 459
 460    def setupcanvas(self, width, height, cwidth, cheight):
 461        self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)
 462        self._canvas.pack(expand=1, fill="both")
 463
 464    def _getcanvas(self):
 465        return self._canvas
 466
 467    def set_geometry(self, width, height, startx, starty):
 468        self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))
 469
 470    def ondestroy(self, destroy):
 471        self.wm_protocol("WM_DELETE_WINDOW", destroy)
 472
 473    def win_width(self):
 474        return self.winfo_screenwidth()
 475
 476    def win_height(self):
 477        return self.winfo_screenheight()
 478
 479Canvas = TK.Canvas
 480
 481
 482class TurtleScreenBase(object):
 483    """Provide the basic graphics functionality.
 484       Interface between Tkinter and turtle.py.
 485
 486       To port turtle.py to some different graphics toolkit
 487       a corresponding TurtleScreenBase class has to be implemented.
 488    """
 489
 490    @staticmethod
 491    def _blankimage():
 492        """return a blank image object
 493        """
 494        img = TK.PhotoImage(width=1, height=1)
 495        img.blank()
 496        return img
 497
 498    @staticmethod
 499    def _image(filename):
 500        """return an image object containing the
 501        imagedata from a gif-file named filename.
 502        """
 503        return TK.PhotoImage(file=filename)
 504
 505    def __init__(self, cv):
 506        self.cv = cv
 507        if isinstance(cv, ScrolledCanvas):
 508            w = self.cv.canvwidth
 509            h = self.cv.canvheight
 510        else:  # expected: ordinary TK.Canvas
 511            w = int(self.cv.cget("width"))
 512            h = int(self.cv.cget("height"))
 513            self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 ))
 514        self.canvwidth = w
 515        self.canvheight = h
 516        self.xscale = self.yscale = 1.0
 517
 518    def _createpoly(self):
 519        """Create an invisible polygon item on canvas self.cv)
 520        """
 521        return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="")
 522
 523    def _drawpoly(self, polyitem, coordlist, fill=None,
 524                  outline=None, width=None, top=False):
 525        """Configure polygonitem polyitem according to provided
 526        arguments:
 527        coordlist is sequence of coordinates
 528        fill is filling color
 529        outline is outline color
 530        top is a boolean value, which specifies if polyitem
 531        will be put on top of the canvas' displaylist so it
 532        will not be covered by other items.
 533        """
 534        cl = []
 535        for x, y in coordlist:
 536            cl.append(x * self.xscale)
 537            cl.append(-y * self.yscale)
 538        self.cv.coords(polyitem, *cl)
 539        if fill is not None:
 540            self.cv.itemconfigure(polyitem, fill=fill)
 541        if outline is not None:
 542            self.cv.itemconfigure(polyitem, outline=outline)
 543        if width is not None:
 544            self.cv.itemconfigure(polyitem, width=width)
 545        if top:
 546            self.cv.tag_raise(polyitem)
 547
 548    def _createline(self):
 549        """Create an invisible line item on canvas self.cv)
 550        """
 551        return self.cv.create_line(0, 0, 0, 0, fill="", width=2,
 552                                   capstyle = TK.ROUND)
 553
 554    def _drawline(self, lineitem, coordlist=None,
 555                  fill=None, width=None, top=False):
 556        """Configure lineitem according to provided arguments:
 557        coordlist is sequence of coordinates
 558        fill is drawing color
 559        width is width of drawn line.
 560        top is a boolean value, which specifies if polyitem
 561        will be put on top of the canvas' displaylist so it
 562        will not be covered by other items.
 563        """
 564        if coordlist is not None:
 565            cl = []
 566            for x, y in coordlist:
 567                cl.append(x * self.xscale)
 568                cl.append(-y * self.yscale)
 569            self.cv.coords(lineitem, *cl)
 570        if fill is not None:
 571            self.cv.itemconfigure(lineitem, fill=fill)
 572        if width is not None:
 573            self.cv.itemconfigure(lineitem, width=width)
 574        if top:
 575            self.cv.tag_raise(lineitem)
 576
 577    def _delete(self, item):
 578        """Delete graphics item from canvas.
 579        If item is"all" delete all graphics items.
 580        """
 581        self.cv.delete(item)
 582
 583    def _update(self):
 584        """Redraw graphics items on canvas
 585        """
 586        self.cv.update()
 587
 588    def _delay(self, delay):
 589        """Delay subsequent canvas actions for delay ms."""
 590        self.cv.after(delay)
 591
 592    def _iscolorstring(self, color):
 593        """Check if the string color is a legal Tkinter color string.
 594        """
 595        try:
 596            rgb = self.cv.winfo_rgb(color)
 597            ok = True
 598        except TK.TclError:
 599            ok = False
 600        return ok
 601
 602    def _bgcolor(self, color=None):
 603        """Set canvas' backgroundcolor if color is not None,
 604        else return backgroundcolor."""
 605        if color is not None:
 606            self.cv.config(bg = color)
 607            self._update()
 608        else:
 609            return self.cv.cget("bg")
 610
 611    def _write(self, pos, txt, align, font, pencolor):
 612        """Write txt at pos in canvas with specified font
 613        and color.
 614        Return text item and x-coord of right bottom corner
 615        of text's bounding box."""
 616        x, y = pos
 617        x = x * self.xscale
 618        y = y * self.yscale
 619        anchor = {"left":"sw", "center":"s", "right":"se" }
 620        item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align],
 621                                        fill = pencolor, font = font)
 622        x0, y0, x1, y1 = self.cv.bbox(item)
 623        self.cv.update()
 624        return item, x1-1
 625
 626##    def _dot(self, pos, size, color):
 627##        """may be implemented for some other graphics toolkit"""
 628
 629    def _onclick(self, item, fun, num=1, add=None):
 630        """Bind fun to mouse-click event on turtle.
 631        fun must be a function with two arguments, the coordinates
 632        of the clicked point on the canvas.
 633        num, the number of the mouse-button defaults to 1
 634        """
 635        if fun is None:
 636            self.cv.tag_unbind(item, "<Button-%s>" % num)
 637        else:
 638            def eventfun(event):
 639                x, y = (self.cv.canvasx(event.x)/self.xscale,
 640                        -self.cv.canvasy(event.y)/self.yscale)
 641                fun(x, y)
 642            self.cv.tag_bind(item, "<Button-%s>" % num, eventfun, add)
 643
 644    def _onrelease(self, item, fun, num=1, add=None):
 645        """Bind fun to mouse-button-release event on turtle.
 646        fun must be a function with two arguments, the coordinates
 647        of the point on the canvas where mouse button is released.
 648        num, the number of the mouse-button defaults to 1
 649
 650        If a turtle is clicked, first _onclick-event will be performed,
 651        then _onscreensclick-event.
 652        """
 653        if fun is None:
 654            self.cv.tag_unbind(item, "<Button%s-ButtonRelease>" % num)
 655        else:
 656            def eventfun(event):
 657                x, y = (self.cv.canvasx(event.x)/self.xscale,
 658                        -self.cv.canvasy(event.y)/self.yscale)
 659                fun(x, y)
 660            self.cv.tag_bind(item, "<Button%s-ButtonRelease>" % num,
 661                             eventfun, add)
 662
 663    def _ondrag(self, item, fun, num=1, add=None):
 664        """Bind fun to mouse-move-event (with pressed mouse button) on turtle.
 665        fun must be a function with two arguments, the coordinates of the
 666        actual mouse position on the canvas.
 667        num, the number of the mouse-button defaults to 1
 668
 669        Every sequence of mouse-move-events on a turtle is preceded by a
 670        mouse-click event on that turtle.
 671        """
 672        if fun is None:
 673            self.cv.tag_unbind(item, "<Button%s-Motion>" % num)
 674        else:
 675            def eventfun(event):
 676                try:
 677                    x, y = (self.cv.canvasx(event.x)/self.xscale,
 678                           -self.cv.canvasy(event.y)/self.yscale)
 679                    fun(x, y)
 680                except:
 681                    pass
 682            self.cv.tag_bind(item, "<Button%s-Motion>" % num, eventfun, add)
 683
 684    def _onscreenclick(self, fun, num=1, add=None):
 685        """Bind fun to mouse-click event on canvas.
 686        fun must be a function with two arguments, the coordinates
 687        of the clicked point on the canvas.
 688        num, the number of the mouse-button defaults to 1
 689
 690        If a turtle is clicked, first _onclick-event will be performed,
 691        then _onscreensclick-event.
 692        """
 693        if fun is None:
 694            self.cv.unbind("<Button-%s>" % num)
 695        else:
 696            def eventfun(event):
 697                x, y = (self.cv.canvasx(event.x)/self.xscale,
 698                        -self.cv.canvasy(event.y)/self.yscale)
 699                fun(x, y)
 700            self.cv.bind("<Button-%s>" % num, eventfun, add)
 701
 702    def _onkey(self, fun, key):
 703        """Bind fun to key-release event of key.
 704        Canvas must have focus. See method listen
 705        """
 706        if fun is None:
 707            self.cv.unbind("<KeyRelease-%s>" % key, None)
 708        else:
 709            def eventfun(event):
 710                fun()
 711            self.cv.bind("<KeyRelease-%s>" % key, eventfun)
 712
 713    def _listen(self):
 714        """Set focus on canvas (in order to collect key-events)
 715        """
 716        self.cv.focus_force()
 717
 718    def _ontimer(self, fun, t):
 719        """Install a timer, which calls fun after t milliseconds.
 720        """
 721        if t == 0:
 722            self.cv.after_idle(fun)
 723        else:
 724            self.cv.after(t, fun)
 725
 726    def _createimage(self, image):
 727        """Create and return image item on canvas.
 728        """
 729        return self.cv.create_image(0, 0, image=image)
 730
 731    def _drawimage(self, item, (x, y), image):
 732        """Configure image item as to draw image object
 733        at position (x,y) on canvas)
 734        """
 735        self.cv.coords(item, (x * self.xscale, -y * self.yscale))
 736        self.cv.itemconfig(item, image=image)
 737
 738    def _setbgpic(self, item, image):
 739        """Configure image item as to draw image object
 740        at center of canvas. Set item to the first item
 741        in the displaylist, so it will be drawn below
 742        any other item ."""
 743        self.cv.itemconfig(item, image=image)
 744        self.cv.tag_lower(item)
 745
 746    def _type(self, item):
 747        """Return 'line' or 'polygon' or 'image' depending on
 748        type of item.
 749        """
 750        return self.cv.type(item)
 751
 752    def _pointlist(self, item):
 753        """returns list of coordinate-pairs of points of item
 754        Example (for insiders):
 755        >>> from turtle import *
 756        >>> getscreen()._pointlist(getturtle().turtle._item)
 757        [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982),
 758        (9.9999999999999982, 0.0)]
 759        >>> """
 760        cl = self.cv.coords(item)
 761        pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)]
 762        return  pl
 763
 764    def _setscrollregion(self, srx1, sry1, srx2, sry2):
 765        self.cv.config(scrollregion=(srx1, sry1, srx2, sry2))
 766
 767    def _rescale(self, xscalefactor, yscalefactor):
 768        items = self.cv.find_all()
 769        for item in items:
 770            coordinates = self.cv.coords(item)
 771            newcoordlist = []
 772            while coordinates:
 773                x, y = coordinates[:2]
 774                newcoordlist.append(x * xscalefactor)
 775                newcoordlist.append(y * yscalefactor)
 776                coordinates = coordinates[2:]
 777            self.cv.coords(item, *newcoordlist)
 778
 779    def _resize(self, canvwidth=None, canvheight=None, bg=None):
 780        """Resize the canvas the turtles are drawing on. Does
 781        not alter the drawing window.
 782        """
 783        # needs amendment
 784        if not isinstance(self.cv, ScrolledCanvas):
 785            return self.canvwidth, self.canvheight
 786        if canvwidth is None and canvheight is None and bg is None:
 787            return self.cv.canvwidth, self.cv.canvheight
 788        if canvwidth is not None:
 789            self.canvwidth = canvwidth
 790        if canvheight is not None:
 791            self.canvheight = canvheight
 792        self.cv.reset(canvwidth, canvheight, bg)
 793
 794    def _window_size(self):
 795        """ Return the width and height of the turtle window.
 796        """
 797        width = self.cv.winfo_width()
 798        if width <= 1:  # the window isn't managed by a geometry manager
 799            width = self.cv['width']
 800        height = self.cv.winfo_height()
 801        if height <= 1: # the window isn't managed by a geometry manager
 802            height = self.cv['height']
 803        return width, height
 804
 805
 806##############################################################################
 807###                  End of Tkinter - interface                            ###
 808##############################################################################
 809
 810
 811class Terminator (Exception):
 812    """Will be raised in TurtleScreen.update, if _RUNNING becomes False.
 813
 814    Thus stops execution of turtle graphics script. Main purpose: use in
 815    in the Demo-Viewer turtle.Demo.py.
 816    """
 817    pass
 818
 819
 820class TurtleGraphicsError(Exception):
 821    """Some TurtleGraphics Error
 822    """
 823
 824
 825class Shape(object):
 826    """Data structure modeling shapes.
 827
 828    attribute _type is one of "polygon", "image", "compound"
 829    attribute _data is - depending on _type a poygon-tuple,
 830    an image or a list constructed using the addcomponent method.
 831    """
 832    def __init__(self, type_, data=None):
 833        self._type = type_
 834        if type_ == "polygon":
 835            if isinstance(data, list):
 836                data = tuple(data)
 837        elif type_ == "image":
 838            if isinstance(data, str):
 839                if data.lower().endswith(".gif") and isfile(data):
 840                    data = TurtleScreen._image(data)
 841                # else data assumed to be Photoimage
 842        elif type_ == "compound":
 843            data = []
 844        else:
 845            raise TurtleGraphicsError("There is no shape type %s" % type_)
 846        self._data = data
 847
 848    def addcomponent(self, poly, fill, outline=None):
 849        """Add component to a shape of type compound.
 850
 851        Arguments: poly is a polygon, i. e. a tuple of number pairs.
 852        fill is the fillcolor of the component,
 853        outline is the outline color of the component.
 854
 855        call (for a Shapeobject namend s):
 856        --   s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue")
 857
 858        Example:
 859        >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
 860        >>> s = Shape("compound")
 861        >>> s.addcomponent(poly, "red", "blue")
 862        ### .. add more components and then use register_shape()
 863        """
 864        if self._type != "compound":
 865            raise TurtleGraphicsError("Cannot add component to %s Shape"
 866                                                                % self._type)
 867        if outline is None:
 868            outline = fill
 869        self._data.append([poly, fill, outline])
 870
 871
 872class Tbuffer(object):
 873    """Ring buffer used as undobuffer for RawTurtle objects."""
 874    def __init__(self, bufsize=10):
 875        self.bufsize = bufsize
 876        self.buffer = [[None]] * bufsize
 877        self.ptr = -1
 878        self.cumulate = False
 879    def reset(self, bufsize=None):
 880        if bufsize is None:
 881            for i in range(self.bufsize):
 882                self.buffer[i] = [None]
 883        else:
 884            self.bufsize = bufsize
 885            self.buffer = [[None]] * bufsize
 886        self.ptr = -1
 887    def push(self, item):
 888        if self.bufsize > 0:
 889            if not self.cumulate:
 890                self.ptr = (self.ptr + 1) % self.bufsize
 891                self.buffer[self.ptr] = item
 892            else:
 893                self.buffer[self.ptr].append(item)
 894    def pop(self):
 895        if self.bufsize > 0:
 896            item = self.buffer[self.ptr]
 897            if item is None:
 898                return None
 899            else:
 900                self.buffer[self.ptr] = [None]
 901                self.ptr = (self.ptr - 1) % self.bufsize
 902                return (item)
 903    def nr_of_items(self):
 904        return self.bufsize - self.buffer.count([None])
 905    def __repr__(self):
 906        return str(self.buffer) + " " + str(self.ptr)
 907
 908
 909
 910class TurtleScreen(TurtleScreenBase):
 911    """Provides screen oriented methods like setbg etc.
 912
 913    Only relies upon the methods of TurtleScreenBase and NOT
 914    upon components of the underlying graphics toolkit -
 915    which is Tkinter in this case.
 916    """
 917#    _STANDARD_DELAY = 5
 918    _RUNNING = True
 919
 920    def __init__(self, cv, mode=_CFG["mode"],
 921                 colormode=_CFG["colormode"], delay=_CFG["delay"]):
 922        self._shapes = {
 923                   "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))),
 924                  "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7),
 925                              (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6),
 926                              (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6),
 927                              (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10),
 928                              (2,14))),
 929                  "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88),
 930                              (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51),
 931                              (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0),
 932                              (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09),
 933                              (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51),
 934                              (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))),
 935                  "square" : Shape("polygon", ((10,-10), (10,10), (-10,10),
 936                              (-10,-10))),
 937                "triangle" : Shape("polygon", ((10,-5.77), (0,11.55),
 938                              (-10,-5.77))),
 939                  "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))),
 940                   "blank" : Shape("image", self._blankimage())
 941                  }
 942
 943        self._bgpics = {"nopic" : ""}
 944
 945        TurtleScreenBase.__init__(self, cv)
 946        self._mode = mode
 947        self._delayvalue = delay
 948        self._colormode = _CFG["colormode"]
 949        self._keys = []
 950        self.clear()
 951
 952    def clear(self):
 953        """Delete all drawings and all turtles from the TurtleScreen.
 954
 955        Reset empty TurtleScreen to its initial state: white background,
 956        no backgroundimage, no eventbindings and tracing on.
 957
 958        No argument.
 959
 960        Example (for a TurtleScreen instance named screen):
 961        screen.clear()
 962
 963        Note: this method is not available as function.
 964        """
 965        self._delayvalue = _CFG["delay"]
 966        self._colormode = _CFG["colormode"]
 967        self._delete("all")
 968        self._bgpic = self._createimage("")
 969        self._bgpicname = "nopic"
 970        self._tracing = 1
 971        self._updatecounter = 0
 972        self._turtles = []
 973        self.bgcolor("white")
 974        for btn in 1, 2, 3:
 975            self.onclick(None, btn)
 976        for key in self._keys[:]:
 977            self.onkey(None, key)
 978        Turtle._pen = None
 979
 980    def mode(self, mode=None):
 981        """Set turtle-mode ('standard', 'logo' or 'world') and perform reset.
 982
 983        Optional argument:
 984        mode -- on of the strings 'standard', 'logo' or 'world'
 985
 986        Mode 'standard' is compatible with turtle.py.
 987        Mode 'logo' is compatible with most Logo-Turtle-Graphics.
 988        Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in
 989        this mode angles appear distorted if x/y unit-ratio doesn't equal 1.
 990        If mode is not given, return the current mode.
 991
 992             Mode      Initial turtle heading     positive angles
 993         ------------|-------------------------|-------------------
 994          'standard'    to the right (east)       counterclockwise
 995            'logo'        upward    (north)         clockwise
 996
 997        Examples:
 998        >>> mode('logo')   # resets turtle heading to north
 999        >>> mode()
1000        'logo'
1001        """
1002        if mode == None:
1003            return self._mode
1004        mode = mode.lower()
1005        if mode not in ["standard", "logo", "world"]:
1006            raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode)
1007        self._mode = mode
1008        if mode in ["standard", "logo"]:
1009            self._setscrollregion(-self.canvwidth//2, -self.canvheight//2,
1010                                       self.canvwidth//2, self.canvheight//2)
1011            self.xscale = self.yscale = 1.0
1012        self.reset()
1013
1014    def setworldcoordinates(self, llx, lly, urx, ury):
1015        """Set up a user defined coordinate-system.
1016
1017        Arguments:
1018        llx -- a number, x-coordinate of lower left corner of canvas
1019        lly -- a number, y-coordinate of lower left corner of canvas
1020        urx -- a number, x-coordinate of upper right corner of canvas
1021        ury -- a number, y-coordinate of upper right corner of canvas
1022
1023        Set up user coodinat-system and switch to mode 'world' if necessary.
1024        This performs a screen.reset. If mode 'world' is already active,
1025        all drawings are redrawn according to the new coordinates.
1026
1027        But ATTENTION: in user-defined coordinatesystems angles may appear
1028        distorted. (see Screen.mode())
1029
1030        Example (for a TurtleScreen instance named screen):
1031        >>> screen.setworldcoordinates(-10,-0.5,50,1.5)
1032        >>> for _ in range(36):
1033                left(10)
1034                forward(0.5)
1035        """
1036        if self.mode() != "world":
1037            self.mode("world")
1038        xspan = float(urx - llx)
1039        yspan = float(ury - lly)
1040        wx, wy = self._window_size()
1041        self.screensize(wx-20, wy-20)
1042        oldxscale, oldyscale = self.xscale, self.yscale
1043        self.xscale = self.canvwidth / xspan
1044        self.yscale = self.canvheight / yspan
1045        srx1 = llx * self.xscale
1046        sry1 = -ury * self.yscale
1047        srx2 = self.canvwidth + srx1
1048        sry2 = self.canvheight + sry1
1049        self._setscrollregion(srx1, sry1, srx2, sry2)
1050        self._rescale(self.xscale/oldxscale, self.yscale/oldyscale)
1051        self.update()
1052
1053    def register_shape(self, name, shape=None):
1054        """Adds a turtle shape to TurtleScreen's shapelist.
1055
1056        Arguments:
1057        (1) name is the name of a gif-file and shape is None.
1058            Installs the corresponding image shape.
1059            !! Image-shapes DO NOT rotate when turning the turtle,
1060            !! so they do not display the heading of the turtle!
1061        (2) name is an arbitrary string and shape is a tuple
1062            of pairs of coordinates. Installs the corresponding
1063            polygon shape
1064        (3) name is an arbitrary string and shape is a
1065            (compound) Shape object. Installs the corresponding
1066            compound shape.
1067        To use a shape, you have to issue the command shape(shapename).
1068
1069        call: register_shape("turtle.gif")
1070        --or: register_shape("tri", ((0,0), (10,10), (-10,10)))
1071
1072        Example (for a TurtleScreen instance named screen):
1073        >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3)))
1074
1075        """
1076        if shape is None:
1077            # image
1078            if name.lower().endswith(".gif"):
1079                shape = Shape("image", self._image(name))
1080            else:
1081                raise TurtleGraphicsError("Bad arguments for register_shape.\n"
1082                                          + "Use  help(register_shape)" )
1083        elif isinstance(shape, tuple):
1084            shape = Shape("polygon", shape)
1085        ## else shape assumed to be Shape-instance
1086        self._shapes[name] = shape
1087        # print "shape added:" , self._shapes
1088
1089    def _colorstr(self, color):
1090        """Return color string corresponding to args.
1091
1092        Argument may be a string or a tuple of three
1093        numbers corresponding to actual colormode,
1094        i.e. in the range 0<=n<=colormode.
1095
1096        If the argument doesn't represent a color,
1097        an error is raised.
1098        """
1099        if len(color) == 1:
1100            color = color[0]
1101        if isinstance(color, str):
1102            if self._iscolorstring(color) or color == "":
1103                return color
1104            else:
1105                raise TurtleGraphicsError("bad color string: %s" % str(color))
1106        try:
1107            r, g, b = color
1108        except:
1109            raise TurtleGraphicsError("bad color arguments: %s" % str(color))
1110        if self._colormode == 1.0:
1111            r, g, b = [round(255.0*x) for x in (r, g, b)]
1112        if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
1113            raise TurtleGraphicsError("bad color sequence: %s" % str(color))
1114        return "#%02x%02x%02x" % (r, g, b)
1115
1116    def _color(self, cstr):
1117        if not cstr.startswith("#"):
1118            return cstr
1119        if len(cstr) == 7:
1120            cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)]
1121        elif len(cstr) == 4:
1122            cl = [16*int(cstr[h], 16) for h in cstr[1:]]
1123        else:
1124            raise TurtleGraphicsError("bad colorstring: %s" % cstr)
1125        return tuple([c * self._colormode/255 for c in cl])
1126
1127    def colormode(self, cmode=None):
1128        """Return the colormode or set it to 1.0 or 255.
1129
1130        Optional argument:
1131        cmode -- one of the values 1.0 or 255
1132
1133        r, g, b values of colortriples have to be in range 0..cmode.
1134
1135        Example (for a TurtleScreen instance named screen):
1136        >>> screen.colormode()
1137        1.0
1138        >>> screen.colormode(255)
1139        >>> turtle.pencolor(240,160,80)
1140        """
1141        if cmode is None:
1142            return self._colormode
1143        if cmode == 1.0:
1144            self._colormode = float(cmode)
1145        elif cmode == 255:
1146            self._colormode = int(cmode)
1147
1148    def reset(self):
1149        """Reset all Turtles on the Screen to their initial state.
1150
1151        No argument.
1152
1153        Example (for a TurtleScreen instance named screen):
1154        >>> screen.reset()
1155        """
1156        for turtle in self._turtles:
1157            turtle._setmode(self._mode)
1158            turtle.reset()
1159
1160    def turtles(self):
1161        """Return the list of turtles on the screen.
1162
1163        Example (for a TurtleScreen instance named screen):
1164        >>> screen.turtles()
1165        [<turtle.Turtle object at 0x00E11FB0>]
1166        """
1167        return self._turtles
1168
1169    def bgcolor(self, *args):
1170        """Set or return backgroundcolor of the TurtleScreen.
1171
1172        Arguments (if given): a color string or three numbers
1173        in the range 0..colormode or a 3-tuple of such numbers.
1174
1175        Example (for a TurtleScreen instance named screen):
1176        >>> screen.bgcolor("orange")
1177        >>> screen.bgcolor()
1178        'orange'
1179        >>> screen.bgcolor(0.5,0,0.5)
1180        >>> screen.bgcolor()
1181        '#800080'
1182        """
1183        if args:
1184            color = self._colorstr(args)
1185        else:
1186            color = None
1187        color = self._bgcolor(color)
1188        if color is not None:
1189            color = self._color(color)
1190        return color
1191
1192    def tracer(self, n=None, delay=None):
1193        """Turns turtle animation on/off and set delay for update drawings.
1194
1195        Optional arguments:
1196        n -- nonnegative  integer
1197        delay -- nonnegative  integer
1198
1199        If n is given, only each n-th regular screen update is really performed.
1200        (Can be used to accelerate the drawing of complex graphics.)
1201        Second arguments sets delay value (see RawTurtle.delay())
1202
1203        Example (for a TurtleScreen instance named screen):
1204        >>> screen.tracer(8, 25)
1205        >>> dist = 2
1206        >>> for i in range(200):
1207                fd(dist)
1208                rt(90)
1209                dist += 2
1210        """
1211        if n is None:
1212            return self._tracing
1213        self._tracing = int(n)
1214        self._updatecounter = 0
1215        if delay is not None:
1216            self._delayvalue = int(delay)
1217        if self._tracing:
1218            self.update()
1219
1220    def delay(self, delay=None):
1221        """ Return or set the drawing delay in milliseconds.
1222
1223        Optional argument:
1224        delay -- positive integer
1225
1226        Example (for a TurtleScreen instance named screen):
1227        >>> screen.delay(15)
1228        >>> screen.delay()
1229        15
1230        """
1231        if delay is None:
1232            return self._delayvalue
1233        self._delayvalue = int(delay)
1234
1235    def _incrementudc(self):
1236        "Increment upadate counter."""
1237        if not TurtleScreen._RUNNING:
1238            TurtleScreen._RUNNNING = True
1239            raise Terminator
1240        if self._tracing > 0:
1241            self._updatecounter += 1
1242            self._updatecounter %= self._tracing
1243
1244    def update(self):
1245        """Perform a TurtleScreen update.
1246        """
1247        tracing = self._tracing
1248        self._tracing = True
1249        for t in self.turtles():
1250            t._update_data()
1251            t._drawturtle()
1252        self._tracing = tracing
1253        self._update()
1254
1255    def window_width(self):
1256        """ Return the width of the turtle window.
1257
1258        Example (for a TurtleScreen instance named screen):
1259        >>> screen.window_width()
1260        640
1261        """
1262        return self._window_size()[0]
1263
1264    def window_height(self):
1265        """ Return the height of the turtle window.
1266
1267        Example (for a TurtleScreen instance named screen):
1268        >>> screen.window_height()
1269        480
1270        """
1271        return self._window_size()[1]
1272
1273    def getcanvas(self):
1274        """Return the Canvas of this TurtleScreen.
1275
1276        No argument.
1277
1278        Example (for a Screen instance named screen):
1279        >>> cv = screen.getcanvas()
1280        >>> cv
1281        <turtle.ScrolledCanvas instance at 0x010742D8>
1282        """
1283        return self.cv
1284
1285    def getshapes(self):
1286        """Return a list of names of all currently available turtle shapes.
1287
1288        No argument.
1289
1290        Example (for a TurtleScreen instance named screen):
1291        >>> screen.getshapes()
1292        ['arrow', 'blank', 'circle', ... , 'turtle']
1293        """
1294        return sorted(self._shapes.keys())
1295
1296    def onclick(self, fun, btn=1, add=None):
1297        """Bind fun to mouse-click event on canvas.
1298
1299        Arguments:
1300        fun -- a function with two arguments, the coordinates of the
1301               clicked point on the canvas.
1302        num -- the number of the mouse-button, defaults to 1
1303
1304        Example (for a TurtleScreen instance named screen
1305        and a Turtle instance named turtle):
1306
1307        >>> screen.onclick(turtle.goto)
1308
1309        ### Subsequently clicking into the TurtleScreen will
1310        ### make the turtle move to the clicked point.
1311        >>> screen.onclick(None)
1312
1313        ### event-binding will be removed
1314        """
1315        self._onscreenclick(fun, btn, add)
1316
1317    def onkey(self, fun, key):
1318        """Bind fun to key-release event of key.
1319
1320        Arguments:
1321        fun -- a function with no arguments
1322        key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
1323
1324        In order to be able to register key-events, TurtleScreen
1325        must have focus. (See method listen.)
1326
1327        Example (for a TurtleScreen instance named screen
1328        and a Turtle instance named turtle):
1329
1330        >>> def f():
1331                fd(50)
1332                lt(60)
1333
1334
1335        >>> screen.onkey(f, "Up")
1336        >>> screen.listen()
1337
1338        ### Subsequently the turtle can be moved by
1339        ### repeatedly pressing the up-arrow key,
1340        ### consequently drawing a hexagon
1341        """
1342        if fun == None:
1343            if key in self._keys:
1344                self._keys.remove(key)
1345        elif key not in self._keys:
1346            self._keys.append(key)
1347        self._onkey(fun, key)
1348
1349    def listen(self, xdummy=None, ydummy=None):
1350        """Set focus on TurtleScreen (in order to collect key-events)
1351
1352        No arguments.
1353        Dummy arguments are provided in order
1354        to be able to pass listen to the onclick method.
1355
1356        Example (for a TurtleScreen instance named screen):
1357        >>> screen.listen()
1358        """
1359        self._listen()
1360
1361    def ontimer(self, fun, t=0):
1362        """Install a timer, which calls fun after t milliseconds.
1363
1364        Arguments:
1365        fun -- a function with no arguments.
1366        t -- a number >= 0
1367
1368        Example (for a TurtleScreen instance named screen):
1369
1370        >>> running = True
1371        >>> def f():
1372                if running:
1373                        fd(50)
1374                        lt(60)
1375                        screen.ontimer(f, 250)
1376
1377

Large files files are truncated, but you can click here to view the full file