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