~pythonregexp2.7/python/issue2636

« back to all changes in this revision

Viewing changes to Lib/lib-tk/turtle.py

  • Committer: Jeffrey C. "The TimeHorse" Jacobs
  • Date: 2008-06-09 14:36:32 UTC
  • mfrom: (39021.1.402 Regexp-2.6)
  • Revision ID: darklord@timehorse.com-20080609143632-wwwkx92u1t5l7yd3
Merged in changes from the latest python source snapshot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# LogoMation-like turtle graphics
 
1
#
 
2
# turtle.py: a Tkinter based turtle graphics module for Python
 
3
# Version 1.0b1 - 31. 5. 2008
 
4
#
 
5
# Copyright (C) 2006 - 2008  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
 
2
24
 
3
25
"""
4
26
Turtle graphics is a popular way for introducing programming to
5
27
kids. It was part of the original Logo programming language developed
6
 
by Wally Feurzeig and Seymour Papert in 1966.
 
28
by Wally Feurzig and Seymour Papert in 1966.
7
29
 
8
30
Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it
9
31
the command turtle.forward(15), and it moves (on-screen!) 15 pixels in
12
34
 
13
35
By combining together these and similar commands, intricate shapes and
14
36
pictures can easily be drawn.
 
37
 
 
38
----- turtle.py
 
39
 
 
40
This module is an extended reimplementation of turtle.py from the
 
41
Python standard distribution up to Python 2.5. (See: http:\\www.python.org)
 
42
 
 
43
It tries to keep the merits of turtle.py and to be (nearly) 100%
 
44
compatible with it. This means in the first place to enable the
 
45
learning programmer to use all the commands, classes and methods
 
46
interactively when using the module from within IDLE run with
 
47
the -n switch.
 
48
 
 
49
Roughly 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 stgretched and tilted, which
 
58
  makes turtles zu 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 graphcis.
 
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
 
 
98
Behind the scenes there are some features included with possible
 
99
extensionsin in mind. These will be commented and documented elsewhere.
 
100
 
15
101
"""
16
102
 
17
 
from math import * # Also for export
18
 
from time import sleep
19
 
import Tkinter
20
 
 
21
 
speeds = ['fastest', 'fast', 'normal', 'slow', 'slowest']
22
 
 
23
 
class Error(Exception):
 
103
_ver = "turtle 1.0b1 - for Python 2.6   -  30. 5. 2008, 18:08"
 
104
 
 
105
#print _ver
 
106
 
 
107
import Tkinter as TK
 
108
import types
 
109
import math
 
110
import time
 
111
import os
 
112
 
 
113
from os.path import isfile, split, join
 
114
from copy import deepcopy
 
115
 
 
116
from 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
 
 
183
def 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
 
 
213
def 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
 
 
250
try:
 
251
    readconfig(_CFG)
 
252
except:
 
253
    print "No configfile read, reason unknown"
 
254
 
 
255
 
 
256
class 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 graphcis-toolkit     ###
 
303
##############################################################################
 
304
 
 
305
## helper functions for Scrolled Canvas, to forward Canvas-methods
 
306
## to ScrolledCanvas class
 
307
 
 
308
def __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
 
 
318
def __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
 
 
328
def __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
 
 
353
class 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._root = 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._root.bind('<Configure>', self.onResize)
 
383
 
 
384
    def reset(self, canvwidth=None, canvheight=None, bg = None):
 
385
        """Ajust 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
 
 
455
class _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
 
 
479
Canvas = TK.Canvas
 
480
 
 
481
 
 
482
class 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, -y))
 
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
 
 
811
class 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
    """
24
817
    pass
25
818
 
26
 
class RawPen:
27
 
 
28
 
    def __init__(self, canvas):
29
 
        self._canvas = canvas
30
 
        self._items = []
 
819
 
 
820
class TurtleGraphicsError(Exception):
 
821
    """Some TurtleGraphics Error
 
822
    """
 
823
 
 
824
 
 
825
class 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
 
 
872
class 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
 
 
910
class 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 it's 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"
31
970
        self._tracing = 1
32
 
        self._arrow = 0
33
 
        self._delay = 10     # default delay for drawing
34
 
        self._angle = 0.0
 
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
        for t in self.turtles():
 
1248
            t._update_data()
 
1249
            t._drawturtle()
 
1250
        self._update()
 
1251
 
 
1252
    def window_width(self):
 
1253
        """ Return the width of the turtle window.
 
1254
 
 
1255
        Example (for a TurtleScreen instance named screen):
 
1256
        >>> screen.window_width()
 
1257
        640
 
1258
        """
 
1259
        return self._window_size()[0]
 
1260
 
 
1261
    def window_height(self):
 
1262
        """ Return the height of the turtle window.
 
1263
 
 
1264
        Example (for a TurtleScreen instance named screen):
 
1265
        >>> screen.window_height()
 
1266
        480
 
1267
        """
 
1268
        return self._window_size()[1]
 
1269
 
 
1270
    def getcanvas(self):
 
1271
        """Return the Canvas of this TurtleScreen.
 
1272
 
 
1273
        No argument.
 
1274
 
 
1275
        Example (for a Screen instance named screen):
 
1276
        >>> cv = screen.getcanvas()
 
1277
        >>> cv
 
1278
        <turtle.ScrolledCanvas instance at 0x010742D8>
 
1279
        """
 
1280
        return self.cv
 
1281
 
 
1282
    def getshapes(self):
 
1283
        """Return a list of names of all currently available turtle shapes.
 
1284
 
 
1285
        No argument.
 
1286
 
 
1287
        Example (for a TurtleScreen instance named screen):
 
1288
        >>> screen.getshapes()
 
1289
        ['arrow', 'blank', 'circle', ... , 'turtle']
 
1290
        """
 
1291
        return sorted(self._shapes.keys())
 
1292
 
 
1293
    def onclick(self, fun, btn=1, add=None):
 
1294
        """Bind fun to mouse-click event on canvas.
 
1295
 
 
1296
        Arguments:
 
1297
        fun -- a function with two arguments, the coordinates of the
 
1298
               clicked point on the canvas.
 
1299
        num -- the number of the mouse-button, defaults to 1
 
1300
 
 
1301
        Example (for a TurtleScreen instance named screen
 
1302
        and a Turtle instance named turtle):
 
1303
 
 
1304
        >>> screen.onclick(turtle.goto)
 
1305
 
 
1306
        ### Subsequently clicking into the TurtleScreen will
 
1307
        ### make the turtle move to the clicked point.
 
1308
        >>> screen.onclick(None)
 
1309
 
 
1310
        ### event-binding will be removed
 
1311
        """
 
1312
        self._onscreenclick(fun, btn, add)
 
1313
 
 
1314
    def onkey(self, fun, key):
 
1315
        """Bind fun to key-release event of key.
 
1316
 
 
1317
        Arguments:
 
1318
        fun -- a function with no arguments
 
1319
        key -- a string: key (e.g. "a") or key-symbol (e.g. "space")
 
1320
 
 
1321
        In order ro be able to register key-events, TurtleScreen
 
1322
        must have focus. (See method listen.)
 
1323
 
 
1324
        Example (for a TurtleScreen instance named screen
 
1325
        and a Turtle instance named turtle):
 
1326
 
 
1327
        >>> def f():
 
1328
                fd(50)
 
1329
                lt(60)
 
1330
 
 
1331
 
 
1332
        >>> screen.onkey(f, "Up")
 
1333
        >>> screen.listen()
 
1334
 
 
1335
        ### Subsequently the turtle can be moved by
 
1336
        ### repeatedly pressing the up-arrow key,
 
1337
        ### consequently drawing a hexagon
 
1338
        """
 
1339
        if fun == None:
 
1340
            self._keys.remove(key)
 
1341
        elif key not in self._keys:
 
1342
            self._keys.append(key)
 
1343
        self._onkey(fun, key)
 
1344
 
 
1345
    def listen(self, xdummy=None, ydummy=None):
 
1346
        """Set focus on TurtleScreen (in order to collect key-events)
 
1347
 
 
1348
        No arguments.
 
1349
        Dummy arguments are provided in order
 
1350
        to be able to pass listen to the onclick method.
 
1351
 
 
1352
        Example (for a TurtleScreen instance named screen):
 
1353
        >>> screen.listen()
 
1354
        """
 
1355
        self._listen()
 
1356
 
 
1357
    def ontimer(self, fun, t=0):
 
1358
        """Install a timer, which calls fun after t milliseconds.
 
1359
 
 
1360
        Arguments:
 
1361
        fun -- a function with no arguments.
 
1362
        t -- a number >= 0
 
1363
 
 
1364
        Example (for a TurtleScreen instance named screen):
 
1365
 
 
1366
        >>> running = True
 
1367
        >>> def f():
 
1368
                if running:
 
1369
                        fd(50)
 
1370
                        lt(60)
 
1371
                        screen.ontimer(f, 250)
 
1372
 
 
1373
        >>> f()   ### makes the turtle marching around
 
1374
        >>> running = False
 
1375
        """
 
1376
        self._ontimer(fun, t)
 
1377
 
 
1378
    def bgpic(self, picname=None):
 
1379
        """Set background image or return name of current backgroundimage.
 
1380
 
 
1381
        Optional argument:
 
1382
        picname -- a string, name of a gif-file or "nopic".
 
1383
 
 
1384
        If picname is a filename, set the corresponing image as background.
 
1385
        If picname is "nopic", delete backgroundimage, if present.
 
1386
        If picname is None, return the filename of the current backgroundimage.
 
1387
 
 
1388
        Example (for a TurtleScreen instance named screen):
 
1389
        >>> screen.bgpic()
 
1390
        'nopic'
 
1391
        >>> screen.bgpic("landscape.gif")
 
1392
        >>> screen.bgpic()
 
1393
        'landscape.gif'
 
1394
        """
 
1395
        if picname is None:
 
1396
            return self._bgpicname
 
1397
        if picname not in self._bgpics:
 
1398
            self._bgpics[picname] = self._image(picname)
 
1399
        self._setbgpic(self._bgpic, self._bgpics[picname])
 
1400
        self._bgpicname = picname
 
1401
 
 
1402
    def screensize(self, canvwidth=None, canvheight=None, bg=None):
 
1403
        """Resize the canvas, the turtles are drawing on.
 
1404
 
 
1405
        Optional arguments:
 
1406
        canvwidth -- positive integer, new width of canvas in pixels
 
1407
        canvheight --  positive integer, new height of canvas in pixels
 
1408
        bg -- colorstring or color-tupel, new backgroundcolor
 
1409
        If no arguments are given, return current (canvaswidth, canvasheight)
 
1410
 
 
1411
        Do not alter the drawing window. To observe hidden parts of
 
1412
        the canvas use the scrollbars. (Can make visible those parts
 
1413
        of a drawing, which were outside the canvas before!)
 
1414
 
 
1415
        Example (for a Turtle instance named turtle):
 
1416
        >>> turtle.screensize(2000,1500)
 
1417
            ### e. g. to search for an erroneously escaped turtle ;-)
 
1418
        """
 
1419
        return self._resize(canvwidth, canvheight, bg)
 
1420
 
 
1421
    onscreenclick = onclick
 
1422
    resetscreen = reset
 
1423
    clearscreen = clear
 
1424
    addshape = register_shape
 
1425
 
 
1426
class TNavigator(object):
 
1427
    """Navigation part of the RawTurtle.
 
1428
    Implements methods for turtle movement.
 
1429
    """
 
1430
    START_ORIENTATION = {
 
1431
        "standard": Vec2D(1.0, 0.0),
 
1432
        "world"   : Vec2D(1.0, 0.0),
 
1433
        "logo"    : Vec2D(0.0, 1.0)  }
 
1434
    DEFAULT_MODE = "standard"
 
1435
    DEFAULT_ANGLEOFFSET = 0
 
1436
    DEFAULT_ANGLEORIENT = 1
 
1437
 
 
1438
    def __init__(self, mode=DEFAULT_MODE):
 
1439
        self._angleOffset = self.DEFAULT_ANGLEOFFSET
 
1440
        self._angleOrient = self.DEFAULT_ANGLEORIENT
 
1441
        self._mode = mode
 
1442
        self.undobuffer = None
35
1443
        self.degrees()
36
 
        self.reset()
 
1444
        self._mode = None
 
1445
        self._setmode(mode)
 
1446
        TNavigator.reset(self)
 
1447
 
 
1448
    def reset(self):
 
1449
        """reset turtle to its initial values
 
1450
 
 
1451
        Will be overwritten by parent class
 
1452
        """
 
1453
        self._position = Vec2D(0.0, 0.0)
 
1454
        self._orient =  TNavigator.START_ORIENTATION[self._mode]
 
1455
 
 
1456
    def _setmode(self, mode=None):
 
1457
        """Set turtle-mode to 'standard', 'world' or 'logo'.
 
1458
        """
 
1459
        if mode == None:
 
1460
            return self._mode
 
1461
        if mode not in ["standard", "logo", "world"]:
 
1462
            return
 
1463
        self._mode = mode
 
1464
        if mode in ["standard", "world"]:
 
1465
            self._angleOffset = 0
 
1466
            self._angleOrient = 1
 
1467
        else: # mode == "logo":
 
1468
            self._angleOffset = self._fullcircle/4.
 
1469
            self._angleOrient = -1
 
1470
 
 
1471
    def _setDegreesPerAU(self, fullcircle):
 
1472
        """Helper function for degrees() and radians()"""
 
1473
        self._fullcircle = fullcircle
 
1474
        self._degreesPerAU = 360/fullcircle
 
1475
        if self._mode == "standard":
 
1476
            self._angleOffset = 0
 
1477
        else:
 
1478
            self._angleOffset = fullcircle/4.
37
1479
 
38
1480
    def degrees(self, fullcircle=360.0):
39
1481
        """ Set angle measurement units to degrees.
40
1482
 
41
 
        Example:
42
 
        >>> turtle.degrees()
 
1483
        Optional argument:
 
1484
        fullcircle -  a number
 
1485
 
 
1486
        Set angle measurement units, i. e. set number
 
1487
        of 'degrees' for a full circle. Dafault value is
 
1488
        360 degrees.
 
1489
 
 
1490
        Example (for a Turtle instance named turtle):
 
1491
        >>> turtle.left(90)
 
1492
        >>> turtle.heading()
 
1493
        90
 
1494
        >>> turtle.degrees(400.0)  # angle measurement in gon
 
1495
        >>> turtle.heading()
 
1496
        100
 
1497
 
43
1498
        """
44
 
        # Don't try to change _angle if it is 0, because
45
 
        # _fullcircle might not be set, yet
46
 
        if self._angle:
47
 
            self._angle = (self._angle / self._fullcircle) * fullcircle
48
 
        self._fullcircle = fullcircle
49
 
        self._invradian = pi / (fullcircle * 0.5)
 
1499
        self._setDegreesPerAU(fullcircle)
50
1500
 
51
1501
    def radians(self):
52
1502
        """ Set the angle measurement units to radians.
53
1503
 
54
 
        Example:
 
1504
        No arguments.
 
1505
 
 
1506
        Example (for a Turtle instance named turtle):
 
1507
        >>> turtle.heading()
 
1508
        90
55
1509
        >>> turtle.radians()
56
 
        """
57
 
        self.degrees(2.0*pi)
58
 
 
59
 
    def reset(self):
60
 
        """ Clear the screen, re-center the pen, and set variables to
61
 
        the default values.
62
 
 
63
 
        Example:
64
 
        >>> turtle.position()
65
 
        [0.0, -22.0]
66
 
        >>> turtle.heading()
67
 
        100.0
68
 
        >>> turtle.reset()
69
 
        >>> turtle.position()
70
 
        [0.0, 0.0]
71
 
        >>> turtle.heading()
72
 
        0.0
73
 
        """
74
 
        canvas = self._canvas
75
 
        self._canvas.update()
76
 
        width = canvas.winfo_width()
77
 
        height = canvas.winfo_height()
78
 
        if width <= 1:
79
 
            width = canvas['width']
80
 
        if height <= 1:
81
 
            height = canvas['height']
82
 
        self._origin = float(width)/2.0, float(height)/2.0
83
 
        self._position = self._origin
84
 
        self._angle = 0.0
85
 
        self._drawing = 1
86
 
        self._width = 1
87
 
        self._color = "black"
88
 
        self._filling = 0
89
 
        self._path = []
90
 
        self.clear()
91
 
        canvas._root().tkraise()
92
 
 
93
 
    def clear(self):
94
 
        """ Clear the screen. The turtle does not move.
95
 
 
96
 
        Example:
97
 
        >>> turtle.clear()
98
 
        """
99
 
        self.fill(0)
100
 
        canvas = self._canvas
101
 
        items = self._items
102
 
        self._items = []
103
 
        for item in items:
104
 
            canvas.delete(item)
105
 
        self._delete_turtle()
106
 
        self._draw_turtle()
107
 
 
108
 
    def tracer(self, flag):
109
 
        """ Set tracing on if flag is True, and off if it is False.
110
 
        Tracing means line are drawn more slowly, with an
111
 
        animation of an arrow along the line.
112
 
 
113
 
        Example:
114
 
        >>> turtle.tracer(False)   # turns off Tracer
115
 
        """
116
 
        self._tracing = flag
117
 
        if not self._tracing:
118
 
            self._delete_turtle()
119
 
        self._draw_turtle()
 
1510
        >>> turtle.heading()
 
1511
        1.5707963267948966
 
1512
        """
 
1513
        self._setDegreesPerAU(2*math.pi)
 
1514
 
 
1515
    def _go(self, distance):
 
1516
        """move turtle forward by specified distance"""
 
1517
        ende = self._position + self._orient * distance
 
1518
        self._goto(ende)
 
1519
 
 
1520
    def _rotate(self, angle):
 
1521
        """Turn turtle counterclockwise by specified angle if angle > 0."""
 
1522
        angle *= self._degreesPerAU
 
1523
        self._orient = self._orient.rotate(angle)
 
1524
 
 
1525
    def _goto(self, end):
 
1526
        """move turtle to position end."""
 
1527
        self._position = end
120
1528
 
121
1529
    def forward(self, distance):
122
 
        """ Go forward distance steps.
123
 
 
124
 
        Example:
 
1530
        """Move the turtle forward by the specified distance.
 
1531
 
 
1532
        Aliases: forward | fd
 
1533
 
 
1534
        Argument:
 
1535
        distance -- a number (integer or float)
 
1536
 
 
1537
        Move the turtle forward by the specified distance, in the direction
 
1538
        the turtle is headed.
 
1539
 
 
1540
        Example (for a Turtle instance named turtle):
125
1541
        >>> turtle.position()
126
 
        [0.0, 0.0]
 
1542
        (0.00, 0.00)
127
1543
        >>> turtle.forward(25)
128
1544
        >>> turtle.position()
129
 
        [25.0, 0.0]
 
1545
        (25.00,0.00)
130
1546
        >>> turtle.forward(-75)
131
1547
        >>> turtle.position()
132
 
        [-50.0, 0.0]
 
1548
        (-50.00,0.00)
133
1549
        """
134
 
        x0, y0 = start = self._position
135
 
        x1 = x0 + distance * cos(self._angle*self._invradian)
136
 
        y1 = y0 - distance * sin(self._angle*self._invradian)
137
 
        self._goto(x1, y1)
138
 
 
139
 
    def backward(self, distance):
140
 
        """ Go backwards distance steps.
141
 
 
142
 
        The turtle's heading does not change.
143
 
 
144
 
        Example:
 
1550
        self._go(distance)
 
1551
 
 
1552
    def back(self, distance):
 
1553
        """Move the turtle backward by distance.
 
1554
 
 
1555
        Aliases: back | backward | bk
 
1556
 
 
1557
        Argument:
 
1558
        distance -- a number
 
1559
 
 
1560
        Move the turtle backward by distance ,opposite to the direction the
 
1561
        turtle is headed. Do not change the turtle's heading.
 
1562
 
 
1563
        Example (for a Turtle instance named turtle):
145
1564
        >>> turtle.position()
146
 
        [0.0, 0.0]
 
1565
        (0.00, 0.00)
147
1566
        >>> turtle.backward(30)
148
1567
        >>> turtle.position()
149
 
        [-30.0, 0.0]
150
 
        """
151
 
        self.forward(-distance)
152
 
 
153
 
    def left(self, angle):
154
 
        """ Turn left angle units (units are by default degrees,
155
 
        but can be set via the degrees() and radians() functions.)
156
 
 
157
 
        When viewed from above, the turning happens in-place around
158
 
        its front tip.
159
 
 
160
 
        Example:
161
 
        >>> turtle.heading()
162
 
        22
163
 
        >>> turtle.left(45)
164
 
        >>> turtle.heading()
165
 
        67.0
166
 
        """
167
 
        self._angle = (self._angle + angle) % self._fullcircle
168
 
        self._draw_turtle()
 
1568
        (-30.00, 0.00)
 
1569
        """
 
1570
        self._go(-distance)
169
1571
 
170
1572
    def right(self, angle):
171
 
        """ Turn right angle units (units are by default degrees,
 
1573
        """Turn turtle right by angle units.
 
1574
 
 
1575
        Aliases: right | rt
 
1576
 
 
1577
        Argument:
 
1578
        angle -- a number (integer or float)
 
1579
 
 
1580
        Turn turtle right by angle units. (Units are by default degrees,
172
1581
        but can be set via the degrees() and radians() functions.)
173
 
 
174
 
        When viewed from above, the turning happens in-place around
175
 
        its front tip.
176
 
 
177
 
        Example:
 
1582
        Angle orientation depends on mode. (See this.)
 
1583
 
 
1584
        Example (for a Turtle instance named turtle):
178
1585
        >>> turtle.heading()
179
 
        22
 
1586
        22.0
180
1587
        >>> turtle.right(45)
181
1588
        >>> turtle.heading()
182
1589
        337.0
183
1590
        """
184
 
        self.left(-angle)
185
 
 
186
 
    def up(self):
187
 
        """ Pull the pen up -- no drawing when moving.
188
 
 
189
 
        Example:
190
 
        >>> turtle.up()
191
 
        """
192
 
        self._drawing = 0
193
 
 
194
 
    def down(self):
195
 
        """ Put the pen down -- draw when moving.
196
 
 
197
 
        Example:
198
 
        >>> turtle.down()
199
 
        """
200
 
        self._drawing = 1
201
 
 
202
 
    def width(self, width):
203
 
        """ Set the line to thickness to width.
204
 
 
205
 
        Example:
206
 
        >>> turtle.width(10)
207
 
        """
208
 
        self._width = float(width)
209
 
 
210
 
    def color(self, *args):
211
 
        """ Set the pen color.
212
 
 
213
 
        Three input formats are allowed:
214
 
 
215
 
            color(s)
216
 
            s is a Tk specification string, such as "red" or "yellow"
217
 
 
218
 
            color((r, g, b))
219
 
            *a tuple* of r, g, and b, which represent, an RGB color,
220
 
            and each of r, g, and b are in the range [0..1]
221
 
 
222
 
            color(r, g, b)
223
 
            r, g, and b represent an RGB color, and each of r, g, and b
224
 
            are in the range [0..1]
225
 
 
226
 
        Example:
227
 
 
228
 
        >>> turtle.color('brown')
229
 
        >>> tup = (0.2, 0.8, 0.55)
230
 
        >>> turtle.color(tup)
231
 
        >>> turtle.color(0, .5, 0)
232
 
        """
233
 
        if not args:
234
 
            raise Error, "no color arguments"
235
 
        if len(args) == 1:
236
 
            color = args[0]
237
 
            if type(color) == type(""):
238
 
                # Test the color first
239
 
                try:
240
 
                    id = self._canvas.create_line(0, 0, 0, 0, fill=color)
241
 
                except Tkinter.TclError:
242
 
                    raise Error, "bad color string: %r" % (color,)
243
 
                self._set_color(color)
244
 
                return
245
 
            try:
246
 
                r, g, b = color
247
 
            except:
248
 
                raise Error, "bad color sequence: %r" % (color,)
 
1591
        self._rotate(-angle)
 
1592
 
 
1593
    def left(self, angle):
 
1594
        """Turn turtle left by angle units.
 
1595
 
 
1596
        Aliases: left | lt
 
1597
 
 
1598
        Argument:
 
1599
        angle -- a number (integer or float)
 
1600
 
 
1601
        Turn turtle left by angle units. (Units are by default degrees,
 
1602
        but can be set via the degrees() and radians() functions.)
 
1603
        Angle orientation depends on mode. (See this.)
 
1604
 
 
1605
        Example (for a Turtle instance named turtle):
 
1606
        >>> turtle.heading()
 
1607
        22.0
 
1608
        >>> turtle.left(45)
 
1609
        >>> turtle.heading()
 
1610
        67.0
 
1611
        """
 
1612
        self._rotate(angle)
 
1613
 
 
1614
    def pos(self):
 
1615
        """Return the turtle's current location (x,y), as a Vec2D-vector.
 
1616
 
 
1617
        Aliases: pos | position
 
1618
 
 
1619
        No arguments.
 
1620
 
 
1621
        Example (for a Turtle instance named turtle):
 
1622
        >>> turtle.pos()
 
1623
        (0.00, 240.00)
 
1624
        """
 
1625
        return self._position
 
1626
 
 
1627
    def xcor(self):
 
1628
        """ Return the turtle's x coordinate.
 
1629
 
 
1630
        No arguments.
 
1631
 
 
1632
        Example (for a Turtle instance named turtle):
 
1633
        >>> reset()
 
1634
        >>> turtle.left(60)
 
1635
        >>> turtle.forward(100)
 
1636
        >>> print turtle.xcor()
 
1637
        50.0
 
1638
        """
 
1639
        return self._position[0]
 
1640
 
 
1641
    def ycor(self):
 
1642
        """ Return the turtle's y coordinate
 
1643
        ---
 
1644
        No arguments.
 
1645
 
 
1646
        Example (for a Turtle instance named turtle):
 
1647
        >>> reset()
 
1648
        >>> turtle.left(60)
 
1649
        >>> turtle.forward(100)
 
1650
        >>> print turtle.ycor()
 
1651
        86.6025403784
 
1652
        """
 
1653
        return self._position[1]
 
1654
 
 
1655
 
 
1656
    def goto(self, x, y=None):
 
1657
        """Move turtle to an absolute position.
 
1658
 
 
1659
        Aliases: setpos | setposition | goto:
 
1660
 
 
1661
        Arguments:
 
1662
        x -- a number      or     a pair/vector of numbers
 
1663
        y -- a number             None
 
1664
 
 
1665
        call: goto(x, y)         # two coordinates
 
1666
        --or: goto((x, y))       # a pair (tuple) of coordinates
 
1667
        --or: goto(vec)          # e.g. as returned by pos()
 
1668
 
 
1669
        Move turtle to an absolute position. If the pen is down,
 
1670
        a line will be drawn. The turtle's orientation does not change.
 
1671
 
 
1672
        Example (for a Turtle instance named turtle):
 
1673
        >>> tp = turtle.pos()
 
1674
        >>> tp
 
1675
        (0.00, 0.00)
 
1676
        >>> turtle.setpos(60,30)
 
1677
        >>> turtle.pos()
 
1678
        (60.00,30.00)
 
1679
        >>> turtle.setpos((20,80))
 
1680
        >>> turtle.pos()
 
1681
        (20.00,80.00)
 
1682
        >>> turtle.setpos(tp)
 
1683
        >>> turtle.pos()
 
1684
        (0.00,0.00)
 
1685
        """
 
1686
        if y is None:
 
1687
            self._goto(Vec2D(*x))
249
1688
        else:
250
 
            try:
251
 
                r, g, b = args
252
 
            except:
253
 
                raise Error, "bad color arguments: %r" % (args,)
254
 
        assert 0 <= r <= 1
255
 
        assert 0 <= g <= 1
256
 
        assert 0 <= b <= 1
257
 
        x = 255.0
258
 
        y = 0.5
259
 
        self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y)))
260
 
 
261
 
    def _set_color(self,color):
262
 
        self._color = color
263
 
        self._draw_turtle()
264
 
 
265
 
    def write(self, text, move=False):
266
 
        """ Write text at the current pen position.
267
 
 
268
 
        If move is true, the pen is moved to the bottom-right corner
269
 
        of the text. By default, move is False.
270
 
 
271
 
        Example:
272
 
        >>> turtle.write('The race is on!')
273
 
        >>> turtle.write('Home = (0, 0)', True)
274
 
        """
275
 
        x, y  = self._position
276
 
        x = x-1 # correction -- calibrated for Windows
277
 
        item = self._canvas.create_text(x, y,
278
 
                                        text=str(text), anchor="sw",
279
 
                                        fill=self._color)
280
 
        self._items.append(item)
281
 
        if move:
282
 
            x0, y0, x1, y1 = self._canvas.bbox(item)
283
 
            self._goto(x1, y1)
284
 
        self._draw_turtle()
285
 
 
286
 
    def fill(self, flag):
287
 
        """ Call fill(1) before drawing the shape you
288
 
         want to fill, and fill(0) when done.
289
 
 
290
 
        Example:
291
 
        >>> turtle.fill(1)
292
 
        >>> turtle.forward(100)
293
 
        >>> turtle.left(90)
294
 
        >>> turtle.forward(100)
295
 
        >>> turtle.left(90)
296
 
        >>> turtle.forward(100)
297
 
        >>> turtle.left(90)
298
 
        >>> turtle.forward(100)
299
 
        >>> turtle.fill(0)
300
 
        """
301
 
        if self._filling:
302
 
            path = tuple(self._path)
303
 
            smooth = self._filling < 0
304
 
            if len(path) > 2:
305
 
                item = self._canvas._create('polygon', path,
306
 
                                            {'fill': self._color,
307
 
                                             'smooth': smooth})
308
 
                self._items.append(item)
309
 
        self._path = []
310
 
        self._filling = flag
311
 
        if flag:
312
 
            self._path.append(self._position)
313
 
 
314
 
    def begin_fill(self):
315
 
        """ Called just before drawing a shape to be filled.
316
 
            Must eventually be followed by a corresponding end_fill() call.
317
 
            Otherwise it will be ignored.
318
 
 
319
 
        Example:
320
 
        >>> turtle.begin_fill()
321
 
        >>> turtle.forward(100)
322
 
        >>> turtle.left(90)
323
 
        >>> turtle.forward(100)
324
 
        >>> turtle.left(90)
325
 
        >>> turtle.forward(100)
326
 
        >>> turtle.left(90)
327
 
        >>> turtle.forward(100)
328
 
        >>> turtle.end_fill()
329
 
        """
330
 
        self._path = [self._position]
331
 
        self._filling = 1
332
 
 
333
 
    def end_fill(self):
334
 
        """ Called after drawing a shape to be filled.
335
 
 
336
 
        Example:
337
 
        >>> turtle.begin_fill()
338
 
        >>> turtle.forward(100)
339
 
        >>> turtle.left(90)
340
 
        >>> turtle.forward(100)
341
 
        >>> turtle.left(90)
342
 
        >>> turtle.forward(100)
343
 
        >>> turtle.left(90)
344
 
        >>> turtle.forward(100)
345
 
        >>> turtle.end_fill()
346
 
        """
347
 
        self.fill(0)
348
 
 
349
 
    def circle(self, radius, extent = None):
 
1689
            self._goto(Vec2D(x, y))
 
1690
 
 
1691
    def home(self):
 
1692
        """Move turtle to the origin - coordinates (0,0).
 
1693
 
 
1694
        No arguments.
 
1695
 
 
1696
        Move turtle to the origin - coordinates (0,0) and set it's
 
1697
        heading to it's start-orientation (which depends on mode).
 
1698
 
 
1699
        Example (for a Turtle instance named turtle):
 
1700
        >>> turtle.home()
 
1701
        """
 
1702
        self.goto(0, 0)
 
1703
        self.setheading(0)
 
1704
 
 
1705
    def setx(self, x):
 
1706
        """Set the turtle's first coordinate to x
 
1707
 
 
1708
        Argument:
 
1709
        x -- a number (integer or float)
 
1710
 
 
1711
        Set the turtle's first coordinate to x, leave second coordinate
 
1712
        unchanged.
 
1713
 
 
1714
        Example (for a Turtle instance named turtle):
 
1715
        >>> turtle.position()
 
1716
        (0.00, 240.00)
 
1717
        >>> turtle.setx(10)
 
1718
        >>> turtle.position()
 
1719
        (10.00, 240.00)
 
1720
        """
 
1721
        self._goto(Vec2D(x, self._position[1]))
 
1722
 
 
1723
    def sety(self, y):
 
1724
        """Set the turtle's second coordinate to y
 
1725
 
 
1726
        Argument:
 
1727
        y -- a number (integer or float)
 
1728
 
 
1729
        Set the turtle's first coordinate to x, second coordinate remains
 
1730
        unchanged.
 
1731
 
 
1732
        Example (for a Turtle instance named turtle):
 
1733
        >>> turtle.position()
 
1734
        (0.00, 40.00)
 
1735
        >>> turtle.sety(-10)
 
1736
        >>> turtle.position()
 
1737
        (0.00, -10.00)
 
1738
        """
 
1739
        self._goto(Vec2D(self._position[0], y))
 
1740
 
 
1741
    def distance(self, x, y=None):
 
1742
        """Return the distance from the turtle to (x,y) in turtle step units.
 
1743
 
 
1744
        Arguments:
 
1745
        x -- a number   or  a pair/vector of numbers   or   a turtle instance
 
1746
        y -- a number       None                            None
 
1747
 
 
1748
        call: distance(x, y)         # two coordinates
 
1749
        --or: distance((x, y))       # a pair (tuple) of coordinates
 
1750
        --or: distance(vec)          # e.g. as returned by pos()
 
1751
        --or: distance(mypen)        # where mypen is another turtle
 
1752
 
 
1753
        Example (for a Turtle instance named turtle):
 
1754
        >>> turtle.pos()
 
1755
        (0.00, 0.00)
 
1756
        >>> turtle.distance(30,40)
 
1757
        50.0
 
1758
        >>> pen = Turtle()
 
1759
        >>> pen.forward(77)
 
1760
        >>> turtle.distance(pen)
 
1761
        77.0
 
1762
        """
 
1763
        if y is not None:
 
1764
            pos = Vec2D(x, y)
 
1765
        if isinstance(x, Vec2D):
 
1766
            pos = x
 
1767
        elif isinstance(x, tuple):
 
1768
            pos = Vec2D(*x)
 
1769
        elif isinstance(x, TNavigator):
 
1770
            pos = x._position
 
1771
        return abs(pos - self._position)
 
1772
 
 
1773
    def towards(self, x, y=None):
 
1774
        """Return the angle of the line from the turtle's position to (x, y).
 
1775
 
 
1776
        Arguments:
 
1777
        x -- a number   or  a pair/vector of numbers   or   a turtle instance
 
1778
        y -- a number       None                            None
 
1779
 
 
1780
        call: distance(x, y)         # two coordinates
 
1781
        --or: distance((x, y))       # a pair (tuple) of coordinates
 
1782
        --or: distance(vec)          # e.g. as returned by pos()
 
1783
        --or: distance(mypen)        # where mypen is another turtle
 
1784
 
 
1785
        Return the angle, between the line from turtle-position to position
 
1786
        specified by x, y and the turtle's start orientation. (Depends on
 
1787
        modes - "standard" or "logo")
 
1788
 
 
1789
        Example (for a Turtle instance named turtle):
 
1790
        >>> turtle.pos()
 
1791
        (10.00, 10.00)
 
1792
        >>> turtle.towards(0,0)
 
1793
        225.0
 
1794
        """
 
1795
        if y is not None:
 
1796
            pos = Vec2D(x, y)
 
1797
        if isinstance(x, Vec2D):
 
1798
            pos = x
 
1799
        elif isinstance(x, tuple):
 
1800
            pos = Vec2D(*x)
 
1801
        elif isinstance(x, TNavigator):
 
1802
            pos = x._position
 
1803
        x, y = pos - self._position
 
1804
        result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
 
1805
        result /= self._degreesPerAU
 
1806
        return (self._angleOffset + self._angleOrient*result) % self._fullcircle
 
1807
 
 
1808
    def heading(self):
 
1809
        """ Return the turtle's current heading.
 
1810
 
 
1811
        No arguments.
 
1812
 
 
1813
        Example (for a Turtle instance named turtle):
 
1814
        >>> turtle.left(67)
 
1815
        >>> turtle.heading()
 
1816
        67.0
 
1817
        """
 
1818
        x, y = self._orient
 
1819
        result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0
 
1820
        result /= self._degreesPerAU
 
1821
        return (self._angleOffset + self._angleOrient*result) % self._fullcircle
 
1822
 
 
1823
    def setheading(self, to_angle):
 
1824
        """Set the orientation of the turtle to to_angle.
 
1825
 
 
1826
        Aliases:  setheading | seth
 
1827
 
 
1828
        Argument:
 
1829
        to_angle -- a number (integer or float)
 
1830
 
 
1831
        Set the orientation of the turtle to to_angle.
 
1832
        Here are some common directions in degrees:
 
1833
 
 
1834
         standard - mode:          logo-mode:
 
1835
        -------------------|--------------------
 
1836
           0 - east                0 - north
 
1837
          90 - north              90 - east
 
1838
         180 - west              180 - south
 
1839
         270 - south             270 - west
 
1840
 
 
1841
        Example (for a Turtle instance named turtle):
 
1842
        >>> turtle.setheading(90)
 
1843
        >>> turtle.heading()
 
1844
        90
 
1845
        """
 
1846
        angle = (to_angle - self.heading())*self._angleOrient
 
1847
        full = self._fullcircle
 
1848
        angle = (angle+full/2.)%full - full/2.
 
1849
        self._rotate(angle)
 
1850
 
 
1851
    def circle(self, radius, extent = None, steps = None):
350
1852
        """ Draw a circle with given radius.
351
 
        The center is radius units left of the turtle; extent
352
 
        determines which part of the circle is drawn. If not given,
353
 
        the entire circle is drawn.
354
 
 
 
1853
 
 
1854
        Arguments:
 
1855
        radius -- a number
 
1856
        extent (optional) -- a number
 
1857
        steps (optional) -- an integer
 
1858
 
 
1859
        Draw a circle with given radius. The center is radius units left
 
1860
        of the turtle; extent - an angle - determines which part of the
 
1861
        circle is drawn. If extent is not given, draw the entire circle.
355
1862
        If extent is not a full circle, one endpoint of the arc is the
356
 
        current pen position. The arc is drawn in a counter clockwise
357
 
        direction if radius is positive, otherwise in a clockwise
358
 
        direction. In the process, the direction of the turtle is
359
 
        changed by the amount of the extent.
360
 
 
 
1863
        current pen position. Draw the arc in counterclockwise direction
 
1864
        if radius is positive, otherwise in clockwise direction. Finally
 
1865
        the direction of the turtle is changed by the amount of extent.
 
1866
 
 
1867
        As the circle is approximated by an inscribed regular polygon,
 
1868
        steps determines the number of steps to use. If not given,
 
1869
        it will be calculated automatically. Maybe used to draw regular
 
1870
        polygons.
 
1871
 
 
1872
        call: circle(radius)                  # full circle
 
1873
        --or: circle(radius, extent)          # arc
 
1874
        --or: circle(radius, extent, steps)
 
1875
        --or: circle(radius, steps=6)         # 6-sided polygon
 
1876
 
 
1877
        Example (for a Turtle instance named turtle):
361
1878
        >>> turtle.circle(50)
362
 
        >>> turtle.circle(120, 180)  # half a circle
 
1879
        >>> turtle.circle(120, 180)  # semicircle
363
1880
        """
 
1881
        if self.undobuffer:
 
1882
            self.undobuffer.push(["seq"])
 
1883
            self.undobuffer.cumulate = True
 
1884
        speed = self.speed()
364
1885
        if extent is None:
365
1886
            extent = self._fullcircle
366
 
        frac = abs(extent)/self._fullcircle
367
 
        steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
 
1887
        if steps is None:
 
1888
            frac = abs(extent)/self._fullcircle
 
1889
            steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac)
368
1890
        w = 1.0 * extent / steps
369
1891
        w2 = 0.5 * w
370
 
        l = 2.0 * radius * sin(w2*self._invradian)
 
1892
        l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU)
371
1893
        if radius < 0:
372
1894
            l, w, w2 = -l, -w, -w2
373
 
        self.left(w2)
 
1895
        tr = self.tracer()
 
1896
        dl = self._delay()
 
1897
        if speed == 0:
 
1898
            self.tracer(0, 0)
 
1899
        else:
 
1900
            self.speed(0)
 
1901
        self._rotate(w2)
374
1902
        for i in range(steps):
375
 
            self.forward(l)
376
 
            self.left(w)
377
 
        self.right(w2)
378
 
 
379
 
    def heading(self):
380
 
        """ Return the turtle's current heading.
381
 
 
382
 
        Example:
383
 
        >>> turtle.heading()
384
 
        67.0
385
 
        """
386
 
        return self._angle
387
 
 
388
 
    def setheading(self, angle):
389
 
        """ Set the turtle facing the given angle.
390
 
 
391
 
        Here are some common directions in degrees:
392
 
 
393
 
           0 - east
394
 
          90 - north
395
 
         180 - west
396
 
         270 - south
397
 
 
398
 
        Example:
399
 
        >>> turtle.setheading(90)
400
 
        >>> turtle.heading()
401
 
        90
402
 
        >>> turtle.setheading(128)
403
 
        >>> turtle.heading()
404
 
        128
405
 
        """
406
 
        self._angle = angle
407
 
        self._draw_turtle()
 
1903
            self.speed(speed)
 
1904
            self._go(l)
 
1905
            self.speed(0)
 
1906
            self._rotate(w)
 
1907
        self._rotate(-w2)
 
1908
        if speed == 0:
 
1909
            self.tracer(tr, dl)
 
1910
        self.speed(speed)
 
1911
        if self.undobuffer:
 
1912
            self.undobuffer.cumulate = False
 
1913
 
 
1914
## three dummy methods to be implemented by child class:
 
1915
 
 
1916
    def speed(self, s=0):
 
1917
        """dummy method - to be overwritten by child class"""
 
1918
    def tracer(self, a=None, b=None):
 
1919
        """dummy method - to be overwritten by child class"""
 
1920
    def _delay(self, n=None):
 
1921
        """dummy method - to be overwritten by child class"""
 
1922
 
 
1923
    fd = forward
 
1924
    bk = back
 
1925
    backward = back
 
1926
    rt = right
 
1927
    lt = left
 
1928
    position = pos
 
1929
    setpos = goto
 
1930
    setposition = goto
 
1931
    seth = setheading
 
1932
 
 
1933
 
 
1934
class TPen(object):
 
1935
    """Drawing part of the RawTurtle.
 
1936
    Implements drawing properties.
 
1937
    """
 
1938
    def __init__(self, resizemode=_CFG["resizemode"]):
 
1939
        self._resizemode = resizemode # or "user" or "noresize"
 
1940
        self.undobuffer = None
 
1941
        TPen._reset(self)
 
1942
 
 
1943
    def _reset(self, pencolor=_CFG["pencolor"],
 
1944
                     fillcolor=_CFG["fillcolor"]):
 
1945
        self._pensize = 1
 
1946
        self._shown = True
 
1947
        self._pencolor = pencolor
 
1948
        self._fillcolor = fillcolor
 
1949
        self._drawing = True
 
1950
        self._speed = 3
 
1951
        self._stretchfactor = (1, 1)
 
1952
        self._tilt = 0
 
1953
        self._outlinewidth = 1
 
1954
        ### self.screen = None  # to override by child class
 
1955
 
 
1956
    def resizemode(self, rmode=None):
 
1957
        """Set resizemode to one of the values: "auto", "user", "noresize".
 
1958
 
 
1959
        (Optional) Argument:
 
1960
        rmode -- one of the strings "auto", "user", "noresize"
 
1961
 
 
1962
        Different resizemodes have the following effects:
 
1963
          - "auto" adapts the appearance of the turtle
 
1964
                   corresponding to the value of pensize.
 
1965
          - "user" adapts the appearance of the turtle according to the
 
1966
                   values of stretchfactor and outlinewidth (outline),
 
1967
                   which are set by shapesize()
 
1968
          - "noresize" no adaption of the turtle's appearance takes place.
 
1969
        If no argument is given, return current resizemode.
 
1970
        resizemode("user") is called by a call of shapesize with arguments.
 
1971
 
 
1972
 
 
1973
        Examples (for a Turtle instance named turtle):
 
1974
        >>> turtle.resizemode("noresize")
 
1975
        >>> turtle.resizemode()
 
1976
        'noresize'
 
1977
        """
 
1978
        if rmode is None:
 
1979
            return self._resizemode
 
1980
        rmode = rmode.lower()
 
1981
        if rmode in ["auto", "user", "noresize"]:
 
1982
            self.pen(resizemode=rmode)
 
1983
 
 
1984
    def pensize(self, width=None):
 
1985
        """Set or return the line thickness.
 
1986
 
 
1987
        Aliases:  pensize | width
 
1988
 
 
1989
        Argument:
 
1990
        width -- positive number
 
1991
 
 
1992
        Set the line thickness to width or return it. If resizemode is set
 
1993
        to "auto" and turtleshape is a polygon, that polygon is drawn with
 
1994
        the same line thickness. If no argument is given, current pensize
 
1995
        is returned.
 
1996
 
 
1997
        Example (for a Turtle instance named turtle):
 
1998
        >>> turtle.pensize()
 
1999
        1
 
2000
        turtle.pensize(10)   # from here on lines of width 10 are drawn
 
2001
        """
 
2002
        if width is None:
 
2003
            return self._pensize
 
2004
        self.pen(pensize=width)
 
2005
 
 
2006
 
 
2007
    def penup(self):
 
2008
        """Pull the pen up -- no drawing when moving.
 
2009
 
 
2010
        Aliases: penup | pu | up
 
2011
 
 
2012
        No argument
 
2013
 
 
2014
        Example (for a Turtle instance named turtle):
 
2015
        >>> turtle.penup()
 
2016
        """
 
2017
        if not self._drawing:
 
2018
            return
 
2019
        self.pen(pendown=False)
 
2020
 
 
2021
    def pendown(self):
 
2022
        """Pull the pen down -- drawing when moving.
 
2023
 
 
2024
        Aliases: pendown | pd | down
 
2025
 
 
2026
        No argument.
 
2027
 
 
2028
        Example (for a Turtle instance named turtle):
 
2029
        >>> turtle.pendown()
 
2030
        """
 
2031
        if self._drawing:
 
2032
            return
 
2033
        self.pen(pendown=True)
 
2034
 
 
2035
    def isdown(self):
 
2036
        """Return True if pen is down, False if it's up.
 
2037
 
 
2038
        No argument.
 
2039
 
 
2040
        Example (for a Turtle instance named turtle):
 
2041
        >>> turtle.penup()
 
2042
        >>> turtle.isdown()
 
2043
        False
 
2044
        >>> turtle.pendown()
 
2045
        >>> turtle.isdown()
 
2046
        True
 
2047
        """
 
2048
        return self._drawing
 
2049
 
 
2050
    def speed(self, speed=None):
 
2051
        """ Return or set the turtle's speed.
 
2052
 
 
2053
        Optional argument:
 
2054
        speed -- an integer in the range 0..10 or a speedstring (see below)
 
2055
 
 
2056
        Set the turtle's speed to an integer value in the range 0 .. 10.
 
2057
        If no argument is given: return current speed.
 
2058
 
 
2059
        If input is a number greater than 10 or smaller than 0.5,
 
2060
        speed is set to 0.
 
2061
        Speedstrings  are mapped to speedvalues in the following way:
 
2062
            'fastest' :  0
 
2063
            'fast'    :  10
 
2064
            'normal'  :  6
 
2065
            'slow'    :  3
 
2066
            'slowest' :  1
 
2067
        speeds from 1 to 10 enforce increasingly faster animation of
 
2068
        line drawing and turtle turning.
 
2069
 
 
2070
        Attention:
 
2071
        speed = 0 : *no* animation takes place. forward/back makes turtle jump
 
2072
        and likewise left/right make the turtle turn instantly.
 
2073
 
 
2074
        Example (for a Turtle instance named turtle):
 
2075
        >>> turtle.speed(3)
 
2076
        """
 
2077
        speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 }
 
2078
        if speed is None:
 
2079
            return self._speed
 
2080
        if speed in speeds:
 
2081
            speed = speeds[speed]
 
2082
        elif 0.5 < speed < 10.5:
 
2083
            speed = int(round(speed))
 
2084
        else:
 
2085
            speed = 0
 
2086
        self.pen(speed=speed)
 
2087
 
 
2088
    def color(self, *args):
 
2089
        """Return or set the pencolor and fillcolor.
 
2090
 
 
2091
        Arguments:
 
2092
        Several input formats are allowed.
 
2093
        They use 0, 1, 2, or 3 arguments as follows:
 
2094
 
 
2095
        color()
 
2096
            Return the current pencolor and the current fillcolor
 
2097
            as a pair of color specification strings as are returned
 
2098
            by pencolor and fillcolor.
 
2099
        color(colorstring), color((r,g,b)), color(r,g,b)
 
2100
            inputs as in pencolor, set both, fillcolor and pencolor,
 
2101
            to the given value.
 
2102
        color(colorstring1, colorstring2),
 
2103
        color((r1,g1,b1), (r2,g2,b2))
 
2104
            equivalent to pencolor(colorstring1) and fillcolor(colorstring2)
 
2105
            and analogously, if the other input format is used.
 
2106
 
 
2107
        If turtleshape is a polygon, outline and interior of that polygon
 
2108
        is drawn with the newly set colors.
 
2109
        For mor info see: pencolor, fillcolor
 
2110
 
 
2111
        Example (for a Turtle instance named turtle):
 
2112
        >>> turtle.color('red', 'green')
 
2113
        >>> turtle.color()
 
2114
        ('red', 'green')
 
2115
        >>> colormode(255)
 
2116
        >>> color((40, 80, 120), (160, 200, 240))
 
2117
        >>> color()
 
2118
        ('#285078', '#a0c8f0')
 
2119
        """
 
2120
        if args:
 
2121
            l = len(args)
 
2122
            if l == 1:
 
2123
                pcolor = fcolor = args[0]
 
2124
            elif l == 2:
 
2125
                pcolor, fcolor = args
 
2126
            elif l == 3:
 
2127
                pcolor = fcolor = args
 
2128
            pcolor = self._colorstr(pcolor)
 
2129
            fcolor = self._colorstr(fcolor)
 
2130
            self.pen(pencolor=pcolor, fillcolor=fcolor)
 
2131
        else:
 
2132
            return self._color(self._pencolor), self._color(self._fillcolor)
 
2133
 
 
2134
    def pencolor(self, *args):
 
2135
        """ Return or set the pencolor.
 
2136
 
 
2137
        Arguments:
 
2138
        Four input formats are allowed:
 
2139
          - pencolor()
 
2140
            Return the current pencolor as color specification string,
 
2141
            possibly in hex-number format (see example).
 
2142
            May be used as input to another color/pencolor/fillcolor call.
 
2143
          - pencolor(colorstring)
 
2144
            s is a Tk color specification string, such as "red" or "yellow"
 
2145
          - pencolor((r, g, b))
 
2146
            *a tuple* of r, g, and b, which represent, an RGB color,
 
2147
            and each of r, g, and b are in the range 0..colormode,
 
2148
            where colormode is either 1.0 or 255
 
2149
          - pencolor(r, g, b)
 
2150
            r, g, and b represent an RGB color, and each of r, g, and b
 
2151
            are in the range 0..colormode
 
2152
 
 
2153
        If turtleshape is a polygon, the outline of that polygon is drawn
 
2154
        with the newly set pencolor.
 
2155
 
 
2156
        Example (for a Turtle instance named turtle):
 
2157
        >>> turtle.pencolor('brown')
 
2158
        >>> tup = (0.2, 0.8, 0.55)
 
2159
        >>> turtle.pencolor(tup)
 
2160
        >>> turtle.pencolor()
 
2161
        '#33cc8c'
 
2162
        """
 
2163
        if args:
 
2164
            color = self._colorstr(args)
 
2165
            if color == self._pencolor:
 
2166
                return
 
2167
            self.pen(pencolor=color)
 
2168
        else:
 
2169
            return self._color(self._pencolor)
 
2170
 
 
2171
    def fillcolor(self, *args):
 
2172
        """ Return or set the fillcolor.
 
2173
 
 
2174
        Arguments:
 
2175
        Four input formats are allowed:
 
2176
          - fillcolor()
 
2177
            Return the current fillcolor as color specification string,
 
2178
            possibly in hex-number format (see example).
 
2179
            May be used as input to another color/pencolor/fillcolor call.
 
2180
          - fillcolor(colorstring)
 
2181
            s is a Tk color specification string, such as "red" or "yellow"
 
2182
          - fillcolor((r, g, b))
 
2183
            *a tuple* of r, g, and b, which represent, an RGB color,
 
2184
            and each of r, g, and b are in the range 0..colormode,
 
2185
            where colormode is either 1.0 or 255
 
2186
          - fillcolor(r, g, b)
 
2187
            r, g, and b represent an RGB color, and each of r, g, and b
 
2188
            are in the range 0..colormode
 
2189
 
 
2190
        If turtleshape is a polygon, the interior of that polygon is drawn
 
2191
        with the newly set fillcolor.
 
2192
 
 
2193
        Example (for a Turtle instance named turtle):
 
2194
        >>> turtle.fillcolor('violet')
 
2195
        >>> col = turtle.pencolor()
 
2196
        >>> turtle.fillcolor(col)
 
2197
        >>> turtle.fillcolor(0, .5, 0)
 
2198
        """
 
2199
        if args:
 
2200
            color = self._colorstr(args)
 
2201
            if color == self._fillcolor:
 
2202
                return
 
2203
            self.pen(fillcolor=color)
 
2204
        else:
 
2205
            return self._color(self._fillcolor)
 
2206
 
 
2207
    def showturtle(self):
 
2208
        """Makes the turtle visible.
 
2209
 
 
2210
        Aliases: showturtle | st
 
2211
 
 
2212
        No argument.
 
2213
 
 
2214
        Example (for a Turtle instance named turtle):
 
2215
        >>> turtle.hideturtle()
 
2216
        >>> turtle.showturtle()
 
2217
        """
 
2218
        self.pen(shown=True)
 
2219
 
 
2220
    def hideturtle(self):
 
2221
        """Makes the turtle invisible.
 
2222
 
 
2223
        Aliases: hideturtle | ht
 
2224
 
 
2225
        No argument.
 
2226
 
 
2227
        It's a good idea to do this while you're in the
 
2228
        middle of a complicated drawing, because hiding
 
2229
        the turtle speeds up the drawing observably.
 
2230
 
 
2231
        Example (for a Turtle instance named turtle):
 
2232
        >>> turtle.hideturtle()
 
2233
        """
 
2234
        self.pen(shown=False)
 
2235
 
 
2236
    def isvisible(self):
 
2237
        """Return True if the Turtle is shown, False if it's hidden.
 
2238
 
 
2239
        No argument.
 
2240
 
 
2241
        Example (for a Turtle instance named turtle):
 
2242
        >>> turtle.hideturtle()
 
2243
        >>> print turtle.isvisible():
 
2244
        False
 
2245
        """
 
2246
        return self._shown
 
2247
 
 
2248
    def pen(self, pen=None, **pendict):
 
2249
        """Return or set the pen's attributes.
 
2250
 
 
2251
        Arguments:
 
2252
            pen -- a dictionary with some or all of the below listed keys.
 
2253
            **pendict -- one or more keyword-arguments with the below
 
2254
                         listed keys as keywords.
 
2255
 
 
2256
        Return or set the pen's attributes in a 'pen-dictionary'
 
2257
        with the following key/value pairs:
 
2258
           "shown"      :   True/False
 
2259
           "pendown"    :   True/False
 
2260
           "pencolor"   :   color-string or color-tuple
 
2261
           "fillcolor"  :   color-string or color-tuple
 
2262
           "pensize"    :   positive number
 
2263
           "speed"      :   number in range 0..10
 
2264
           "resizemode" :   "auto" or "user" or "noresize"
 
2265
           "stretchfactor": (positive number, positive number)
 
2266
           "outline"    :   positive number
 
2267
           "tilt"       :   number
 
2268
 
 
2269
        This dicionary can be used as argument for a subsequent
 
2270
        pen()-call to restore the former pen-state. Moreover one
 
2271
        or more of these attributes can be provided as keyword-arguments.
 
2272
        This can be used to set several pen attributes in one statement.
 
2273
 
 
2274
 
 
2275
        Examples (for a Turtle instance named turtle):
 
2276
        >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
 
2277
        >>> turtle.pen()
 
2278
        {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
 
2279
        'pencolor': 'red', 'pendown': True, 'fillcolor': 'black',
 
2280
        'stretchfactor': (1,1), 'speed': 3}
 
2281
        >>> penstate=turtle.pen()
 
2282
        >>> turtle.color("yellow","")
 
2283
        >>> turtle.penup()
 
2284
        >>> turtle.pen()
 
2285
        {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
 
2286
        'pencolor': 'yellow', 'pendown': False, 'fillcolor': '',
 
2287
        'stretchfactor': (1,1), 'speed': 3}
 
2288
        >>> p.pen(penstate, fillcolor="green")
 
2289
        >>> p.pen()
 
2290
        {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1,
 
2291
        'pencolor': 'red', 'pendown': True, 'fillcolor': 'green',
 
2292
        'stretchfactor': (1,1), 'speed': 3}
 
2293
        """
 
2294
        _pd =  {"shown"         : self._shown,
 
2295
                "pendown"       : self._drawing,
 
2296
                "pencolor"      : self._pencolor,
 
2297
                "fillcolor"     : self._fillcolor,
 
2298
                "pensize"       : self._pensize,
 
2299
                "speed"         : self._speed,
 
2300
                "resizemode"    : self._resizemode,
 
2301
                "stretchfactor" : self._stretchfactor,
 
2302
                "outline"       : self._outlinewidth,
 
2303
                "tilt"          : self._tilt
 
2304
               }
 
2305
 
 
2306
        if not (pen or pendict):
 
2307
            return _pd
 
2308
 
 
2309
        if isinstance(pen, dict):
 
2310
            p = pen
 
2311
        else:
 
2312
            p = {}
 
2313
        p.update(pendict)
 
2314
 
 
2315
        _p_buf = {}
 
2316
        for key in p:
 
2317
            _p_buf[key] = _pd[key]
 
2318
 
 
2319
        if self.undobuffer:
 
2320
            self.undobuffer.push(("pen", _p_buf))
 
2321
 
 
2322
        newLine = False
 
2323
        if "pendown" in p:
 
2324
            if self._drawing != p["pendown"]:
 
2325
                newLine = True
 
2326
        if "pencolor" in p:
 
2327
            if isinstance(p["pencolor"], tuple):
 
2328
                p["pencolor"] = self._colorstr((p["pencolor"],))
 
2329
            if self._pencolor != p["pencolor"]:
 
2330
                newLine = True
 
2331
        if "pensize" in p:
 
2332
            if self._pensize != p["pensize"]:
 
2333
                newLine = True
 
2334
        if newLine:
 
2335
            self._newLine()
 
2336
        if "pendown" in p:
 
2337
            self._drawing = p["pendown"]
 
2338
        if "pencolor" in p:
 
2339
            self._pencolor = p["pencolor"]
 
2340
        if "pensize" in p:
 
2341
            self._pensize = p["pensize"]
 
2342
        if "fillcolor" in p:
 
2343
            if isinstance(p["fillcolor"], tuple):
 
2344
                p["fillcolor"] = self._colorstr((p["fillcolor"],))
 
2345
            self._fillcolor = p["fillcolor"]
 
2346
        if "speed" in p:
 
2347
            self._speed = p["speed"]
 
2348
        if "resizemode" in p:
 
2349
            self._resizemode = p["resizemode"]
 
2350
        if "stretchfactor" in p:
 
2351
            sf = p["stretchfactor"]
 
2352
            if isinstance(sf, (int, float)):
 
2353
                sf = (sf, sf)
 
2354
            self._stretchfactor = sf
 
2355
        if "outline" in p:
 
2356
            self._outlinewidth = p["outline"]
 
2357
        if "shown" in p:
 
2358
            self._shown = p["shown"]
 
2359
        if "tilt" in p:
 
2360
            self._tilt = p["tilt"]
 
2361
        self._update()
 
2362
 
 
2363
## three dummy methods to be implemented by child class:
 
2364
 
 
2365
    def _newLine(self, usePos = True):
 
2366
        """dummy method - to be overwritten by child class"""
 
2367
    def _update(self, count=True, forced=False):
 
2368
        """dummy method - to be overwritten by child class"""
 
2369
    def _color(self, args):
 
2370
        """dummy method - to be overwritten by child class"""
 
2371
    def _colorstr(self, args):
 
2372
        """dummy method - to be overwritten by child class"""
 
2373
 
 
2374
    width = pensize
 
2375
    up = penup
 
2376
    pu = penup
 
2377
    pd = pendown
 
2378
    down = pendown
 
2379
    st = showturtle
 
2380
    ht = hideturtle
 
2381
 
 
2382
 
 
2383
class _TurtleImage(object):
 
2384
    """Helper class: Datatype to store Turtle attributes
 
2385
    """
 
2386
 
 
2387
    def __init__(self, screen, shapeIndex):
 
2388
        self.screen = screen
 
2389
        self._type = None
 
2390
        self._setshape(shapeIndex)
 
2391
 
 
2392
    def _setshape(self, shapeIndex):
 
2393
        screen = self.screen # RawTurtle.screens[self.screenIndex]
 
2394
        self.shapeIndex = shapeIndex
 
2395
        if self._type == "polygon" == screen._shapes[shapeIndex]._type:
 
2396
            return
 
2397
        if self._type == "image" == screen._shapes[shapeIndex]._type:
 
2398
            return
 
2399
        if self._type in ["image", "polygon"]:
 
2400
            screen._delete(self._item)
 
2401
        elif self._type == "compound":
 
2402
            for item in self._item:
 
2403
                screen._delete(item)
 
2404
        self._type = screen._shapes[shapeIndex]._type
 
2405
        if self._type == "polygon":
 
2406
            self._item = screen._createpoly()
 
2407
        elif self._type == "image":
 
2408
            self._item = screen._createimage(screen._shapes["blank"]._data)
 
2409
        elif self._type == "compound":
 
2410
            self._item = [screen._createpoly() for item in
 
2411
                                          screen._shapes[shapeIndex]._data]
 
2412
 
 
2413
 
 
2414
class RawTurtle(TPen, TNavigator):
 
2415
    """Animation part of the RawTurtle.
 
2416
    Puts RawTurtle upon a TurtleScreen and provides tools for
 
2417
    it's animation.
 
2418
    """
 
2419
    screens = []
 
2420
 
 
2421
    def __init__(self, canvas=None,
 
2422
                 shape=_CFG["shape"],
 
2423
                 undobuffersize=_CFG["undobuffersize"],
 
2424
                 visible=_CFG["visible"]):
 
2425
        if isinstance(canvas, Screen):
 
2426
            self.screen = canvas
 
2427
        elif isinstance(canvas, TurtleScreen):
 
2428
            if canvas not in RawTurtle.screens:
 
2429
                RawTurtle.screens.append(canvas)
 
2430
            self.screen = canvas
 
2431
        elif isinstance(canvas, (ScrolledCanvas, Canvas)):
 
2432
            for screen in RawTurtle.screens:
 
2433
                if screen.cv == canvas:
 
2434
                    self.screen = screen
 
2435
                    break
 
2436
            else:
 
2437
                self.screen = TurtleScreen(canvas)
 
2438
                RawTurtle.screens.append(self.screen)
 
2439
        else:
 
2440
            raise TurtleGraphicsError("bad cavas argument %s" % canvas)
 
2441
 
 
2442
        screen = self.screen
 
2443
        TNavigator.__init__(self, screen.mode())
 
2444
        TPen.__init__(self)
 
2445
        screen._turtles.append(self)
 
2446
        self.drawingLineItem = screen._createline()
 
2447
        self.turtle = _TurtleImage(screen, shape)
 
2448
        self._poly = None
 
2449
        self._creatingPoly = False
 
2450
        self._fillitem = self._fillpath = None
 
2451
        self._shown = visible
 
2452
        self._hidden_from_screen = False
 
2453
        self.currentLineItem = screen._createline()
 
2454
        self.currentLine = [self._position]
 
2455
        self.items = [self.currentLineItem]
 
2456
        self.stampItems = []
 
2457
        self._undobuffersize = undobuffersize
 
2458
        self.undobuffer = Tbuffer(undobuffersize)
 
2459
        self._update()
 
2460
 
 
2461
    def reset(self):
 
2462
        """Delete the turtle's drawings and restore it's default values.
 
2463
 
 
2464
        No argument.
 
2465
,
 
2466
        Delete the turtle's drawings from the screen, re-center the turtle
 
2467
        and set variables to the default values.
 
2468
 
 
2469
        Example (for a Turtle instance named turtle):
 
2470
        >>> turtle.position()
 
2471
        (0.00,-22.00)
 
2472
        >>> turtle.heading()
 
2473
        100.0
 
2474
        >>> turtle.reset()
 
2475
        >>> turtle.position()
 
2476
        (0.00,0.00)
 
2477
        >>> turtle.heading()
 
2478
        0.0
 
2479
        """
 
2480
        TNavigator.reset(self)
 
2481
        TPen._reset(self)
 
2482
        self._clear()
 
2483
        self._drawturtle()
 
2484
        self._update()
 
2485
 
 
2486
    def setundobuffer(self, size):
 
2487
        """Set or disable undobuffer.
 
2488
 
 
2489
        Argument:
 
2490
        size -- an integer or None
 
2491
 
 
2492
        If size is an integer an empty undobuffer of given size is installed.
 
2493
        Size gives the maximum number of turtle-actions that can be undone
 
2494
        by the undo() function.
 
2495
        If size is None, no undobuffer is present.
 
2496
 
 
2497
        Example (for a Turtle instance named turtle):
 
2498
        >>> turtle.setundobuffer(42)
 
2499
        """
 
2500
        if size is None:
 
2501
            self.undobuffer = None
 
2502
        else:
 
2503
            self.undobuffer = Tbuffer(size)
 
2504
 
 
2505
    def undobufferentries(self):
 
2506
        """Return count of entries in the undobuffer.
 
2507
 
 
2508
        No argument.
 
2509
 
 
2510
        Example (for a Turtle instance named turtle):
 
2511
        >>> while undobufferentries():
 
2512
                undo()
 
2513
        """
 
2514
        if self.undobuffer is None:
 
2515
            return 0
 
2516
        return self.undobuffer.nr_of_items()
 
2517
 
 
2518
    def _clear(self):
 
2519
        """Delete all of pen's drawings"""
 
2520
        self._fillitem = self._fillpath = None
 
2521
        for item in self.items:
 
2522
            self.screen._delete(item)
 
2523
        self.currentLineItem = self.screen._createline()
 
2524
        self.currentLine = []
 
2525
        if self._drawing:
 
2526
            self.currentLine.append(self._position)
 
2527
        self.items = [self.currentLineItem]
 
2528
        self.clearstamps()
 
2529
        self.setundobuffer(self._undobuffersize)
 
2530
 
 
2531
 
 
2532
    def clear(self):
 
2533
        """Delete the turtle's drawings from the screen. Do not move turtle.
 
2534
 
 
2535
        No arguments.
 
2536
 
 
2537
        Delete the turtle's drawings from the screen. Do not move turtle.
 
2538
        State and position of the turtle as well as drawings of other
 
2539
        turtles are not affected.
 
2540
 
 
2541
        Examples (for a Turtle instance named turtle):
 
2542
        >>> turtle.clear()
 
2543
        """
 
2544
        self._clear()
 
2545
        self._update()
 
2546
 
 
2547
    def _update_data(self):
 
2548
        self.screen._incrementudc()
 
2549
        if self.screen._updatecounter != 0:
 
2550
            return
 
2551
        if len(self.currentLine)>1:
 
2552
            self.screen._drawline(self.currentLineItem, self.currentLine,
 
2553
                                  self._pencolor, self._pensize)
 
2554
 
 
2555
    def _update(self):
 
2556
        """Perform a Turtle-data update.
 
2557
        """
 
2558
        screen = self.screen
 
2559
        if screen._tracing == 0:
 
2560
            return
 
2561
        elif screen._tracing == 1:
 
2562
            self._update_data()
 
2563
            self._drawturtle()
 
2564
            screen._update()                  # TurtleScreenBase
 
2565
            screen._delay(screen._delayvalue) # TurtleScreenBase
 
2566
        else:
 
2567
            self._update_data()
 
2568
            if screen._updatecounter == 0:
 
2569
                for t in screen.turtles():
 
2570
                    t._drawturtle()
 
2571
                screen._update()
 
2572
 
 
2573
    def tracer(self, flag=None, delay=None):
 
2574
        """Turns turtle animation on/off and set delay for update drawings.
 
2575
 
 
2576
        Optional arguments:
 
2577
        n -- nonnegative  integer
 
2578
        delay -- nonnegative  integer
 
2579
 
 
2580
        If n is given, only each n-th regular screen update is really performed.
 
2581
        (Can be used to accelerate the drawing of complex graphics.)
 
2582
        Second arguments sets delay value (see RawTurtle.delay())
 
2583
 
 
2584
        Example (for a Turtle instance named turtle):
 
2585
        >>> turtle.tracer(8, 25)
 
2586
        >>> dist = 2
 
2587
        >>> for i in range(200):
 
2588
                turtle.fd(dist)
 
2589
                turtle.rt(90)
 
2590
                dist += 2
 
2591
        """
 
2592
        return self.screen.tracer(flag, delay)
 
2593
 
 
2594
    def _color(self, args):
 
2595
        return self.screen._color(args)
 
2596
 
 
2597
    def _colorstr(self, args):
 
2598
        return self.screen._colorstr(args)
 
2599
 
 
2600
    def _cc(self, args):
 
2601
        """Convert colortriples to hexstrings.
 
2602
        """
 
2603
        if isinstance(args, str):
 
2604
            return args
 
2605
        try:
 
2606
            r, g, b = args
 
2607
        except:
 
2608
            raise TurtleGraphicsError("bad color arguments: %s" % str(args))
 
2609
        if self.screen._colormode == 1.0:
 
2610
            r, g, b = [round(255.0*x) for x in (r, g, b)]
 
2611
        if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)):
 
2612
            raise TurtleGraphicsError("bad color sequence: %s" % str(args))
 
2613
        return "#%02x%02x%02x" % (r, g, b)
 
2614
 
 
2615
    def clone(self):
 
2616
        """Create and return a clone of the turtle.
 
2617
 
 
2618
        No argument.
 
2619
 
 
2620
        Create and return a clone of the turtle with same position, heading
 
2621
        and turtle properties.
 
2622
 
 
2623
        Example (for a Turtle instance named mick):
 
2624
        mick = Turtle()
 
2625
        joe = mick.clone()
 
2626
        """
 
2627
        screen = self.screen
 
2628
        self._newLine(self._drawing)
 
2629
 
 
2630
        turtle = self.turtle
 
2631
        self.screen = None
 
2632
        self.turtle = None  # too make self deepcopy-able
 
2633
 
 
2634
        q = deepcopy(self)
 
2635
 
 
2636
        self.screen = screen
 
2637
        self.turtle = turtle
 
2638
 
 
2639
        q.screen = screen
 
2640
        q.turtle = _TurtleImage(screen, self.turtle.shapeIndex)
 
2641
 
 
2642
        screen._turtles.append(q)
 
2643
        ttype = screen._shapes[self.turtle.shapeIndex]._type
 
2644
        if ttype == "polygon":
 
2645
            q.turtle._item = screen._createpoly()
 
2646
        elif ttype == "image":
 
2647
            q.turtle._item = screen._createimage(screen._shapes["blank"]._data)
 
2648
        elif ttype == "compound":
 
2649
            q.turtle._item = [screen._createpoly() for item in
 
2650
                              screen._shapes[self.turtle.shapeIndex]._data]
 
2651
        q.currentLineItem = screen._createline()
 
2652
        q._update()
 
2653
        return q
 
2654
 
 
2655
    def shape(self, name=None):
 
2656
        """Set turtle shape to shape with given name / return current shapename.
 
2657
 
 
2658
        Optional argument:
 
2659
        name -- a string, which is a valid shapename
 
2660
 
 
2661
        Set turtle shape to shape with given name or, if name is not given,
 
2662
        return name of current shape.
 
2663
        Shape with name must exist in the TurtleScreen's shape dictionary.
 
2664
        Initially there are the following polygon shapes:
 
2665
        'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'.
 
2666
        To learn about how to deal with shapes see Screen-method register_shape.
 
2667
 
 
2668
        Example (for a Turtle instance named turtle):
 
2669
        >>> turtle.shape()
 
2670
        'arrow'
 
2671
        >>> turtle.shape("turtle")
 
2672
        >>> turtle.shape()
 
2673
        'turtle'
 
2674
        """
 
2675
        if name is None:
 
2676
            return self.turtle.shapeIndex
 
2677
        if not name in self.screen.getshapes():
 
2678
            raise TurtleGraphicsError("There is no shape named %s" % name)
 
2679
        self.turtle._setshape(name)
 
2680
        self._update()
 
2681
 
 
2682
    def shapesize(self, stretch_wid=None, stretch_len=None, outline=None):
 
2683
        """Set/return turtle's stretchfactors/outline. Set resizemode to "user".
 
2684
 
 
2685
        Optinonal arguments:
 
2686
           stretch_wid : positive number
 
2687
           stretch_len : positive number
 
2688
           outline  : positive number
 
2689
 
 
2690
        Return or set the pen's attributes x/y-stretchfactors and/or outline.
 
2691
        Set resizemode to "user".
 
2692
        If and only if resizemode is set to "user", the turtle will be displayed
 
2693
        stretched according to its stretchfactors:
 
2694
        stretch_wid is stretchfactor perpendicular to orientation
 
2695
        stretch_len is stretchfactor in direction of turtles orientation.
 
2696
        outline determines the width of the shapes's outline.
 
2697
 
 
2698
        Examples (for a Turtle instance named turtle):
 
2699
        >>> turtle.resizemode("user")
 
2700
        >>> turtle.shapesize(5, 5, 12)
 
2701
        >>> turtle.shapesize(outline=8)
 
2702
        """
 
2703
        if stretch_wid is None and stretch_len is None and outline == None:
 
2704
            stretch_wid, stretch_len = self._stretchfactor
 
2705
            return stretch_wid, stretch_len, self._outlinewidth
 
2706
        if stretch_wid is not None:
 
2707
            if stretch_len is None:
 
2708
                stretchfactor = stretch_wid, stretch_wid
 
2709
            else:
 
2710
                stretchfactor = stretch_wid, stretch_len
 
2711
        elif stretch_len is not None:
 
2712
            stretchfactor = self._stretchfactor[0], stretch_len
 
2713
        else:
 
2714
            stretchfactor = self._stretchfactor
 
2715
        if outline is None:
 
2716
            outline = self._outlinewidth
 
2717
        self.pen(resizemode="user",
 
2718
                 stretchfactor=stretchfactor, outline=outline)
 
2719
 
 
2720
    def settiltangle(self, angle):
 
2721
        """Rotate the turtleshape to point in the specified direction
 
2722
 
 
2723
        Optional argument:
 
2724
        angle -- number
 
2725
 
 
2726
        Rotate the turtleshape to point in the direction specified by angle,
 
2727
        regardless of its current tilt-angle. DO NOT change the turtle's
 
2728
        heading (direction of movement).
 
2729
 
 
2730
 
 
2731
        Examples (for a Turtle instance named turtle):
 
2732
        >>> turtle.shape("circle")
 
2733
        >>> turtle.shapesize(5,2)
 
2734
        >>> turtle.settiltangle(45)
 
2735
        >>> stamp()
 
2736
        >>> turtle.fd(50)
 
2737
        >>> turtle.settiltangle(-45)
 
2738
        >>> stamp()
 
2739
        >>> turtle.fd(50)
 
2740
        """
 
2741
        tilt = -angle * self._degreesPerAU * self._angleOrient
 
2742
        tilt = (tilt * math.pi / 180.0) % (2*math.pi)
 
2743
        self.pen(resizemode="user", tilt=tilt)
 
2744
 
 
2745
    def tiltangle(self):
 
2746
        """Return the current tilt-angle.
 
2747
 
 
2748
        No argument.
 
2749
 
 
2750
        Return the current tilt-angle, i. e. the angle between the
 
2751
        orientation of the turtleshape and the heading of the turtle
 
2752
        (it's direction of movement).
 
2753
 
 
2754
        Examples (for a Turtle instance named turtle):
 
2755
        >>> turtle.shape("circle")
 
2756
        >>> turtle.shapesize(5,2)
 
2757
        >>> turtle.tilt(45)
 
2758
        >>> turtle.tiltangle()
 
2759
        >>>
 
2760
        """
 
2761
        tilt = -self._tilt * (180.0/math.pi) * self._angleOrient
 
2762
        return (tilt / self._degreesPerAU) % self._fullcircle
 
2763
 
 
2764
    def tilt(self, angle):
 
2765
        """Rotate the turtleshape by angle.
 
2766
 
 
2767
        Argument:
 
2768
        angle - a number
 
2769
 
 
2770
        Rotate the turtleshape by angle from its current tilt-angle,
 
2771
        but do NOT change the turtle's heading (direction of movement).
 
2772
 
 
2773
        Examples (for a Turtle instance named turtle):
 
2774
        >>> turtle.shape("circle")
 
2775
        >>> turtle.shapesize(5,2)
 
2776
        >>> turtle.tilt(30)
 
2777
        >>> turtle.fd(50)
 
2778
        >>> turtle.tilt(30)
 
2779
        >>> turtle.fd(50)
 
2780
        """
 
2781
        self.settiltangle(angle + self.tiltangle())
 
2782
 
 
2783
    def _polytrafo(self, poly):
 
2784
        """Computes transformed polygon shapes from a shape
 
2785
        according to current position and heading.
 
2786
        """
 
2787
        screen = self.screen
 
2788
        p0, p1 = self._position
 
2789
        e0, e1 = self._orient
 
2790
        e = Vec2D(e0, e1 * screen.yscale / screen.xscale)
 
2791
        e0, e1 = (1.0 / abs(e)) * e
 
2792
        return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale)
 
2793
                                                           for (x, y) in poly]
 
2794
 
 
2795
    def _drawturtle(self):
 
2796
        """Manages the correct rendering of the turtle with respect to
 
2797
        it's shape, resizemode, strech and tilt etc."""
 
2798
        screen = self.screen
 
2799
        shape = screen._shapes[self.turtle.shapeIndex]
 
2800
        ttype = shape._type
 
2801
        titem = self.turtle._item
 
2802
        if self._shown and screen._updatecounter == 0 and screen._tracing > 0:
 
2803
            self._hidden_from_screen = False
 
2804
            tshape = shape._data
 
2805
            if ttype == "polygon":
 
2806
                if self._resizemode == "noresize":
 
2807
                    w = 1
 
2808
                    shape = tshape
 
2809
                else:
 
2810
                    if self._resizemode == "auto":
 
2811
                        lx = ly = max(1, self._pensize/5.0)
 
2812
                        w = self._pensize
 
2813
                        tiltangle = 0
 
2814
                    elif self._resizemode == "user":
 
2815
                        lx, ly = self._stretchfactor
 
2816
                        w = self._outlinewidth
 
2817
                        tiltangle = self._tilt
 
2818
                    shape = [(lx*x, ly*y) for (x, y) in tshape]
 
2819
                    t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
 
2820
                    shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
 
2821
                shape = self._polytrafo(shape)
 
2822
                fc, oc = self._fillcolor, self._pencolor
 
2823
                screen._drawpoly(titem, shape, fill=fc, outline=oc,
 
2824
                                                      width=w, top=True)
 
2825
            elif ttype == "image":
 
2826
                screen._drawimage(titem, self._position, tshape)
 
2827
            elif ttype == "compound":
 
2828
                lx, ly = self._stretchfactor
 
2829
                w = self._outlinewidth
 
2830
                for item, (poly, fc, oc) in zip(titem, tshape):
 
2831
                    poly = [(lx*x, ly*y) for (x, y) in poly]
 
2832
                    poly = self._polytrafo(poly)
 
2833
                    screen._drawpoly(item, poly, fill=self._cc(fc),
 
2834
                                     outline=self._cc(oc), width=w, top=True)
 
2835
        else:
 
2836
            if self._hidden_from_screen:
 
2837
                return
 
2838
            if ttype == "polygon":
 
2839
                screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "")
 
2840
            elif ttype == "image":
 
2841
                screen._drawimage(titem, self._position,
 
2842
                                          screen._shapes["blank"]._data)
 
2843
            elif ttype == "compound":
 
2844
                for item in titem:
 
2845
                    screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "")
 
2846
            self._hidden_from_screen = True
 
2847
 
 
2848
##############################  stamp stuff  ###############################
 
2849
 
 
2850
    def stamp(self):
 
2851
        """Stamp a copy of the turtleshape onto the canvas and return it's id.
 
2852
 
 
2853
        No argument.
 
2854
 
 
2855
        Stamp a copy of the turtle shape onto the canvas at the current
 
2856
        turtle position. Return a stamp_id for that stamp, which can be
 
2857
        used to delete it by calling clearstamp(stamp_id).
 
2858
 
 
2859
        Example (for a Turtle instance named turtle):
 
2860
        >>> turtle.color("blue")
 
2861
        >>> turtle.stamp()
 
2862
        13
 
2863
        >>> turtle.fd(50)
 
2864
        """
 
2865
        screen = self.screen
 
2866
        shape = screen._shapes[self.turtle.shapeIndex]
 
2867
        ttype = shape._type
 
2868
        tshape = shape._data
 
2869
        if ttype == "polygon":
 
2870
            stitem = screen._createpoly()
 
2871
            if self._resizemode == "noresize":
 
2872
                w = 1
 
2873
                shape = tshape
 
2874
            else:
 
2875
                if self._resizemode == "auto":
 
2876
                    lx = ly = max(1, self._pensize/5.0)
 
2877
                    w = self._pensize
 
2878
                    tiltangle = 0
 
2879
                elif self._resizemode == "user":
 
2880
                    lx, ly = self._stretchfactor
 
2881
                    w = self._outlinewidth
 
2882
                    tiltangle = self._tilt
 
2883
                shape = [(lx*x, ly*y) for (x, y) in tshape]
 
2884
                t0, t1 = math.sin(tiltangle), math.cos(tiltangle)
 
2885
                shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape]
 
2886
            shape = self._polytrafo(shape)
 
2887
            fc, oc = self._fillcolor, self._pencolor
 
2888
            screen._drawpoly(stitem, shape, fill=fc, outline=oc,
 
2889
                                                  width=w, top=True)
 
2890
        elif ttype == "image":
 
2891
            stitem = screen._createimage("")
 
2892
            screen._drawimage(stitem, self._position, tshape)
 
2893
        elif ttype == "compound":
 
2894
            stitem = []
 
2895
            for element in tshape:
 
2896
                item = screen._createpoly()
 
2897
                stitem.append(item)
 
2898
            stitem = tuple(stitem)
 
2899
            lx, ly = self._stretchfactor
 
2900
            w = self._outlinewidth
 
2901
            for item, (poly, fc, oc) in zip(stitem, tshape):
 
2902
                poly = [(lx*x, ly*y) for (x, y) in poly]
 
2903
                poly = self._polytrafo(poly)
 
2904
                screen._drawpoly(item, poly, fill=self._cc(fc),
 
2905
                                 outline=self._cc(oc), width=w, top=True)
 
2906
        self.stampItems.append(stitem)
 
2907
        self.undobuffer.push(("stamp", stitem))
 
2908
        return stitem
 
2909
 
 
2910
    def _clearstamp(self, stampid):
 
2911
        """does the work for clearstamp() and clearstamps()
 
2912
        """
 
2913
        if stampid in self.stampItems:
 
2914
            if isinstance(stampid, tuple):
 
2915
                for subitem in stampid:
 
2916
                    self.screen._delete(subitem)
 
2917
            else:
 
2918
                self.screen._delete(stampid)
 
2919
            self.stampItems.remove(stampid)
 
2920
        # Delete stampitem from undobuffer if necessary
 
2921
        # if clearstamp is called directly.
 
2922
        item = ("stamp", stampid)
 
2923
        buf = self.undobuffer
 
2924
        if item not in buf.buffer:
 
2925
            return
 
2926
        index = buf.buffer.index(item)
 
2927
        buf.buffer.remove(item)
 
2928
        if index <= buf.ptr:
 
2929
            buf.ptr = (buf.ptr - 1) % buf.bufsize
 
2930
        buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None])
 
2931
 
 
2932
    def clearstamp(self, stampid):
 
2933
        """Delete stamp with given stampid
 
2934
 
 
2935
        Argument:
 
2936
        stampid - an integer, must be return value of previous stamp() call.
 
2937
 
 
2938
        Example (for a Turtle instance named turtle):
 
2939
        >>> turtle.color("blue")
 
2940
        >>> astamp = turtle.stamp()
 
2941
        >>> turtle.fd(50)
 
2942
        >>> turtle.clearstamp(astamp)
 
2943
        """
 
2944
        self._clearstamp(stampid)
 
2945
        self._update()
 
2946
 
 
2947
    def clearstamps(self, n=None):
 
2948
        """Delete all or first/last n of turtle's stamps.
 
2949
 
 
2950
        Optional argument:
 
2951
        n -- an integer
 
2952
 
 
2953
        If n is None, delete all of pen's stamps,
 
2954
        else if n > 0 delete first n stamps
 
2955
        else if n < 0 delete last n stamps.
 
2956
 
 
2957
        Example (for a Turtle instance named turtle):
 
2958
        >>> for i in range(8):
 
2959
                turtle.stamp(); turtle.fd(30)
 
2960
        ...
 
2961
        >>> turtle.clearstamps(2)
 
2962
        >>> turtle.clearstamps(-2)
 
2963
        >>> turtle.clearstamps()
 
2964
        """
 
2965
        if n is None:
 
2966
            toDelete = self.stampItems[:]
 
2967
        elif n >= 0:
 
2968
            toDelete = self.stampItems[:n]
 
2969
        else:
 
2970
            toDelete = self.stampItems[n:]
 
2971
        for item in toDelete:
 
2972
            self._clearstamp(item)
 
2973
        self._update()
 
2974
 
 
2975
    def _goto(self, end):
 
2976
        """Move the pen to the point end, thereby drawing a line
 
2977
        if pen is down. All other methodes for turtle movement depend
 
2978
        on this one.
 
2979
        """
 
2980
        ## Version mit undo-stuff
 
2981
        go_modes = ( self._drawing,
 
2982
                     self._pencolor,
 
2983
                     self._pensize,
 
2984
                     isinstance(self._fillpath, list))
 
2985
        screen = self.screen
 
2986
        undo_entry = ("go", self._position, end, go_modes,
 
2987
                      (self.currentLineItem,
 
2988
                      self.currentLine[:],
 
2989
                      screen._pointlist(self.currentLineItem),
 
2990
                      self.items[:])
 
2991
                      )
 
2992
        if self.undobuffer:
 
2993
            self.undobuffer.push(undo_entry)
 
2994
        start = self._position
 
2995
        if self._speed and screen._tracing == 1:
 
2996
            diff = (end-start)
 
2997
            diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
 
2998
            nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
 
2999
            delta = diff * (1.0/nhops)
 
3000
            for n in range(1, nhops):
 
3001
                if n == 1:
 
3002
                    top = True
 
3003
                else:
 
3004
                    top = False
 
3005
                self._position = start + delta * n
 
3006
                if self._drawing:
 
3007
                    screen._drawline(self.drawingLineItem,
 
3008
                                     (start, self._position),
 
3009
                                     self._pencolor, self._pensize, top)
 
3010
                self._update()
 
3011
            if self._drawing:
 
3012
                screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
 
3013
                                               fill="", width=self._pensize)
 
3014
        # Turtle now at end,
 
3015
        if self._drawing: # now update currentLine
 
3016
            self.currentLine.append(end)
 
3017
        if isinstance(self._fillpath, list):
 
3018
            self._fillpath.append(end)
 
3019
        ######    vererbung!!!!!!!!!!!!!!!!!!!!!!
 
3020
        self._position = end
 
3021
        if self._creatingPoly:
 
3022
            self._poly.append(end)
 
3023
        if len(self.currentLine) > 42: # 42! answer to the ultimate question
 
3024
                                       # of life, the universe and everything
 
3025
            self._newLine()
 
3026
        self._update() #count=True)
 
3027
 
 
3028
    def _undogoto(self, entry):
 
3029
        """Reverse a _goto. Used for undo()
 
3030
        """
 
3031
        old, new, go_modes, coodata = entry
 
3032
        drawing, pc, ps, filling = go_modes
 
3033
        cLI, cL, pl, items = coodata
 
3034
        screen = self.screen
 
3035
        if abs(self._position - new) > 0.5:
 
3036
            print "undogoto: HALLO-DA-STIMMT-WAS-NICHT!"
 
3037
        # restore former situation
 
3038
        self.currentLineItem = cLI
 
3039
        self.currentLine = cL
 
3040
 
 
3041
        if pl == [(0, 0), (0, 0)]:
 
3042
            usepc = ""
 
3043
        else:
 
3044
            usepc = pc
 
3045
        screen._drawline(cLI, pl, fill=usepc, width=ps)
 
3046
 
 
3047
        todelete = [i for i in self.items if (i not in items) and
 
3048
                                       (screen._type(i) == "line")]
 
3049
        for i in todelete:
 
3050
            screen._delete(i)
 
3051
            self.items.remove(i)
 
3052
 
 
3053
        start = old
 
3054
        if self._speed and screen._tracing == 1:
 
3055
            diff = old - new
 
3056
            diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2
 
3057
            nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed))
 
3058
            delta = diff * (1.0/nhops)
 
3059
            for n in range(1, nhops):
 
3060
                if n == 1:
 
3061
                    top = True
 
3062
                else:
 
3063
                    top = False
 
3064
                self._position = new + delta * n
 
3065
                if drawing:
 
3066
                    screen._drawline(self.drawingLineItem,
 
3067
                                     (start, self._position),
 
3068
                                     pc, ps, top)
 
3069
                self._update()
 
3070
            if drawing:
 
3071
                screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
 
3072
                                               fill="", width=ps)
 
3073
        # Turtle now at position old,
 
3074
        self._position = old
 
3075
        ##  if undo is done during crating a polygon, the last vertex
 
3076
        ##  will be deleted. if the polygon is entirel deleted,
 
3077
        ##  creatigPoly will be set to False.
 
3078
        ##  Polygons created before the last one will not be affected by undo()
 
3079
        if self._creatingPoly:
 
3080
            if len(self._poly) > 0:
 
3081
                self._poly.pop()
 
3082
            if self._poly == []:
 
3083
                self._creatingPoly = False
 
3084
                self._poly = None
 
3085
        if filling:
 
3086
            if self._fillpath == []:
 
3087
                self._fillpath = None
 
3088
                print "Unwahrscheinlich in _undogoto!"
 
3089
            elif self._fillpath is not None:
 
3090
                self._fillpath.pop()
 
3091
        self._update() #count=True)
 
3092
 
 
3093
    def _rotate(self, angle):
 
3094
        """Turns pen clockwise by angle.
 
3095
        """
 
3096
        if self.undobuffer:
 
3097
            self.undobuffer.push(("rot", angle, self._degreesPerAU))
 
3098
        angle *= self._degreesPerAU
 
3099
        neworient = self._orient.rotate(angle)
 
3100
        tracing = self.screen._tracing
 
3101
        if tracing == 1 and self._speed > 0:
 
3102
            anglevel = 3.0 * self._speed
 
3103
            steps = 1 + int(abs(angle)/anglevel)
 
3104
            delta = 1.0*angle/steps
 
3105
            for _ in range(steps):
 
3106
                self._orient = self._orient.rotate(delta)
 
3107
                self._update()
 
3108
        self._orient = neworient
 
3109
        self._update()
 
3110
 
 
3111
    def _newLine(self, usePos=True):
 
3112
        """Closes current line item and starts a new one.
 
3113
           Remark: if current line became too long, animation
 
3114
           performance (via _drawline) slowed down considerably.
 
3115
        """
 
3116
        if len(self.currentLine) > 1:
 
3117
            self.screen._drawline(self.currentLineItem, self.currentLine,
 
3118
                                      self._pencolor, self._pensize)
 
3119
            self.currentLineItem = self.screen._createline()
 
3120
            self.items.append(self.currentLineItem)
 
3121
        else:
 
3122
            self.screen._drawline(self.currentLineItem, top=True)
 
3123
        self.currentLine = []
 
3124
        if usePos:
 
3125
            self.currentLine = [self._position]
 
3126
 
 
3127
    def fill(self, flag=None):
 
3128
        """Call fill(True) before drawing a shape to fill, fill(False) when done.
 
3129
 
 
3130
        Optional argument:
 
3131
        flag -- True/False (or 1/0 respectively)
 
3132
 
 
3133
        Call fill(True) before drawing the shape you want to fill,
 
3134
        and  fill(False) when done.
 
3135
        When used without argument: return fillstate (True if filling,
 
3136
        False else)
 
3137
 
 
3138
        Example (for a Turtle instance named turtle):
 
3139
        >>> turtle.fill(True)
 
3140
        >>> turtle.forward(100)
 
3141
        >>> turtle.left(90)
 
3142
        >>> turtle.forward(100)
 
3143
        >>> turtle.left(90)
 
3144
        >>> turtle.forward(100)
 
3145
        >>> turtle.left(90)
 
3146
        >>> turtle.forward(100)
 
3147
        >>> turtle.fill(False)
 
3148
        """
 
3149
        filling = isinstance(self._fillpath, list)
 
3150
        if flag is None:
 
3151
            return filling
 
3152
        screen = self.screen
 
3153
        entry1 = entry2 = ()
 
3154
        if filling:
 
3155
            if len(self._fillpath) > 2:
 
3156
                self.screen._drawpoly(self._fillitem, self._fillpath,
 
3157
                                      fill=self._fillcolor)
 
3158
                entry1 = ("dofill", self._fillitem)
 
3159
        if flag:
 
3160
            self._fillitem = self.screen._createpoly()
 
3161
            self.items.append(self._fillitem)
 
3162
            self._fillpath = [self._position]
 
3163
            entry2 = ("beginfill", self._fillitem) # , self._fillpath)
 
3164
            self._newLine()
 
3165
        else:
 
3166
            self._fillitem = self._fillpath = None
 
3167
        if self.undobuffer:
 
3168
            if entry1 == ():
 
3169
                if entry2 != ():
 
3170
                    self.undobuffer.push(entry2)
 
3171
            else:
 
3172
                if entry2 == ():
 
3173
                    self.undobuffer.push(entry1)
 
3174
                else:
 
3175
                    self.undobuffer.push(["seq", entry1, entry2])
 
3176
        self._update()
 
3177
 
 
3178
    def begin_fill(self):
 
3179
        """Called just before drawing a shape to be filled.
 
3180
 
 
3181
        No argument.
 
3182
 
 
3183
        Example (for a Turtle instance named turtle):
 
3184
        >>> turtle.begin_fill()
 
3185
        >>> turtle.forward(100)
 
3186
        >>> turtle.left(90)
 
3187
        >>> turtle.forward(100)
 
3188
        >>> turtle.left(90)
 
3189
        >>> turtle.forward(100)
 
3190
        >>> turtle.left(90)
 
3191
        >>> turtle.forward(100)
 
3192
        >>> turtle.end_fill()
 
3193
        """
 
3194
        self.fill(True)
 
3195
 
 
3196
    def end_fill(self):
 
3197
        """Fill the shape drawn after the call begin_fill().
 
3198
 
 
3199
        No argument.
 
3200
 
 
3201
        Example (for a Turtle instance named turtle):
 
3202
        >>> turtle.begin_fill()
 
3203
        >>> turtle.forward(100)
 
3204
        >>> turtle.left(90)
 
3205
        >>> turtle.forward(100)
 
3206
        >>> turtle.left(90)
 
3207
        >>> turtle.forward(100)
 
3208
        >>> turtle.left(90)
 
3209
        >>> turtle.forward(100)
 
3210
        >>> turtle.end_fill()
 
3211
        """
 
3212
        self.fill(False)
 
3213
 
 
3214
    def dot(self, size=None, *color):
 
3215
        """Draw a dot with diameter size, using color.
 
3216
 
 
3217
        Optional argumentS:
 
3218
        size -- an integer >= 1 (if given)
 
3219
        color -- a colorstring or a numeric color tuple
 
3220
 
 
3221
        Draw a circular dot with diameter size, using color.
 
3222
        If size is not given, the maximum of pensize+4 and 2*pensize is used.
 
3223
 
 
3224
        Example (for a Turtle instance named turtle):
 
3225
        >>> turtle.dot()
 
3226
        >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
 
3227
        """
 
3228
        #print "dot-1:", size, color
 
3229
        if not color:
 
3230
            if isinstance(size, (str, tuple)):
 
3231
                color = self._colorstr(size)
 
3232
                size = self._pensize + max(self._pensize, 4)
 
3233
            else:
 
3234
                color = self._pencolor
 
3235
                if not size:
 
3236
                    size = self._pensize + max(self._pensize, 4)
 
3237
        else:
 
3238
            if size is None:
 
3239
                size = self._pensize + max(self._pensize, 4)
 
3240
            color = self._colorstr(color)
 
3241
        #print "dot-2:", size, color
 
3242
        if hasattr(self.screen, "_dot"):
 
3243
            item = self.screen._dot(self._position, size, color)
 
3244
            #print "dot:", size, color, "item:", item
 
3245
            self.items.append(item)
 
3246
            if self.undobuffer:
 
3247
                self.undobuffer.push(("dot", item))
 
3248
        else:
 
3249
            pen = self.pen()
 
3250
            if self.undobuffer:
 
3251
                self.undobuffer.push(["seq"])
 
3252
                self.undobuffer.cumulate = True
 
3253
            try:
 
3254
                if self.resizemode() == 'auto':
 
3255
                    self.ht()
 
3256
                self.pendown()
 
3257
                self.pensize(size)
 
3258
                self.pencolor(color)
 
3259
                self.forward(0)
 
3260
            finally:
 
3261
                self.pen(pen)
 
3262
            if self.undobuffer:
 
3263
                self.undobuffer.cumulate = False
 
3264
 
 
3265
    def _write(self, txt, align, font):
 
3266
        """Performs the writing for write()
 
3267
        """
 
3268
        item, end = self.screen._write(self._position, txt, align, font,
 
3269
                                                          self._pencolor)
 
3270
        self.items.append(item)
 
3271
        if self.undobuffer:
 
3272
            self.undobuffer.push(("wri", item))
 
3273
        return end
 
3274
 
 
3275
    def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")):
 
3276
        """Write text at the current turtle position.
 
3277
 
 
3278
        Arguments:
 
3279
        arg -- info, which is to be written to the TurtleScreen
 
3280
        move (optional) -- True/False
 
3281
        align (optional) -- one of the strings "left", "center" or right"
 
3282
        font (optional) -- a triple (fontname, fontsize, fonttype)
 
3283
 
 
3284
        Write text - the string representation of arg - at the current
 
3285
        turtle position according to align ("left", "center" or right")
 
3286
        and with the given font.
 
3287
        If move is True, the pen is moved to the bottom-right corner
 
3288
        of the text. By default, move is False.
 
3289
 
 
3290
        Example (for a Turtle instance named turtle):
 
3291
        >>> turtle.write('Home = ', True, align="center")
 
3292
        >>> turtle.write((0,0), True)
 
3293
        """
 
3294
        if self.undobuffer:
 
3295
            self.undobuffer.push(["seq"])
 
3296
            self.undobuffer.cumulate = True
 
3297
        end = self._write(str(arg), align.lower(), font)
 
3298
        if move:
 
3299
            x, y = self.pos()
 
3300
            self.setpos(end, y)
 
3301
        if self.undobuffer:
 
3302
            self.undobuffer.cumulate = False
 
3303
 
 
3304
    def begin_poly(self):
 
3305
        """Start recording the vertices of a polygon.
 
3306
 
 
3307
        No argument.
 
3308
 
 
3309
        Start recording the vertices of a polygon. Current turtle position
 
3310
        is first point of polygon.
 
3311
 
 
3312
        Example (for a Turtle instance named turtle):
 
3313
        >>> turtle.begin_poly()
 
3314
        """
 
3315
        self._poly = [self._position]
 
3316
        self._creatingPoly = True
 
3317
 
 
3318
    def end_poly(self):
 
3319
        """Stop recording the vertices of a polygon.
 
3320
 
 
3321
        No argument.
 
3322
 
 
3323
        Stop recording the vertices of a polygon. Current turtle position is
 
3324
        last point of polygon. This will be connected with the first point.
 
3325
 
 
3326
        Example (for a Turtle instance named turtle):
 
3327
        >>> turtle.end_poly()
 
3328
        """
 
3329
        self._creatingPoly = False
 
3330
 
 
3331
    def get_poly(self):
 
3332
        """Return the lastly recorded polygon.
 
3333
 
 
3334
        No argument.
 
3335
 
 
3336
        Example (for a Turtle instance named turtle):
 
3337
        >>> p = turtle.get_poly()
 
3338
        >>> turtle.register_shape("myFavouriteShape", p)
 
3339
        """
 
3340
        ## check if there is any poly?  -- 1st solution:
 
3341
        if self._poly is not None:
 
3342
            return tuple(self._poly)
 
3343
 
 
3344
    def getscreen(self):
 
3345
        """Return the TurtleScreen object, the turtle is drawing  on.
 
3346
 
 
3347
        No argument.
 
3348
 
 
3349
        Return the TurtleScreen object, the turtle is drawing  on.
 
3350
        So TurtleScreen-methods can be called for that object.
 
3351
 
 
3352
        Example (for a Turtle instance named turtle):
 
3353
        >>> ts = turtle.getscreen()
 
3354
        >>> ts
 
3355
        <turtle.TurtleScreen object at 0x0106B770>
 
3356
        >>> ts.bgcolor("pink")
 
3357
        """
 
3358
        return self.screen
 
3359
 
 
3360
    def getturtle(self):
 
3361
        """Return the Turtleobject itself.
 
3362
 
 
3363
        No argument.
 
3364
 
 
3365
        Only reasonable use: as a function to return the 'anonymous turtle':
 
3366
 
 
3367
        Example:
 
3368
        >>> pet = getturtle()
 
3369
        >>> pet.fd(50)
 
3370
        >>> pet
 
3371
        <turtle.Turtle object at 0x0187D810>
 
3372
        >>> turtles()
 
3373
        [<turtle.Turtle object at 0x0187D810>]
 
3374
        """
 
3375
        return self
 
3376
 
 
3377
    getpen = getturtle
 
3378
 
 
3379
 
 
3380
    ################################################################
 
3381
    ### screen oriented methods recurring to methods of TurtleScreen
 
3382
    ################################################################
408
3383
 
409
3384
    def window_width(self):
410
3385
        """ Returns the width of the turtle window.
411
3386
 
412
 
        Example:
413
 
        >>> turtle.window_width()
 
3387
        No argument.
 
3388
 
 
3389
        Example (for a TurtleScreen instance named screen):
 
3390
        >>> screen.window_width()
414
3391
        640
415
3392
        """
416
 
        width = self._canvas.winfo_width()
417
 
        if width <= 1:  # the window isn't managed by a geometry manager
418
 
            width = self._canvas['width']
419
 
        return width
 
3393
        return self.screen._window_size()[0]
420
3394
 
421
3395
    def window_height(self):
422
3396
        """ Return the height of the turtle window.
423
3397
 
424
 
        Example:
425
 
        >>> turtle.window_height()
426
 
        768
427
 
        """
428
 
        height = self._canvas.winfo_height()
429
 
        if height <= 1: # the window isn't managed by a geometry manager
430
 
            height = self._canvas['height']
431
 
        return height
432
 
 
433
 
    def position(self):
434
 
        """ Return the current (x, y) location of the turtle.
435
 
 
436
 
        Example:
437
 
        >>> turtle.position()
438
 
        [0.0, 240.0]
439
 
        """
440
 
        x0, y0 = self._origin
441
 
        x1, y1 = self._position
442
 
        return [x1-x0, -y1+y0]
443
 
 
444
 
    def setx(self, xpos):
445
 
        """ Set the turtle's x coordinate to be xpos.
446
 
 
447
 
        Example:
448
 
        >>> turtle.position()
449
 
        [10.0, 240.0]
450
 
        >>> turtle.setx(10)
451
 
        >>> turtle.position()
452
 
        [10.0, 240.0]
453
 
        """
454
 
        x0, y0 = self._origin
455
 
        x1, y1 = self._position
456
 
        self._goto(x0+xpos, y1)
457
 
 
458
 
    def sety(self, ypos):
459
 
        """ Set the turtle's y coordinate to be ypos.
460
 
 
461
 
        Example:
462
 
        >>> turtle.position()
463
 
        [0.0, 0.0]
464
 
        >>> turtle.sety(-22)
465
 
        >>> turtle.position()
466
 
        [0.0, -22.0]
467
 
        """
468
 
        x0, y0 = self._origin
469
 
        x1, y1 = self._position
470
 
        self._goto(x1, y0-ypos)
471
 
 
472
 
    def towards(self, *args):
473
 
        """Returs the angle, which corresponds to the line
474
 
        from turtle-position to point (x,y).
475
 
 
476
 
        Argument can be two coordinates or one pair of coordinates
477
 
        or a RawPen/Pen instance.
478
 
 
479
 
        Example:
480
 
        >>> turtle.position()
481
 
        [10.0, 10.0]
482
 
        >>> turtle.towards(0,0)
483
 
        225.0
484
 
        """
485
 
        if len(args) == 2:
486
 
            x, y = args
487
 
        else:
488
 
            arg = args[0]
489
 
            if isinstance(arg, RawPen):
490
 
                x, y = arg.position()
491
 
            else:
492
 
                x, y = arg
493
 
        x0, y0 = self.position()
494
 
        dx = x - x0
495
 
        dy = y - y0
496
 
        return (atan2(dy,dx) / self._invradian) % self._fullcircle
497
 
 
498
 
    def goto(self, *args):
499
 
        """ Go to the given point.
500
 
 
501
 
        If the pen is down, then a line will be drawn. The turtle's
502
 
        orientation does not change.
503
 
 
504
 
        Two input formats are accepted:
505
 
 
506
 
           goto(x, y)
507
 
           go to point (x, y)
508
 
 
509
 
           goto((x, y))
510
 
           go to point (x, y)
511
 
 
512
 
        Example:
513
 
        >>> turtle.position()
514
 
        [0.0, 0.0]
515
 
        >>> turtle.goto(50, -45)
516
 
        >>> turtle.position()
517
 
        [50.0, -45.0]
518
 
        """
519
 
        if len(args) == 1:
520
 
            try:
521
 
                x, y = args[0]
522
 
            except:
523
 
                raise Error, "bad point argument: %r" % (args[0],)
524
 
        else:
525
 
            try:
526
 
                x, y = args
527
 
            except:
528
 
                raise Error, "bad coordinates: %r" % (args[0],)
529
 
        x0, y0 = self._origin
530
 
        self._goto(x0+x, y0-y)
531
 
 
532
 
    def _goto(self, x1, y1):
533
 
        x0, y0 = self._position
534
 
        self._position = map(float, (x1, y1))
535
 
        if self._filling:
536
 
            self._path.append(self._position)
537
 
        if self._drawing:
538
 
            if self._tracing:
539
 
                dx = float(x1 - x0)
540
 
                dy = float(y1 - y0)
541
 
                distance = hypot(dx, dy)
542
 
                nhops = int(distance)
543
 
                item = self._canvas.create_line(x0, y0, x0, y0,
544
 
                                                width=self._width,
545
 
                                                capstyle="round",
546
 
                                                fill=self._color)
547
 
                try:
548
 
                    for i in range(1, 1+nhops):
549
 
                        x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
550
 
                        self._canvas.coords(item, x0, y0, x, y)
551
 
                        self._draw_turtle((x,y))
552
 
                        self._canvas.update()
553
 
                        self._canvas.after(self._delay)
554
 
                    # in case nhops==0
555
 
                    self._canvas.coords(item, x0, y0, x1, y1)
556
 
                    self._canvas.itemconfigure(item, arrow="none")
557
 
                except Tkinter.TclError:
558
 
                    # Probably the window was closed!
559
 
                    return
560
 
            else:
561
 
                item = self._canvas.create_line(x0, y0, x1, y1,
562
 
                                                width=self._width,
563
 
                                                capstyle="round",
564
 
                                                fill=self._color)
565
 
            self._items.append(item)
566
 
        self._draw_turtle()
567
 
 
568
 
    def speed(self, speed):
569
 
        """ Set the turtle's speed.
570
 
 
571
 
        speed must one of these five strings:
572
 
 
573
 
            'fastest' is a 0 ms delay
574
 
            'fast' is a 5 ms delay
575
 
            'normal' is a 10 ms delay
576
 
            'slow' is a 15 ms delay
577
 
            'slowest' is a 20 ms delay
578
 
 
579
 
         Example:
580
 
         >>> turtle.speed('slow')
581
 
        """
582
 
        try:
583
 
            speed = speed.strip().lower()
584
 
            self._delay = speeds.index(speed) * 5
585
 
        except:
586
 
            raise ValueError("%r is not a valid speed. speed must be "
587
 
                             "one of %s" % (speed, speeds))
588
 
 
589
 
 
590
 
    def delay(self, delay):
591
 
        """ Set the drawing delay in milliseconds.
592
 
 
593
 
        This is intended to allow finer control of the drawing speed
594
 
        than the speed() method
595
 
 
596
 
        Example:
597
 
        >>> turtle.delay(15)
598
 
        """
599
 
        if int(delay) < 0:
600
 
            raise ValueError("delay must be greater than or equal to 0")
601
 
        self._delay = int(delay)
602
 
 
603
 
    def _draw_turtle(self, position=[]):
604
 
        if not self._tracing:
605
 
            self._canvas.update()
606
 
            return
607
 
        if position == []:
608
 
            position = self._position
609
 
        x,y = position
610
 
        distance = 8
611
 
        dx = distance * cos(self._angle*self._invradian)
612
 
        dy = distance * sin(self._angle*self._invradian)
613
 
        self._delete_turtle()
614
 
        self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
615
 
                                          width=self._width,
616
 
                                          arrow="last",
617
 
                                          capstyle="round",
618
 
                                          fill=self._color)
619
 
        self._canvas.update()
620
 
 
621
 
    def _delete_turtle(self):
622
 
        if self._arrow != 0:
623
 
            self._canvas.delete(self._arrow)
624
 
            self._arrow = 0
625
 
 
626
 
 
627
 
_root = None
628
 
_canvas = None
629
 
_pen = None
630
 
_width = 0.50                  # 50% of window width
631
 
_height = 0.75                 # 75% of window height
632
 
_startx = None
633
 
_starty = None
634
 
_title = "Turtle Graphics"     # default title
635
 
 
636
 
class Pen(RawPen):
 
3398
        No argument.
 
3399
 
 
3400
        Example (for a TurtleScreen instance named screen):
 
3401
        >>> screen.window_height()
 
3402
        480
 
3403
        """
 
3404
        return self.screen._window_size()[1]
 
3405
 
 
3406
    def _delay(self, delay=None):
 
3407
        """Set delay value which determines speed of turtle animation.
 
3408
        """
 
3409
        return self.screen.delay(delay)
 
3410
 
 
3411
    #####   event binding methods   #####
 
3412
 
 
3413
    def onclick(self, fun, btn=1, add=None):
 
3414
        """Bind fun to mouse-click event on this turtle on canvas.
 
3415
 
 
3416
        Arguments:
 
3417
        fun --  a function with two arguments, to which will be assigned
 
3418
                the coordinates of the clicked point on the canvas.
 
3419
        num --  number of the mouse-button defaults to 1 (left mouse button).
 
3420
        add --  True or False. If True, new binding will be added, otherwise
 
3421
                it will replace a former binding.
 
3422
 
 
3423
        Example for the anonymous turtle, i. e. the procedural way:
 
3424
 
 
3425
        >>> def turn(x, y):
 
3426
                left(360)
 
3427
 
 
3428
        >>> onclick(turn) # Now clicking into the turtle will turn it.
 
3429
        >>> onclick(None)  # event-binding will be removed
 
3430
        """
 
3431
        self.screen._onclick(self.turtle._item, fun, btn, add)
 
3432
        self._update()
 
3433
 
 
3434
    def onrelease(self, fun, btn=1, add=None):
 
3435
        """Bind fun to mouse-button-release event on this turtle on canvas.
 
3436
 
 
3437
        Arguments:
 
3438
        fun -- a function with two arguments, to which will be assigned
 
3439
                the coordinates of the clicked point on the canvas.
 
3440
        num --  number of the mouse-button defaults to 1 (left mouse button).
 
3441
 
 
3442
        Example (for a MyTurtle instance named joe):
 
3443
        >>> class MyTurtle(Turtle):
 
3444
                def glow(self,x,y):
 
3445
                        self.fillcolor("red")
 
3446
                def unglow(self,x,y):
 
3447
                        self.fillcolor("")
 
3448
 
 
3449
        >>> joe = MyTurtle()
 
3450
        >>> joe.onclick(joe.glow)
 
3451
        >>> joe.onrelease(joe.unglow)
 
3452
        ### clicking on joe turns fillcolor red,
 
3453
        ### unclicking turns it to transparent.
 
3454
        """
 
3455
        self.screen._onrelease(self.turtle._item, fun, btn, add)
 
3456
        self._update()
 
3457
 
 
3458
    def ondrag(self, fun, btn=1, add=None):
 
3459
        """Bind fun to mouse-move event on this turtle on canvas.
 
3460
 
 
3461
        Arguments:
 
3462
        fun -- a function with two arguments, to which will be assigned
 
3463
               the coordinates of the clicked point on the canvas.
 
3464
        num -- number of the mouse-button defaults to 1 (left mouse button).
 
3465
 
 
3466
        Every sequence of mouse-move-events on a turtle is preceded by a
 
3467
        mouse-click event on that turtle.
 
3468
 
 
3469
        Example (for a Turtle instance named turtle):
 
3470
        >>> turtle.ondrag(turtle.goto)
 
3471
 
 
3472
        ### Subsequently clicking and dragging a Turtle will
 
3473
        ### move it across the screen thereby producing handdrawings
 
3474
        ### (if pen is down).
 
3475
        """
 
3476
        self.screen._ondrag(self.turtle._item, fun, btn, add)
 
3477
 
 
3478
 
 
3479
    def _undo(self, action, data):
 
3480
        """Does the main part of the work for undo()
 
3481
        """
 
3482
        if self.undobuffer is None:
 
3483
            return
 
3484
        if action == "rot":
 
3485
            angle, degPAU = data
 
3486
            self._rotate(-angle*degPAU/self._degreesPerAU)
 
3487
            dummy = self.undobuffer.pop()
 
3488
        elif action == "stamp":
 
3489
            stitem = data[0]
 
3490
            self.clearstamp(stitem)
 
3491
        elif action == "go":
 
3492
            self._undogoto(data)
 
3493
        elif action in ["wri", "dot"]:
 
3494
            item = data[0]
 
3495
            self.screen._delete(item)
 
3496
            self.items.remove(item)
 
3497
        elif action == "dofill":
 
3498
            item = data[0]
 
3499
            self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)),
 
3500
                                  fill="", outline="")
 
3501
        elif action == "beginfill":
 
3502
            item = data[0]
 
3503
            self._fillitem = self._fillpath = None
 
3504
            self.screen._delete(item)
 
3505
            self.items.remove(item)
 
3506
        elif action == "pen":
 
3507
            TPen.pen(self, data[0])
 
3508
            self.undobuffer.pop()
 
3509
 
 
3510
    def undo(self):
 
3511
        """undo (repeatedly) the last turtle action.
 
3512
 
 
3513
        No argument.
 
3514
 
 
3515
        undo (repeatedly) the last turtle action.
 
3516
        Number of available undo actions is determined by the size of
 
3517
        the undobuffer.
 
3518
 
 
3519
        Example (for a Turtle instance named turtle):
 
3520
        >>> for i in range(4):
 
3521
                turtle.fd(50); turtle.lt(80)
 
3522
 
 
3523
        >>> for i in range(8):
 
3524
                turtle.undo()
 
3525
        """
 
3526
        if self.undobuffer is None:
 
3527
            return
 
3528
        item = self.undobuffer.pop()
 
3529
        action = item[0]
 
3530
        data = item[1:]
 
3531
        if action == "seq":
 
3532
            while data:
 
3533
                item = data.pop()
 
3534
                self._undo(item[0], item[1:])
 
3535
        else:
 
3536
            self._undo(action, data)
 
3537
 
 
3538
    turtlesize = shapesize
 
3539
 
 
3540
RawPen = RawTurtle
 
3541
 
 
3542
###  Screen - Klasse  ########################
 
3543
 
 
3544
class Screen(TurtleScreen):
 
3545
 
 
3546
    _root = None
 
3547
    _canvas = None
 
3548
    _title = _CFG["title"]
 
3549
 
 
3550
    # Borg-Idiom
 
3551
 
 
3552
    _shared_state = {}
 
3553
 
 
3554
    def __new__(cls, *args, **kwargs):
 
3555
        obj = object.__new__(cls, *args, **kwargs)
 
3556
        obj.__dict__ = cls._shared_state
 
3557
        return obj
637
3558
 
638
3559
    def __init__(self):
639
 
        global _root, _canvas
640
 
        if _root is None:
641
 
            _root = Tkinter.Tk()
642
 
            _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
643
 
            _root.title(_title)
644
 
 
645
 
        if _canvas is None:
646
 
            # XXX Should have scroll bars
647
 
            _canvas = Tkinter.Canvas(_root, background="white")
648
 
            _canvas.pack(expand=1, fill="both")
649
 
 
650
 
            setup(width=_width, height= _height, startx=_startx, starty=_starty)
651
 
 
652
 
        RawPen.__init__(self, _canvas)
 
3560
        if Screen._root is None:
 
3561
            Screen._root = self._root = _Root()
 
3562
            self._root.title(Screen._title)
 
3563
            self._root.ondestroy(self._destroy)
 
3564
        if Screen._canvas is None:
 
3565
            width = _CFG["width"]
 
3566
            height = _CFG["height"]
 
3567
            canvwidth = _CFG["canvwidth"]
 
3568
            canvheight = _CFG["canvheight"]
 
3569
            leftright = _CFG["leftright"]
 
3570
            topbottom = _CFG["topbottom"]
 
3571
            self._root.setupcanvas(width, height, canvwidth, canvheight)
 
3572
            Screen._canvas = self._root._getcanvas()
 
3573
            self.setup(width, height, leftright, topbottom)
 
3574
        TurtleScreen.__init__(self, Screen._canvas)
 
3575
        Turtle._screen = self
 
3576
 
 
3577
    def setup(self, width=_CFG["width"], height=_CFG["height"],
 
3578
              startx=_CFG["leftright"], starty=_CFG["topbottom"]):
 
3579
        """ Set the size and position of the main window.
 
3580
 
 
3581
        Arguments:
 
3582
        width: as integer a size in pixels, as float a fraction of the screen.
 
3583
          Default is 50% of screen.
 
3584
        height: as integer the height in pixels, as float a fraction of the
 
3585
          screen. Default is 75% of screen.
 
3586
        startx: if positive, starting position in pixels from the left
 
3587
          edge of the screen, if negative from the right edge
 
3588
          Default, startx=None is to center window horizontally.
 
3589
        starty: if positive, starting position in pixels from the top
 
3590
          edge of the screen, if negative from the bottom edge
 
3591
          Default, starty=None is to center window vertically.
 
3592
 
 
3593
        Examples (for a Screen instance named screen):
 
3594
        >>> screen.setup (width=200, height=200, startx=0, starty=0)
 
3595
 
 
3596
        sets window to 200x200 pixels, in upper left of screen
 
3597
 
 
3598
        >>> screen.setup(width=.75, height=0.5, startx=None, starty=None)
 
3599
 
 
3600
        sets window to 75% of screen by 50% of screen and centers
 
3601
        """
 
3602
        if not hasattr(self._root, "set_geometry"):
 
3603
            return
 
3604
        sw = self._root.win_width()
 
3605
        sh = self._root.win_height()
 
3606
        if isinstance(width, float) and 0 <= width <= 1:
 
3607
            width = sw*width
 
3608
        if startx is None:
 
3609
            startx = (sw - width) / 2
 
3610
        if isinstance(height, float) and 0 <= height <= 1:
 
3611
            height = sh*height
 
3612
        if starty is None:
 
3613
            starty = (sh - height) / 2
 
3614
        self._root.set_geometry(width, height, startx, starty)
 
3615
 
 
3616
    def title(self, titlestring):
 
3617
        """Set title of turtle-window
 
3618
 
 
3619
        Argument:
 
3620
        titlestring -- a string, to appear in the titlebar of the
 
3621
                       turtle graphics window.
 
3622
 
 
3623
        This is a method of Screen-class. Not available for TurtleScreen-
 
3624
        objects.
 
3625
 
 
3626
        Example (for a Screen instance named screen):
 
3627
        >>> screen.title("Welcome to the turtle-zoo!")
 
3628
        """
 
3629
        if Screen._root is not None:
 
3630
            Screen._root.title(titlestring)
 
3631
        Screen._title = titlestring
653
3632
 
654
3633
    def _destroy(self):
655
 
        global _root, _canvas, _pen
656
 
        root = self._canvas._root()
657
 
        if root is _root:
658
 
            _pen = None
659
 
            _root = None
660
 
            _canvas = None
 
3634
        root = self._root
 
3635
        if root is Screen._root:
 
3636
            Turtle._pen = None
 
3637
            Turtle._screen = None
 
3638
            Screen._root = None
 
3639
            Screen._canvas = None
 
3640
        TurtleScreen._RUNNING = True
661
3641
        root.destroy()
662
3642
 
 
3643
    def bye(self):
 
3644
        """Shut the turtlegraphics window.
 
3645
 
 
3646
        Example (for a TurtleScreen instance named screen):
 
3647
        >>> screen.bye()
 
3648
        """
 
3649
        self._destroy()
 
3650
 
 
3651
    def exitonclick(self):
 
3652
        """Go into mainloop until the mouse is clicked.
 
3653
 
 
3654
        No arguments.
 
3655
 
 
3656
        Bind bye() method to mouseclick on TurtleScreen.
 
3657
        If "using_IDLE" - value in configuration dictionary is False
 
3658
        (default value), enter mainloop.
 
3659
        If IDLE with -n switch (no subprocess) is used, this value should be
 
3660
        set to True in turtle.cfg. In this case IDLE's mainloop
 
3661
        is active also for the client script.
 
3662
 
 
3663
        This is a method of the Screen-class and not available for
 
3664
        TurtleScreen instances.
 
3665
 
 
3666
        Example (for a Screen instance named screen):
 
3667
        >>> screen.exitonclick()
 
3668
 
 
3669
        """
 
3670
        def exitGracefully(x, y):
 
3671
            """Screen.bye() with two dummy-parameters"""
 
3672
            self.bye()
 
3673
        self.onclick(exitGracefully)
 
3674
        if _CFG["using_IDLE"]:
 
3675
            return
 
3676
        try:
 
3677
            mainloop()
 
3678
        except AttributeError:
 
3679
            exit(0)
 
3680
 
 
3681
 
 
3682
class Turtle(RawTurtle):
 
3683
    """RawTurtle auto-crating (scrolled) canvas.
 
3684
 
 
3685
    When a Turtle object is created or a function derived from some
 
3686
    Turtle method is called a TurtleScreen object is automatically created.
 
3687
    """
 
3688
    _pen = None
 
3689
    _screen = None
 
3690
 
 
3691
    def __init__(self,
 
3692
                 shape=_CFG["shape"],
 
3693
                 undobuffersize=_CFG["undobuffersize"],
 
3694
                 visible=_CFG["visible"]):
 
3695
        if Turtle._screen is None:
 
3696
            Turtle._screen = Screen()
 
3697
        RawTurtle.__init__(self, Turtle._screen,
 
3698
                           shape=shape,
 
3699
                           undobuffersize=undobuffersize,
 
3700
                           visible=visible)
 
3701
 
 
3702
Pen = Turtle
 
3703
 
663
3704
def _getpen():
664
 
    global _pen
665
 
    if not _pen:
666
 
        _pen = Pen()
667
 
    return _pen
668
 
 
669
 
class Turtle(Pen):
670
 
    pass
671
 
 
672
 
"""For documentation of the following functions see
673
 
   the RawPen methods with the same names
674
 
"""
675
 
 
676
 
def degrees(): _getpen().degrees()
677
 
def radians(): _getpen().radians()
678
 
def reset(): _getpen().reset()
679
 
def clear(): _getpen().clear()
680
 
def tracer(flag): _getpen().tracer(flag)
681
 
def forward(distance): _getpen().forward(distance)
682
 
def backward(distance): _getpen().backward(distance)
683
 
def left(angle): _getpen().left(angle)
684
 
def right(angle): _getpen().right(angle)
685
 
def up(): _getpen().up()
686
 
def down(): _getpen().down()
687
 
def width(width): _getpen().width(width)
688
 
def color(*args): _getpen().color(*args)
689
 
def write(arg, move=0): _getpen().write(arg, move)
690
 
def fill(flag): _getpen().fill(flag)
691
 
def begin_fill(): _getpen().begin_fill()
692
 
def end_fill(): _getpen().end_fill()
693
 
def circle(radius, extent=None): _getpen().circle(radius, extent)
694
 
def goto(*args): _getpen().goto(*args)
695
 
def heading(): return _getpen().heading()
696
 
def setheading(angle): _getpen().setheading(angle)
697
 
def position(): return _getpen().position()
698
 
def window_width(): return _getpen().window_width()
699
 
def window_height(): return _getpen().window_height()
700
 
def setx(xpos): _getpen().setx(xpos)
701
 
def sety(ypos): _getpen().sety(ypos)
702
 
def towards(*args): return _getpen().towards(*args)
703
 
 
704
 
def done(): _root.mainloop()
705
 
def delay(delay): return _getpen().delay(delay)
706
 
def speed(speed): return _getpen().speed(speed)
707
 
 
708
 
for methodname in dir(RawPen):
709
 
    """ copies RawPen docstrings to module functions of same name """
710
 
    if not methodname.startswith("_"):
711
 
        eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__
712
 
 
713
 
 
714
 
def setup(**geometry):
715
 
    """ Sets the size and position of the main window.
716
 
 
717
 
    Keywords are width, height, startx and starty:
718
 
 
719
 
    width: either a size in pixels or a fraction of the screen.
720
 
      Default is 50% of screen.
721
 
    height: either the height in pixels or a fraction of the screen.
722
 
      Default is 75% of screen.
723
 
 
724
 
    Setting either width or height to None before drawing will force
725
 
      use of default geometry as in older versions of turtle.py
726
 
 
727
 
    startx: starting position in pixels from the left edge of the screen.
728
 
      Default is to center window. Setting startx to None is the default
729
 
      and centers window horizontally on screen.
730
 
 
731
 
    starty: starting position in pixels from the top edge of the screen.
732
 
      Default is to center window. Setting starty to None is the default
733
 
      and centers window vertically on screen.
734
 
 
735
 
    Examples:
736
 
    >>> setup (width=200, height=200, startx=0, starty=0)
737
 
 
738
 
    sets window to 200x200 pixels, in upper left of screen
739
 
 
740
 
    >>> setup(width=.75, height=0.5, startx=None, starty=None)
741
 
 
742
 
    sets window to 75% of screen by 50% of screen and centers
743
 
 
744
 
    >>> setup(width=None)
745
 
 
746
 
    forces use of default geometry as in older versions of turtle.py
747
 
    """
748
 
 
749
 
    global _width, _height, _startx, _starty
750
 
 
751
 
    width = geometry.get('width',_width)
752
 
    if width >= 0 or width is None:
753
 
        _width = width
754
 
    else:
755
 
        raise ValueError, "width can not be less than 0"
756
 
 
757
 
    height = geometry.get('height',_height)
758
 
    if height >= 0 or height is None:
759
 
        _height = height
760
 
    else:
761
 
        raise ValueError, "height can not be less than 0"
762
 
 
763
 
    startx = geometry.get('startx', _startx)
764
 
    if startx >= 0 or startx is None:
765
 
        _startx = _startx
766
 
    else:
767
 
        raise ValueError, "startx can not be less than 0"
768
 
 
769
 
    starty = geometry.get('starty', _starty)
770
 
    if starty >= 0 or starty is None:
771
 
        _starty = starty
772
 
    else:
773
 
        raise ValueError, "startx can not be less than 0"
774
 
 
775
 
 
776
 
    if _root and _width and _height:
777
 
        if 0 < _width <= 1:
778
 
            _width = _root.winfo_screenwidth() * +width
779
 
        if 0 < _height <= 1:
780
 
            _height = _root.winfo_screenheight() * _height
781
 
 
782
 
        # center window on screen
783
 
        if _startx is None:
784
 
            _startx = (_root.winfo_screenwidth() - _width) / 2
785
 
 
786
 
        if _starty is None:
787
 
            _starty = (_root.winfo_screenheight() - _height) / 2
788
 
 
789
 
        _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty))
790
 
 
791
 
def title(title):
792
 
    """Set the window title.
793
 
 
794
 
    By default this is set to 'Turtle Graphics'
795
 
 
796
 
    Example:
797
 
    >>> title("My Window")
798
 
    """
799
 
 
800
 
    global _title
801
 
    _title = title
802
 
 
803
 
def demo():
804
 
    reset()
805
 
    tracer(1)
806
 
    up()
807
 
    backward(100)
808
 
    down()
809
 
    # draw 3 squares; the last filled
810
 
    width(3)
811
 
    for i in range(3):
812
 
        if i == 2:
813
 
            fill(1)
814
 
        for j in range(4):
815
 
            forward(20)
816
 
            left(90)
817
 
        if i == 2:
818
 
            color("maroon")
819
 
            fill(0)
 
3705
    """Create the 'anonymous' turtle if not already present."""
 
3706
    if Turtle._pen is None:
 
3707
        Turtle._pen = Turtle()
 
3708
    return Turtle._pen
 
3709
 
 
3710
def _getscreen():
 
3711
    """Create a TurtleScreen if not already present."""
 
3712
    if Turtle._screen is None:
 
3713
        Turtle._screen = Screen()
 
3714
    return Turtle._screen
 
3715
 
 
3716
def write_docstringdict(filename="turtle_docstringdict"):
 
3717
    """Create and write docstring-dictionary to file.
 
3718
 
 
3719
    Optional argument:
 
3720
    filename -- a string, used as filename
 
3721
                default value is turtle_docstringdict
 
3722
 
 
3723
    Has to be called explicitely, (not used by the turtle-graphics classes)
 
3724
    The docstring dictionary will be written to the Python script <filname>.py
 
3725
    It is intended to serve as a template for translation of the docstrings
 
3726
    into different languages.
 
3727
    """
 
3728
    docsdict = {}
 
3729
 
 
3730
    for methodname in _tg_screen_functions:
 
3731
        key = "Screen."+methodname
 
3732
        docsdict[key] = eval(key).__doc__
 
3733
    for methodname in _tg_turtle_functions:
 
3734
        key = "Turtle."+methodname
 
3735
        docsdict[key] = eval(key).__doc__
 
3736
 
 
3737
    f = open("%s.py" % filename,"w")
 
3738
    keys = sorted([x for x in docsdict.keys()
 
3739
                        if x.split('.')[1] not in _alias_list])
 
3740
    f.write('docsdict = {\n\n')
 
3741
    for key in keys[:-1]:
 
3742
        f.write('%s :\n' % repr(key))
 
3743
        f.write('        """%s\n""",\n\n' % docsdict[key])
 
3744
    key = keys[-1]
 
3745
    f.write('%s :\n' % repr(key))
 
3746
    f.write('        """%s\n"""\n\n' % docsdict[key])
 
3747
    f.write("}\n")
 
3748
    f.close()
 
3749
 
 
3750
def read_docstrings(lang):
 
3751
    """Read in docstrings from lang-specific docstring dictionary.
 
3752
 
 
3753
    Transfer docstrings, translated to lang, from a dictionary-file
 
3754
    to the methods of classes Screen and Turtle and - in revised form -
 
3755
    to the corresponding functions.
 
3756
    """
 
3757
    modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()}
 
3758
    module = __import__(modname)
 
3759
    docsdict = module.docsdict
 
3760
    for key in docsdict:
 
3761
        #print key
 
3762
        try:
 
3763
            eval(key).im_func.__doc__ = docsdict[key]
 
3764
        except:
 
3765
            print "Bad docstring-entry: %s" % key
 
3766
 
 
3767
_LANGUAGE = _CFG["language"]
 
3768
 
 
3769
try:
 
3770
    if _LANGUAGE != "english":
 
3771
        read_docstrings(_LANGUAGE)
 
3772
except ImportError:
 
3773
    print "Cannot find docsdict for", _LANGUAGE
 
3774
except:
 
3775
    print ("Unknown Error when trying to import %s-docstring-dictionary" %
 
3776
                                                                  _LANGUAGE)
 
3777
 
 
3778
 
 
3779
def getmethparlist(ob):
 
3780
    "Get strings describing the arguments for the given object"
 
3781
    argText1 = argText2 = ""
 
3782
    # bit of a hack for methods - turn it into a function
 
3783
    # but we drop the "self" param.
 
3784
    if type(ob)==types.MethodType:
 
3785
        fob = ob.im_func
 
3786
        argOffset = 1
 
3787
    else:
 
3788
        fob = ob
 
3789
        argOffset = 0
 
3790
    # Try and build one for Python defined functions
 
3791
    if type(fob) in [types.FunctionType, types.LambdaType]:
 
3792
        try:
 
3793
            counter = fob.func_code.co_argcount
 
3794
            items2 = list(fob.func_code.co_varnames[argOffset:counter])
 
3795
            realArgs = fob.func_code.co_varnames[argOffset:counter]
 
3796
            defaults = fob.func_defaults or []
 
3797
            defaults = list(map(lambda name: "=%s" % repr(name), defaults))
 
3798
            defaults = [""] * (len(realArgs)-len(defaults)) + defaults
 
3799
            items1 = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
 
3800
            if fob.func_code.co_flags & 0x4:
 
3801
                items1.append("*"+fob.func_code.co_varnames[counter])
 
3802
                items2.append("*"+fob.func_code.co_varnames[counter])
 
3803
                counter += 1
 
3804
            if fob.func_code.co_flags & 0x8:
 
3805
                items1.append("**"+fob.func_code.co_varnames[counter])
 
3806
                items2.append("**"+fob.func_code.co_varnames[counter])
 
3807
            argText1 = ", ".join(items1)
 
3808
            argText1 = "(%s)" % argText1
 
3809
            argText2 = ", ".join(items2)
 
3810
            argText2 = "(%s)" % argText2
 
3811
        except:
 
3812
            pass
 
3813
    return argText1, argText2
 
3814
 
 
3815
def _turtle_docrevise(docstr):
 
3816
    """To reduce docstrings from RawTurtle class for functions
 
3817
    """
 
3818
    import re
 
3819
    if docstr is None:
 
3820
        return None
 
3821
    turtlename = _CFG["exampleturtle"]
 
3822
    newdocstr = docstr.replace("%s." % turtlename,"")
 
3823
    parexp = re.compile(r' \(.+ %s\):' % turtlename)
 
3824
    newdocstr = parexp.sub(":", newdocstr)
 
3825
    return newdocstr
 
3826
 
 
3827
def _screen_docrevise(docstr):
 
3828
    """To reduce docstrings from TurtleScreen class for functions
 
3829
    """
 
3830
    import re
 
3831
    if docstr is None:
 
3832
        return None
 
3833
    screenname = _CFG["examplescreen"]
 
3834
    newdocstr = docstr.replace("%s." % screenname,"")
 
3835
    parexp = re.compile(r' \(.+ %s\):' % screenname)
 
3836
    newdocstr = parexp.sub(":", newdocstr)
 
3837
    return newdocstr
 
3838
 
 
3839
## The following mechanism makes all methods of RawTurtle and Turtle available
 
3840
## as functions. So we can enhance, change, add, delete methods to these
 
3841
## classes and do not need to change anything here.
 
3842
 
 
3843
 
 
3844
for methodname in _tg_screen_functions:
 
3845
    pl1, pl2 = getmethparlist(eval('Screen.' + methodname))
 
3846
    if pl1 == "":
 
3847
        print ">>>>>>", pl1, pl2
 
3848
        continue
 
3849
    defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" %
 
3850
                                   {'key':methodname, 'pl1':pl1, 'pl2':pl2})
 
3851
    exec defstr
 
3852
    eval(methodname).__doc__ = _screen_docrevise(eval('Screen.'+methodname).__doc__)
 
3853
 
 
3854
for methodname in _tg_turtle_functions:
 
3855
    pl1, pl2 = getmethparlist(eval('Turtle.' + methodname))
 
3856
    if pl1 == "":
 
3857
        print ">>>>>>", pl1, pl2
 
3858
        continue
 
3859
    defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" %
 
3860
                                   {'key':methodname, 'pl1':pl1, 'pl2':pl2})
 
3861
    exec defstr
 
3862
    eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__)
 
3863
 
 
3864
 
 
3865
done = mainloop = TK.mainloop
 
3866
del pl1, pl2, defstr
 
3867
 
 
3868
if __name__ == "__main__":
 
3869
    def switchpen():
 
3870
        if isdown():
 
3871
            pu()
 
3872
        else:
 
3873
            pd()
 
3874
 
 
3875
    def demo1():
 
3876
        """Demo of old turtle.py - module"""
 
3877
        reset()
 
3878
        tracer(True)
820
3879
        up()
821
 
        forward(30)
 
3880
        backward(100)
822
3881
        down()
823
 
    width(1)
824
 
    color("black")
825
 
    # move out of the way
826
 
    tracer(0)
827
 
    up()
828
 
    right(90)
829
 
    forward(100)
830
 
    right(90)
831
 
    forward(100)
832
 
    right(180)
833
 
    down()
834
 
    # some text
835
 
    write("startstart", 1)
836
 
    write("start", 1)
837
 
    color("red")
838
 
    # staircase
839
 
    for i in range(5):
840
 
        forward(20)
841
 
        left(90)
842
 
        forward(20)
843
 
        right(90)
844
 
    # filled staircase
845
 
    fill(1)
846
 
    for i in range(5):
847
 
        forward(20)
848
 
        left(90)
849
 
        forward(20)
850
 
        right(90)
851
 
    fill(0)
852
 
    tracer(1)
853
 
    # more text
854
 
    write("end")
855
 
 
856
 
def demo2():
857
 
    # exercises some new and improved features
858
 
    speed('fast')
859
 
    width(3)
860
 
 
861
 
    # draw a segmented half-circle
862
 
    setheading(towards(0,0))
863
 
    x,y = position()
864
 
    r = (x**2+y**2)**.5/2.0
865
 
    right(90)
866
 
    pendown = True
867
 
    for i in range(18):
868
 
        if pendown:
 
3882
        # draw 3 squares; the last filled
 
3883
        width(3)
 
3884
        for i in range(3):
 
3885
            if i == 2:
 
3886
                fill(1)
 
3887
            for _ in range(4):
 
3888
                forward(20)
 
3889
                left(90)
 
3890
            if i == 2:
 
3891
                color("maroon")
 
3892
                fill(0)
869
3893
            up()
870
 
            pendown = False
871
 
        else:
 
3894
            forward(30)
872
3895
            down()
873
 
            pendown = True
874
 
        circle(r,10)
875
 
    sleep(2)
876
 
 
877
 
    reset()
878
 
    left(90)
879
 
 
880
 
    # draw a series of triangles
881
 
    l = 10
882
 
    color("green")
883
 
    width(3)
884
 
    left(180)
885
 
    sp = 5
886
 
    for i in range(-2,16):
887
 
        if i > 0:
888
 
            color(1.0-0.05*i,0,0.05*i)
889
 
            fill(1)
890
 
            color("green")
891
 
        for j in range(3):
892
 
            forward(l)
893
 
            left(120)
894
 
        l += 10
895
 
        left(15)
896
 
        if sp > 0:
897
 
            sp = sp-1
898
 
            speed(speeds[sp])
899
 
    color(0.25,0,0.75)
900
 
    fill(0)
901
 
 
902
 
    # draw and fill a concave shape
903
 
    left(120)
904
 
    up()
905
 
    forward(70)
906
 
    right(30)
907
 
    down()
908
 
    color("red")
909
 
    speed("fastest")
910
 
    fill(1)
911
 
    for i in range(4):
912
 
        circle(50,90)
913
 
        right(90)
914
 
        forward(30)
915
 
        right(90)
916
 
    color("yellow")
917
 
    fill(0)
918
 
    left(90)
919
 
    up()
920
 
    forward(30)
921
 
    down();
922
 
 
923
 
    color("red")
924
 
 
925
 
    # create a second turtle and make the original pursue and catch it
926
 
    turtle=Turtle()
927
 
    turtle.reset()
928
 
    turtle.left(90)
929
 
    turtle.speed('normal')
930
 
    turtle.up()
931
 
    turtle.goto(280,40)
932
 
    turtle.left(24)
933
 
    turtle.down()
934
 
    turtle.speed('fast')
935
 
    turtle.color("blue")
936
 
    turtle.width(2)
937
 
    speed('fastest')
938
 
 
939
 
    # turn default turtle towards new turtle object
940
 
    setheading(towards(turtle))
941
 
    while ( abs(position()[0]-turtle.position()[0])>4 or
942
 
            abs(position()[1]-turtle.position()[1])>4):
943
 
        turtle.forward(3.5)
944
 
        turtle.left(0.6)
945
 
        # turn default turtle towards new turtle object
 
3896
        width(1)
 
3897
        color("black")
 
3898
        # move out of the way
 
3899
        tracer(False)
 
3900
        up()
 
3901
        right(90)
 
3902
        forward(100)
 
3903
        right(90)
 
3904
        forward(100)
 
3905
        right(180)
 
3906
        down()
 
3907
        # some text
 
3908
        write("startstart", 1)
 
3909
        write("start", 1)
 
3910
        color("red")
 
3911
        # staircase
 
3912
        for i in range(5):
 
3913
            forward(20)
 
3914
            left(90)
 
3915
            forward(20)
 
3916
            right(90)
 
3917
        # filled staircase
 
3918
        tracer(True)
 
3919
        fill(1)
 
3920
        for i in range(5):
 
3921
            forward(20)
 
3922
            left(90)
 
3923
            forward(20)
 
3924
            right(90)
 
3925
        fill(0)
 
3926
        # more text
 
3927
 
 
3928
    def demo2():
 
3929
        """Demo of some new features."""
 
3930
        speed(1)
 
3931
        st()
 
3932
        pensize(3)
 
3933
        setheading(towards(0, 0))
 
3934
        radius = distance(0, 0)/2.0
 
3935
        rt(90)
 
3936
        for _ in range(18):
 
3937
            switchpen()
 
3938
            circle(radius, 10)
 
3939
        write("wait a moment...")
 
3940
        while undobufferentries():
 
3941
            undo()
 
3942
        reset()
 
3943
        lt(90)
 
3944
        colormode(255)
 
3945
        laenge = 10
 
3946
        pencolor("green")
 
3947
        pensize(3)
 
3948
        lt(180)
 
3949
        for i in range(-2, 16):
 
3950
            if i > 0:
 
3951
                begin_fill()
 
3952
                fillcolor(255-15*i, 0, 15*i)
 
3953
            for _ in range(3):
 
3954
                fd(laenge)
 
3955
                lt(120)
 
3956
            laenge += 10
 
3957
            lt(15)
 
3958
            speed((speed()+1)%12)
 
3959
        end_fill()
 
3960
 
 
3961
        lt(120)
 
3962
        pu()
 
3963
        fd(70)
 
3964
        rt(30)
 
3965
        pd()
 
3966
        color("red","yellow")
 
3967
        speed(0)
 
3968
        fill(1)
 
3969
        for _ in range(4):
 
3970
            circle(50, 90)
 
3971
            rt(90)
 
3972
            fd(30)
 
3973
            rt(90)
 
3974
        fill(0)
 
3975
        lt(90)
 
3976
        pu()
 
3977
        fd(30)
 
3978
        pd()
 
3979
        shape("turtle")
 
3980
 
 
3981
        tri = getturtle()
 
3982
        tri.resizemode("auto")
 
3983
        turtle = Turtle()
 
3984
        turtle.resizemode("auto")
 
3985
        turtle.shape("turtle")
 
3986
        turtle.reset()
 
3987
        turtle.left(90)
 
3988
        turtle.speed(0)
 
3989
        turtle.up()
 
3990
        turtle.goto(280, 40)
 
3991
        turtle.lt(30)
 
3992
        turtle.down()
 
3993
        turtle.speed(6)
 
3994
        turtle.color("blue","orange")
 
3995
        turtle.pensize(2)
 
3996
        tri.speed(6)
946
3997
        setheading(towards(turtle))
947
 
        forward(4)
948
 
    write("CAUGHT! ", move=True)
949
 
 
950
 
 
951
 
 
952
 
if __name__ == '__main__':
953
 
    demo()
954
 
    sleep(3)
 
3998
        count = 1
 
3999
        while tri.distance(turtle) > 4:
 
4000
            turtle.fd(3.5)
 
4001
            turtle.lt(0.6)
 
4002
            tri.setheading(tri.towards(turtle))
 
4003
            tri.fd(4)
 
4004
            if count % 20 == 0:
 
4005
                turtle.stamp()
 
4006
                tri.stamp()
 
4007
                switchpen()
 
4008
            count += 1
 
4009
        tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right")
 
4010
        tri.pencolor("black")
 
4011
        tri.pencolor("red")
 
4012
 
 
4013
        def baba(xdummy, ydummy):
 
4014
            clearscreen()
 
4015
            bye()
 
4016
 
 
4017
        time.sleep(2)
 
4018
 
 
4019
        while undobufferentries():
 
4020
            tri.undo()
 
4021
            turtle.undo()
 
4022
        tri.fd(50)
 
4023
        tri.write("  Click me!", font = ("Courier", 12, "bold") )
 
4024
        tri.onclick(baba, 1)
 
4025
 
 
4026
    demo1()
955
4027
    demo2()
956
 
    done()
 
4028
    exitonclick()