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