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