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