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