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