From 97cf99fc7e8c56868711f7c91c3b1e37e4c66cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 10 Jun 2008 04:44:07 +0000 Subject: Patch #3064: Port new turtle module and demos to 3.0. --- Demo/turtle/about_turtle.txt | 76 + Demo/turtle/about_turtledemo.txt | 13 + Demo/turtle/demohelp.txt | 75 + Demo/turtle/tdemo_I_dontlike_tiltdemo.py | 58 + Demo/turtle/tdemo_bytedesign.py | 162 ++ Demo/turtle/tdemo_chaos.py | 63 + Demo/turtle/tdemo_clock.py | 132 + Demo/turtle/tdemo_colormixer.py | 60 + Demo/turtle/tdemo_forest.py | 109 + Demo/turtle/tdemo_fractalcurves.py | 138 + Demo/turtle/tdemo_lindenmayer_indian.py | 119 + Demo/turtle/tdemo_minimal_hanoi.py | 76 + Demo/turtle/tdemo_paint.py | 50 + Demo/turtle/tdemo_peace.py | 65 + Demo/turtle/tdemo_penrose.py | 181 ++ Demo/turtle/tdemo_planet_and_moon.py | 113 + Demo/turtle/tdemo_tree.py | 63 + Demo/turtle/tdemo_wikipedia.py | 65 + Demo/turtle/tdemo_yinyang.py | 49 + Demo/turtle/turtle.cfg | 10 + Demo/turtle/turtleDemo.py | 291 ++ Demo/turtle/turtledemo_two_canvases.py | 52 + Doc/library/tkinter.turtle.rst | 1885 +++++++++++- Lib/tkinter/turtle.py | 4679 +++++++++++++++++++++++++----- Misc/ACKS | 1 + 25 files changed, 7650 insertions(+), 935 deletions(-) create mode 100644 Demo/turtle/about_turtle.txt create mode 100644 Demo/turtle/about_turtledemo.txt create mode 100644 Demo/turtle/demohelp.txt create mode 100644 Demo/turtle/tdemo_I_dontlike_tiltdemo.py create mode 100644 Demo/turtle/tdemo_bytedesign.py create mode 100644 Demo/turtle/tdemo_chaos.py create mode 100644 Demo/turtle/tdemo_clock.py create mode 100644 Demo/turtle/tdemo_colormixer.py create mode 100644 Demo/turtle/tdemo_forest.py create mode 100644 Demo/turtle/tdemo_fractalcurves.py create mode 100644 Demo/turtle/tdemo_lindenmayer_indian.py create mode 100644 Demo/turtle/tdemo_minimal_hanoi.py create mode 100644 Demo/turtle/tdemo_paint.py create mode 100644 Demo/turtle/tdemo_peace.py create mode 100644 Demo/turtle/tdemo_penrose.py create mode 100644 Demo/turtle/tdemo_planet_and_moon.py create mode 100644 Demo/turtle/tdemo_tree.py create mode 100644 Demo/turtle/tdemo_wikipedia.py create mode 100644 Demo/turtle/tdemo_yinyang.py create mode 100644 Demo/turtle/turtle.cfg create mode 100644 Demo/turtle/turtleDemo.py create mode 100644 Demo/turtle/turtledemo_two_canvases.py diff --git a/Demo/turtle/about_turtle.txt b/Demo/turtle/about_turtle.txt new file mode 100644 index 0000000..e4ba217 --- /dev/null +++ b/Demo/turtle/about_turtle.txt @@ -0,0 +1,76 @@ + +======================================================== + A new turtle module for Python +======================================================== + +Turtle graphics is a popular way for introducing programming to +kids. It was part of the original Logo programming language developed +by Wally Feurzig and Seymour Papert in 1966. + +Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it +the command turtle.forward(15), and it moves (on-screen!) 15 pixels in +the direction it is facing, drawing a line as it moves. Give it the +command turtle.left(25), and it rotates in-place 25 degrees clockwise. + +By combining together these and similar commands, intricate shapes and +pictures can easily be drawn. + +----- turtle.py + +This module is an extended reimplementation of turtle.py from the +Python standard distribution up to Python 2.5. (See: http:\\www.python.org) + +It tries to keep the merits of turtle.py and to be (nearly) 100% +compatible with it. This means in the first place to enable the +learning programmer to use all the commands, classes and methods +interactively when using the module from within IDLE run with +the -n switch. + +Roughly it has the following features added: + +- Better animation of the turtle movements, especially of turning the + turtle. So the turtles can more easily be used as a visual feedback + instrument by the (beginning) programmer. + +- Different turtle shapes, gif-images as turtle shapes, user defined + and user controllable turtle shapes, among them compound + (multicolored) shapes. Turtle shapes can be stgretched and tilted, which + makes turtles zu very versatile geometrical objects. + +- Fine control over turtle movement and screen updates via delay(), + and enhanced tracer() and speed() methods. + +- Aliases for the most commonly used commands, like fd for forward etc., + following the early Logo traditions. This reduces the boring work of + typing long sequences of commands, which often occur in a natural way + when kids try to program fancy pictures on their first encounter with + turtle graphcis. + +- Turtles now have an undo()-method with configurable undo-buffer. + +- Some simple commands/methods for creating event driven programs + (mouse-, key-, timer-events). Especially useful for programming games. + +- A scrollable Canvas class. The default scrollable Canvas can be + extended interactively as needed while playing around with the turtle(s). + +- A TurtleScreen class with methods controlling background color or + background image, window and canvas size and other properties of the + TurtleScreen. + +- There is a method, setworldcoordinates(), to install a user defined + coordinate-system for the TurtleScreen. + +- The implementation uses a 2-vector class named Vec2D, derived from tuple. + This class is public, so it can be imported by the application programmer, + which makes certain types of computations very natural and compact. + +- Appearance of the TurtleScreen and the Turtles at startup/import can be + configured by means of a turtle.cfg configuration file. + The default configuration mimics the appearance of the old turtle module. + +- If configured appropriately the module reads in docstrings from a docstring + dictionary in some different language, supplied separately and replaces + the english ones by those read in. There is a utility function + write_docstringdict() to write a dictionary with the original (english) + docstrings to disc, so it can serve as a template for translations. diff --git a/Demo/turtle/about_turtledemo.txt b/Demo/turtle/about_turtledemo.txt new file mode 100644 index 0000000..54c25a5 --- /dev/null +++ b/Demo/turtle/about_turtledemo.txt @@ -0,0 +1,13 @@ + + -------------------------------------- + About turtleDemo.py + -------------------------------------- + + Tiny demo Viewer to view turtle graphics example scripts. + + Quickly and dirtyly assembled by Gregor Lingl. + June, 2006 + + For more information see: turtleDemo - Help + + Have fun! diff --git a/Demo/turtle/demohelp.txt b/Demo/turtle/demohelp.txt new file mode 100644 index 0000000..5683875 --- /dev/null +++ b/Demo/turtle/demohelp.txt @@ -0,0 +1,75 @@ + + + ---------------------------------------------- + + turtleDemo - Help + + ---------------------------------------------- + + This document has two sections: + + (1) How to use the demo viewer + (2) How to add your own demos to the demo repository + + + (1) How to use the demo viewer. + + Select a demoscript from the example menu. + The (syntax coloured) source code appears in the left + source code window. IT CANNOT BE EDITED, but ONLY VIEWED! + + - Press START button to start the demo. + - Stop execution by pressing the STOP button. + - Clear screen by pressing the CLEAR button. + - Restart by pressing the START button again. + + SPECIAL demos are those which run EVENTDRIVEN. + (For example clock.py - or oldTurtleDemo.py which + in the end expects a mouse click.): + + Press START button to start the demo. + + - Until the EVENTLOOP is entered everything works + as in an ordinary demo script. + + - When the EVENTLOOP is entered, you control the + application by using the mouse and/or keys (or it's + controlled by some timer events) + To stop it you can and must press the STOP button. + + While the EVENTLOOP is running, the examples menu is disabled. + + - Only after having pressed the STOP button, you may + restart it or choose another example script. + + * * * * * * * * + In some rare situations there may occur interferences/conflicts + between events concerning the demo script and those concerning the + demo-viewer. (They run in the same process.) Strange behaviour may be + the consequence and in the worst case you must close and restart the + viewer. + * * * * * * * * + + + (2) How to add your own demos to the demo repository + + - scriptname: must begin with tdemo_ , + so it must have the form tdemo_.py + + - place: same directory as turtleDemo.py or some + subdirectory, the name of which must also begin with + tdemo_..... + + - requirements on source code: + code must contain a main() function which will + be executed by the viewer (see provided example scripts) + main() may return a string which will be displayed + in the Label below the source code window (when execution + has finished.) + + !! For programs, which are EVENT DRIVEN, main must return + !! the string "EVENTLOOP". This informs the viewer, that the + !! script is still running and must be stopped by the user! + + + diff --git a/Demo/turtle/tdemo_I_dontlike_tiltdemo.py b/Demo/turtle/tdemo_I_dontlike_tiltdemo.py new file mode 100644 index 0000000..7cae675 --- /dev/null +++ b/Demo/turtle/tdemo_I_dontlike_tiltdemo.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo-I_dont_like_tiltdemo.py + +Demostrates + (a) use of a tilted ellipse as + turtle shape + (b) stamping that shape + +We can remove it, if you don't like it. + Without using reset() ;-) + --------------------------------------- +""" +from tkinter.turtle import * +import time + +def main(): + reset() + shape("circle") + resizemode("user") + + pu(); bk(24*18/6.283); rt(90); pd() + tilt(45) + + pu() + + turtlesize(16,10,5) + color("red", "violet") + for i in range(18): + fd(24) + lt(20) + stamp() + color("red", "") + for i in range(18): + fd(24) + lt(20) + stamp() + + tilt(-15) + turtlesize(3, 1, 4) + color("blue", "yellow") + for i in range(17): + fd(24) + lt(20) + if i%2 == 0: + stamp() + time.sleep(1) + while undobufferentries(): + undo() + ht() + write("OK, OVER!", align="center", font=("Courier", 18, "bold")) + return "Done!" + +if __name__=="__main__": + msg = main() + print(msg) +# mainloop() diff --git a/Demo/turtle/tdemo_bytedesign.py b/Demo/turtle/tdemo_bytedesign.py new file mode 100644 index 0000000..742df06 --- /dev/null +++ b/Demo/turtle/tdemo_bytedesign.py @@ -0,0 +1,162 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_bytedesign.py + +An example adapted from the example-suite +of PythonCard's turtle graphcis. + +It's based on an article in BYTE magazine +Problem Solving with Logo: Using Turtle +Graphics to Redraw a Design +November 1982, p. 118 - 134 + +------------------------------------------- + +Due to the statement + +t.delay(0) + +in line 152, which sets the animation delay +to 0, this animation runs in "line per line" +mode as fast as possible. +""" + +import math +from tkinter.turtle import Turtle, mainloop +from time import clock + +# wrapper for any additional drawing routines +# that need to know about each other +class Designer(Turtle): + + def design(self, homePos, scale): + self.up() + for i in range(5): + self.forward(64.65 * scale) + self.down() + self.wheel(self.position(), scale) + self.up() + self.backward(64.65 * scale) + self.right(72) + self.up() + self.goto(homePos) + self.right(36) + self.forward(24.5 * scale) + self.right(198) + self.down() + self.centerpiece(46 * scale, 143.4, scale) + self.getscreen().tracer(True) + + def wheel(self, initpos, scale): + self.right(54) + for i in range(4): + self.pentpiece(initpos, scale) + self.down() + self.left(36) + for i in range(5): + self.tripiece(initpos, scale) + self.left(36) + for i in range(5): + self.down() + self.right(72) + self.forward(28 * scale) + self.up() + self.backward(28 * scale) + self.left(54) + self.getscreen().update() + + def tripiece(self, initpos, scale): + oldh = self.heading() + self.down() + self.backward(2.5 * scale) + self.tripolyr(31.5 * scale, scale) + self.up() + self.goto(initpos) + self.setheading(oldh) + self.down() + self.backward(2.5 * scale) + self.tripolyl(31.5 * scale, scale) + self.up() + self.goto(initpos) + self.setheading(oldh) + self.left(72) + self.getscreen().update() + + def pentpiece(self, initpos, scale): + oldh = self.heading() + self.up() + self.forward(29 * scale) + self.down() + for i in range(5): + self.forward(18 * scale) + self.right(72) + self.pentr(18 * scale, 75, scale) + self.up() + self.goto(initpos) + self.setheading(oldh) + self.forward(29 * scale) + self.down() + for i in range(5): + self.forward(18 * scale) + self.right(72) + self.pentl(18 * scale, 75, scale) + self.up() + self.goto(initpos) + self.setheading(oldh) + self.left(72) + self.getscreen().update() + + def pentl(self, side, ang, scale): + if side < (2 * scale): return + self.forward(side) + self.left(ang) + self.pentl(side - (.38 * scale), ang, scale) + + def pentr(self, side, ang, scale): + if side < (2 * scale): return + self.forward(side) + self.right(ang) + self.pentr(side - (.38 * scale), ang, scale) + + def tripolyr(self, side, scale): + if side < (4 * scale): return + self.forward(side) + self.right(111) + self.forward(side / 1.78) + self.right(111) + self.forward(side / 1.3) + self.right(146) + self.tripolyr(side * .75, scale) + + def tripolyl(self, side, scale): + if side < (4 * scale): return + self.forward(side) + self.left(111) + self.forward(side / 1.78) + self.left(111) + self.forward(side / 1.3) + self.left(146) + self.tripolyl(side * .75, scale) + + def centerpiece(self, s, a, scale): + self.forward(s); self.left(a) + if s < (7.5 * scale): + return + self.centerpiece(s - (1.2 * scale), a, scale) + +def main(): + t = Designer() + t.speed(0) + t.hideturtle() + t.getscreen().delay(0) + t.getscreen().tracer(0) + at = clock() + t.design(t.position(), 2) + et = clock() + return "runtime: %.2f sec." % (et-at) + +if __name__ == '__main__': + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_chaos.py b/Demo/turtle/tdemo_chaos.py new file mode 100644 index 0000000..73a0453 --- /dev/null +++ b/Demo/turtle/tdemo_chaos.py @@ -0,0 +1,63 @@ +# Datei: chaosplotter.py +# Autor: Gregor Lingl +# Datum: 31. 5. 2008 + +# Ein einfaches Programm zur Demonstration von "chaotischem Verhalten". + +from tkinter.turtle import * + +def f(x): + return 3.9*x*(1-x) + +def g(x): + return 3.9*(x-x**2) + +def h(x): + return 3.9*x-3.9*x*x + +def coosys(): + penup() + goto(-1,0) + pendown() + goto(n+1,0) + penup() + goto(0, -0.1) + pendown() + goto(-0.1, 1.1) + +def plot(fun, start, farbe): + x = start + pencolor(farbe) + penup() + goto(0, x) + pendown() + dot(5) + for i in range(n): + x=fun(x) + goto(i+1,x) + dot(5) + +def main(): + global n + n = 80 + ox=-250.0 + oy=-150.0 + ex= -2.0*ox / n + ey=300.0 + + reset() + setworldcoordinates(-1.0,-0.1, n+1, 1.1) + speed(0) + hideturtle() + coosys() + plot(f, 0.35, "blue") + plot(g, 0.35, "green") + plot(h, 0.35, "red") + for s in range(100): + setworldcoordinates(0.5*s,-0.1, n+1, 1.1) + + return "Done!" + +if __name__ == "__main__": + main() + mainloop() diff --git a/Demo/turtle/tdemo_clock.py b/Demo/turtle/tdemo_clock.py new file mode 100644 index 0000000..c91d4c5 --- /dev/null +++ b/Demo/turtle/tdemo_clock.py @@ -0,0 +1,132 @@ +#!/usr/bin/python +# -*- coding: cp1252 -*- +""" turtle-example-suite: + + tdemo_clock.py + +Enhanced clock-program, showing date +and time + ------------------------------------ + Press STOP to exit the program! + ------------------------------------ +""" +from tkinter.turtle import * +from datetime import datetime + +mode("logo") + +def jump(distanz, winkel=0): + penup() + right(winkel) + forward(distanz) + left(winkel) + pendown() + +def hand(laenge, spitze): + fd(laenge*1.15) + rt(90) + fd(spitze/2.0) + lt(120) + fd(spitze) + lt(120) + fd(spitze) + lt(120) + fd(spitze/2.0) + +def make_hand_shape(name, laenge, spitze): + reset() + jump(-laenge*0.15) + begin_poly() + hand(laenge, spitze) + end_poly() + hand_form = get_poly() + register_shape(name, hand_form) + + +def clockface(radius): + reset() + pensize(7) + for i in range(60): + jump(radius) + if i % 5 == 0: + fd(25) + jump(-radius-25) + else: + dot(3) + jump(-radius) + rt(6) + +def setup(): + global second_hand, minute_hand, hour_hand, writer + mode("logo") + make_hand_shape("second_hand", 125, 25) + make_hand_shape("minute_hand", 130, 25) + make_hand_shape("hour_hand", 90, 25) + clockface(160) + second_hand = Turtle() + second_hand.shape("second_hand") + second_hand.color("gray20", "gray80") + minute_hand = Turtle() + minute_hand.shape("minute_hand") + minute_hand.color("blue1", "red1") + hour_hand = Turtle() + hour_hand.shape("hour_hand") + hour_hand.color("blue3", "red3") + for hand in second_hand, minute_hand, hour_hand: + hand.resizemode("user") + hand.shapesize(1, 1, 3) + hand.speed(0) + ht() + writer = Turtle() + #writer.mode("logo") + writer.ht() + writer.pu() + writer.bk(85) + + +def wochentag(t): + wochentag = ["Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", "Sunday"] + return wochentag[t.weekday()] + +def datum(z): + monat = ["Jan.", "Feb.", "Mar.", "Apr.", "May", "June", + "July", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."] + j = z.year + m = monat[z.month - 1] + t = z.day + return "%s %d %d" % (m, t, j) + +def tick(): + t = datetime.today() + sekunde = t.second + t.microsecond*0.000001 + minute = t.minute + sekunde/60.0 + stunde = t.hour + minute/60.0 + tracer(False) + writer.clear() + writer.home() + writer.forward(65) + writer.write(wochentag(t), + align="center", font=("Courier", 14, "bold")) + writer.back(150) + writer.write(datum(t), + align="center", font=("Courier", 14, "bold")) + writer.forward(85) + tracer(True) + second_hand.setheading(6*sekunde) + minute_hand.setheading(6*minute) + hour_hand.setheading(30*stunde) + tracer(True) + ontimer(tick, 100) + +def main(): + tracer(False) + setup() + tracer(True) + tick() + return "EVENTLOOP" + +if __name__ == "__main__": + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_colormixer.py b/Demo/turtle/tdemo_colormixer.py new file mode 100644 index 0000000..93179b3 --- /dev/null +++ b/Demo/turtle/tdemo_colormixer.py @@ -0,0 +1,60 @@ +# colormixer + +from tkinter.turtle import Screen, Turtle, mainloop +import sys +sys.setrecursionlimit(20000) # overcomes, for now, an instability of Python 3.0 + +class ColorTurtle(Turtle): + + def __init__(self, x, y): + Turtle.__init__(self) + self.shape("turtle") + self.resizemode("user") + self.shapesize(3,3,5) + self.pensize(10) + self._color = [0,0,0] + self.x = x + self._color[x] = y + self.color(self._color) + self.speed(0) + self.left(90) + self.pu() + self.goto(x,0) + self.pd() + self.sety(1) + self.pu() + self.sety(y) + self.pencolor("gray25") + self.ondrag(self.shift) + + def shift(self, x, y): + self.sety(max(0,min(y,1))) + self._color[self.x] = self.ycor() + self.fillcolor(self._color) + setbgcolor() + +def setbgcolor(): + screen.bgcolor(red.ycor(), green.ycor(), blue.ycor()) + +def main(): + global screen, red, green, blue + screen = Screen() + screen.delay(0) + screen.setworldcoordinates(-1, -0.3, 3, 1.3) + + red = ColorTurtle(0, .5) + green = ColorTurtle(1, .5) + blue = ColorTurtle(2, .5) + setbgcolor() + + writer = Turtle() + writer.ht() + writer.pu() + writer.goto(1,1.15) + writer.write("DRAG!",align="center",font=("Arial",30,("bold","italic"))) + return "EVENTLOOP" + +if __name__ == "__main__": + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_forest.py b/Demo/turtle/tdemo_forest.py new file mode 100644 index 0000000..b117e3b --- /dev/null +++ b/Demo/turtle/tdemo_forest.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +""" turtlegraphics-example-suite: + + tdemo_forest.py + +Displays a 'forest' of 3 'breadth-first-trees' +similar to the one from example tree. +For further remarks see xtx_tree.py + +This example is a 'breadth-first'-rewrite of +a Logo program written by Erich Neuwirth. See: +http://homepage.univie.ac.at/erich.neuwirth/ +""" +from tkinter.turtle import Turtle, colormode, tracer, mainloop +from random import randrange +from time import clock + +def symRandom(n): + return randrange(-n,n+1) + +def randomize( branchlist, angledist, sizedist ): + return [ (angle+symRandom(angledist), + sizefactor*1.01**symRandom(sizedist)) + for angle, sizefactor in branchlist ] + +def randomfd( t, distance, parts, angledist ): + for i in range(parts): + t.left(symRandom(angledist)) + t.forward( (1.0 * distance)/parts ) + +def tree(tlist, size, level, widthfactor, branchlists, angledist=10, sizedist=5): + # benutzt Liste von turtles und Liste von Zweiglisten, + # fuer jede turtle eine! + if level > 0: + lst = [] + brs = [] + for t, branchlist in list(zip(tlist,branchlists)): + t.pensize( size * widthfactor ) + t.pencolor( 255 - (180 - 11 * level + symRandom(15)), + 180 - 11 * level + symRandom(15), + 0 ) + t.pendown() + randomfd(t, size, level, angledist ) + yield 1 + for angle, sizefactor in branchlist: + t.left(angle) + lst.append(t.clone()) + brs.append(randomize(branchlist, angledist, sizedist)) + t.right(angle) + for x in tree(lst, size*sizefactor, level-1, widthfactor, brs, + angledist, sizedist): + yield None + + +def start(t,x,y): + colormode(255) + t.reset() + t.speed(0) + t.hideturtle() + t.left(90) + t.penup() + t.setpos(x,y) + t.pendown() + +def doit1(level, pen): + pen.hideturtle() + start(pen, 20, -208) + t = tree( [pen], 80, level, 0.1, [[ (45,0.69), (0,0.65), (-45,0.71) ]] ) + return t + +def doit2(level, pen): + pen.hideturtle() + start(pen, -135, -130) + t = tree( [pen], 120, level, 0.1, [[ (45,0.69), (-45,0.71) ]] ) + return t + +def doit3(level, pen): + pen.hideturtle() + start(pen, 190, -90) + t = tree( [pen], 100, level, 0.1, [[ (45,0.7), (0,0.72), (-45,0.65) ]] ) + return t + +# Hier 3 Baumgeneratoren: +def main(): + p = Turtle() + p.ht() + tracer(75,0) + u = doit1(6, Turtle(undobuffersize=1)) + s = doit2(7, Turtle(undobuffersize=1)) + t = doit3(5, Turtle(undobuffersize=1)) + a = clock() + while True: + done = 0 + for b in u,s,t: + try: + b.__next__() + except: + done += 1 + if done == 3: + break + + tracer(1,10) + b = clock() + return "runtime: %.2f sec." % (b-a) + +if __name__ == '__main__': + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_fractalcurves.py b/Demo/turtle/tdemo_fractalcurves.py new file mode 100644 index 0000000..b7c387b --- /dev/null +++ b/Demo/turtle/tdemo_fractalcurves.py @@ -0,0 +1,138 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_fractalCurves.py + +This program draws two fractal-curve-designs: +(1) A hilbert curve (in a box) +(2) A combination of Koch-curves. + +The CurvesTurtle class and the fractal-curve- +methods are taken from the PythonCard example +scripts for turtle-graphics. +""" +from tkinter.turtle import * +from time import sleep, clock + +class CurvesTurtle(Pen): + # example derived from + # Turtle Geometry: The Computer as a Medium for Exploring Mathematics + # by Harold Abelson and Andrea diSessa + # p. 96-98 + def hilbert(self, size, level, parity): + if level == 0: + return + # rotate and draw first subcurve with opposite parity to big curve + self.left(parity * 90) + self.hilbert(size, level - 1, -parity) + # interface to and draw second subcurve with same parity as big curve + self.forward(size) + self.right(parity * 90) + self.hilbert(size, level - 1, parity) + # third subcurve + self.forward(size) + self.hilbert(size, level - 1, parity) + # fourth subcurve + self.right(parity * 90) + self.forward(size) + self.hilbert(size, level - 1, -parity) + # a final turn is needed to make the turtle + # end up facing outward from the large square + self.left(parity * 90) + + # Visual Modeling with Logo: A Structural Approach to Seeing + # by James Clayson + # Koch curve, after Helge von Koch who introduced this geometric figure in 1904 + # p. 146 + def fractalgon(self, n, rad, lev, dir): + import math + + # if dir = 1 turn outward + # if dir = -1 turn inward + edge = 2 * rad * math.sin(math.pi / n) + self.pu() + self.fd(rad) + self.pd() + self.rt(180 - (90 * (n - 2) / n)) + for i in range(n): + self.fractal(edge, lev, dir) + self.rt(360 / n) + self.lt(180 - (90 * (n - 2) / n)) + self.pu() + self.bk(rad) + self.pd() + + # p. 146 + def fractal(self, dist, depth, dir): + if depth < 1: + self.fd(dist) + return + self.fractal(dist / 3, depth - 1, dir) + self.lt(60 * dir) + self.fractal(dist / 3, depth - 1, dir) + self.rt(120 * dir) + self.fractal(dist / 3, depth - 1, dir) + self.lt(60 * dir) + self.fractal(dist / 3, depth - 1, dir) + +def main(): + ft = CurvesTurtle() + + ft.reset() + ft.speed(0) + ft.ht() + ft.getscreen().tracer(1,0) + ft.pu() + + size = 6 + ft.setpos(-33*size, -32*size) + ft.pd() + + ta=clock() + ft.fillcolor("red") + ft.begin_fill() + ft.fd(size) + + ft.hilbert(size, 6, 1) + + # frame + ft.fd(size) + for i in range(3): + ft.lt(90) + ft.fd(size*(64+i%2)) + ft.pu() + for i in range(2): + ft.fd(size) + ft.rt(90) + ft.pd() + for i in range(4): + ft.fd(size*(66+i%2)) + ft.rt(90) + ft.end_fill() + tb=clock() + res = "Hilbert: %.2fsec. " % (tb-ta) + + sleep(3) + + ft.reset() + ft.speed(0) + ft.ht() + ft.getscreen().tracer(1,0) + + ta=clock() + ft.color("black", "blue") + ft.begin_fill() + ft.fractalgon(3, 250, 4, 1) + ft.end_fill() + ft.begin_fill() + ft.color("red") + ft.fractalgon(3, 200, 4, -1) + ft.end_fill() + tb=clock() + res += "Koch: %.2fsec." % (tb-ta) + return res + +if __name__ == '__main__': + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_lindenmayer_indian.py b/Demo/turtle/tdemo_lindenmayer_indian.py new file mode 100644 index 0000000..07dd936 --- /dev/null +++ b/Demo/turtle/tdemo_lindenmayer_indian.py @@ -0,0 +1,119 @@ +#!/usr/bin/python +""" turtle-example-suite: + + xtx_lindenmayer_indian.py + +Each morning women in Tamil Nadu, in southern +India, place designs, created by using rice +flour and known as kolam on the thresholds of +their homes. + +These can be described by Lindenmayer systems, +which can easily be implemented with turtle +graphics and Python. + +Two examples are shown here: +(1) the snake kolam +(2) anklets of Krishna + +Taken from Marcia Ascher: Mathematics +Elsewhere, An Exploration of Ideas Across +Cultures + +""" +################################ +# Mini Lindenmayer tool +############################### + +from tkinter.turtle import * + +def replace( seq, replacementRules, n ): + for i in range(n): + newseq = "" + for element in seq: + newseq = newseq + replacementRules.get(element,element) + seq = newseq + return seq + +def draw( commands, rules ): + for b in commands: + try: + rules[b]() + except TypeError: + try: + draw(rules[b], rules) + except: + pass + + +def main(): + ################################ + # Example 1: Snake kolam + ################################ + + + def r(): + right(45) + + def l(): + left(45) + + def f(): + forward(7.5) + + snake_rules = {"-":r, "+":l, "f":f, "b":"f+f+f--f--f+f+f"} + snake_replacementRules = {"b": "b+f+b--f--b+f+b"} + snake_start = "b--f--b--f" + + drawing = replace(snake_start, snake_replacementRules, 3) + + reset() + speed(3) + tracer(1,0) + ht() + up() + backward(195) + down() + draw(drawing, snake_rules) + + from time import sleep + sleep(3) + + ################################ + # Example 2: Anklets of Krishna + ################################ + + def A(): + color("red") + circle(10,90) + + def B(): + from math import sqrt + color("black") + l = 5/sqrt(2) + forward(l) + circle(l, 270) + forward(l) + + def F(): + color("green") + forward(10) + + krishna_rules = {"a":A, "b":B, "f":F} + krishna_replacementRules = {"a" : "afbfa", "b" : "afbfbfbfa" } + krishna_start = "fbfbfbfb" + + reset() + speed(0) + tracer(3,0) + ht() + left(45) + drawing = replace(krishna_start, krishna_replacementRules, 3) + draw(drawing, krishna_rules) + tracer(1) + return "Done!" + +if __name__=='__main__': + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_minimal_hanoi.py b/Demo/turtle/tdemo_minimal_hanoi.py new file mode 100644 index 0000000..97e937f --- /dev/null +++ b/Demo/turtle/tdemo_minimal_hanoi.py @@ -0,0 +1,76 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_minimal_hanoi.py + +A minimal 'Towers of Hanoi' animation: +A tower of 6 discs is transferred from the +left to the right peg. + +An imho quite elegant and concise +implementation using a tower class, which +is derived from the built-in type list. + +Discs are turtles with shape "square", but +stretched to rectangles by shapesize() + --------------------------------------- + To exit press STOP button + --------------------------------------- +""" +from tkinter.turtle import * + +class Disc(Turtle): + def __init__(self, n): + Turtle.__init__(self, shape="square", visible=False) + self.pu() + self.shapesize(1.5, n*1.5, 2) # square-->rectangle + self.fillcolor(n/6., 0, 1-n/6.) + self.st() + +class Tower(list): + "Hanoi tower, a subclass of built-in type list" + def __init__(self, x): + "create an empty tower. x is x-position of peg" + self.x = x + def push(self, d): + d.setx(self.x) + d.sety(-150+34*len(self)) + self.append(d) + def pop(self): + d = list.pop(self) + d.sety(150) + return d + +def hanoi(n, from_, with_, to_): + if n > 0: + hanoi(n-1, from_, to_, with_) + to_.push(from_.pop()) + hanoi(n-1, with_, from_, to_) + +def play(): + onkey(None,"space") + clear() + hanoi(6, t1, t2, t3) + write("press STOP button to exit", + align="center", font=("Courier", 16, "bold")) + +def main(): + global t1, t2, t3 + ht(); penup(); goto(0, -225) # writer turtle + t1 = Tower(-250) + t2 = Tower(0) + t3 = Tower(250) + # make tower of 6 discs + for i in range(6,0,-1): + t1.push(Disc(i)) + # prepare spartanic user interface ;-) + write("press spacebar to start game", + align="center", font=("Courier", 16, "bold")) + onkey(play, "space") + listen() + return "EVENTLOOP" + +if __name__=="__main__": + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_paint.py b/Demo/turtle/tdemo_paint.py new file mode 100644 index 0000000..97c8756 --- /dev/null +++ b/Demo/turtle/tdemo_paint.py @@ -0,0 +1,50 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_paint.py + +A simple eventdriven paint program + +- use left mouse button to move turtle +- middle mouse button to change color +- right mouse button do turn filling on/off + ------------------------------------------- + Play around by clicking into the canvas + using all three mouse buttons. + ------------------------------------------- + To exit press STOP button + ------------------------------------------- +""" +from tkinter.turtle import * + +def switchupdown(x=0, y=0): + if pen()["pendown"]: + end_fill() + up() + else: + down() + begin_fill() + +def changecolor(x=0, y=0): + global colors + colors = colors[1:]+colors[:1] + color(colors[0]) + +def main(): + global colors + shape("circle") + resizemode("user") + shapesize(.5) + width(3) + colors=["red", "green", "blue", "yellow"] + color(colors[0]) + switchupdown() + onscreenclick(goto,1) + onscreenclick(changecolor,2) + onscreenclick(switchupdown,3) + return "EVENTLOOP" + +if __name__ == "__main__": + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_peace.py b/Demo/turtle/tdemo_peace.py new file mode 100644 index 0000000..7f7db14 --- /dev/null +++ b/Demo/turtle/tdemo_peace.py @@ -0,0 +1,65 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_peace.py + +A very simple drawing suitable as a beginner's +programming example. + +Uses only commands, which are also available in +old turtle.py. + +Intentionally no variables are used except for the +colorloop: +""" + +from tkinter.turtle import * + +def main(): + peacecolors = ("red3", "orange", "yellow", + "seagreen4", "orchid4", + "royalblue1", "dodgerblue4") + + reset() + s = Screen() + up() + goto(-320,-195) + width(70) + + for pcolor in peacecolors: + color(pcolor) + down() + forward(640) + up() + backward(640) + left(90) + forward(66) + right(90) + + width(25) + color("white") + goto(0,-170) + down() + + circle(170) + left(90) + forward(340) + up() + left(180) + forward(170) + right(45) + down() + forward(170) + up() + backward(170) + left(90) + down() + forward(170) + up() + + goto(0,300) # vanish if hideturtle() is not available ;-) + return "Done!!" + +if __name__ == "__main__": + main() + mainloop() diff --git a/Demo/turtle/tdemo_penrose.py b/Demo/turtle/tdemo_penrose.py new file mode 100644 index 0000000..e99a5f5 --- /dev/null +++ b/Demo/turtle/tdemo_penrose.py @@ -0,0 +1,181 @@ +#!/usr/bin/python +""" xturtle-example-suite: + + xtx_kites_and_darts.py + +Constructs two aperiodic penrose-tilings, +consisting of kites and darts, by the method +of inflation in six steps. + +Starting points are the patterns "sun" +consisting of five kites and "star" +consisting of five darts. + +For more information see: + http://en.wikipedia.org/wiki/Penrose_tiling + ------------------------------------------- +""" +from tkinter.turtle import * +from math import cos, pi +from time import clock, sleep + +f = (5**0.5-1)/2.0 # (sqrt(5)-1)/2 -- golden ratio +d = 2 * cos(3*pi/10) + +def kite(l): + fl = f * l + lt(36) + fd(l) + rt(108) + fd(fl) + rt(36) + fd(fl) + rt(108) + fd(l) + rt(144) + +def dart(l): + fl = f * l + lt(36) + fd(l) + rt(144) + fd(fl) + lt(36) + fd(fl) + rt(144) + fd(l) + rt(144) + +def inflatekite(l, n): + if n == 0: + px, py = pos() + h, x, y = int(heading()), round(px,3), round(py,3) + tiledict[(h,x,y)] = True + return + fl = f * l + lt(36) + inflatedart(fl, n-1) + fd(l) + rt(144) + inflatekite(fl, n-1) + lt(18) + fd(l*d) + rt(162) + inflatekite(fl, n-1) + lt(36) + fd(l) + rt(180) + inflatedart(fl, n-1) + lt(36) + +def inflatedart(l, n): + if n == 0: + px, py = pos() + h, x, y = int(heading()), round(px,3), round(py,3) + tiledict[(h,x,y)] = False + return + fl = f * l + inflatekite(fl, n-1) + lt(36) + fd(l) + rt(180) + inflatedart(fl, n-1) + lt(54) + fd(l*d) + rt(126) + inflatedart(fl, n-1) + fd(l) + rt(144) + +def draw(l, n, th=2): + clear() + l = l * f**n + shapesize(l/100.0, l/100.0, th) + for k in tiledict: + h, x, y = k + setpos(x, y) + setheading(h) + if tiledict[k]: + shape("kite") + color("black", (0, 0.75, 0)) + else: + shape("dart") + color("black", (0.75, 0, 0)) + stamp() + +def sun(l, n): + for i in range(5): + inflatekite(l, n) + lt(72) + +def star(l,n): + for i in range(5): + inflatedart(l, n) + lt(72) + +def makeshapes(): + tracer(0) + begin_poly() + kite(100) + end_poly() + register_shape("kite", get_poly()) + begin_poly() + dart(100) + end_poly() + register_shape("dart", get_poly()) + tracer(1) + +def start(): + reset() + ht() + pu() + makeshapes() + resizemode("user") + +def test(l=200, n=4, fun=sun, startpos=(0,0), th=2): + global tiledict + goto(startpos) + setheading(0) + tiledict = {} + a = clock() + tracer(0) + fun(l, n) + b = clock() + draw(l, n, th) + tracer(1) + c = clock() + print("Calculation: %7.4f s" % (b - a)) + print("Drawing: %7.4f s" % (c - b)) + print("Together: %7.4f s" % (c - a)) + nk = len([x for x in tiledict if tiledict[x]]) + nd = len([x for x in tiledict if not tiledict[x]]) + print("%d kites and %d darts = %d pieces." % (nk, nd, nk+nd)) + +def demo(fun=sun): + start() + for i in range(8): + a = clock() + test(300, i, fun) + b = clock() + t = b - a + if t < 2: + sleep(2 - t) + +def main(): + #title("Penrose-tiling with kites and darts.") + mode("logo") + bgcolor(0.3, 0.3, 0) + demo(sun) + sleep(2) + demo(star) + pencolor("black") + goto(0,-200) + pencolor(0.7,0.7,1) + write("Please wait...", + align="center", font=('Arial Black', 36, 'bold')) + test(600, 8, startpos=(70, 117)) + return "Done" + +if __name__ == "__main__": + msg = main() + mainloop() diff --git a/Demo/turtle/tdemo_planet_and_moon.py b/Demo/turtle/tdemo_planet_and_moon.py new file mode 100644 index 0000000..855b56f --- /dev/null +++ b/Demo/turtle/tdemo_planet_and_moon.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_planets_and_moon.py + +Gravitational system simulation using the +approximation method from Feynman-lectures, +p.9-8, using turtlegraphics. + +Example: heavy central body, light planet, +very light moon! +Planet has a circular orbit, moon a stable +orbit around the planet. + +You can hold the movement temporarily by pressing +the left mouse button with mouse over the +scrollbar of the canvas. + +""" +from tkinter.turtle import Shape, Turtle, mainloop, Vec2D as Vec +from time import sleep + +G = 8 + +class GravSys(object): + def __init__(self): + self.planets = [] + self.t = 0 + self.dt = 0.01 + def init(self): + for p in self.planets: + p.init() + def start(self): + for i in range(10000): + self.t += self.dt + for p in self.planets: + p.step() + +class Star(Turtle): + def __init__(self, m, x, v, gravSys, shape): + Turtle.__init__(self, shape=shape) + self.penup() + self.m = m + self.setpos(x) + self.v = v + gravSys.planets.append(self) + self.gravSys = gravSys + self.resizemode("user") + self.pendown() + def init(self): + dt = self.gravSys.dt + self.a = self.acc() + self.v = self.v + 0.5*dt*self.a + def acc(self): + a = Vec(0,0) + for planet in self.gravSys.planets: + if planet != self: + v = planet.pos()-self.pos() + a += (G*planet.m/abs(v)**3)*v + return a + def step(self): + dt = self.gravSys.dt + self.setpos(self.pos() + dt*self.v) + if self.gravSys.planets.index(self) != 0: + self.setheading(self.towards(self.gravSys.planets[0])) + self.a = self.acc() + self.v = self.v + dt*self.a + +## create compound yellow/blue turtleshape for planets + +def main(): + s = Turtle() + s.reset() + s.getscreen().tracer(0,0) + s.ht() + s.pu() + s.fd(6) + s.lt(90) + s.begin_poly() + s.circle(6, 180) + s.end_poly() + m1 = s.get_poly() + s.begin_poly() + s.circle(6,180) + s.end_poly() + m2 = s.get_poly() + + planetshape = Shape("compound") + planetshape.addcomponent(m1,"orange") + planetshape.addcomponent(m2,"blue") + s.getscreen().register_shape("planet", planetshape) + s.getscreen().tracer(1,0) + + ## setup gravitational system + gs = GravSys() + sun = Star(1000000, Vec(0,0), Vec(0,-2.5), gs, "circle") + sun.color("yellow") + sun.shapesize(1.8) + sun.pu() + earth = Star(12500, Vec(210,0), Vec(0,195), gs, "planet") + earth.pencolor("green") + earth.shapesize(0.8) + moon = Star(1, Vec(220,0), Vec(0,295), gs, "planet") + moon.pencolor("blue") + moon.shapesize(0.5) + gs.init() + gs.start() + return "Done!" + +if __name__ == '__main__': + msg = main() + print(msg) + #mainloop() diff --git a/Demo/turtle/tdemo_tree.py b/Demo/turtle/tdemo_tree.py new file mode 100644 index 0000000..3e7f34f --- /dev/null +++ b/Demo/turtle/tdemo_tree.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_tree.py + +Displays a 'breadth-first-tree' - in contrast +to the classical Logo tree drawing programs, +which use a depth-first-algorithm. + +Uses: +(1) a tree-generator, where the drawing is +quasi the side-effect, whereas the generator +always yields None. +(2) Turtle-cloning: At each branching point the +current pen is cloned. So in the end there +are 1024 turtles. +""" +from tkinter.turtle import Turtle, mainloop +from time import clock + +def tree(plist, l, a, f): + """ plist is list of pens + l is length of branch + a is half of the angle between 2 branches + f is factor by which branch is shortened + from level to level.""" + if l > 3: + lst = [] + for p in plist: + p.forward(l) + q = p.clone() + p.left(a) + q.right(a) + lst.append(p) + lst.append(q) + for x in tree(lst, l*f, a, f): + yield None + +def maketree(): + p = Turtle() + p.setundobuffer(None) + p.hideturtle() + p.speed(0) + p.getscreen().tracer(30,0) + p.left(90) + p.penup() + p.forward(-210) + p.pendown() + t = tree([p], 200, 65, 0.6375) + for x in t: + pass + print(len(p.getscreen().turtles())) + +def main(): + a=clock() + maketree() + b=clock() + return "done: %.2f sec." % (b-a) + +if __name__ == "__main__": + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_wikipedia.py b/Demo/turtle/tdemo_wikipedia.py new file mode 100644 index 0000000..f694312 --- /dev/null +++ b/Demo/turtle/tdemo_wikipedia.py @@ -0,0 +1,65 @@ +""" turtle-example-suite: + + tdemo_wikipedia3.py + +This example is +inspired by the Wikipedia article on turtle +graphics. (See example wikipedia1 for URLs) + +First we create (ne-1) (i.e. 35 in this +example) copies of our first turtle p. +Then we let them perform their steps in +parallel. + +Followed by a complete undo(). +""" +from tkinter.turtle import Screen, Turtle, mainloop +from time import clock, sleep + +def mn_eck(p, ne,sz): + turtlelist = [p] + #create ne-1 additional turtles + for i in range(1,ne): + q = p.clone() + q.rt(360.0/ne) + turtlelist.append(q) + p = q + for i in range(ne): + c = abs(ne/2.0-i)/(ne*.7) + # let those ne turtles make a step + # in parallel: + for t in turtlelist: + t.rt(360./ne) + t.pencolor(1-c,0,c) + t.fd(sz) + +def main(): + s = Screen() + s.bgcolor("black") + p=Turtle() + p.speed(0) + p.hideturtle() + p.pencolor("red") + p.pensize(3) + + s.tracer(36,0) + + at = clock() + mn_eck(p, 36, 19) + et = clock() + z1 = et-at + + sleep(1) + + at = clock() + while any([t.undobufferentries() for t in s.turtles()]): + for t in s.turtles(): + t.undo() + et = clock() + return "Laufzeit: %.3f sec" % (z1+et-at) + + +if __name__ == '__main__': + msg = main() + print(msg) + mainloop() diff --git a/Demo/turtle/tdemo_yinyang.py b/Demo/turtle/tdemo_yinyang.py new file mode 100644 index 0000000..a47dd71 --- /dev/null +++ b/Demo/turtle/tdemo_yinyang.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +""" turtle-example-suite: + + tdemo_yinyang.py + +Another drawing suitable as a beginner's +programming example. + +The small circles are drawn by the circle +command. + +""" + +from tkinter.turtle import * + +def yin(radius, color1, color2): + width(3) + color("black", color1) + begin_fill() + circle(radius/2., 180) + circle(radius, 180) + left(180) + circle(-radius/2., 180) + end_fill() + left(90) + up() + forward(radius*0.35) + right(90) + down() + color(color1, color2) + begin_fill() + circle(radius*0.15) + end_fill() + left(90) + up() + backward(radius*0.35) + down() + left(90) + +def main(): + reset() + yin(200, "black", "white") + yin(200, "white", "black") + ht() + return "Done!" + +if __name__ == '__main__': + main() + mainloop() diff --git a/Demo/turtle/turtle.cfg b/Demo/turtle/turtle.cfg new file mode 100644 index 0000000..bd89a74 --- /dev/null +++ b/Demo/turtle/turtle.cfg @@ -0,0 +1,10 @@ +width = 800 +height = 600 +canvwidth = 1200 +canvheight = 900 +shape = arrow +mode = standard +resizemode = auto +fillcolor = "" +title = Python turtle graphics demo. + diff --git a/Demo/turtle/turtleDemo.py b/Demo/turtle/turtleDemo.py new file mode 100644 index 0000000..a454382 --- /dev/null +++ b/Demo/turtle/turtleDemo.py @@ -0,0 +1,291 @@ +#!/usr/bin/python +import sys +import os + +from tkinter import * +from idlelib.Percolator import Percolator +from idlelib.ColorDelegator import ColorDelegator +from idlelib.textView import view_file # TextViewer +from imp import reload + +from tkinter import turtle +import time + +STARTUP = 1 +READY = 2 +RUNNING = 3 +DONE = 4 +EVENTDRIVEN = 5 + +menufont = ("Arial", 12, NORMAL) +btnfont = ("Arial", 12, 'bold') +txtfont = ('Lucida Console', 8, 'normal') + +def getExampleEntries(): + cwd = os.getcwd() + #print(cwd, os.listdir(cwd)) + if "turtleDemo.py" not in os.listdir(cwd): + print("Directory of turtleDemo must be current working directory!") + print("But in your case this is", cwd) + sys.exit() + entries1 = [entry for entry in os.listdir(cwd) if + entry.startswith("tdemo_") and + not entry.endswith(".pyc")] + entries2 = [] + for entry in entries1: + if entry.endswith(".py"): + entries2.append(entry) + else: + path = os.path.join(cwd,entry) + sys.path.append(path) + subdir = [entry] + scripts = [script for script in os.listdir(path) if + script.startswith("tdemo_") and + script.endswith(".py")] + entries2.append(subdir+scripts) + return entries2 + +def showDemoHelp(): + view_file(demo.root, "Help on turtleDemo", "demohelp.txt") + +def showAboutDemo(): + view_file(demo.root, "About turtleDemo", "about_turtledemo.txt") + +def showAboutTurtle(): + view_file(demo.root, "About the new turtle module.", "about_turtle.txt") + +class DemoWindow(object): + + def __init__(self, filename=None): #, root=None): + self.root = root = turtle._root = Tk() + root.wm_protocol("WM_DELETE_WINDOW", self._destroy) + + ################# + self.mBar = Frame(root, relief=RAISED, borderwidth=2) + self.mBar.pack(fill=X) + + self.ExamplesBtn = self.makeLoadDemoMenu() + self.OptionsBtn = self.makeHelpMenu() + self.mBar.tk_menuBar(self.ExamplesBtn, self.OptionsBtn) #, QuitBtn) + + root.title('Python turtle-graphics examples') + ################# + self.left_frame = left_frame = Frame(root) + self.text_frame = text_frame = Frame(left_frame) + self.vbar = vbar =Scrollbar(text_frame, name='vbar') + self.text = text = Text(text_frame, + name='text', padx=5, wrap='none', + width=45) + vbar['command'] = text.yview + vbar.pack(side=LEFT, fill=Y) + ##################### + self.hbar = hbar =Scrollbar(text_frame, name='hbar', orient=HORIZONTAL) + hbar['command'] = text.xview + hbar.pack(side=BOTTOM, fill=X) + ##################### + text['yscrollcommand'] = vbar.set + text.config(font=txtfont) + text.config(xscrollcommand=hbar.set) + text.pack(side=LEFT, fill=Y, expand=1) + ##################### + self.output_lbl = Label(left_frame, height= 1,text=" --- ", bg = "#ddf", + font = ("Arial", 16, 'normal')) + self.output_lbl.pack(side=BOTTOM, expand=0, fill=X) + ##################### + text_frame.pack(side=LEFT, fill=BOTH, expand=0) + left_frame.pack(side=LEFT, fill=BOTH, expand=0) + self.graph_frame = g_frame = Frame(root) + + turtle.Screen._root = g_frame + turtle.Screen._canvas = turtle.ScrolledCanvas(g_frame, 800, 600, 1000, 800) + #xturtle.Screen._canvas.pack(expand=1, fill="both") + self.screen = _s_ = turtle.Screen() +##### + turtle.TurtleScreen.__init__(_s_, _s_._canvas) +##### + self.scanvas = _s_._canvas + #xturtle.RawTurtle.canvases = [self.scanvas] + turtle.RawTurtle.screens = [_s_] + + self.scanvas.pack(side=TOP, fill=BOTH, expand=1) + + self.btn_frame = btn_frame = Frame(g_frame, height=100) + self.start_btn = Button(btn_frame, text=" START ", font=btnfont, fg = "white", + disabledforeground = "#fed", command=self.startDemo) + self.start_btn.pack(side=LEFT, fill=X, expand=1) + self.stop_btn = Button(btn_frame, text=" STOP ", font=btnfont, fg = "white", + disabledforeground = "#fed", command = self.stopIt) + self.stop_btn.pack(side=LEFT, fill=X, expand=1) + self.clear_btn = Button(btn_frame, text=" CLEAR ", font=btnfont, fg = "white", + disabledforeground = "#fed", command = self.clearCanvas) + self.clear_btn.pack(side=LEFT, fill=X, expand=1) + + self.btn_frame.pack(side=TOP, fill=BOTH, expand=0) + self.graph_frame.pack(side=TOP, fill=BOTH, expand=1) + + Percolator(text).insertfilter(ColorDelegator()) + self.dirty = False + self.exitflag = False + if filename: + self.loadfile(filename) + self.configGUI(NORMAL, DISABLED, DISABLED, DISABLED, + "Choose example from menu", "black") + self.state = STARTUP + + def _destroy(self): + self.root.destroy() + sys.exit() + + def configGUI(self, menu, start, stop, clear, txt="", color="blue"): + self.ExamplesBtn.config(state=menu) + + self.start_btn.config(state=start) + if start==NORMAL: + self.start_btn.config(bg="#d00") + else: + self.start_btn.config(bg="#fca") + + self.stop_btn.config(state=stop) + if stop==NORMAL: + self.stop_btn.config(bg="#d00") + else: + self.stop_btn.config(bg="#fca") + self.clear_btn.config(state=clear) + + self.clear_btn.config(state=clear) + if clear==NORMAL: + self.clear_btn.config(bg="#d00") + else: + self.clear_btn.config(bg="#fca") + + self.output_lbl.config(text=txt, fg=color) + + + def makeLoadDemoMenu(self): + CmdBtn = Menubutton(self.mBar, text='Examples', underline=0, font=menufont) + CmdBtn.pack(side=LEFT, padx="2m") + CmdBtn.menu = Menu(CmdBtn) + + for entry in getExampleEntries(): + def loadexample(x): + def emit(): + self.loadfile(x) + return emit + if isinstance(entry,str): + CmdBtn.menu.add_command(label=entry[6:-3], underline=0, font=menufont, + command=loadexample(entry)) + else: + _dir, entries = entry[0], entry[1:] + CmdBtn.menu.choices = Menu(CmdBtn.menu) + for e in entries: + CmdBtn.menu.choices.add_command(label=e[6:-3], underline=0, font=menufont, + command = loadexample(os.path.join(_dir,e))) + + CmdBtn.menu.add_cascade(label=_dir[6:], + menu = CmdBtn.menu.choices, font=menufont ) + + CmdBtn['menu'] = CmdBtn.menu + return CmdBtn + + + def makeHelpMenu(self): + CmdBtn = Menubutton(self.mBar, text='Help', underline=0, font = menufont) + CmdBtn.pack(side=LEFT, padx='2m') + CmdBtn.menu = Menu(CmdBtn) + + CmdBtn.menu.add_command(label='About turtle.py', font=menufont, command=showAboutTurtle) + CmdBtn.menu.add_command(label='turtleDemo - Help', font=menufont, command=showDemoHelp) + CmdBtn.menu.add_command(label='About turtleDemo', font=menufont, command=showAboutDemo) + + CmdBtn['menu'] = CmdBtn.menu + return CmdBtn + + def refreshCanvas(self): + if not self.dirty: return + self.screen.clear() + #self.screen.mode("standard") + self.dirty=False + + def loadfile(self,filename): + self.refreshCanvas() + if os.path.exists(filename) and not os.path.isdir(filename): + # load and display file text + f = open(filename,'r') + chars = f.read() + f.close() + self.text.delete("1.0", "end") + self.text.insert("1.0",chars) + direc, fname = os.path.split(filename) + self.root.title(fname[6:-3]+" - a Python turtle graphics example") + self.module = __import__(fname[:-3]) + reload(self.module) + self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED, + "Press start button", "red") + self.state = READY + + def startDemo(self): + self.refreshCanvas() + self.dirty = True + turtle.TurtleScreen._RUNNING = True + self.configGUI(DISABLED, DISABLED, NORMAL, DISABLED, + "demo running...", "black") + self.screen.clear() + self.screen.mode("standard") + self.state = RUNNING + + try: + result = self.module.main() + if result == "EVENTLOOP": + self.state = EVENTDRIVEN + else: + self.state = DONE + except turtle.Terminator: + self.state = DONE + result = "stopped!" + if self.state == DONE: + self.configGUI(NORMAL, NORMAL, DISABLED, NORMAL, + result) + elif self.state == EVENTDRIVEN: + self.exitflag = True + self.configGUI(DISABLED, DISABLED, NORMAL, DISABLED, + "use mouse/keys or STOP", "red") + + def clearCanvas(self): + self.refreshCanvas() + self.screen._delete("all") + self.scanvas.config(cursor="") + self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED) + + def stopIt(self): + if self.exitflag: + self.clearCanvas() + self.exitflag = False + self.configGUI(NORMAL, NORMAL, DISABLED, DISABLED, + "STOPPED!", "red") + turtle.TurtleScreen._RUNNING = False + #print "stopIT: exitflag = True" + else: + turtle.TurtleScreen._RUNNING = False + #print "stopIt: exitflag = False" + +if __name__ == '__main__': + demo = DemoWindow() + RUN = True + while RUN: + try: + #print("ENTERING mainloop") + demo.root.mainloop() + except AttributeError: + #print("AttributeError!- WAIT A MOMENT!") + time.sleep(0.3) + print("GOING ON ..") + demo.ckearCanvas() + except TypeError: + demo.screen._delete("all") + #print("CRASH!!!- WAIT A MOMENT!") + time.sleep(0.3) + #print("GOING ON ..") + demo.clearCanvas() + except: + print("BYE!") + RUN = False diff --git a/Demo/turtle/turtledemo_two_canvases.py b/Demo/turtle/turtledemo_two_canvases.py new file mode 100644 index 0000000..4fdb59a --- /dev/null +++ b/Demo/turtle/turtledemo_two_canvases.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +## DEMONSTRATES USE OF 2 CANVASES, SO CANNOT BE RUN IN DEMOVIEWER! +"""turtle example: Using TurtleScreen and RawTurtle +for drawing on two distinct canvases. +""" +from tkinter.turtle import TurtleScreen, RawTurtle, TK + +root = TK.Tk() +cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff") +cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee") +cv1.pack() +cv2.pack() + +s1 = TurtleScreen(cv1) +s1.bgcolor(0.85, 0.85, 1) +s2 = TurtleScreen(cv2) +s2.bgcolor(1, 0.85, 0.85) + +p = RawTurtle(s1) +q = RawTurtle(s2) + +p.color("red", (1, 0.85, 0.85)) +p.width(3) +q.color("blue", (0.85, 0.85, 1)) +q.width(3) + +for t in p,q: + t.shape("turtle") + t.lt(36) + +q.lt(180) + +for t in p, q: + t.begin_fill() +for i in range(5): + for t in p, q: + t.fd(50) + t.lt(72) +for t in p,q: + t.end_fill() + t.lt(54) + t.pu() + t.bk(50) + +## Want to get some info? + +print(s1, s2) +print(p, q) +print(s1.turtles()) +print(s2.turtles()) + +TK.mainloop() diff --git a/Doc/library/tkinter.turtle.rst b/Doc/library/tkinter.turtle.rst index d91ac8e..575c2fc 100644 --- a/Doc/library/tkinter.turtle.rst +++ b/Doc/library/tkinter.turtle.rst @@ -1,288 +1,1887 @@ -:mod:`tkinter.turtle` --- Turtle graphics for Tk -================================================ +======================================== +:mod:`turtle` --- Turtle graphics for Tk +======================================== + +Introduction +============ + +Turtle graphics is a popular way for introducing programming to kids. It was +part of the original Logo programming language developed by Wally Feurzig and +Seymour Papert in 1966. + +Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it the +command ``turtle.forward(15)``, and it moves (on-screen!) 15 pixels in the +direction it is facing, drawing a line as it moves. Give it the command +``turtle.left(25)``, and it rotates in-place 25 degrees clockwise. + +By combining together these and similar commands, intricate shapes and pictures +can easily be drawn. + +The :mod:`turtle` module is an extended reimplementation of the same-named +module from the Python standard distribution up to version Python 2.5. + +It tries to keep the merits of the old turtle module and to be (nearly) 100% +compatible with it. This means in the first place to enable the learning +programmer to use all the commands, classes and methods interactively when using +the module from within IDLE run with the ``-n`` switch. + +The turtle module provides turtle graphics primitives, in both object-oriented +and procedure-oriented ways. Because it uses :mod:`Tkinter` for the underlying +graphics, it needs a version of python installed with Tk support. + +The object-oriented interface uses essentially two+two classes: + +1. The :class:`TurtleScreen` class defines graphics windows as a playground for + the drawing turtles. Its constructor needs a :class:`Tkinter.Canvas` or a + :class:`ScrolledCanvas` as argument. It should be used when :mod:`turtle` is + used as part of some application. + + Derived from :class:`TurtleScreen` is the subclass :class:`Screen`. Screen + is implemented as sort of singleton, so there can exist only one instance of + Screen at a time. It should be used when :mod:`turtle` is used as a + standalone tool for doing graphics. + + All methods of TurtleScreen/Screen also exist as functions, i.e. as part of + the procedure-oriented interface. + +2. :class:`RawTurtle` (alias: :class:`RawPen`) defines Turtle objects which draw + on a :class:`TurtleScreen`. Its constructor needs a Canvas, ScrolledCanvas + or TurtleScreen as argument, so the RawTurtle objects know where to draw. + + Derived from RawTurtle is the subclass :class:`Turtle` (alias: :class:`Pen`), + which draws on "the" :class:`Screen` - instance which is automatically + created, if not already present. + + All methods of RawTurtle/Turtle also exist as functions, i.e. part of the + procedure-oriented interface. + +The procedural interface provides functions which are derived from the methods +of the classes :class:`Screen` and :class:`Turtle`. They have the same names as +the corresponding methods. A screen object is automativally created whenever a +function derived from a Screen method is called. An (unnamed) turtle object is +automatically created whenever any of the functions derived from a Turtle method +is called. + +To use multiple turtles an a screen one has to use the object-oriented interface. + +.. note:: + In the following documentation the argument list for functions is given. + Methods, of course, have the additional first argument *self* which is + omitted here. + + +Overview over available Turtle and Screen methods +================================================= + +Turtle methods +-------------- + +Turtle motion + Move and draw + | :func:`forward` | :func:`fd` + | :func:`backward` | :func:`bk` | :func:`back` + | :func:`right` | :func:`rt` + | :func:`left` | :func:`lt` + | :func:`goto` | :func:`setpos` | :func:`setposition` + | :func:`setx` + | :func:`sety` + | :func:`setheading` | :func:`seth` + | :func:`home` + | :func:`circle` + | :func:`dot` + | :func:`stamp` + | :func:`clearstamp` + | :func:`clearstamps` + | :func:`undo` + | :func:`speed` + + Tell Turtle's state + | :func:`position` | :func:`pos` + | :func:`towards` + | :func:`xcor` + | :func:`ycor` + | :func:`heading` + | :func:`distance` + + Setting and measurement + | :func:`degrees` + | :func:`radians` + +Pen control + Drawing state + | :func:`pendown` | :func:`pd` | :func:`down` + | :func:`penup` | :func:`pu` | :func:`up` + | :func:`pensize` | :func:`width` + | :func:`pen` + | :func:`isdown` + + Color control + | :func:`color` + | :func:`pencolor` + | :func:`fillcolor` + + Filling + | :func:`filling` + | :func:`begin_fill` + | :func:`end_fill` + + More drawing control + | :func:`reset` + | :func:`clear` + | :func:`write` + +Turtle state + Visibility + | :func:`showturtle` | :func:`st` + | :func:`hideturtle` | :func:`ht` + | :func:`isvisible` + + Appearance + | :func:`shape` + | :func:`resizemode` + | :func:`shapesize` | :func:`turtlesize` + | :func:`settiltangle` + | :func:`tiltangle` + | :func:`tilt` + +Using events + | :func:`onclick` + | :func:`onrelease` + | :func:`ondrag` + +Special Turtle methods + | :func:`begin_poly` + | :func:`end_poly` + | :func:`get_poly` + | :func:`clone` + | :func:`getturtle` | :func:`getpen` + | :func:`getscreen` + | :func:`setundobuffer` + | :func:`undobufferentries` + + +Methods of TurtleScreen/Screen +------------------------------ + +Window control + | :func:`bgcolor` + | :func:`bgpic` + | :func:`clear` | :func:`clearscreen` + | :func:`reset` | :func:`resetscreen` + | :func:`screensize` + | :func:`setworldcoordinates` + +Animation control + | :func:`delay` + | :func:`tracer` + | :func:`update` + +Using screen events + | :func:`listen` + | :func:`onkey` + | :func:`onclick` | :func:`onscreenclick` + | :func:`ontimer` + +Settings and special methods + | :func:`mode` + | :func:`colormode` + | :func:`getcanvas` + | :func:`getshapes` + | :func:`register_shape` | :func:`addshape` + | :func:`turtles` + | :func:`window_height` + | :func:`window_width` + +Methods specific to Screen + | :func:`bye` + | :func:`exitonclick` + | :func:`setup` + | :func:`title` + + +Methods of RawTurtle/Turtle and corresponding functions +======================================================= + +Most of the examples in this section refer to a Turtle instance called +``turtle``. + +Turtle motion +------------- + +.. function:: forward(distance) + fd(distance) + + :param distance: a number (integer or float) + + Move the turtle forward by the specified *distance*, in the direction the + turtle is headed. + + >>> turtle.position() + (0.00, 0.00) + >>> turtle.forward(25) + >>> turtle.position() + (25.00,0.00) + >>> turtle.forward(-75) + >>> turtle.position() + (-50.00,0.00) + + +.. function:: back(distance) + bk(distance) + backward(distance) + + :param distance: a number + + Move the turtle backward by *distance*, opposite to the direction the + turtle is headed. Do not change the turtle's heading. + + >>> turtle.position() + (0.00, 0.00) + >>> turtle.backward(30) + >>> turtle.position() + (-30.00, 0.00) + + +.. function:: right(angle) + rt(angle) + + :param angle: a number (integer or float) + + Turn turtle right by *angle* units. (Units are by default degrees, but + can be set via the :func:`degrees` and :func:`radians` functions.) Angle + orientation depends on the turtle mode, see :func:`mode`. + + >>> turtle.heading() + 22.0 + >>> turtle.right(45) + >>> turtle.heading() + 337.0 + + +.. function:: left(angle) + lt(angle) + + :param angle: a number (integer or float) + + Turn turtle left by *angle* units. (Units are by default degrees, but + can be set via the :func:`degrees` and :func:`radians` functions.) Angle + orientation depends on the turtle mode, see :func:`mode`. + + >>> turtle.heading() + 22.0 + >>> turtle.left(45) + >>> turtle.heading() + 67.0 + +.. function:: goto(x, y=None) + setpos(x, y=None) + setposition(x, y=None) + + :param x: a number or a pair/vector of numbers + :param y: a number or ``None`` + + If *y* is ``None``, *x* must be a pair of coordinates or a :class:`Vec2D` + (e.g. as returned by :func:`pos`). + + Move turtle to an absolute position. If the pen is down, draw line. Do + not change the turtle's orientation. + + >>> tp = turtle.pos() + >>> tp + (0.00, 0.00) + >>> turtle.setpos(60,30) + >>> turtle.pos() + (60.00,30.00) + >>> turtle.setpos((20,80)) + >>> turtle.pos() + (20.00,80.00) + >>> turtle.setpos(tp) + >>> turtle.pos() + (0.00,0.00) + + +.. function:: setx(x) + + :param x: a number (integer or float) + + Set the turtle's first coordinate to *x*, leave second coordinate + unchanged. + + >>> turtle.position() + (0.00, 240.00) + >>> turtle.setx(10) + >>> turtle.position() + (10.00, 240.00) + + +.. function:: sety(y) + + :param y: a number (integer or float) + + Set the turtle's first coordinate to *y*, leave second coordinate + unchanged. + + >>> turtle.position() + (0.00, 40.00) + >>> turtle.sety(-10) + >>> turtle.position() + (0.00, -10.00) + + +.. function:: setheading(to_angle) + seth(to_angle) + + :param to_angle: a number (integer or float) -.. module:: tkinter.turtle - :platform: Tk - :synopsis: An environment for turtle graphics. -.. moduleauthor:: Guido van Rossum + Set the orientation of the turtle to *to_angle*. Here are some common + directions in degrees: + =================== ==================== + standard mode logo mode + =================== ==================== + 0 - east 0 - north + 90 - north 90 - east + 180 - west 180 - south + 270 - south 270 - west + =================== ==================== -.. sectionauthor:: Moshe Zadka + >>> turtle.setheading(90) + >>> turtle.heading() + 90 -The :mod:`tkinter.turtle` module provides turtle graphics primitives, in both an -object-oriented and procedure-oriented ways. Because it uses :mod:`tkinter` for -the underlying graphics, it needs a version of python installed with Tk support. +.. function:: home() + + Move turtle to the origin -- coordinates (0,0) -- and set its heading to + its start-orientation (which depends on the mode, see :func:`mode`). + + +.. function:: circle(radius, extent=None, steps=None) + + :param radius: a number + :param extent: a number (or ``None``) + :param steps: an integer (or ``None``) + + Draw a circle with given *radius*. The center is *radius* units left of + the turtle; *extent* -- an angle -- determines which part of the circle + is drawn. If *extent* is not given, draw the entire circle. If *extent* + is not a full circle, one endpoint of the arc is the current pen + position. Draw the arc in counterclockwise direction if *radius* is + positive, otherwise in clockwise direction. Finally the direction of the + turtle is changed by the amount of *extent*. + + As the circle is approximated by an inscribed regular polygon, *steps* + determines the number of steps to use. If not given, it will be + calculated automatically. May be used to draw regular polygons. + + >>> turtle.circle(50) + >>> turtle.circle(120, 180) # draw a semicircle + + +.. function:: dot(size=None, *color) + + :param size: an integer >= 1 (if given) + :param color: a colorstring or a numeric color tuple + + Draw a circular dot with diameter *size*, using *color*. If *size* is + not given, the maximum of pensize+4 and 2*pensize is used. + + >>> turtle.dot() + >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50) + + +.. function:: stamp() + + Stamp a copy of the turtle shape onto the canvas at the current turtle + position. Return a stamp_id for that stamp, which can be used to delete + it by calling ``clearstamp(stamp_id)``. + + >>> turtle.color("blue") + >>> turtle.stamp() + 13 + >>> turtle.fd(50) + + +.. function:: clearstamp(stampid) + + :param stampid: an integer, must be return value of previous + :func:`stamp` call + + Delete stamp with given *stampid*. + + >>> turtle.color("blue") + >>> astamp = turtle.stamp() + >>> turtle.fd(50) + >>> turtle.clearstamp(astamp) + + +.. function:: clearstamps(n=None) + + :param n: an integer (or ``None``) + + Delete all or first/last *n* of turtle's stamps. If *n* is None, delete + all stamps, if *n* > 0 delete first *n* stamps, else if *n* < 0 delete + last *n* stamps. + + >>> for i in range(8): + ... turtle.stamp(); turtle.fd(30) + >>> turtle.clearstamps(2) + >>> turtle.clearstamps(-2) + >>> turtle.clearstamps() + + +.. function:: undo() + + Undo (repeatedly) the last turtle action(s). Number of available + undo actions is determined by the size of the undobuffer. + + >>> for i in range(4): + ... turtle.fd(50); turtle.lt(80) + ... + >>> for i in range(8): + ... turtle.undo() + + +.. function:: speed(speed=None) + + :param speed: an integer in the range 0..10 or a speedstring (see below) + + Set the turtle's speed to an integer value in the range 0..10. If no + argument is given, return current speed. + + If input is a number greater than 10 or smaller than 0.5, speed is set + to 0. Speedstrings are mapped to speedvalues as follows: + + * "fastest": 0 + * "fast": 10 + * "normal": 6 + * "slow": 3 + * "slowest": 1 + + Speeds from 1 to 10 enforce increasingly faster animation of line drawing + and turtle turning. + + Attention: *speed* = 0 means that *no* animation takes + place. forward/back makes turtle jump and likewise left/right make the + turtle turn instantly. + + >>> turtle.speed(3) + + +Tell Turtle's state +------------------- + +.. function:: position() + pos() -The procedural interface uses a pen and a canvas which are automagically created -when any of the functions are called. + Return the turtle's current location (x,y) (as a :class:`Vec2D` vector). -The :mod:`tkinter.turtle` module defines the following functions: + >>> turtle.pos() + (0.00, 240.00) -.. function:: degrees() +.. function:: towards(x, y=None) - Set angle measurement units to degrees. + :param x: a number or a pair/vector of numbers or a turtle instance + :param y: a number if *x* is a number, else ``None`` + + Return the angle between the line from turtle position to position specified + by (x,y), the vector or the other turtle. This depends on the turtle's start + orientation which depends on the mode - "standard"/"world" or "logo"). + + >>> turtle.pos() + (10.00, 10.00) + >>> turtle.towards(0,0) + 225.0 + + +.. function:: xcor() + + Return the turtle's x coordinate. + + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.xcor() + 50.0 + + +.. function:: ycor() + + Return the turtle's y coordinate. + + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.ycor() + 86.6025403784 + + +.. function:: heading() + + Return the turtle's current heading (value depends on the turtle mode, see + :func:`mode`). + + >>> turtle.left(67) + >>> turtle.heading() + 67.0 + + +.. function:: distance(x, y=None) + + :param x: a number or a pair/vector of numbers or a turtle instance + :param y: a number if *x* is a number, else ``None`` + + Return the distance from the turtle to (x,y), the given vector, or the given + other turtle, in turtle step units. + + >>> turtle.pos() + (0.00, 0.00) + >>> turtle.distance(30,40) + 50.0 + >>> joe = Turtle() + >>> joe.forward(77) + >>> turtle.distance(joe) + 77.0 + + +Settings for measurement +------------------------ + +.. function:: degrees(fullcircle=360.0) + + :param fullcircle: a number + + Set angle measurement units, i.e. set number of "degrees" for a full circle. + Default value is 360 degrees. + + >>> turtle.left(90) + >>> turtle.heading() + 90 + >>> turtle.degrees(400.0) # angle measurement in gon + >>> turtle.heading() + 100 .. function:: radians() - Set angle measurement units to radians. + Set the angle measurement units to radians. Equivalent to + ``degrees(2*math.pi)``. + >>> turtle.heading() + 90 + >>> turtle.radians() + >>> turtle.heading() + 1.5707963267948966 -.. function:: setup(**kwargs) - Sets the size and position of the main window. Keywords are: +Pen control +----------- - * ``width``: either a size in pixels or a fraction of the screen. The default is - 50% of the screen. +Drawing state +~~~~~~~~~~~~~ - * ``height``: either a size in pixels or a fraction of the screen. The default - is 50% of the screen. +.. function:: pendown() + pd() + down() - * ``startx``: starting position in pixels from the left edge of the screen. - ``None`` is the default value and centers the window horizontally on screen. + Pull the pen down -- drawing when moving. - * ``starty``: starting position in pixels from the top edge of the screen. - ``None`` is the default value and centers the window vertically on screen. - Examples:: +.. function:: penup() + pu() + up() - # Uses default geometry: 50% x 50% of screen, centered. - setup() + Pull the pen up -- no drawing when moving. - # Sets window to 200x200 pixels, in upper left of screen - setup (width=200, height=200, startx=0, starty=0) - # Sets window to 75% of screen by 50% of screen, and centers it. - setup(width=.75, height=0.5, startx=None, starty=None) +.. function:: pensize(width=None) + width(width=None) + :param width: a positive number -.. function:: title(title_str) + Set the line thickness to *width* or return it. If resizemode is set to + "auto" and turtleshape is a polygon, that polygon is drawn with the same line + thickness. If no argument is given, the current pensize is returned. - Set the window's title to *title*. + >>> turtle.pensize() + 1 + >>> turtle.pensize(10) # from here on lines of width 10 are drawn -.. function:: done() +.. function:: pen(pen=None, **pendict) - Enters the Tk main loop. The window will continue to be displayed until the - user closes it or the process is killed. + :param pen: a dictionary with some or all of the below listed keys + :param pendict: one or more keyword-arguments with the below listed keys as keywords + Return or set the pen's attributes in a "pen-dictionary" with the following + key/value pairs: -.. function:: reset() + * "shown": True/False + * "pendown": True/False + * "pencolor": color-string or color-tuple + * "fillcolor": color-string or color-tuple + * "pensize": positive number + * "speed": number in range 0..10 + * "resizemode": "auto" or "user" or "noresize" + * "stretchfactor": (positive number, positive number) + * "outline": positive number + * "tilt": number - Clear the screen, re-center the pen, and set variables to the default values. + This dicionary can be used as argument for a subsequent call to :func:`pen` + to restore the former pen-state. Moreover one or more of these attributes + can be provided as keyword-arguments. This can be used to set several pen + attributes in one statement. + >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black', + 'stretchfactor': (1,1), 'speed': 3} + >>> penstate=turtle.pen() + >>> turtle.color("yellow","") + >>> turtle.penup() + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '', + 'stretchfactor': (1,1), 'speed': 3} + >>> p.pen(penstate, fillcolor="green") + >>> p.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green', + 'stretchfactor': (1,1), 'speed': 3} -.. function:: clear() - Clear the screen. +.. function:: isdown() + Return ``True`` if pen is down, ``False`` if it's up. -.. function:: tracer(flag) + >>> turtle.penup() + >>> turtle.isdown() + False + >>> turtle.pendown() + >>> turtle.isdown() + True - Set tracing on/off (according to whether flag is true or not). Tracing means - line are drawn more slowly, with an animation of an arrow along the line. +Color control +~~~~~~~~~~~~~ -.. function:: speed(speed) +.. function:: pencolor(*args) - Set the speed of the turtle. Valid values for the parameter *speed* are - ``'fastest'`` (no delay), ``'fast'``, (delay 5ms), ``'normal'`` (delay 10ms), - ``'slow'`` (delay 15ms), and ``'slowest'`` (delay 20ms). + Return or set the pencolor. + Four input formats are allowed: -.. function:: delay(delay) + ``pencolor()`` + Return the current pencolor as color specification string, possibly in + hex-number format (see example). May be used as input to another + color/pencolor/fillcolor call. - Set the speed of the turtle to *delay*, which is given in ms. + ``pencolor(colorstring)`` + Set pencolor to *colorstring*, which is a Tk color specification string, + such as ``"red"``, ``"yellow"``, or ``"#33cc8c"``. + ``pencolor((r, g, b))`` + Set pencolor to the RGB color represented by the tuple of *r*, *g*, and + *b*. Each of *r*, *g*, and *b* must be in the range 0..colormode, where + colormode is either 1.0 or 255 (see :func:`colormode`). -.. function:: forward(distance) + ``pencolor(r, g, b)`` + Set pencolor to the RGB color represented by *r*, *g*, and *b*. Each of + *r*, *g*, and *b* must be in the range 0..colormode. - Go forward *distance* steps. + If turtleshape is a polygon, the outline of that polygon is drawn with the + newly set pencolor. + >>> turtle.pencolor("brown") + >>> tup = (0.2, 0.8, 0.55) + >>> turtle.pencolor(tup) + >>> turtle.pencolor() + "#33cc8c" -.. function:: backward(distance) - Go backward *distance* steps. +.. function:: fillcolor(*args) + Return or set the fillcolor. -.. function:: left(angle) + Four input formats are allowed: - Turn left *angle* units. Units are by default degrees, but can be set via the - :func:`degrees` and :func:`radians` functions. + ``fillcolor()`` + Return the current fillcolor as color specification string, possibly in + hex-number format (see example). May be used as input to another + color/pencolor/fillcolor call. + ``fillcolor(colorstring)`` + Set fillcolor to *colorstring*, which is a Tk color specification string, + such as ``"red"``, ``"yellow"``, or ``"#33cc8c"``. -.. function:: right(angle) + ``fillcolor((r, g, b))`` + Set fillcolor to the RGB color represented by the tuple of *r*, *g*, and + *b*. Each of *r*, *g*, and *b* must be in the range 0..colormode, where + colormode is either 1.0 or 255 (see :func:`colormode`). - Turn right *angle* units. Units are by default degrees, but can be set via the - :func:`degrees` and :func:`radians` functions. + ``fillcolor(r, g, b)`` + Set fillcolor to the RGB color represented by *r*, *g*, and *b*. Each of + *r*, *g*, and *b* must be in the range 0..colormode. + If turtleshape is a polygon, the interior of that polygon is drawn + with the newly set fillcolor. -.. function:: up() + >>> turtle.fillcolor("violet") + >>> col = turtle.pencolor() + >>> turtle.fillcolor(col) + >>> turtle.fillcolor(0, .5, 0) - Move the pen up --- stop drawing. +.. function:: color(*args) -.. function:: down() + Return or set pencolor and fillcolor. - Move the pen down --- draw when moving. + Several input formats are allowed. They use 0 to 3 arguments as + follows: + ``color()`` + Return the current pencolor and the current fillcolor as a pair of color + specification strings as returned by :func:`pencolor` and + :func:`fillcolor`. -.. function:: width(width) + ``color(colorstring)``, ``color((r,g,b))``, ``color(r,g,b)`` + Inputs as in :func:`pencolor`, set both, fillcolor and pencolor, to the + given value. - Set the line width to *width*. + ``color(colorstring1, colorstring2)``, ``color((r1,g1,b1), (r2,g2,b2))`` + Equivalent to ``pencolor(colorstring1)`` and ``fillcolor(colorstring2)`` + and analogously if the other input format is used. + If turtleshape is a polygon, outline and interior of that polygon is drawn + with the newly set colors. -.. function:: color(s) - color((r, g, b)) - color(r, g, b) + >>> turtle.color("red", "green") + >>> turtle.color() + ("red", "green") + >>> colormode(255) + >>> color((40, 80, 120), (160, 200, 240)) + >>> color() + ("#285078", "#a0c8f0") - Set the pen color. In the first form, the color is specified as a Tk color - specification as a string. The second form specifies the color as a tuple of - the RGB values, each in the range [0..1]. For the third form, the color is - specified giving the RGB values as three separate parameters (each in the range - [0..1]). +See also: Screen method :func:`colormode`. -.. function:: write(text[, move]) - Write *text* at the current pen position. If *move* is true, the pen is moved to - the bottom-right corner of the text. By default, *move* is false. +Filling +~~~~~~~ +.. function:: filling() -.. function:: fill(flag) + Return fillstate (``True`` if filling, ``False`` else). - The complete specifications are rather complex, but the recommended usage is: - call ``fill(1)`` before drawing a path you want to fill, and call ``fill(0)`` - when you finish to draw the path. + >>> turtle.begin_fill() + >>> if turtle.filling(): + ... turtle.pensize(5) + else: + ... turtle.pensize(3) .. function:: begin_fill() - Switch turtle into filling mode; Must eventually be followed by a corresponding - end_fill() call. Otherwise it will be ignored. + To be called just before drawing a shape to be filled. + + >>> turtle.color("black", "red") + >>> turtle.begin_fill() + >>> turtle.circle(60) + >>> turtle.end_fill() .. function:: end_fill() - End filling mode, and fill the shape; equivalent to ``fill(0)``. + Fill the shape drawn after the last call to :func:`begin_fill`. -.. function:: circle(radius[, extent]) +More drawing control +~~~~~~~~~~~~~~~~~~~~ - Draw a circle with radius *radius* whose center-point is *radius* units left of - the turtle. *extent* determines which part of a circle is drawn: if not given it - defaults to a full circle. +.. function:: reset() - If *extent* is not a full circle, one endpoint of the arc is the current pen - position. The arc is drawn in a counter clockwise direction if *radius* is - positive, otherwise in a clockwise direction. In the process, the direction of - the turtle is changed by the amount of the *extent*. + Delete the turtle's drawings from the screen, re-center the turtle and set + variables to the default values. + >>> turtle.position() + (0.00,-22.00) + >>> turtle.heading() + 100.0 + >>> turtle.reset() + >>> turtle.position() + (0.00,0.00) + >>> turtle.heading() + 0.0 -.. function:: goto(x, y) - goto((x, y)) - Go to co-ordinates *x*, *y*. The co-ordinates may be specified either as two - separate arguments or as a 2-tuple. +.. function:: clear() + Delete the turtle's drawings from the screen. Do not move turtle. State and + position of the turtle as well as drawings of other turtles are not affected. -.. function:: towards(x, y) - Return the angle of the line from the turtle's position to the point *x*, *y*. - The co-ordinates may be specified either as two separate arguments, as a - 2-tuple, or as another pen object. +.. function:: write(arg, move=False, align="left", font=("Arial", 8, "normal")) + :param arg: object to be written to the TurtleScreen + :param move: True/False + :param align: one of the strings "left", "center" or right" + :param font: a triple (fontname, fontsize, fonttype) -.. function:: heading() + Write text - the string representation of *arg* - at the current turtle + position according to *align* ("left", "center" or right") and with the given + font. If *move* is True, the pen is moved to the bottom-right corner of the + text. By default, *move* is False. - Return the current orientation of the turtle. + >>> turtle.write("Home = ", True, align="center") + >>> turtle.write((0,0), True) -.. function:: setheading(angle) +Turtle state +------------ - Set the orientation of the turtle to *angle*. +Visibility +~~~~~~~~~~ +.. function:: showturtle() + st() -.. function:: position() + Make the turtle visible. - Return the current location of the turtle as an ``(x,y)`` pair. + >>> turtle.hideturtle() + >>> turtle.showturtle() -.. function:: setx(x) +.. function:: hideturtle() + ht() - Set the x coordinate of the turtle to *x*. + Make the turtle invisible. It's a good idea to do this while you're in the + middle of doing some complex drawing, because hiding the turtle speeds up the + drawing observably. + >>> turtle.hideturtle() -.. function:: sety(y) - Set the y coordinate of the turtle to *y*. +.. function:: isvisible() + Return True if the Turtle is shown, False if it's hidden. -.. function:: window_width() + >>> turtle.hideturtle() + >>> print turtle.isvisible(): + False + + +Appearance +~~~~~~~~~~ + +.. function:: shape(name=None) + + :param name: a string which is a valid shapename + + Set turtle shape to shape with given *name* or, if name is not given, return + name of current shape. Shape with *name* must exist in the TurtleScreen's + shape dictionary. Initially there are the following polygon shapes: "arrow", + "turtle", "circle", "square", "triangle", "classic". To learn about how to + deal with shapes see Screen method :func:`register_shape`. + + >>> turtle.shape() + "arrow" + >>> turtle.shape("turtle") + >>> turtle.shape() + "turtle" + + +.. function:: resizemode(rmode=None) + + :param rmode: one of the strings "auto", "user", "noresize" + + Set resizemode to one of the values: "auto", "user", "noresize". If *rmode* + is not given, return current resizemode. Different resizemodes have the + following effects: + + - "auto": adapts the appearance of the turtle corresponding to the value of pensize. + - "user": adapts the appearance of the turtle according to the values of + stretchfactor and outlinewidth (outline), which are set by + :func:`shapesize`. + - "noresize": no adaption of the turtle's appearance takes place. + + resizemode("user") is called by :func:`shapesize` when used with arguments. + + >>> turtle.resizemode("noresize") + >>> turtle.resizemode() + "noresize" + + +.. function:: shapesize(stretch_wid=None, stretch_len=None, outline=None) + + :param stretch_wid: positive number + :param stretch_len: positive number + :param outline: positive number + + Return or set the pen's attributes x/y-stretchfactors and/or outline. Set + resizemode to "user". If and only if resizemode is set to "user", the turtle + will be displayed stretched according to its stretchfactors: *stretch_wid* is + stretchfactor perpendicular to its orientation, *stretch_len* is + stretchfactor in direction of its orientation, *outline* determines the width + of the shapes's outline. + + >>> turtle.resizemode("user") + >>> turtle.shapesize(5, 5, 12) + >>> turtle.shapesize(outline=8) + + +.. function:: tilt(angle) + + :param angle: a number + + Rotate the turtleshape by *angle* from its current tilt-angle, but do *not* + change the turtle's heading (direction of movement). + + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(30) + >>> turtle.fd(50) + >>> turtle.tilt(30) + >>> turtle.fd(50) + + +.. function:: settiltangle(angle) + + :param angle: a number + + Rotate the turtleshape to point in the direction specified by *angle*, + regardless of its current tilt-angle. *Do not* change the turtle's heading + (direction of movement). + + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.settiltangle(45) + >>> stamp() + >>> turtle.fd(50) + >>> turtle.settiltangle(-45) + >>> stamp() + >>> turtle.fd(50) + + +.. function:: tiltangle() + + Return the current tilt-angle, i.e. the angle between the orientation of the + turtleshape and the heading of the turtle (its direction of movement). + + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(45) + >>> turtle.tiltangle() + 45 + + +Using events +------------ + +.. function:: onclick(fun, btn=1, add=None) + + :param fun: a function with two arguments which will be called with the + coordinates of the clicked point on the canvas + :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param add: ``True`` or ``False`` -- if ``True``, a new binding will be + added, otherwise it will replace a former binding + + Bind *fun* to mouse-click events on this turtle. If *fun* is ``None``, + existing bindings are removed. Example for the anonymous turtle, i.e. the + procedural way: + + >>> def turn(x, y): + ... left(180) + ... + >>> onclick(turn) # Now clicking into the turtle will turn it. + >>> onclick(None) # event-binding will be removed + + +.. function:: onrelease(fun, btn=1, add=None) + + :param fun: a function with two arguments which will be called with the + coordinates of the clicked point on the canvas + :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param add: ``True`` or ``False`` -- if ``True``, a new binding will be + added, otherwise it will replace a former binding + + Bind *fun* to mouse-button-release events on this turtle. If *fun* is + ``None``, existing bindings are removed. + + >>> class MyTurtle(Turtle): + ... def glow(self,x,y): + ... self.fillcolor("red") + ... def unglow(self,x,y): + ... self.fillcolor("") + ... + >>> turtle = MyTurtle() + >>> turtle.onclick(turtle.glow) # clicking on turtle turns fillcolor red, + >>> turtle.onrelease(turtle.unglow) # releasing turns it to transparent. + + +.. function:: ondrag(fun, btn=1, add=None) + + :param fun: a function with two arguments which will be called with the + coordinates of the clicked point on the canvas + :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param add: ``True`` or ``False`` -- if ``True``, a new binding will be + added, otherwise it will replace a former binding + + Bind *fun* to mouse-move events on this turtle. If *fun* is ``None``, + existing bindings are removed. - Return the width of the canvas window. + Remark: Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + + >>> turtle.ondrag(turtle.goto) + # Subsequently, clicking and dragging the Turtle will move it across + # the screen thereby producing handdrawings (if pen is down). + + +Special Turtle methods +---------------------- + +.. function:: begin_poly() + + Start recording the vertices of a polygon. Current turtle position is first + vertex of polygon. + + +.. function:: end_poly() + + Stop recording the vertices of a polygon. Current turtle position is last + vertex of polygon. This will be connected with the first vertex. + + +.. function:: get_poly() + + Return the last recorded polygon. + + >>> p = turtle.get_poly() + >>> turtle.register_shape("myFavouriteShape", p) + + +.. function:: clone() + + Create and return a clone of the turtle with same position, heading and + turtle properties. + + >>> mick = Turtle() + >>> joe = mick.clone() + + +.. function:: getturtle() + + Return the Turtle object itself. Only reasonable use: as a function to + return the "anonymous turtle": + + >>> pet = getturtle() + >>> pet.fd(50) + >>> pet + + >>> turtles() + [] + + +.. function:: getscreen() + + Return the :class:`TurtleScreen` object the turtle is drawing on. + TurtleScreen methods can then be called for that object. + + >>> ts = turtle.getscreen() + >>> ts + + >>> ts.bgcolor("pink") + + +.. function:: setundobuffer(size) + + :param size: an integer or ``None`` + + Set or disable undobuffer. If *size* is an integer an empty undobuffer of + given size is installed. *size* gives the maximum number of turtle actions + that can be undone by the :func:`undo` method/function. If *size* is + ``None``, the undobuffer is disabled. + + >>> turtle.setundobuffer(42) + + +.. function:: undobufferentries() + + Return number of entries in the undobuffer. + + >>> while undobufferentries(): + ... undo() + + +.. _compoundshapes: + +Excursus about the use of compound shapes +----------------------------------------- + +To use compound turtle shapes, which consist of several polygons of different +color, you must use the helper class :class:`Shape` explicitly as described +below: + +1. Create an empty Shape object of type "compound". +2. Add as many components to this object as desired, using the + :meth:`addcomponent` method. + + For example: + + >>> s = Shape("compound") + >>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s.addcomponent(poly1, "red", "blue") + >>> poly2 = ((0,0),(10,-5),(-10,-5)) + >>> s.addcomponent(poly2, "blue", "red") + +3. Now add the Shape to the Screen's shapelist and use it: + + >>> register_shape("myshape", s) + >>> shape("myshape") + + +.. note:: + + The :class:`Shape` class is used internally by the :func:`register_shape` + method in different ways. The application programmer has to deal with the + Shape class *only* when using compound shapes like shown above! + + +Methods of TurtleScreen/Screen and corresponding functions +========================================================== + +Most of the examples in this section refer to a TurtleScreen instance called +``screen``. + + +Window control +-------------- + +.. function:: bgcolor(*args) + + :param args: a color string or three numbers in the range 0..colormode or a + 3-tuple of such numbers + + Set or return background color of the TurtleScreen. + + >>> screen.bgcolor("orange") + >>> screen.bgcolor() + "orange" + >>> screen.bgcolor(0.5,0,0.5) + >>> screen.bgcolor() + "#800080" + + +.. function:: bgpic(picname=None) + + :param picname: a string, name of a gif-file or ``"nopic"``, or ``None`` + + Set background image or return name of current backgroundimage. If *picname* + is a filename, set the corresponding image as background. If *picname* is + ``"nopic"``, delete background image, if present. If *picname* is ``None``, + return the filename of the current backgroundimage. + + >>> screen.bgpic() + "nopic" + >>> screen.bgpic("landscape.gif") + >>> screen.bgpic() + "landscape.gif" + + +.. function:: clear() + clearscreen() + + Delete all drawings and all turtles from the TurtleScreen. Reset the now + empty TurtleScreen to its initial state: white background, no background + image, no event bindings and tracing on. + + .. note:: + This TurtleScreen method is available as a global function only under the + name ``clearscreen``. The global function ``clear`` is another one + derived from the Turtle method ``clear``. + + +.. function:: reset() + resetscreen() + + Reset all Turtles on the Screen to their initial state. + + .. note:: + This TurtleScreen method is available as a global function only under the + name ``resetscreen``. The global function ``reset`` is another one + derived from the Turtle method ``reset``. + + +.. function:: screensize(canvwidth=None, canvheight=None, bg=None) + + :param canvwidth: positive integer, new width of canvas in pixels + :param canvheight: positive integer, new height of canvas in pixels + :param bg: colorstring or color-tupel, new background color + + If no arguments are given, return current (canvaswidth, canvasheight). Else + resize the canvas the turtles are drawing on. Do not alter the drawing + window. To observe hidden parts of the canvas, use the scrollbars. With this + method, one can make visible those parts of a drawing which were outside the + canvas before. + + >>> turtle.screensize(2000,1500) + # e.g. to search for an erroneously escaped turtle ;-) + + +.. function:: setworldcoordinates(llx, lly, urx, ury) + + :param llx: a number, x-coordinate of lower left corner of canvas + :param lly: a number, y-coordinate of lower left corner of canvas + :param urx: a number, x-coordinate of upper right corner of canvas + :param ury: a number, y-coordinate of upper right corner of canvas + + Set up user-defined coordinate system and switch to mode "world" if + necessary. This performs a ``screen.reset()``. If mode "world" is already + active, all drawings are redrawn according to the new coordinates. + + **ATTENTION**: in user-defined coordinate systems angles may appear + distorted. + + >>> screen.reset() + >>> screen.setworldcoordinates(-50,-7.5,50,7.5) + >>> for _ in range(72): + ... left(10) + ... + >>> for _ in range(8): + ... left(45); fd(2) # a regular octogon + + +Animation control +----------------- + +.. function:: delay(delay=None) + + :param delay: positive integer + + Set or return the drawing *delay* in milliseconds. (This is approximately + the time interval between two consecutived canvas updates.) The longer the + drawing delay, the slower the animation. + + Optional argument: + + >>> screen.delay(15) + >>> screen.delay() + 15 + + +.. function:: tracer(n=None, delay=None) + + :param n: nonnegative integer + :param delay: nonnegative integer + + Turn turtle animation on/off and set delay for update drawings. If *n* is + given, only each n-th regular screen update is really performed. (Can be + used to accelerate the drawing of complex graphics.) Second argument sets + delay value (see :func:`delay`). + + >>> screen.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + ... fd(dist) + ... rt(90) + ... dist += 2 + + +.. function:: update() + + Perform a TurtleScreen update. To be used when tracer is turned off. + +See also the RawTurtle/Turtle method :func:`speed`. + + +Using screen events +------------------- + +.. function:: listen(xdummy=None, ydummy=None) + + Set focus on TurtleScreen (in order to collect key-events). Dummy arguments + are provided in order to be able to pass :func:`listen` to the onclick method. + + +.. function:: onkey(fun, key) + + :param fun: a function with no arguments or ``None`` + :param key: a string: key (e.g. "a") or key-symbol (e.g. "space") + + Bind *fun* to key-release event of key. If *fun* is ``None``, event bindings + are removed. Remark: in order to be able to register key-events, TurtleScreen + must have the focus. (See method :func:`listen`.) + + >>> def f(): + ... fd(50) + ... lt(60) + ... + >>> screen.onkey(f, "Up") + >>> screen.listen() + + +.. function:: onclick(fun, btn=1, add=None) + onscreenclick(fun, btn=1, add=None) + + :param fun: a function with two arguments which will be called with the + coordinates of the clicked point on the canvas + :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param add: ``True`` or ``False`` -- if ``True``, a new binding will be + added, otherwise it will replace a former binding + + Bind *fun* to mouse-click events on this screen. If *fun* is ``None``, + existing bindings are removed. + + Example for a TurtleScreen instance named ``screen`` and a Turtle instance + named turtle: + + >>> screen.onclick(turtle.goto) + # Subsequently clicking into the TurtleScreen will + # make the turtle move to the clicked point. + >>> screen.onclick(None) # remove event binding again + + .. note:: + This TurtleScreen method is available as a global function only under the + name ``onscreenclick``. The global function ``onclick`` is another one + derived from the Turtle method ``onclick``. + + +.. function:: ontimer(fun, t=0) + + :param fun: a function with no arguments + :param t: a number >= 0 + + Install a timer that calls *fun* after *t* milliseconds. + + >>> running = True + >>> def f(): + if running: + fd(50) + lt(60) + screen.ontimer(f, 250) + >>> f() ### makes the turtle marching around + >>> running = False + + +Settings and special methods +---------------------------- + +.. function:: mode(mode=None) + + :param mode: one of the strings "standard", "logo" or "world" + + Set turtle mode ("standard", "logo" or "world") and perform reset. If mode + is not given, current mode is returned. + + Mode "standard" is compatible with old :mod:`turtle`. Mode "logo" is + compatible with most Logo turtle graphics. Mode "world" uses user-defined + "world coordinates". **Attention**: in this mode angles appear distorted if + ``x/y`` unit-ratio doesn't equal 1. + + ============ ========================= =================== + Mode Initial turtle heading positive angles + ============ ========================= =================== + "standard" to the right (east) counterclockwise + "logo" upward (north) clockwise + ============ ========================= =================== + + >>> mode("logo") # resets turtle heading to north + >>> mode() + "logo" + + +.. function:: colormode(cmode=None) + + :param cmode: one of the values 1.0 or 255 + + Return the colormode or set it to 1.0 or 255. Subsequently *r*, *g*, *b* + values of color triples have to be in the range 0..\ *cmode*. + + >>> screen.colormode() + 1.0 + >>> screen.colormode(255) + >>> turtle.pencolor(240,160,80) + + +.. function:: getcanvas() + + Return the Canvas of this TurtleScreen. Useful for insiders who know what to + do with a Tkinter Canvas. + + >>> cv = screen.getcanvas() + >>> cv + + + +.. function:: getshapes() + + Return a list of names of all currently available turtle shapes. + + >>> screen.getshapes() + ["arrow", "blank", "circle", ..., "turtle"] + + +.. function:: register_shape(name, shape=None) + addshape(name, shape=None) + + There are three different ways to call this function: + + (1) *name* is the name of a gif-file and *shape* is ``None``: Install the + corresponding image shape. + + .. note:: + Image shapes *do not* rotate when turning the turtle, so they do not + display the heading of the turtle! + + (2) *name* is an arbitrary string and *shape* is a tuple of pairs of + coordinates: Install the corresponding polygon shape. + + (3) *name* is an arbitrary string and shape is a (compound) :class:`Shape` + object: Install the corresponding compound shape. + + Add a turtle shape to TurtleScreen's shapelist. Only thusly registered + shapes can be used by issuing the command ``shape(shapename)``. + + >>> screen.register_shape("turtle.gif") + >>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3))) + + +.. function:: turtles() + + Return the list of turtles on the screen. + + >>> for turtle in screen.turtles() + ... turtle.color("red") .. function:: window_height() - Return the height of the canvas window. + Return the height of the turtle window. + >>> screen.window_height() + 480 -This module also does ``from math import *``, so see the documentation for the -:mod:`math` module for additional constants and functions useful for turtle -graphics. +.. function:: window_width() + + Return the width of the turtle window. + + >>> screen.window_width() + 640 + + +.. _screenspecific: + +Methods specific to Screen, not inherited from TurtleScreen +----------------------------------------------------------- + +.. function:: bye() + + Shut the turtlegraphics window. + + +.. function:: exitonclick() + + Bind bye() method to mouse clicks on the Screen. -.. function:: demo() - Exercise the module a bit. + If the value "using_IDLE" in the configuration dictionary is ``False`` + (default value), also enter mainloop. Remark: If IDLE with the ``-n`` switch + (no subprocess) is used, this value should be set to ``True`` in + :file:`turtle.cfg`. In this case IDLE's own mainloop is active also for the + client script. -.. exception:: Error +.. function:: setup(width=_CFG["width"], height=_CFG["height"], startx=_CFG["leftright"], starty=_CFG["topbottom"]) - Exception raised on any error caught by this module. + Set the size and position of the main window. Default values of arguments + are stored in the configuration dicionary and can be changed via a + :file:`turtle.cfg` file. -For examples, see the code of the :func:`demo` function. + :param width: if an integer, a size in pixels, if a float, a fraction of the + screen; default is 50% of screen + :param height: if an integer, the height in pixels, if a float, a fraction of + the screen; default is 75% of screen + :param startx: if positive, starting position in pixels from the left + edge of the screen, if negative from the right edge, if None, + center window horizontally + :param startx: if positive, starting position in pixels from the top + edge of the screen, if negative from the bottom edge, if None, + center window vertically -This module defines the following classes: + >>> screen.setup (width=200, height=200, startx=0, starty=0) + # sets window to 200x200 pixels, in upper left of screen + >>> screen.setup(width=.75, height=0.5, startx=None, starty=None) + # sets window to 75% of screen by 50% of screen and centers -.. class:: Pen() +.. function:: title(titlestring) - Define a pen. All above functions can be called as a methods on the given pen. - The constructor automatically creates a canvas do be drawn on. + :param titlestring: a string that is shown in the titlebar of the turtle + graphics window + + Set title of turtle window to *titlestring*. + + >>> screen.title("Welcome to the turtle zoo!") + + +The public classes of the module :mod:`turtle` +============================================== + + +.. class:: RawTurtle(canvas) + RawPen(canvas) + + :param canvas: a :class:`Tkinter.Canvas`, a :class:`ScrolledCanvas` or a + :class:`TurtleScreen` + + Create a turtle. The turtle has all methods described above as "methods of + Turtle/RawTurtle". .. class:: Turtle() - Define a pen. This is essentially a synonym for ``Pen()``; :class:`Turtle` is an - empty subclass of :class:`Pen`. + Subclass of RawTurtle, has the same interface but draws on a default + :class:`Screen` object created automatically when needed for the first time. -.. class:: RawPen(canvas) +.. class:: TurtleScreen(cv) - Define a pen which draws on a canvas *canvas*. This is useful if you want to - use the module to create graphics in a "real" program. + :param cv: a :class:`Tkinter.Canvas` + Provides screen oriented methods like :func:`setbg` etc. that are described + above. -.. _pen-rawpen-objects: +.. class:: Screen() -Turtle, Pen and RawPen Objects ------------------------------- + Subclass of TurtleScreen, with :ref:`four methods added `. + + +.. class:: ScrolledCavas(master) -Most of the global functions available in the module are also available as -methods of the :class:`Turtle`, :class:`Pen` and :class:`RawPen` classes, -affecting only the state of the given pen. + :param master: some Tkinter widget to contain the ScrolledCanvas, i.e. + a Tkinter-canvas with scrollbars added -The only method which is more powerful as a method is :func:`degrees`, which -takes an optional argument letting you specify the number of units -corresponding to a full circle: + Used by class Screen, which thus automatically provides a ScrolledCanvas as + playground for the turtles. +.. class:: Shape(type_, data) -.. method:: Turtle.degrees([fullcircle]) + :param type\_: one of the strings "polygon", "image", "compound" - *fullcircle* is by default 360. This can cause the pen to have any angular units - whatever: give *fullcircle* ``2*pi`` for radians, or 400 for gradians. + Data structure modeling shapes. The pair ``(type_, data)`` must follow this + specification: + + + =========== =========== + *type_* *data* + =========== =========== + "polygon" a polygon-tuple, i.e. a tuple of pairs of coordinates + "image" an image (in this form only used internally!) + "compound" ``None`` (a compund shape has to be constructed using the + :meth:`addcomponent` method) + =========== =========== + + .. method:: addcomponent(poly, fill, outline=None) + + :param poly: a polygon, i.e. a tuple of pairs of numbers + :param fill: a color the *poly* will be filled with + :param outline: a color for the poly's outline (if given) + + Example: + + >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s = Shape("compound") + >>> s.addcomponent(poly, "red", "blue") + # .. add more components and then use register_shape() + + See :ref:`compoundshapes`. + + +.. class:: Vec2D(x, y) + + A two-dimensional vector class, used as a helper class for implementing + turtle graphics. May be useful for turtle graphics programs too. Derived + from tuple, so a vector is a tuple! + + Provides (for *a*, *b* vectors, *k* number): + + * ``a + b`` vector addition + * ``a - b`` vector subtraction + * ``a * b`` inner product + * ``k * a`` and ``a * k`` multiplication with scalar + * ``abs(a)`` absolute value of a + * ``a.rotate(angle)`` rotation + + +Help and configuration +====================== + +How to use help +--------------- + +The public methods of the Screen and Turtle classes are documented extensively +via docstrings. So these can be used as online-help via the Python help +facilities: + +- When using IDLE, tooltips show the signatures and first lines of the + docstrings of typed in function-/method calls. + +- Calling :func:`help` on methods or functions displays the docstrings:: + + >>> help(Screen.bgcolor) + Help on method bgcolor in module turtle: + + bgcolor(self, *args) unbound turtle.Screen method + Set or return backgroundcolor of the TurtleScreen. + + Arguments (if given): a color string or three numbers + in the range 0..colormode or a 3-tuple of such numbers. + + + >>> screen.bgcolor("orange") + >>> screen.bgcolor() + "orange" + >>> screen.bgcolor(0.5,0,0.5) + >>> screen.bgcolor() + "#800080" + + >>> help(Turtle.penup) + Help on method penup in module turtle: + + penup(self) unbound turtle.Turtle method + Pull the pen up -- no drawing when moving. + + Aliases: penup | pu | up + + No argument + + >>> turtle.penup() + +- The docstrings of the functions which are derived from methods have a modified + form:: + + >>> help(bgcolor) + Help on function bgcolor in module turtle: + + bgcolor(*args) + Set or return backgroundcolor of the TurtleScreen. + + Arguments (if given): a color string or three numbers + in the range 0..colormode or a 3-tuple of such numbers. + + Example:: + + >>> bgcolor("orange") + >>> bgcolor() + "orange" + >>> bgcolor(0.5,0,0.5) + >>> bgcolor() + "#800080" + + >>> help(penup) + Help on function penup in module turtle: + + penup() + Pull the pen up -- no drawing when moving. + + Aliases: penup | pu | up + + No argument + + Example: + >>> penup() + +These modified docstrings are created automatically together with the function +definitions that are derived from the methods at import time. + + +Translation of docstrings into different languages +-------------------------------------------------- + +There is a utility to create a dictionary the keys of which are the method names +and the values of which are the docstrings of the public methods of the classes +Screen and Turtle. + +.. function:: write_docstringdict(filename="turtle_docstringdict") + + :param filename: a string, used as filename + + Create and write docstring-dictionary to a Python script with the given + filename. This function has to be called explicitly (it is not used by the + turtle graphics classes). The docstring dictionary will be written to the + Python script :file:`{filename}.py`. It is intended to serve as a template + for translation of the docstrings into different languages. + +If you (or your students) want to use :mod:`turtle` with online help in your +native language, you have to translate the docstrings and save the resulting +file as e.g. :file:`turtle_docstringdict_german.py`. + +If you have an appropriate entry in your :file:`turtle.cfg` file this dictionary +will be read in at import time and will replace the original English docstrings. + +At the time of this writing there are docstring dictionaries in German and in +Italian. (Requests please to glingl@aon.at.) + + + +How to configure Screen and Turtles +----------------------------------- + +The built-in default configuration mimics the appearance and behaviour of the +old turtle module in order to retain best possible compatibility with it. + +If you want to use a different configuration which better reflects the features +of this module or which better fits to your needs, e.g. for use in a classroom, +you can prepare a configuration file ``turtle.cfg`` which will be read at import +time and modify the configuration according to its settings. + +The built in configuration would correspond to the following turtle.cfg:: + + width = 0.5 + height = 0.75 + leftright = None + topbottom = None + canvwidth = 400 + canvheight = 300 + mode = standard + colormode = 1.0 + delay = 10 + undobuffersize = 1000 + shape = classic + pencolor = black + fillcolor = black + resizemode = noresize + visible = True + language = english + exampleturtle = turtle + examplescreen = screen + title = Python Turtle Graphics + using_IDLE = False + +Short explanation of selected entries: + +- The first four lines correspond to the arguments of the :meth:`Screen.setup` + method. +- Line 5 and 6 correspond to the arguments of the method + :meth:`Screen.screensize`. +- *shape* can be any of the built-in shapes, e.g: arrow, turtle, etc. For more + info try ``help(shape)``. +- If you want to use no fillcolor (i.e. make the turtle transparent), you have + to write ``fillcolor = ""`` (but all nonempty strings must not have quotes in + the cfg-file). +- If you want to reflect the turtle its state, you have to use ``resizemode = + auto``. +- If you set e.g. ``language = italian`` the docstringdict + :file:`turtle_docstringdict_italian.py` will be loaded at import time (if + present on the import path, e.g. in the same directory as :mod:`turtle`. +- The entries *exampleturtle* and *examplescreen* define the names of these + objects as they occur in the docstrings. The transformation of + method-docstrings to function-docstrings will delete these names from the + docstrings. +- *using_IDLE*: Set this to ``True`` if you regularly work with IDLE and its -n + switch ("no subprocess"). This will prevent :func:`exitonclick` to enter the + mainloop. + +There can be a :file:`turtle.cfg` file in the directory where :mod:`turtle` is +stored and an additional one in the current working directory. The latter will +override the settings of the first one. + +The :file:`Demo/turtle` directory contains a :file:`turtle.cfg` file. You can +study it as an example and see its effects when running the demos (preferably +not from within the demo-viewer). + + +Demo scripts +============ + +There is a set of demo scripts in the turtledemo directory located in the +:file:`Demo/turtle` directory in the source distribution. + +It contains: + +- a set of 15 demo scripts demonstrating differet features of the new module + :mod:`turtle` +- a demo viewer :file:`turtleDemo.py` which can be used to view the sourcecode + of the scripts and run them at the same time. 14 of the examples can be + accessed via the Examples menu; all of them can also be run standalone. +- The example :file:`turtledemo_two_canvases.py` demonstrates the simultaneous + use of two canvases with the turtle module. Therefore it only can be run + standalone. +- There is a :file:`turtle.cfg` file in this directory, which also serves as an + example for how to write and use such files. + +The demoscripts are: + ++----------------+------------------------------+-----------------------+ +| Name | Description | Features | ++----------------+------------------------------+-----------------------+ +| bytedesign | complex classical | :func:`tracer`, delay,| +| | turtlegraphics pattern | :func:`update` | ++----------------+------------------------------+-----------------------+ +| chaos | graphs verhust dynamics, | world coordinates | +| | proves that you must not | | +| | trust computers' computations| | ++----------------+------------------------------+-----------------------+ +| clock | analog clock showing time | turtles as clock's | +| | of your computer | hands, ontimer | ++----------------+------------------------------+-----------------------+ +| colormixer | experiment with r, g, b | :func:`ondrag` | ++----------------+------------------------------+-----------------------+ +| fractalcurves | Hilbert & Koch curves | recursion | ++----------------+------------------------------+-----------------------+ +| lindenmayer | ethnomathematics | L-System | +| | (indian kolams) | | ++----------------+------------------------------+-----------------------+ +| minimal_hanoi | Towers of Hanoi | Rectangular Turtles | +| | | as Hanoi discs | +| | | (shape, shapesize) | ++----------------+------------------------------+-----------------------+ +| paint | super minimalistic | :func:`onclick` | +| | drawing program | | ++----------------+------------------------------+-----------------------+ +| peace | elementary | turtle: appearance | +| | | and animation | ++----------------+------------------------------+-----------------------+ +| penrose | aperiodic tiling with | :func:`stamp` | +| | kites and darts | | ++----------------+------------------------------+-----------------------+ +| planet_and_moon| simulation of | compound shapes, | +| | gravitational system | :class:`Vec2D` | ++----------------+------------------------------+-----------------------+ +| tree | a (graphical) breadth | :func:`clone` | +| | first tree (using generators)| | ++----------------+------------------------------+-----------------------+ +| wikipedia | a pattern from the wikipedia | :func:`clone`, | +| | article on turtle graphics | :func:`undo` | ++----------------+------------------------------+-----------------------+ +| yingyang | another elementary example | :func:`circle` | ++----------------+------------------------------+-----------------------+ + +Have fun! + + +Changes since Python 2.6 +======================== + +- The methods :meth:`Turtle.tracer`, :meth:`Turtle.window_width` and + :meth:`Turtle.window_height` have been eliminated. + Methods with these names and functionality are now available only + as methods of :class:`Screen`. The functions derived from these remain + available. (In fact already in Python 2.6 these methods were merely + duplications of the corresponding + :class:`TurtleScreen`/:class:`Screen`-methods.) + +- The method :meth:`Turtle.fill` has been eliminated. + The behaviour of :meth:`begin_fill` and :meth:`end_fill` + have changed slightly: now every filling-process must be completed with an + ``end_fill()`` call. + +- A method :meth:`Turtle.filling` has been added. It returns a boolean + value: ``True`` if a filling process is under way, ``False`` otherwise. + This behaviour corresponds to a ``fill()`` call without arguments in + Python 2.6 diff --git a/Lib/tkinter/turtle.py b/Lib/tkinter/turtle.py index 3dee659..1497d2d 100644 --- a/Lib/tkinter/turtle.py +++ b/Lib/tkinter/turtle.py @@ -1,9 +1,31 @@ -# LogoMation-like turtle graphics +# +# turtle.py: a Tkinter based turtle graphics module for Python +# Version 1.0b1 - 31. 5. 2008 +# +# Copyright (C) 2006 - 2008 Gregor Lingl +# email: glingl@aon.at +# +# This software is provided 'as-is', without any express or implied +# warranty. In no event will the authors be held liable for any damages +# arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software +# in a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# 3. This notice may not be removed or altered from any source distribution. + """ Turtle graphics is a popular way for introducing programming to kids. It was part of the original Logo programming language developed -by Wally Feurzeig and Seymour Papert in 1966. +by Wally Feurzig and Seymour Papert in 1966. Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it the command turtle.forward(15), and it moves (on-screen!) 15 pixels in @@ -12,945 +34,4018 @@ command turtle.left(25), and it rotates in-place 25 degrees clockwise. By combining together these and similar commands, intricate shapes and pictures can easily be drawn. + +----- turtle.py + +This module is an extended reimplementation of turtle.py from the +Python standard distribution up to Python 2.5. (See: http:\\www.python.org) + +It tries to keep the merits of turtle.py and to be (nearly) 100% +compatible with it. This means in the first place to enable the +learning programmer to use all the commands, classes and methods +interactively when using the module from within IDLE run with +the -n switch. + +Roughly it has the following features added: + +- Better animation of the turtle movements, especially of turning the + turtle. So the turtles can more easily be used as a visual feedback + instrument by the (beginning) programmer. + +- Different turtle shapes, gif-images as turtle shapes, user defined + and user controllable turtle shapes, among them compound + (multicolored) shapes. Turtle shapes can be stgretched and tilted, which + makes turtles zu very versatile geometrical objects. + +- Fine control over turtle movement and screen updates via delay(), + and enhanced tracer() and speed() methods. + +- Aliases for the most commonly used commands, like fd for forward etc., + following the early Logo traditions. This reduces the boring work of + typing long sequences of commands, which often occur in a natural way + when kids try to program fancy pictures on their first encounter with + turtle graphcis. + +- Turtles now have an undo()-method with configurable undo-buffer. + +- Some simple commands/methods for creating event driven programs + (mouse-, key-, timer-events). Especially useful for programming games. + +- A scrollable Canvas class. The default scrollable Canvas can be + extended interactively as needed while playing around with the turtle(s). + +- A TurtleScreen class with methods controlling background color or + background image, window and canvas size and other properties of the + TurtleScreen. + +- There is a method, setworldcoordinates(), to install a user defined + coordinate-system for the TurtleScreen. + +- The implementation uses a 2-vector class named Vec2D, derived from tuple. + This class is public, so it can be imported by the application programmer, + which makes certain types of computations very natural and compact. + +- Appearance of the TurtleScreen and the Turtles at startup/import can be + configured by means of a turtle.cfg configuration file. + The default configuration mimics the appearance of the old turtle module. + +- If configured appropriately the module reads in docstrings from a docstring + dictionary in some different language, supplied separately and replaces + the english ones by those read in. There is a utility function + write_docstringdict() to write a dictionary with the original (english) + docstrings to disc, so it can serve as a template for translations. + +Behind the scenes there are some features included with possible +extensionsin in mind. These will be commented and documented elsewhere. + """ -from math import * # Also for export -from time import sleep -import tkinter +_ver = "turtle 1.0b1- - for Python 3.0 - 9. 6. 2008, 01:15" + +# print(_ver) + +import tkinter as TK +import types +import math +import time +import os + +from os.path import isfile, split, join +from copy import deepcopy + +#from math import * ## for compatibility with old turtle module + +_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen', + 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D'] +_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye', + 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas', + 'getshapes', 'listen', 'mode', 'onkey', 'onscreenclick', 'ontimer', + 'register_shape', 'resetscreen', 'screensize', 'setup', + 'setworldcoordinates', 'title', 'tracer', 'turtles', 'update', + 'window_height', 'window_width'] +_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk', + 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color', + 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd', + #'fill', + 'fillcolor', 'forward', 'get_poly', 'getpen', 'getscreen', + 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown', + 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd', + 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', + 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt', + 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle', + 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'showturtle', + 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', #'tracer', + 'turtlesize', 'undo', 'undobufferentries', 'up', 'width', + #'window_height', 'window_width', + 'write', 'xcor', 'ycor'] +_tg_utilities = ['write_docstringdict', 'done', 'mainloop'] +##_math_functions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', +## 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', +## 'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'] + +__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + + _tg_utilities) # + _math_functions) + +_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', + 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', + 'turtlesize', 'up', 'width'] + +_CFG = {"width" : 0.5, # Screen + "height" : 0.75, + "canvwidth" : 400, + "canvheight": 300, + "leftright": None, + "topbottom": None, + "mode": "standard", # TurtleScreen + "colormode": 1.0, + "delay": 10, + "undobuffersize": 1000, # RawTurtle + "shape": "classic", + "pencolor" : "black", + "fillcolor" : "black", + "resizemode" : "noresize", + "visible" : True, + "language": "english", # docstrings + "exampleturtle": "turtle", + "examplescreen": "screen", + "title": "Python Turtle Graphics", + "using_IDLE": False + } + +##print "cwd:", os.getcwd() +##print "__file__:", __file__ +## +##def show(dictionary): +## print "==========================" +## for key in sorted(dictionary.keys()): +## print key, ":", dictionary[key] +## print "==========================" +## print + +def config_dict(filename): + """Convert content of config-file into dictionary.""" + f = open(filename, "r") + cfglines = f.readlines() + f.close() + cfgdict = {} + for line in cfglines: + line = line.strip() + if not line or line.startswith("#"): + continue + try: + key, value = line.split("=") + except: + print("Bad line in config-file %s:\n%s" % (filename,line)) + continue + key = key.strip() + value = value.strip() + if value in ["True", "False", "None", "''", '""']: + value = eval(value) + else: + try: + if "." in value: + value = float(value) + else: + value = int(value) + except: + pass # value need not be converted + cfgdict[key] = value + return cfgdict + +def readconfig(cfgdict): + """Read config-files, change configuration-dict accordingly. + + If there is a turtle.cfg file in the current working directory, + read it from there. If this contains an importconfig-value, + say 'myway', construct filename turtle_mayway.cfg else use + turtle.cfg and read it from the import-directory, where + turtle.py is located. + Update configuration dictionary first according to config-file, + in the import directory, then according to config-file in the + current working directory. + If no config-file is found, the default configuration is used. + """ + default_cfg = "turtle.cfg" + cfgdict1 = {} + cfgdict2 = {} + if isfile(default_cfg): + cfgdict1 = config_dict(default_cfg) + #print "1. Loading config-file %s from: %s" % (default_cfg, os.getcwd()) + if "importconfig" in cfgdict1: + default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"] + try: + head, tail = split(__file__) + cfg_file2 = join(head, default_cfg) + except: + cfg_file2 = "" + if isfile(cfg_file2): + #print "2. Loading config-file %s:" % cfg_file2 + cfgdict2 = config_dict(cfg_file2) +## show(_CFG) +## show(cfgdict2) + _CFG.update(cfgdict2) +## show(_CFG) +## show(cfgdict1) + _CFG.update(cfgdict1) +## show(_CFG) + +try: + readconfig(_CFG) +except: + print ("No configfile read, reason unknown") + + +class Vec2D(tuple): + """A 2 dimensional vector class, used as a helper class + for implementing turtle graphics. + May be useful for turtle graphics programs also. + Derived from tuple, so a vector is a tuple! + + Provides (for a, b vectors, k number): + a+b vector addition + a-b vector subtraction + a*b inner product + k*a and a*k multiplication with scalar + |a| absolute value of a + a.rotate(angle) rotation + """ + def __new__(cls, x, y): + return tuple.__new__(cls, (x, y)) + def __add__(self, other): + return Vec2D(self[0]+other[0], self[1]+other[1]) + def __mul__(self, other): + if isinstance(other, Vec2D): + return self[0]*other[0]+self[1]*other[1] + return Vec2D(self[0]*other, self[1]*other) + def __rmul__(self, other): + if isinstance(other, int) or isinstance(other, float): + return Vec2D(self[0]*other, self[1]*other) + def __sub__(self, other): + return Vec2D(self[0]-other[0], self[1]-other[1]) + def __neg__(self): + return Vec2D(-self[0], -self[1]) + def __abs__(self): + return (self[0]**2 + self[1]**2)**0.5 + def rotate(self, angle): + """rotate self counterclockwise by angle + """ + perp = Vec2D(-self[1], self[0]) + angle = angle * math.pi / 180.0 + c, s = math.cos(angle), math.sin(angle) + return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s) + def __getnewargs__(self): + return (self[0], self[1]) + def __repr__(self): + return "(%.2f,%.2f)" % self + + +############################################################################## +### From here up to line : Tkinter - Interface for turtle.py ### +### May be replaced by an interface to some different graphcis-toolkit ### +############################################################################## + +## helper functions for Scrolled Canvas, to forward Canvas-methods +## to ScrolledCanvas class + +def __methodDict(cls, _dict): + """helper function for Scrolled Canvas""" + baseList = list(cls.__bases__) + baseList.reverse() + for _super in baseList: + __methodDict(_super, _dict) + for key, value in cls.__dict__.items(): + if type(value) == types.FunctionType: + _dict[key] = value + +def __methods(cls): + """helper function for Scrolled Canvas""" + _dict = {} + __methodDict(cls, _dict) + return _dict.keys() + +__stringBody = ( + 'def %(method)s(self, *args, **kw): return ' + + 'self.%(attribute)s.%(method)s(*args, **kw)') + +def __forwardmethods(fromClass, toClass, toPart, exclude = ()): + ### MANY CHANGES ### + _dict_1 = {} + __methodDict(toClass, _dict_1) + _dict = {} + mfc = __methods(fromClass) + for ex in _dict_1.keys(): + if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc: + pass + else: + _dict[ex] = _dict_1[ex] + + for method, func in _dict.items(): + d = {'method': method, 'func': func} + if isinstance(toPart, str): + execString = \ + __stringBody % {'method' : method, 'attribute' : toPart} + exec(execString, d) + setattr(fromClass, method, d[method]) ### NEWU! + + +class ScrolledCanvas(TK.Frame): + """Modeled after the scrolled canvas class from Grayons's Tkinter book. + + Used as the default canvas, which pops up automatically when + using turtle graphics functions or the Turtle class. + """ + def __init__(self, master, width=500, height=350, + canvwidth=600, canvheight=500): + TK.Frame.__init__(self, master, width=width, height=height) + self._root = self.winfo_toplevel() + self.width, self.height = width, height + self.canvwidth, self.canvheight = canvwidth, canvheight + self.bg = "white" + self._canvas = TK.Canvas(master, width=width, height=height, + bg=self.bg, relief=TK.SUNKEN, borderwidth=2) + self.hscroll = TK.Scrollbar(master, command=self._canvas.xview, + orient=TK.HORIZONTAL) + self.vscroll = TK.Scrollbar(master, command=self._canvas.yview) + self._canvas.configure(xscrollcommand=self.hscroll.set, + yscrollcommand=self.vscroll.set) + self.rowconfigure(0, weight=1, minsize=0) + self.columnconfigure(0, weight=1, minsize=0) + self._canvas.grid(padx=1, in_ = self, pady=1, row=0, + column=0, rowspan=1, columnspan=1, sticky='news') + self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, + column=1, rowspan=1, columnspan=1, sticky='news') + self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, + column=0, rowspan=1, columnspan=1, sticky='news') + self.reset() + self._root.bind('', self.onResize) + + def reset(self, canvwidth=None, canvheight=None, bg = None): + """Ajust canvas and scrollbars according to given canvas size.""" + if canvwidth: + self.canvwidth = canvwidth + if canvheight: + self.canvheight = canvheight + if bg: + self.bg = bg + self._canvas.config(bg=bg, + scrollregion=(-self.canvwidth//2, -self.canvheight//2, + self.canvwidth//2, self.canvheight//2)) + self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) / + self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) / + self.canvheight) + self.adjustScrolls() + + + def adjustScrolls(self): + """ Adjust scrollbars according to window- and canvas-size. + """ + cwidth = self._canvas.winfo_width() + cheight = self._canvas.winfo_height() + self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) + if cwidth < self.canvwidth or cheight < self.canvheight: + self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, + column=0, rowspan=1, columnspan=1, sticky='news') + self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, + column=1, rowspan=1, columnspan=1, sticky='news') + else: + self.hscroll.grid_forget() + self.vscroll.grid_forget() + + def onResize(self, event): + """self-explanatory""" + self.adjustScrolls() + + def bbox(self, *args): + """ 'forward' method, which canvas itself has inherited... + """ + return self._canvas.bbox(*args) + + def cget(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + return self._canvas.cget(*args, **kwargs) + + def config(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.config(*args, **kwargs) + + def bind(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.bind(*args, **kwargs) + + def unbind(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.unbind(*args, **kwargs) + + def focus_force(self): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.focus_force() + +__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas') + + +class _Root(TK.Tk): + """Root class for Screen based on Tkinter.""" + def __init__(self): + TK.Tk.__init__(self) + + def setupcanvas(self, width, height, cwidth, cheight): + self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight) + self._canvas.pack(expand=1, fill="both") + + def _getcanvas(self): + return self._canvas -speeds = ['fastest', 'fast', 'normal', 'slow', 'slowest'] + def set_geometry(self, width, height, startx, starty): + self.geometry("%dx%d%+d%+d"%(width, height, startx, starty)) -class Error(Exception): + def ondestroy(self, destroy): + self.wm_protocol("WM_DELETE_WINDOW", destroy) + + def win_width(self): + return self.winfo_screenwidth() + + def win_height(self): + return self.winfo_screenheight() + +Canvas = TK.Canvas + + +class TurtleScreenBase(object): + """Provide the basic graphics functionality. + Interface between Tkinter and turtle.py. + + To port turtle.py to some different graphics toolkit + a corresponding TurtleScreenBase class has to be implemented. + """ + + @staticmethod + def _blankimage(): + """return a blank image object + """ + img = TK.PhotoImage(width=1, height=1) + img.blank() + return img + + @staticmethod + def _image(filename): + """return an image object containing the + imagedata from a gif-file named filename. + """ + return TK.PhotoImage(file=filename) + + def __init__(self, cv): + self.cv = cv + if isinstance(cv, ScrolledCanvas): + w = self.cv.canvwidth + h = self.cv.canvheight + else: # expected: ordinary TK.Canvas + w = int(self.cv.cget("width")) + h = int(self.cv.cget("height")) + self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 )) + self.canvwidth = w + self.canvheight = h + self.xscale = self.yscale = 1.0 + + def _createpoly(self): + """Create an invisible polygon item on canvas self.cv) + """ + return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="") + + def _drawpoly(self, polyitem, coordlist, fill=None, + outline=None, width=None, top=False): + """Configure polygonitem polyitem according to provided + arguments: + coordlist is sequence of coordinates + fill is filling color + outline is outline color + top is a boolean value, which specifies if polyitem + will be put on top of the canvas' displaylist so it + will not be covered by other items. + """ + cl = [] + for x, y in coordlist: + cl.append(x * self.xscale) + cl.append(-y * self.yscale) + self.cv.coords(polyitem, *cl) + if fill is not None: + self.cv.itemconfigure(polyitem, fill=fill) + if outline is not None: + self.cv.itemconfigure(polyitem, outline=outline) + if width is not None: + self.cv.itemconfigure(polyitem, width=width) + if top: + self.cv.tag_raise(polyitem) + + def _createline(self): + """Create an invisible line item on canvas self.cv) + """ + return self.cv.create_line(0, 0, 0, 0, fill="", width=2, + capstyle = TK.ROUND) + + def _drawline(self, lineitem, coordlist=None, + fill=None, width=None, top=False): + """Configure lineitem according to provided arguments: + coordlist is sequence of coordinates + fill is drawing color + width is width of drawn line. + top is a boolean value, which specifies if polyitem + will be put on top of the canvas' displaylist so it + will not be covered by other items. + """ + if coordlist is not None: + cl = [] + for x, y in coordlist: + cl.append(x * self.xscale) + cl.append(-y * self.yscale) + self.cv.coords(lineitem, *cl) + if fill is not None: + self.cv.itemconfigure(lineitem, fill=fill) + if width is not None: + self.cv.itemconfigure(lineitem, width=width) + if top: + self.cv.tag_raise(lineitem) + + def _delete(self, item): + """Delete graphics item from canvas. + If item is"all" delete all graphics items. + """ + self.cv.delete(item) + + def _update(self): + """Redraw graphics items on canvas + """ + self.cv.update() + + def _delay(self, delay): + """Delay subsequent canvas actions for delay ms.""" + self.cv.after(delay) + + def _iscolorstring(self, color): + """Check if the string color is a legal Tkinter color string. + """ + try: + rgb = self.cv.winfo_rgb(color) + ok = True + except TK.TclError: + ok = False + return ok + + def _bgcolor(self, color=None): + """Set canvas' backgroundcolor if color is not None, + else return backgroundcolor.""" + if color is not None: + self.cv.config(bg = color) + self._update() + else: + return self.cv.cget("bg") + + def _write(self, pos, txt, align, font, pencolor): + """Write txt at pos in canvas with specified font + and color. + Return text item and x-coord of right bottom corner + of text's bounding box.""" + x, y = pos + x = x * self.xscale + y = y * self.yscale + anchor = {"left":"sw", "center":"s", "right":"se" } + item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align], + fill = pencolor, font = font) + x0, y0, x1, y1 = self.cv.bbox(item) + self.cv.update() + return item, x1-1 + +## def _dot(self, pos, size, color): +## """may be implemented for some other graphics toolkit""" + + def _onclick(self, item, fun, num=1, add=None): + """Bind fun to mouse-click event on turtle. + fun must be a function with two arguments, the coordinates + of the clicked point on the canvas. + num, the number of the mouse-button defaults to 1 + """ + if fun is None: + self.cv.tag_unbind(item, "" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.tag_bind(item, "" % num, eventfun, add) + + def _onrelease(self, item, fun, num=1, add=None): + """Bind fun to mouse-button-release event on turtle. + fun must be a function with two arguments, the coordinates + of the point on the canvas where mouse button is released. + num, the number of the mouse-button defaults to 1 + + If a turtle is clicked, first _onclick-event will be performed, + then _onscreensclick-event. + """ + if fun is None: + self.cv.tag_unbind(item, "" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.tag_bind(item, "" % num, + eventfun, add) + + def _ondrag(self, item, fun, num=1, add=None): + """Bind fun to mouse-move-event (with pressed mouse button) on turtle. + fun must be a function with two arguments, the coordinates of the + actual mouse position on the canvas. + num, the number of the mouse-button defaults to 1 + + Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + """ + if fun is None: + self.cv.tag_unbind(item, "" % num) + else: + def eventfun(event): + try: + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + except: + pass + self.cv.tag_bind(item, "" % num, eventfun, add) + + def _onscreenclick(self, fun, num=1, add=None): + """Bind fun to mouse-click event on canvas. + fun must be a function with two arguments, the coordinates + of the clicked point on the canvas. + num, the number of the mouse-button defaults to 1 + + If a turtle is clicked, first _onclick-event will be performed, + then _onscreensclick-event. + """ + if fun is None: + self.cv.unbind("" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.bind("" % num, eventfun, add) + + def _onkey(self, fun, key): + """Bind fun to key-release event of key. + Canvas must have focus. See method listen + """ + if fun is None: + self.cv.unbind("" % key, None) + else: + def eventfun(event): + fun() + self.cv.bind("" % key, eventfun) + + def _listen(self): + """Set focus on canvas (in order to collect key-events) + """ + self.cv.focus_force() + + def _ontimer(self, fun, t): + """Install a timer, which calls fun after t milliseconds. + """ + if t == 0: + self.cv.after_idle(fun) + else: + self.cv.after(t, fun) + + def _createimage(self, image): + """Create and return image item on canvas. + """ + return self.cv.create_image(0, 0, image=image) + + def _drawimage(self, item, pos, image): + """Configure image item as to draw image object + at position (x,y) on canvas) + """ + x, y = pos + self.cv.coords(item, (x * self.xscale, -y * self.yscale)) + self.cv.itemconfig(item, image=image) + + def _setbgpic(self, item, image): + """Configure image item as to draw image object + at center of canvas. Set item to the first item + in the displaylist, so it will be drawn below + any other item .""" + self.cv.itemconfig(item, image=image) + self.cv.tag_lower(item) + + def _type(self, item): + """Return 'line' or 'polygon' or 'image' depending on + type of item. + """ + return self.cv.type(item) + + def _pointlist(self, item): + """returns list of coordinate-pairs of points of item + Example (for insiders): + >>> from turtle import * + >>> getscreen()._pointlist(getturtle().turtle._item) + [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982), + (9.9999999999999982, 0.0)] + >>> """ + cl = list(self.cv.coords(item)) + pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)] + return pl + + def _setscrollregion(self, srx1, sry1, srx2, sry2): + self.cv.config(scrollregion=(srx1, sry1, srx2, sry2)) + + def _rescale(self, xscalefactor, yscalefactor): + items = self.cv.find_all() + for item in items: + coordinates = list(self.cv.coords(item)) + newcoordlist = [] + while coordinates: + x, y = coordinates[:2] + newcoordlist.append(x * xscalefactor) + newcoordlist.append(y * yscalefactor) + coordinates = coordinates[2:] + self.cv.coords(item, *newcoordlist) + + def _resize(self, canvwidth=None, canvheight=None, bg=None): + """Resize the canvas, the turtles are drawing on. Does + not alter the drawing window. + """ + # needs amendment + if not isinstance(self.cv, ScrolledCanvas): + return self.canvwidth, self.canvheight + if canvwidth is None and canvheight is None and bg is None: + return self.cv.canvwidth, self.cv.canvheight + if canvwidth is not None: + self.canvwidth = canvwidth + if canvheight is not None: + self.canvheight = canvheight + self.cv.reset(canvwidth, canvheight, bg) + + def _window_size(self): + """ Return the width and height of the turtle window. + """ + width = self.cv.winfo_width() + if width <= 1: # the window isn't managed by a geometry manager + width = self.cv['width'] + height = self.cv.winfo_height() + if height <= 1: # the window isn't managed by a geometry manager + height = self.cv['height'] + return width, height + + +############################################################################## +### End of Tkinter - interface ### +############################################################################## + + +class Terminator (Exception): + """Will be raised in TurtleScreen.update, if _RUNNING becomes False. + + Thus stops execution of turtle graphics script. Main purpose: use in + in the Demo-Viewer turtle.Demo.py. + """ pass -class RawPen: - def __init__(self, canvas): - self._canvas = canvas - self._items = [] +class TurtleGraphicsError(Exception): + """Some TurtleGraphics Error + """ + + +class Shape(object): + """Data structure modeling shapes. + + attribute _type is one of "polygon", "image", "compound" + attribute _data is - depending on _type a poygon-tuple, + an image or a list constructed using the addcomponent method. + """ + def __init__(self, type_, data=None): + self._type = type_ + if type_ == "polygon": + if isinstance(data, list): + data = tuple(data) + elif type_ == "image": + if isinstance(data, str): + if data.lower().endswith(".gif") and isfile(data): + data = TurtleScreen._image(data) + # else data assumed to be Photoimage + elif type_ == "compound": + data = [] + else: + raise TurtleGraphicsError("There is no shape type %s" % type_) + self._data = data + + def addcomponent(self, poly, fill, outline=None): + """Add component to a shape of type compound. + + Arguments: poly is a polygon, i. e. a tuple of number pairs. + fill is the fillcolor of the component, + outline is the outline color of the component. + + call (for a Shapeobject namend s): + -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue") + + Example: + >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s = Shape("compound") + >>> s.addcomponent(poly, "red", "blue") + ### .. add more components and then use register_shape() + """ + if self._type != "compound": + raise TurtleGraphicsError("Cannot add component to %s Shape" + % self._type) + if outline is None: + outline = fill + self._data.append([poly, fill, outline]) + + +class Tbuffer(object): + """Ring buffer used as undobuffer for RawTurtle objects.""" + def __init__(self, bufsize=10): + self.bufsize = bufsize + self.buffer = [[None]] * bufsize + self.ptr = -1 + self.cumulate = False + def reset(self, bufsize=None): + if bufsize is None: + for i in range(self.bufsize): + self.buffer[i] = [None] + else: + self.bufsize = bufsize + self.buffer = [[None]] * bufsize + self.ptr = -1 + def push(self, item): + if self.bufsize > 0: + if not self.cumulate: + self.ptr = (self.ptr + 1) % self.bufsize + self.buffer[self.ptr] = item + else: + self.buffer[self.ptr].append(item) + def pop(self): + if self.bufsize > 0: + item = self.buffer[self.ptr] + if item is None: + return None + else: + self.buffer[self.ptr] = [None] + self.ptr = (self.ptr - 1) % self.bufsize + return (item) + def nr_of_items(self): + return self.bufsize - self.buffer.count([None]) + def __repr__(self): + return str(self.buffer) + " " + str(self.ptr) + + + +class TurtleScreen(TurtleScreenBase): + """Provides screen oriented methods like setbg etc. + + Only relies upon the methods of TurtleScreenBase and NOT + upon components of the underlying graphics toolkit - + which is Tkinter in this case. + """ +# _STANDARD_DELAY = 5 + _RUNNING = True + + def __init__(self, cv, mode=_CFG["mode"], + colormode=_CFG["colormode"], delay=_CFG["delay"]): + self._shapes = { + "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))), + "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7), + (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6), + (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6), + (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10), + (2,14))), + "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88), + (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51), + (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0), + (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09), + (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51), + (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))), + "square" : Shape("polygon", ((10,-10), (10,10), (-10,10), + (-10,-10))), + "triangle" : Shape("polygon", ((10,-5.77), (0,11.55), + (-10,-5.77))), + "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))), + "blank" : Shape("image", self._blankimage()) + } + + self._bgpics = {"nopic" : ""} + + TurtleScreenBase.__init__(self, cv) + self._mode = mode + self._delayvalue = delay + self._colormode = _CFG["colormode"] + self._keys = [] + self.clear() + + def clear(self): + """Delete all drawings and all turtles from the TurtleScreen. + + Reset empty TurtleScreen to it's initial state: white background, + no backgroundimage, no eventbindings and tracing on. + + No argument. + + Example (for a TurtleScreen instance named screen): + screen.clear() + + Note: this method is not available as function. + """ + self._delayvalue = _CFG["delay"] + self._colormode = _CFG["colormode"] + self._delete("all") + self._bgpic = self._createimage("") + self._bgpicname = "nopic" self._tracing = 1 - self._arrow = 0 - self._delay = 10 # default delay for drawing - self._angle = 0.0 - self.degrees() + self._updatecounter = 0 + self._turtles = [] + self.bgcolor("white") + for btn in 1, 2, 3: + self.onclick(None, btn) + for key in self._keys[:]: + self.onkey(None, key) + Turtle._pen = None + + def mode(self, mode=None): + """Set turtle-mode ('standard', 'logo' or 'world') and perform reset. + + Optional argument: + mode -- on of the strings 'standard', 'logo' or 'world' + + Mode 'standard' is compatible with turtle.py. + Mode 'logo' is compatible with most Logo-Turtle-Graphics. + Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in + this mode angles appear distorted if x/y unit-ratio doesn't equal 1. + If mode is not given, return the current mode. + + Mode Initial turtle heading positive angles + ------------|-------------------------|------------------- + 'standard' to the right (east) counterclockwise + 'logo' upward (north) clockwise + + Examples: + >>> mode('logo') # resets turtle heading to north + >>> mode() + 'logo' + """ + if mode == None: + return self._mode + mode = mode.lower() + if mode not in ["standard", "logo", "world"]: + raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode) + self._mode = mode + if mode in ["standard", "logo"]: + self._setscrollregion(-self.canvwidth//2, -self.canvheight//2, + self.canvwidth//2, self.canvheight//2) + self.xscale = self.yscale = 1.0 self.reset() + def setworldcoordinates(self, llx, lly, urx, ury): + """Set up a user defined coordinate-system. + + Arguments: + llx -- a number, x-coordinate of lower left corner of canvas + lly -- a number, y-coordinate of lower left corner of canvas + urx -- a number, x-coordinate of upper right corner of canvas + ury -- a number, y-coordinate of upper right corner of canvas + + Set up user coodinat-system and switch to mode 'world' if necessary. + This performs a screen.reset. If mode 'world' is already active, + all drawings are redrawn according to the new coordinates. + + But ATTENTION: in user-defined coordinatesystems angles may appear + distorted. (see Screen.mode()) + + Example (for a TurtleScreen instance named screen): + >>> screen.setworldcoordinates(-10,-0.5,50,1.5) + >>> for _ in range(36): + left(10) + forward(0.5) + """ + if self.mode() != "world": + self.mode("world") + xspan = float(urx - llx) + yspan = float(ury - lly) + wx, wy = self._window_size() + self.screensize(wx-20, wy-20) + oldxscale, oldyscale = self.xscale, self.yscale + self.xscale = self.canvwidth / xspan + self.yscale = self.canvheight / yspan + srx1 = llx * self.xscale + sry1 = -ury * self.yscale + srx2 = self.canvwidth + srx1 + sry2 = self.canvheight + sry1 + self._setscrollregion(srx1, sry1, srx2, sry2) + self._rescale(self.xscale/oldxscale, self.yscale/oldyscale) + self.update() + + def register_shape(self, name, shape=None): + """Adds a turtle shape to TurtleScreen's shapelist. + + Arguments: + (1) name is the name of a gif-file and shape is None. + Installs the corresponding image shape. + !! Image-shapes DO NOT rotate when turning the turtle, + !! so they do not display the heading of the turtle! + (2) name is an arbitrary string and shape is a tuple + of pairs of coordinates. Installs the corresponding + polygon shape + (3) name is an arbitrary string and shape is a + (compound) Shape object. Installs the corresponding + compound shape. + To use a shape, you have to issue the command shape(shapename). + + call: register_shape("turtle.gif") + --or: register_shape("tri", ((0,0), (10,10), (-10,10))) + + Example (for a TurtleScreen instance named screen): + >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3))) + + """ + if shape is None: + # image + if name.lower().endswith(".gif"): + shape = Shape("image", self._image(name)) + else: + raise TurtleGraphicsError("Bad arguments for register_shape.\n" + + "Use help(register_shape)" ) + elif isinstance(shape, tuple): + shape = Shape("polygon", shape) + ## else shape assumed to be Shape-instance + self._shapes[name] = shape + # print "shape added:" , self._shapes + + def _colorstr(self, color): + """Return color string corresponding to args. + + Argument may be a string or a tuple of three + numbers corresponding to actual colormode, + i.e. in the range 0<=n<=colormode. + + If the argument doesn't represent a color, + an error is raised. + """ + if len(color) == 1: + color = color[0] + if isinstance(color, str): + if self._iscolorstring(color) or color == "": + return color + else: + raise TurtleGraphicsError("bad color string: %s" % str(color)) + try: + r, g, b = color + except: + raise TurtleGraphicsError("bad color arguments: %s" % str(color)) + if self._colormode == 1.0: + r, g, b = [round(255.0*x) for x in (r, g, b)] + if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): + raise TurtleGraphicsError("bad color sequence: %s" % str(color)) + return "#%02x%02x%02x" % (r, g, b) + + def _color(self, cstr): + if not cstr.startswith("#"): + return cstr + if len(cstr) == 7: + cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)] + elif len(cstr) == 4: + cl = [16*int(cstr[h], 16) for h in cstr[1:]] + else: + raise TurtleGraphicsError("bad colorstring: %s" % cstr) + return tuple([c * self._colormode/255 for c in cl]) + + def colormode(self, cmode=None): + """Return the colormode or set it to 1.0 or 255. + + Optional argument: + cmode -- one of the values 1.0 or 255 + + r, g, b values of colortriples have to be in range 0..cmode. + + Example (for a TurtleScreen instance named screen): + >>> screen.colormode() + 1.0 + >>> screen.colormode(255) + >>> turtle.pencolor(240,160,80) + """ + if cmode is None: + return self._colormode + if cmode == 1.0: + self._colormode = float(cmode) + elif cmode == 255: + self._colormode = int(cmode) + + def reset(self): + """Reset all Turtles on the Screen to their initial state. + + No argument. + + Example (for a TurtleScreen instance named screen): + >>> screen.reset() + """ + for turtle in self._turtles: + turtle._setmode(self._mode) + turtle.reset() + + def turtles(self): + """Return the list of turtles on the screen. + + Example (for a TurtleScreen instance named screen): + >>> screen.turtles() + [] + """ + return self._turtles + + def bgcolor(self, *args): + """Set or return backgroundcolor of the TurtleScreen. + + Arguments (if given): a color string or three numbers + in the range 0..colormode or a 3-tuple of such numbers. + + Example (for a TurtleScreen instance named screen): + >>> screen.bgcolor("orange") + >>> screen.bgcolor() + 'orange' + >>> screen.bgcolor(0.5,0,0.5) + >>> screen.bgcolor() + '#800080' + """ + if args: + color = self._colorstr(args) + else: + color = None + color = self._bgcolor(color) + if color is not None: + color = self._color(color) + return color + + def tracer(self, n=None, delay=None): + """Turns turtle animation on/off and set delay for update drawings. + + Optional arguments: + n -- nonnegative integer + delay -- nonnegative integer + + If n is given, only each n-th regular screen update is really performed. + (Can be used to accelerate the drawing of complex graphics.) + Second arguments sets delay value (see RawTurtle.delay()) + + Example (for a TurtleScreen instance named screen): + >>> screen.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + fd(dist) + rt(90) + dist += 2 + """ + if n is None: + return self._tracing + self._tracing = int(n) + self._updatecounter = 0 + if delay is not None: + self._delayvalue = int(delay) + if self._tracing: + self.update() + + def delay(self, delay=None): + """ Return or set the drawing delay in milliseconds. + + Optional argument: + delay -- positive integer + + Example (for a TurtleScreen instance named screen): + >>> screen.delay(15) + >>> screen.delay() + 15 + """ + if delay is None: + return self._delayvalue + self._delayvalue = int(delay) + + def _incrementudc(self): + "Increment upadate counter.""" + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNNING = True + raise Terminator + if self._tracing > 0: + self._updatecounter += 1 + self._updatecounter %= self._tracing + + def update(self): + """Perform a TurtleScreen update. + """ + for t in self.turtles(): + t._update_data() + t._drawturtle() + self._update() + + def window_width(self): + """ Return the width of the turtle window. + + Example (for a TurtleScreen instance named screen): + >>> screen.window_width() + 640 + """ + return self._window_size()[0] + + def window_height(self): + """ Return the height of the turtle window. + + Example (for a TurtleScreen instance named screen): + >>> screen.window_height() + 480 + """ + return self._window_size()[1] + + def getcanvas(self): + """Return the Canvas of this TurtleScreen. + + No argument. + + Example (for a Screen instance named screen): + >>> cv = screen.getcanvas() + >>> cv + + """ + return self.cv + + def getshapes(self): + """Return a list of names of all currently available turtle shapes. + + No argument. + + Example (for a TurtleScreen instance named screen): + >>> screen.getshapes() + ['arrow', 'blank', 'circle', ... , 'turtle'] + """ + return sorted(self._shapes.keys()) + + def onclick(self, fun, btn=1, add=None): + """Bind fun to mouse-click event on canvas. + + Arguments: + fun -- a function with two arguments, the coordinates of the + clicked point on the canvas. + num -- the number of the mouse-button, defaults to 1 + + Example (for a TurtleScreen instance named screen + and a Turtle instance named turtle): + + >>> screen.onclick(turtle.goto) + + ### Subsequently clicking into the TurtleScreen will + ### make the turtle move to the clicked point. + >>> screen.onclick(None) + + ### event-binding will be removed + """ + self._onscreenclick(fun, btn, add) + + def onkey(self, fun, key): + """Bind fun to key-release event of key. + + Arguments: + fun -- a function with no arguments + key -- a string: key (e.g. "a") or key-symbol (e.g. "space") + + In order ro be able to register key-events, TurtleScreen + must have focus. (See method listen.) + + Example (for a TurtleScreen instance named screen + and a Turtle instance named turtle): + + >>> def f(): + fd(50) + lt(60) + + + >>> screen.onkey(f, "Up") + >>> screen.listen() + + ### Subsequently the turtle can be moved by + ### repeatedly pressing the up-arrow key, + ### consequently drawing a hexagon + """ + if fun == None: + self._keys.remove(key) + elif key not in self._keys: + self._keys.append(key) + self._onkey(fun, key) + + def listen(self, xdummy=None, ydummy=None): + """Set focus on TurtleScreen (in order to collect key-events) + + No arguments. + Dummy arguments are provided in order + to be able to pass listen to the onclick method. + + Example (for a TurtleScreen instance named screen): + >>> screen.listen() + """ + self._listen() + + def ontimer(self, fun, t=0): + """Install a timer, which calls fun after t milliseconds. + + Arguments: + fun -- a function with no arguments. + t -- a number >= 0 + + Example (for a TurtleScreen instance named screen): + + >>> running = True + >>> def f(): + if running: + fd(50) + lt(60) + screen.ontimer(f, 250) + + >>> f() ### makes the turtle marching around + >>> running = False + """ + self._ontimer(fun, t) + + def bgpic(self, picname=None): + """Set background image or return name of current backgroundimage. + + Optional argument: + picname -- a string, name of a gif-file or "nopic". + + If picname is a filename, set the corresponing image as background. + If picname is "nopic", delete backgroundimage, if present. + If picname is None, return the filename of the current backgroundimage. + + Example (for a TurtleScreen instance named screen): + >>> screen.bgpic() + 'nopic' + >>> screen.bgpic("landscape.gif") + >>> screen.bgpic() + 'landscape.gif' + """ + if picname is None: + return self._bgpicname + if picname not in self._bgpics: + self._bgpics[picname] = self._image(picname) + self._setbgpic(self._bgpic, self._bgpics[picname]) + self._bgpicname = picname + + def screensize(self, canvwidth=None, canvheight=None, bg=None): + """Resize the canvas, the turtles are drawing on. + + Optional arguments: + canvwidth -- positive integer, new width of canvas in pixels + canvheight -- positive integer, new height of canvas in pixels + bg -- colorstring or color-tupel, new backgroundcolor + If no arguments are given, return current (canvaswidth, canvasheight) + + Do not alter the drawing window. To observe hidden parts of + the canvas use the scrollbars. (Can make visible those parts + of a drawing, which were outside the canvas before!) + + Example (for a Turtle instance named turtle): + >>> turtle.screensize(2000,1500) + ### e. g. to search for an erroneously escaped turtle ;-) + """ + return self._resize(canvwidth, canvheight, bg) + + onscreenclick = onclick + resetscreen = reset + clearscreen = clear + addshape = register_shape + +class TNavigator(object): + """Navigation part of the RawTurtle. + Implements methods for turtle movement. + """ + START_ORIENTATION = { + "standard": Vec2D(1.0, 0.0), + "world" : Vec2D(1.0, 0.0), + "logo" : Vec2D(0.0, 1.0) } + DEFAULT_MODE = "standard" + DEFAULT_ANGLEOFFSET = 0 + DEFAULT_ANGLEORIENT = 1 + + def __init__(self, mode=DEFAULT_MODE): + self._angleOffset = self.DEFAULT_ANGLEOFFSET + self._angleOrient = self.DEFAULT_ANGLEORIENT + self._mode = mode + self.undobuffer = None + self.degrees() + self._mode = None + self._setmode(mode) + TNavigator.reset(self) + + def reset(self): + """reset turtle to its initial values + + Will be overwritten by parent class + """ + self._position = Vec2D(0.0, 0.0) + self._orient = TNavigator.START_ORIENTATION[self._mode] + + def _setmode(self, mode=None): + """Set turtle-mode to 'standard', 'world' or 'logo'. + """ + if mode == None: + return self._mode + if mode not in ["standard", "logo", "world"]: + return + self._mode = mode + if mode in ["standard", "world"]: + self._angleOffset = 0 + self._angleOrient = 1 + else: # mode == "logo": + self._angleOffset = self._fullcircle/4. + self._angleOrient = -1 + + def _setDegreesPerAU(self, fullcircle): + """Helper function for degrees() and radians()""" + self._fullcircle = fullcircle + self._degreesPerAU = 360/fullcircle + if self._mode == "standard": + self._angleOffset = 0 + else: + self._angleOffset = fullcircle/4. + def degrees(self, fullcircle=360.0): """ Set angle measurement units to degrees. - Example: - >>> turtle.degrees() + Optional argument: + fullcircle - a number + + Set angle measurement units, i. e. set number + of 'degrees' for a full circle. Dafault value is + 360 degrees. + + Example (for a Turtle instance named turtle): + >>> turtle.left(90) + >>> turtle.heading() + 90 + >>> turtle.degrees(400.0) # angle measurement in gon + >>> turtle.heading() + 100 + + """ + self._setDegreesPerAU(fullcircle) + + def radians(self): + """ Set the angle measurement units to radians. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 90 + >>> turtle.radians() + >>> turtle.heading() + 1.5707963267948966 + """ + self._setDegreesPerAU(2*math.pi) + + def _go(self, distance): + """move turtle forward by specified distance""" + ende = self._position + self._orient * distance + self._goto(ende) + + def _rotate(self, angle): + """Turn turtle counterclockwise by specified angle if angle > 0.""" + angle *= self._degreesPerAU + self._orient = self._orient.rotate(angle) + + def _goto(self, end): + """move turtle to position end.""" + self._position = end + + def forward(self, distance): + """Move the turtle forward by the specified distance. + + Aliases: forward | fd + + Argument: + distance -- a number (integer or float) + + Move the turtle forward by the specified distance, in the direction + the turtle is headed. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 0.00) + >>> turtle.forward(25) + >>> turtle.position() + (25.00,0.00) + >>> turtle.forward(-75) + >>> turtle.position() + (-50.00,0.00) + """ + self._go(distance) + + def back(self, distance): + """Move the turtle backward by distance. + + Aliases: back | backward | bk + + Argument: + distance -- a number + + Move the turtle backward by distance ,opposite to the direction the + turtle is headed. Do not change the turtle's heading. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 0.00) + >>> turtle.backward(30) + >>> turtle.position() + (-30.00, 0.00) + """ + self._go(-distance) + + def right(self, angle): + """Turn turtle right by angle units. + + Aliases: right | rt + + Argument: + angle -- a number (integer or float) + + Turn turtle right by angle units. (Units are by default degrees, + but can be set via the degrees() and radians() functions.) + Angle orientation depends on mode. (See this.) + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 22.0 + >>> turtle.right(45) + >>> turtle.heading() + 337.0 + """ + self._rotate(-angle) + + def left(self, angle): + """Turn turtle left by angle units. + + Aliases: left | lt + + Argument: + angle -- a number (integer or float) + + Turn turtle left by angle units. (Units are by default degrees, + but can be set via the degrees() and radians() functions.) + Angle orientation depends on mode. (See this.) + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 22.0 + >>> turtle.left(45) + >>> turtle.heading() + 67.0 + """ + self._rotate(angle) + + def pos(self): + """Return the turtle's current location (x,y), as a Vec2D-vector. + + Aliases: pos | position + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (0.00, 240.00) + """ + return self._position + + def xcor(self): + """ Return the turtle's x coordinate. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.xcor() + 50.0 + """ + return self._position[0] + + def ycor(self): + """ Return the turtle's y coordinate + --- + No arguments. + + Example (for a Turtle instance named turtle): + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.ycor() + 86.6025403784 + """ + return self._position[1] + + + def goto(self, x, y=None): + """Move turtle to an absolute position. + + Aliases: setpos | setposition | goto: + + Arguments: + x -- a number or a pair/vector of numbers + y -- a number None + + call: goto(x, y) # two coordinates + --or: goto((x, y)) # a pair (tuple) of coordinates + --or: goto(vec) # e.g. as returned by pos() + + Move turtle to an absolute position. If the pen is down, + a line will be drawn. The turtle's orientation does not change. + + Example (for a Turtle instance named turtle): + >>> tp = turtle.pos() + >>> tp + (0.00, 0.00) + >>> turtle.setpos(60,30) + >>> turtle.pos() + (60.00,30.00) + >>> turtle.setpos((20,80)) + >>> turtle.pos() + (20.00,80.00) + >>> turtle.setpos(tp) + >>> turtle.pos() + (0.00,0.00) + """ + if y is None: + self._goto(Vec2D(*x)) + else: + self._goto(Vec2D(x, y)) + + def home(self): + """Move turtle to the origin - coordinates (0,0). + + No arguments. + + Move turtle to the origin - coordinates (0,0) and set it's + heading to it's start-orientation (which depends on mode). + + Example (for a Turtle instance named turtle): + >>> turtle.home() + """ + self.goto(0, 0) + self.setheading(0) + + def setx(self, x): + """Set the turtle's first coordinate to x + + Argument: + x -- a number (integer or float) + + Set the turtle's first coordinate to x, leave second coordinate + unchanged. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 240.00) + >>> turtle.setx(10) + >>> turtle.position() + (10.00, 240.00) + """ + self._goto(Vec2D(x, self._position[1])) + + def sety(self, y): + """Set the turtle's second coordinate to y + + Argument: + y -- a number (integer or float) + + Set the turtle's first coordinate to x, second coordinate remains + unchanged. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 40.00) + >>> turtle.sety(-10) + >>> turtle.position() + (0.00, -10.00) + """ + self._goto(Vec2D(self._position[0], y)) + + def distance(self, x, y=None): + """Return the distance from the turtle to (x,y) in turtle step units. + + Arguments: + x -- a number or a pair/vector of numbers or a turtle instance + y -- a number None None + + call: distance(x, y) # two coordinates + --or: distance((x, y)) # a pair (tuple) of coordinates + --or: distance(vec) # e.g. as returned by pos() + --or: distance(mypen) # where mypen is another turtle + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (0.00, 0.00) + >>> turtle.distance(30,40) + 50.0 + >>> pen = Turtle() + >>> pen.forward(77) + >>> turtle.distance(pen) + 77.0 + """ + if y is not None: + pos = Vec2D(x, y) + if isinstance(x, Vec2D): + pos = x + elif isinstance(x, tuple): + pos = Vec2D(*x) + elif isinstance(x, TNavigator): + pos = x._position + return abs(pos - self._position) + + def towards(self, x, y=None): + """Return the angle of the line from the turtle's position to (x, y). + + Arguments: + x -- a number or a pair/vector of numbers or a turtle instance + y -- a number None None + + call: distance(x, y) # two coordinates + --or: distance((x, y)) # a pair (tuple) of coordinates + --or: distance(vec) # e.g. as returned by pos() + --or: distance(mypen) # where mypen is another turtle + + Return the angle, between the line from turtle-position to position + specified by x, y and the turtle's start orientation. (Depends on + modes - "standard" or "logo") + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (10.00, 10.00) + >>> turtle.towards(0,0) + 225.0 + """ + if y is not None: + pos = Vec2D(x, y) + if isinstance(x, Vec2D): + pos = x + elif isinstance(x, tuple): + pos = Vec2D(*x) + elif isinstance(x, TNavigator): + pos = x._position + x, y = pos - self._position + result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 + result /= self._degreesPerAU + return (self._angleOffset + self._angleOrient*result) % self._fullcircle + + def heading(self): + """ Return the turtle's current heading. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.left(67) + >>> turtle.heading() + 67.0 + """ + x, y = self._orient + result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 + result /= self._degreesPerAU + return (self._angleOffset + self._angleOrient*result) % self._fullcircle + + def setheading(self, to_angle): + """Set the orientation of the turtle to to_angle. + + Aliases: setheading | seth + + Argument: + to_angle -- a number (integer or float) + + Set the orientation of the turtle to to_angle. + Here are some common directions in degrees: + + standard - mode: logo-mode: + -------------------|-------------------- + 0 - east 0 - north + 90 - north 90 - east + 180 - west 180 - south + 270 - south 270 - west + + Example (for a Turtle instance named turtle): + >>> turtle.setheading(90) + >>> turtle.heading() + 90 + """ + angle = (to_angle - self.heading())*self._angleOrient + full = self._fullcircle + angle = (angle+full/2.)%full - full/2. + self._rotate(angle) + + def circle(self, radius, extent = None, steps = None): + """ Draw a circle with given radius. + + Arguments: + radius -- a number + extent (optional) -- a number + steps (optional) -- an integer + + Draw a circle with given radius. The center is radius units left + of the turtle; extent - an angle - determines which part of the + circle is drawn. If extent is not given, draw the entire circle. + If extent is not a full circle, one endpoint of the arc is the + current pen position. Draw the arc in counterclockwise direction + if radius is positive, otherwise in clockwise direction. Finally + the direction of the turtle is changed by the amount of extent. + + As the circle is approximated by an inscribed regular polygon, + steps determines the number of steps to use. If not given, + it will be calculated automatically. Maybe used to draw regular + polygons. + + call: circle(radius) # full circle + --or: circle(radius, extent) # arc + --or: circle(radius, extent, steps) + --or: circle(radius, steps=6) # 6-sided polygon + + Example (for a Turtle instance named turtle): + >>> turtle.circle(50) + >>> turtle.circle(120, 180) # semicircle + """ + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + speed = self.speed() + if extent is None: + extent = self._fullcircle + if steps is None: + frac = abs(extent)/self._fullcircle + steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac) + w = 1.0 * extent / steps + w2 = 0.5 * w + l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU) + if radius < 0: + l, w, w2 = -l, -w, -w2 + tr = self._tracer() + dl = self._delay() + if speed == 0: + self._tracer(0, 0) + else: + self.speed(0) + self._rotate(w2) + for i in range(steps): + self.speed(speed) + self._go(l) + self.speed(0) + self._rotate(w) + self._rotate(-w2) + if speed == 0: + self._tracer(tr, dl) + self.speed(speed) + if self.undobuffer: + self.undobuffer.cumulate = False + +## three dummy methods to be implemented by child class: + + def speed(self, s=0): + """dummy method - to be overwritten by child class""" + def _tracer(self, a=None, b=None): + """dummy method - to be overwritten by child class""" + def _delay(self, n=None): + """dummy method - to be overwritten by child class""" + + fd = forward + bk = back + backward = back + rt = right + lt = left + position = pos + setpos = goto + setposition = goto + seth = setheading + + +class TPen(object): + """Drawing part of the RawTurtle. + Implements drawing properties. + """ + def __init__(self, resizemode=_CFG["resizemode"]): + self._resizemode = resizemode # or "user" or "noresize" + self.undobuffer = None + TPen._reset(self) + + def _reset(self, pencolor=_CFG["pencolor"], + fillcolor=_CFG["fillcolor"]): + self._pensize = 1 + self._shown = True + self._pencolor = pencolor + self._fillcolor = fillcolor + self._drawing = True + self._speed = 3 + self._stretchfactor = (1, 1) + self._tilt = 0 + self._outlinewidth = 1 + ### self.screen = None # to override by child class + + def resizemode(self, rmode=None): + """Set resizemode to one of the values: "auto", "user", "noresize". + + (Optional) Argument: + rmode -- one of the strings "auto", "user", "noresize" + + Different resizemodes have the following effects: + - "auto" adapts the appearance of the turtle + corresponding to the value of pensize. + - "user" adapts the appearance of the turtle according to the + values of stretchfactor and outlinewidth (outline), + which are set by shapesize() + - "noresize" no adaption of the turtle's appearance takes place. + If no argument is given, return current resizemode. + resizemode("user") is called by a call of shapesize with arguments. + + + Examples (for a Turtle instance named turtle): + >>> turtle.resizemode("noresize") + >>> turtle.resizemode() + 'noresize' + """ + if rmode is None: + return self._resizemode + rmode = rmode.lower() + if rmode in ["auto", "user", "noresize"]: + self.pen(resizemode=rmode) + + def pensize(self, width=None): + """Set or return the line thickness. + + Aliases: pensize | width + + Argument: + width -- positive number + + Set the line thickness to width or return it. If resizemode is set + to "auto" and turtleshape is a polygon, that polygon is drawn with + the same line thickness. If no argument is given, current pensize + is returned. + + Example (for a Turtle instance named turtle): + >>> turtle.pensize() + 1 + turtle.pensize(10) # from here on lines of width 10 are drawn + """ + if width is None: + return self._pensize + self.pen(pensize=width) + + + def penup(self): + """Pull the pen up -- no drawing when moving. + + Aliases: penup | pu | up + + No argument + + Example (for a Turtle instance named turtle): + >>> turtle.penup() + """ + if not self._drawing: + return + self.pen(pendown=False) + + def pendown(self): + """Pull the pen down -- drawing when moving. + + Aliases: pendown | pd | down + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.pendown() + """ + if self._drawing: + return + self.pen(pendown=True) + + def isdown(self): + """Return True if pen is down, False if it's up. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.penup() + >>> turtle.isdown() + False + >>> turtle.pendown() + >>> turtle.isdown() + True + """ + return self._drawing + + def speed(self, speed=None): + """ Return or set the turtle's speed. + + Optional argument: + speed -- an integer in the range 0..10 or a speedstring (see below) + + Set the turtle's speed to an integer value in the range 0 .. 10. + If no argument is given: return current speed. + + If input is a number greater than 10 or smaller than 0.5, + speed is set to 0. + Speedstrings are mapped to speedvalues in the following way: + 'fastest' : 0 + 'fast' : 10 + 'normal' : 6 + 'slow' : 3 + 'slowest' : 1 + speeds from 1 to 10 enforce increasingly faster animation of + line drawing and turtle turning. + + Attention: + speed = 0 : *no* animation takes place. forward/back makes turtle jump + and likewise left/right make the turtle turn instantly. + + Example (for a Turtle instance named turtle): + >>> turtle.speed(3) + """ + speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 } + if speed is None: + return self._speed + if speed in speeds: + speed = speeds[speed] + elif 0.5 < speed < 10.5: + speed = int(round(speed)) + else: + speed = 0 + self.pen(speed=speed) + + def color(self, *args): + """Return or set the pencolor and fillcolor. + + Arguments: + Several input formats are allowed. + They use 0, 1, 2, or 3 arguments as follows: + + color() + Return the current pencolor and the current fillcolor + as a pair of color specification strings as are returned + by pencolor and fillcolor. + color(colorstring), color((r,g,b)), color(r,g,b) + inputs as in pencolor, set both, fillcolor and pencolor, + to the given value. + color(colorstring1, colorstring2), + color((r1,g1,b1), (r2,g2,b2)) + equivalent to pencolor(colorstring1) and fillcolor(colorstring2) + and analogously, if the other input format is used. + + If turtleshape is a polygon, outline and interior of that polygon + is drawn with the newly set colors. + For mor info see: pencolor, fillcolor + + Example (for a Turtle instance named turtle): + >>> turtle.color('red', 'green') + >>> turtle.color() + ('red', 'green') + >>> colormode(255) + >>> color((40, 80, 120), (160, 200, 240)) + >>> color() + ('#285078', '#a0c8f0') + """ + if args: + l = len(args) + if l == 1: + pcolor = fcolor = args[0] + elif l == 2: + pcolor, fcolor = args + elif l == 3: + pcolor = fcolor = args + pcolor = self._colorstr(pcolor) + fcolor = self._colorstr(fcolor) + self.pen(pencolor=pcolor, fillcolor=fcolor) + else: + return self._color(self._pencolor), self._color(self._fillcolor) + + def pencolor(self, *args): + """ Return or set the pencolor. + + Arguments: + Four input formats are allowed: + - pencolor() + Return the current pencolor as color specification string, + possibly in hex-number format (see example). + May be used as input to another color/pencolor/fillcolor call. + - pencolor(colorstring) + s is a Tk color specification string, such as "red" or "yellow" + - pencolor((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range 0..colormode, + where colormode is either 1.0 or 255 + - pencolor(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range 0..colormode + + If turtleshape is a polygon, the outline of that polygon is drawn + with the newly set pencolor. + + Example (for a Turtle instance named turtle): + >>> turtle.pencolor('brown') + >>> tup = (0.2, 0.8, 0.55) + >>> turtle.pencolor(tup) + >>> turtle.pencolor() + '#33cc8c' + """ + if args: + color = self._colorstr(args) + if color == self._pencolor: + return + self.pen(pencolor=color) + else: + return self._color(self._pencolor) + + def fillcolor(self, *args): + """ Return or set the fillcolor. + + Arguments: + Four input formats are allowed: + - fillcolor() + Return the current fillcolor as color specification string, + possibly in hex-number format (see example). + May be used as input to another color/pencolor/fillcolor call. + - fillcolor(colorstring) + s is a Tk color specification string, such as "red" or "yellow" + - fillcolor((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range 0..colormode, + where colormode is either 1.0 or 255 + - fillcolor(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range 0..colormode + + If turtleshape is a polygon, the interior of that polygon is drawn + with the newly set fillcolor. + + Example (for a Turtle instance named turtle): + >>> turtle.fillcolor('violet') + >>> col = turtle.pencolor() + >>> turtle.fillcolor(col) + >>> turtle.fillcolor(0, .5, 0) + """ + if args: + color = self._colorstr(args) + if color == self._fillcolor: + return + self.pen(fillcolor=color) + else: + return self._color(self._fillcolor) + + def showturtle(self): + """Makes the turtle visible. + + Aliases: showturtle | st + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + >>> turtle.showturtle() + """ + self.pen(shown=True) + + def hideturtle(self): + """Makes the turtle invisible. + + Aliases: hideturtle | ht + + No argument. + + It's a good idea to do this while you're in the + middle of a complicated drawing, because hiding + the turtle speeds up the drawing observably. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() """ - # Don't try to change _angle if it is 0, because - # _fullcircle might not be set, yet - if self._angle: - self._angle = (self._angle / self._fullcircle) * fullcircle - self._fullcircle = fullcircle - self._invradian = pi / (fullcircle * 0.5) + self.pen(shown=False) - def radians(self): - """ Set the angle measurement units to radians. + def isvisible(self): + """Return True if the Turtle is shown, False if it's hidden. - Example: - >>> turtle.radians() + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + >>> print turtle.isvisible(): + False """ - self.degrees(2.0*pi) + return self._shown + + def pen(self, pen=None, **pendict): + """Return or set the pen's attributes. + + Arguments: + pen -- a dictionary with some or all of the below listed keys. + **pendict -- one or more keyword-arguments with the below + listed keys as keywords. + + Return or set the pen's attributes in a 'pen-dictionary' + with the following key/value pairs: + "shown" : True/False + "pendown" : True/False + "pencolor" : color-string or color-tuple + "fillcolor" : color-string or color-tuple + "pensize" : positive number + "speed" : number in range 0..10 + "resizemode" : "auto" or "user" or "noresize" + "stretchfactor": (positive number, positive number) + "outline" : positive number + "tilt" : number + + This dicionary can be used as argument for a subsequent + pen()-call to restore the former pen-state. Moreover one + or more of these attributes can be provided as keyword-arguments. + This can be used to set several pen attributes in one statement. + + + Examples (for a Turtle instance named turtle): + >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black', + 'stretchfactor': (1,1), 'speed': 3} + >>> penstate=turtle.pen() + >>> turtle.color("yellow","") + >>> turtle.penup() + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '', + 'stretchfactor': (1,1), 'speed': 3} + >>> p.pen(penstate, fillcolor="green") + >>> p.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green', + 'stretchfactor': (1,1), 'speed': 3} + """ + _pd = {"shown" : self._shown, + "pendown" : self._drawing, + "pencolor" : self._pencolor, + "fillcolor" : self._fillcolor, + "pensize" : self._pensize, + "speed" : self._speed, + "resizemode" : self._resizemode, + "stretchfactor" : self._stretchfactor, + "outline" : self._outlinewidth, + "tilt" : self._tilt + } + + if not (pen or pendict): + return _pd + + if isinstance(pen, dict): + p = pen + else: + p = {} + p.update(pendict) + + _p_buf = {} + for key in p: + _p_buf[key] = _pd[key] + + if self.undobuffer: + self.undobuffer.push(("pen", _p_buf)) + + newLine = False + if "pendown" in p: + if self._drawing != p["pendown"]: + newLine = True + if "pencolor" in p: + if isinstance(p["pencolor"], tuple): + p["pencolor"] = self._colorstr((p["pencolor"],)) + if self._pencolor != p["pencolor"]: + newLine = True + if "pensize" in p: + if self._pensize != p["pensize"]: + newLine = True + if newLine: + self._newLine() + if "pendown" in p: + self._drawing = p["pendown"] + if "pencolor" in p: + self._pencolor = p["pencolor"] + if "pensize" in p: + self._pensize = p["pensize"] + if "fillcolor" in p: + if isinstance(p["fillcolor"], tuple): + p["fillcolor"] = self._colorstr((p["fillcolor"],)) + self._fillcolor = p["fillcolor"] + if "speed" in p: + self._speed = p["speed"] + if "resizemode" in p: + self._resizemode = p["resizemode"] + if "stretchfactor" in p: + sf = p["stretchfactor"] + if isinstance(sf, (int, float)): + sf = (sf, sf) + self._stretchfactor = sf + if "outline" in p: + self._outlinewidth = p["outline"] + if "shown" in p: + self._shown = p["shown"] + if "tilt" in p: + self._tilt = p["tilt"] + self._update() + +## three dummy methods to be implemented by child class: + + def _newLine(self, usePos = True): + """dummy method - to be overwritten by child class""" + def _update(self, count=True, forced=False): + """dummy method - to be overwritten by child class""" + def _color(self, args): + """dummy method - to be overwritten by child class""" + def _colorstr(self, args): + """dummy method - to be overwritten by child class""" + + width = pensize + up = penup + pu = penup + pd = pendown + down = pendown + st = showturtle + ht = hideturtle + + +class _TurtleImage(object): + """Helper class: Datatype to store Turtle attributes + """ + + def __init__(self, screen, shapeIndex): + self.screen = screen + self._type = None + self._setshape(shapeIndex) + + def _setshape(self, shapeIndex): + screen = self.screen # RawTurtle.screens[self.screenIndex] + self.shapeIndex = shapeIndex + if self._type == "polygon" == screen._shapes[shapeIndex]._type: + return + if self._type == "image" == screen._shapes[shapeIndex]._type: + return + if self._type in ["image", "polygon"]: + screen._delete(self._item) + elif self._type == "compound": + for item in self._item: + screen._delete(item) + self._type = screen._shapes[shapeIndex]._type + if self._type == "polygon": + self._item = screen._createpoly() + elif self._type == "image": + self._item = screen._createimage(screen._shapes["blank"]._data) + elif self._type == "compound": + self._item = [screen._createpoly() for item in + screen._shapes[shapeIndex]._data] + + +class RawTurtle(TPen, TNavigator): + """Animation part of the RawTurtle. + Puts RawTurtle upon a TurtleScreen and provides tools for + it's animation. + """ + screens = [] + + def __init__(self, canvas=None, + shape=_CFG["shape"], + undobuffersize=_CFG["undobuffersize"], + visible=_CFG["visible"]): + if isinstance(canvas, Screen): + self.screen = canvas + elif isinstance(canvas, TurtleScreen): + if canvas not in RawTurtle.screens: + RawTurtle.screens.append(canvas) + self.screen = canvas + elif isinstance(canvas, (ScrolledCanvas, Canvas)): + for screen in RawTurtle.screens: + if screen.cv == canvas: + self.screen = screen + break + else: + self.screen = TurtleScreen(canvas) + RawTurtle.screens.append(self.screen) + else: + raise TurtleGraphicsError("bad cavas argument %s" % canvas) + + screen = self.screen + TNavigator.__init__(self, screen.mode()) + TPen.__init__(self) + screen._turtles.append(self) + self.drawingLineItem = screen._createline() + self.turtle = _TurtleImage(screen, shape) + self._poly = None + self._creatingPoly = False + self._fillitem = self._fillpath = None + self._shown = visible + self._hidden_from_screen = False + self.currentLineItem = screen._createline() + self.currentLine = [self._position] + self.items = [self.currentLineItem] + self.stampItems = [] + self._undobuffersize = undobuffersize + self.undobuffer = Tbuffer(undobuffersize) + self._update() def reset(self): - """ Clear the screen, re-center the pen, and set variables to - the default values. + """Delete the turtle's drawings and restore it's default values. - Example: + No argument. +, + Delete the turtle's drawings from the screen, re-center the turtle + and set variables to the default values. + + Example (for a Turtle instance named turtle): >>> turtle.position() - [0.0, -22.0] + (0.00,-22.00) >>> turtle.heading() 100.0 >>> turtle.reset() >>> turtle.position() - [0.0, 0.0] + (0.00,0.00) >>> turtle.heading() 0.0 """ - canvas = self._canvas - self._canvas.update() - width = canvas.winfo_width() - height = canvas.winfo_height() - if width <= 1: - width = canvas['width'] - if height <= 1: - height = canvas['height'] - self._origin = float(width)/2.0, float(height)/2.0 - self._position = self._origin - self._angle = 0.0 - self._drawing = 1 - self._width = 1 - self._color = "black" - self._filling = 0 - self._path = [] - self.clear() - canvas._root().tkraise() + TNavigator.reset(self) + TPen._reset(self) + self._clear() + self._drawturtle() + self._update() - def clear(self): - """ Clear the screen. The turtle does not move. + def setundobuffer(self, size): + """Set or disable undobuffer. - Example: - >>> turtle.clear() - """ - self.fill(0) - canvas = self._canvas - items = self._items - self._items = [] - for item in items: - canvas.delete(item) - self._delete_turtle() - self._draw_turtle() + Argument: + size -- an integer or None - def tracer(self, flag): - """ Set tracing on if flag is True, and off if it is False. - Tracing means line are drawn more slowly, with an - animation of an arrow along the line. + If size is an integer an empty undobuffer of given size is installed. + Size gives the maximum number of turtle-actions that can be undone + by the undo() function. + If size is None, no undobuffer is present. - Example: - >>> turtle.tracer(False) # turns off Tracer + Example (for a Turtle instance named turtle): + >>> turtle.setundobuffer(42) """ - self._tracing = flag - if not self._tracing: - self._delete_turtle() - self._draw_turtle() + if size is None: + self.undobuffer = None + else: + self.undobuffer = Tbuffer(size) - def forward(self, distance): - """ Go forward distance steps. + def undobufferentries(self): + """Return count of entries in the undobuffer. - Example: - >>> turtle.position() - [0.0, 0.0] - >>> turtle.forward(25) - >>> turtle.position() - [25.0, 0.0] - >>> turtle.forward(-75) - >>> turtle.position() - [-50.0, 0.0] + No argument. + + Example (for a Turtle instance named turtle): + >>> while undobufferentries(): + undo() """ - x0, y0 = start = self._position - x1 = x0 + distance * cos(self._angle*self._invradian) - y1 = y0 - distance * sin(self._angle*self._invradian) - self._goto(x1, y1) + if self.undobuffer is None: + return 0 + return self.undobuffer.nr_of_items() + + def _clear(self): + """Delete all of pen's drawings""" + self._fillitem = self._fillpath = None + for item in self.items: + self.screen._delete(item) + self.currentLineItem = self.screen._createline() + self.currentLine = [] + if self._drawing: + self.currentLine.append(self._position) + self.items = [self.currentLineItem] + self.clearstamps() + self.setundobuffer(self._undobuffersize) - def backward(self, distance): - """ Go backwards distance steps. - The turtle's heading does not change. + def clear(self): + """Delete the turtle's drawings from the screen. Do not move turtle. - Example: - >>> turtle.position() - [0.0, 0.0] - >>> turtle.backward(30) - >>> turtle.position() - [-30.0, 0.0] - """ - self.forward(-distance) + No arguments. - def left(self, angle): - """ Turn left angle units (units are by default degrees, - but can be set via the degrees() and radians() functions.) + Delete the turtle's drawings from the screen. Do not move turtle. + State and position of the turtle as well as drawings of other + turtles are not affected. - When viewed from above, the turning happens in-place around - its front tip. + Examples (for a Turtle instance named turtle): + >>> turtle.clear() + """ + self._clear() + self._update() - Example: - >>> turtle.heading() - 22 - >>> turtle.left(45) - >>> turtle.heading() - 67.0 + def _update_data(self): + self.screen._incrementudc() + if self.screen._updatecounter != 0: + return + if len(self.currentLine)>1: + self.screen._drawline(self.currentLineItem, self.currentLine, + self._pencolor, self._pensize) + + def _update(self): + """Perform a Turtle-data update. """ - self._angle = (self._angle + angle) % self._fullcircle - self._draw_turtle() + screen = self.screen + if screen._tracing == 0: + return + elif screen._tracing == 1: + self._update_data() + self._drawturtle() + screen._update() # TurtleScreenBase + screen._delay(screen._delayvalue) # TurtleScreenBase + else: + self._update_data() + if screen._updatecounter == 0: + for t in screen.turtles(): + t._drawturtle() + screen._update() + + def _tracer(self, flag=None, delay=None): + """Turns turtle animation on/off and set delay for update drawings. + + Optional arguments: + n -- nonnegative integer + delay -- nonnegative integer + + If n is given, only each n-th regular screen update is really performed. + (Can be used to accelerate the drawing of complex graphics.) + Second arguments sets delay value (see RawTurtle.delay()) + + Example (for a Turtle instance named turtle): + >>> turtle.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + turtle.fd(dist) + turtle.rt(90) + dist += 2 + """ + return self.screen.tracer(flag, delay) - def right(self, angle): - """ Turn right angle units (units are by default degrees, - but can be set via the degrees() and radians() functions.) + def _color(self, args): + return self.screen._color(args) - When viewed from above, the turning happens in-place around - its front tip. + def _colorstr(self, args): + return self.screen._colorstr(args) - Example: - >>> turtle.heading() - 22 - >>> turtle.right(45) - >>> turtle.heading() - 337.0 + def _cc(self, args): + """Convert colortriples to hexstrings. """ - self.left(-angle) + if isinstance(args, str): + return args + try: + r, g, b = args + except: + raise TurtleGraphicsError("bad color arguments: %s" % str(args)) + if self.screen._colormode == 1.0: + r, g, b = [round(255.0*x) for x in (r, g, b)] + if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): + raise TurtleGraphicsError("bad color sequence: %s" % str(args)) + return "#%02x%02x%02x" % (r, g, b) - def up(self): - """ Pull the pen up -- no drawing when moving. + def clone(self): + """Create and return a clone of the turtle. - Example: - >>> turtle.up() - """ - self._drawing = 0 + No argument. - def down(self): - """ Put the pen down -- draw when moving. + Create and return a clone of the turtle with same position, heading + and turtle properties. - Example: - >>> turtle.down() + Example (for a Turtle instance named mick): + mick = Turtle() + joe = mick.clone() + """ + screen = self.screen + self._newLine(self._drawing) + + turtle = self.turtle + self.screen = None + self.turtle = None # too make self deepcopy-able + + q = deepcopy(self) + + self.screen = screen + self.turtle = turtle + + q.screen = screen + q.turtle = _TurtleImage(screen, self.turtle.shapeIndex) + + screen._turtles.append(q) + ttype = screen._shapes[self.turtle.shapeIndex]._type + if ttype == "polygon": + q.turtle._item = screen._createpoly() + elif ttype == "image": + q.turtle._item = screen._createimage(screen._shapes["blank"]._data) + elif ttype == "compound": + q.turtle._item = [screen._createpoly() for item in + screen._shapes[self.turtle.shapeIndex]._data] + q.currentLineItem = screen._createline() + q._update() + return q + + def shape(self, name=None): + """Set turtle shape to shape with given name / return current shapename. + + Optional argument: + name -- a string, which is a valid shapename + + Set turtle shape to shape with given name or, if name is not given, + return name of current shape. + Shape with name must exist in the TurtleScreen's shape dictionary. + Initially there are the following polygon shapes: + 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'. + To learn about how to deal with shapes see Screen-method register_shape. + + Example (for a Turtle instance named turtle): + >>> turtle.shape() + 'arrow' + >>> turtle.shape("turtle") + >>> turtle.shape() + 'turtle' + """ + if name is None: + return self.turtle.shapeIndex + if not name in self.screen.getshapes(): + raise TurtleGraphicsError("There is no shape named %s" % name) + self.turtle._setshape(name) + self._update() + + def shapesize(self, stretch_wid=None, stretch_len=None, outline=None): + """Set/return turtle's stretchfactors/outline. Set resizemode to "user". + + Optinonal arguments: + stretch_wid : positive number + stretch_len : positive number + outline : positive number + + Return or set the pen's attributes x/y-stretchfactors and/or outline. + Set resizemode to "user". + If and only if resizemode is set to "user", the turtle will be displayed + stretched according to its stretchfactors: + stretch_wid is stretchfactor perpendicular to orientation + stretch_len is stretchfactor in direction of turtles orientation. + outline determines the width of the shapes's outline. + + Examples (for a Turtle instance named turtle): + >>> turtle.resizemode("user") + >>> turtle.shapesize(5, 5, 12) + >>> turtle.shapesize(outline=8) + """ + if stretch_wid is None and stretch_len is None and outline == None: + stretch_wid, stretch_len = self._stretchfactor + return stretch_wid, stretch_len, self._outlinewidth + if stretch_wid is not None: + if stretch_len is None: + stretchfactor = stretch_wid, stretch_wid + else: + stretchfactor = stretch_wid, stretch_len + elif stretch_len is not None: + stretchfactor = self._stretchfactor[0], stretch_len + else: + stretchfactor = self._stretchfactor + if outline is None: + outline = self._outlinewidth + self.pen(resizemode="user", + stretchfactor=stretchfactor, outline=outline) + + def settiltangle(self, angle): + """Rotate the turtleshape to point in the specified direction + + Optional argument: + angle -- number + + Rotate the turtleshape to point in the direction specified by angle, + regardless of its current tilt-angle. DO NOT change the turtle's + heading (direction of movement). + + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.settiltangle(45) + >>> stamp() + >>> turtle.fd(50) + >>> turtle.settiltangle(-45) + >>> stamp() + >>> turtle.fd(50) """ - self._drawing = 1 + tilt = -angle * self._degreesPerAU * self._angleOrient + tilt = (tilt * math.pi / 180.0) % (2*math.pi) + self.pen(resizemode="user", tilt=tilt) - def width(self, width): - """ Set the line to thickness to width. + def tiltangle(self): + """Return the current tilt-angle. - Example: - >>> turtle.width(10) - """ - self._width = float(width) + No argument. - def color(self, *args): - """ Set the pen color. + Return the current tilt-angle, i. e. the angle between the + orientation of the turtleshape and the heading of the turtle + (it's direction of movement). - Three input formats are allowed: + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(45) + >>> turtle.tiltangle() + >>> + """ + tilt = -self._tilt * (180.0/math.pi) * self._angleOrient + return (tilt / self._degreesPerAU) % self._fullcircle - color(s) - s is a Tk specification string, such as "red" or "yellow" + def tilt(self, angle): + """Rotate the turtleshape by angle. - color((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range [0..1] + Argument: + angle - a number - color(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range [0..1] + Rotate the turtleshape by angle from its current tilt-angle, + but do NOT change the turtle's heading (direction of movement). - Example: + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(30) + >>> turtle.fd(50) + >>> turtle.tilt(30) + >>> turtle.fd(50) + """ + self.settiltangle(angle + self.tiltangle()) - >>> turtle.color('brown') - >>> tup = (0.2, 0.8, 0.55) - >>> turtle.color(tup) - >>> turtle.color(0, .5, 0) - """ - if not args: - raise Error("no color arguments") - if len(args) == 1: - color = args[0] - if type(color) == type(""): - # Test the color first - try: - id = self._canvas.create_line(0, 0, 0, 0, fill=color) - except tkinter.TclError: - raise Error("bad color string: %r" % (color,)) - self._set_color(color) + def _polytrafo(self, poly): + """Computes transformed polygon shapes from a shape + according to current position and heading. + """ + screen = self.screen + p0, p1 = self._position + e0, e1 = self._orient + e = Vec2D(e0, e1 * screen.yscale / screen.xscale) + e0, e1 = (1.0 / abs(e)) * e + return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale) + for (x, y) in poly] + + def _drawturtle(self): + """Manages the correct rendering of the turtle with respect to + it's shape, resizemode, strech and tilt etc.""" + screen = self.screen + shape = screen._shapes[self.turtle.shapeIndex] + ttype = shape._type + titem = self.turtle._item + if self._shown and screen._updatecounter == 0 and screen._tracing > 0: + self._hidden_from_screen = False + tshape = shape._data + if ttype == "polygon": + if self._resizemode == "noresize": + w = 1 + shape = tshape + else: + if self._resizemode == "auto": + lx = ly = max(1, self._pensize/5.0) + w = self._pensize + tiltangle = 0 + elif self._resizemode == "user": + lx, ly = self._stretchfactor + w = self._outlinewidth + tiltangle = self._tilt + shape = [(lx*x, ly*y) for (x, y) in tshape] + t0, t1 = math.sin(tiltangle), math.cos(tiltangle) + shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape] + shape = self._polytrafo(shape) + fc, oc = self._fillcolor, self._pencolor + screen._drawpoly(titem, shape, fill=fc, outline=oc, + width=w, top=True) + elif ttype == "image": + screen._drawimage(titem, self._position, tshape) + elif ttype == "compound": + lx, ly = self._stretchfactor + w = self._outlinewidth + for item, (poly, fc, oc) in zip(titem, tshape): + poly = [(lx*x, ly*y) for (x, y) in poly] + poly = self._polytrafo(poly) + screen._drawpoly(item, poly, fill=self._cc(fc), + outline=self._cc(oc), width=w, top=True) + else: + if self._hidden_from_screen: return - try: - r, g, b = color - except: - raise Error("bad color sequence: %r" % (color,)) + if ttype == "polygon": + screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "") + elif ttype == "image": + screen._drawimage(titem, self._position, + screen._shapes["blank"]._data) + elif ttype == "compound": + for item in titem: + screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "") + self._hidden_from_screen = True + +############################## stamp stuff ############################### + + def stamp(self): + """Stamp a copy of the turtleshape onto the canvas and return it's id. + + No argument. + + Stamp a copy of the turtle shape onto the canvas at the current + turtle position. Return a stamp_id for that stamp, which can be + used to delete it by calling clearstamp(stamp_id). + + Example (for a Turtle instance named turtle): + >>> turtle.color("blue") + >>> turtle.stamp() + 13 + >>> turtle.fd(50) + """ + screen = self.screen + shape = screen._shapes[self.turtle.shapeIndex] + ttype = shape._type + tshape = shape._data + if ttype == "polygon": + stitem = screen._createpoly() + if self._resizemode == "noresize": + w = 1 + shape = tshape + else: + if self._resizemode == "auto": + lx = ly = max(1, self._pensize/5.0) + w = self._pensize + tiltangle = 0 + elif self._resizemode == "user": + lx, ly = self._stretchfactor + w = self._outlinewidth + tiltangle = self._tilt + shape = [(lx*x, ly*y) for (x, y) in tshape] + t0, t1 = math.sin(tiltangle), math.cos(tiltangle) + shape = [(t1*x+t0*y, -t0*x+t1*y) for (x, y) in shape] + shape = self._polytrafo(shape) + fc, oc = self._fillcolor, self._pencolor + screen._drawpoly(stitem, shape, fill=fc, outline=oc, + width=w, top=True) + elif ttype == "image": + stitem = screen._createimage("") + screen._drawimage(stitem, self._position, tshape) + elif ttype == "compound": + stitem = [] + for element in tshape: + item = screen._createpoly() + stitem.append(item) + stitem = tuple(stitem) + lx, ly = self._stretchfactor + w = self._outlinewidth + for item, (poly, fc, oc) in zip(stitem, tshape): + poly = [(lx*x, ly*y) for (x, y) in poly] + poly = self._polytrafo(poly) + screen._drawpoly(item, poly, fill=self._cc(fc), + outline=self._cc(oc), width=w, top=True) + self.stampItems.append(stitem) + self.undobuffer.push(("stamp", stitem)) + return stitem + + def _clearstamp(self, stampid): + """does the work for clearstamp() and clearstamps() + """ + if stampid in self.stampItems: + if isinstance(stampid, tuple): + for subitem in stampid: + self.screen._delete(subitem) + else: + self.screen._delete(stampid) + self.stampItems.remove(stampid) + # Delete stampitem from undobuffer if necessary + # if clearstamp is called directly. + item = ("stamp", stampid) + buf = self.undobuffer + if item not in buf.buffer: + return + index = buf.buffer.index(item) + buf.buffer.remove(item) + if index <= buf.ptr: + buf.ptr = (buf.ptr - 1) % buf.bufsize + buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None]) + + def clearstamp(self, stampid): + """Delete stamp with given stampid + + Argument: + stampid - an integer, must be return value of previous stamp() call. + + Example (for a Turtle instance named turtle): + >>> turtle.color("blue") + >>> astamp = turtle.stamp() + >>> turtle.fd(50) + >>> turtle.clearstamp(astamp) + """ + self._clearstamp(stampid) + self._update() + + def clearstamps(self, n=None): + """Delete all or first/last n of turtle's stamps. + + Optional argument: + n -- an integer + + If n is None, delete all of pen's stamps, + else if n > 0 delete first n stamps + else if n < 0 delete last n stamps. + + Example (for a Turtle instance named turtle): + >>> for i in range(8): + turtle.stamp(); turtle.fd(30) + ... + >>> turtle.clearstamps(2) + >>> turtle.clearstamps(-2) + >>> turtle.clearstamps() + """ + if n is None: + toDelete = self.stampItems[:] + elif n >= 0: + toDelete = self.stampItems[:n] else: - try: - r, g, b = args - except: - raise Error("bad color arguments: %r" % (args,)) - assert 0 <= r <= 1 - assert 0 <= g <= 1 - assert 0 <= b <= 1 - x = 255.0 - y = 0.5 - self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y))) - - def _set_color(self,color): - self._color = color - self._draw_turtle() - - def write(self, text, move=False): - """ Write text at the current pen position. - - If move is true, the pen is moved to the bottom-right corner - of the text. By default, move is False. + toDelete = self.stampItems[n:] + for item in toDelete: + self._clearstamp(item) + self._update() + + def _goto(self, end): + """Move the pen to the point end, thereby drawing a line + if pen is down. All other methodes for turtle movement depend + on this one. + """ + ## Version mit undo-stuff + go_modes = ( self._drawing, + self._pencolor, + self._pensize, + isinstance(self._fillpath, list)) + screen = self.screen + undo_entry = ("go", self._position, end, go_modes, + (self.currentLineItem, + self.currentLine[:], + screen._pointlist(self.currentLineItem), + self.items[:]) + ) + if self.undobuffer: + self.undobuffer.push(undo_entry) + start = self._position + if self._speed and screen._tracing == 1: + diff = (end-start) + diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 + nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) + delta = diff * (1.0/nhops) + for n in range(1, nhops): + if n == 1: + top = True + else: + top = False + self._position = start + delta * n + if self._drawing: + screen._drawline(self.drawingLineItem, + (start, self._position), + self._pencolor, self._pensize, top) + self._update() + if self._drawing: + screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), + fill="", width=self._pensize) + # Turtle now at end, + if self._drawing: # now update currentLine + self.currentLine.append(end) + if isinstance(self._fillpath, list): + self._fillpath.append(end) + ###### vererbung!!!!!!!!!!!!!!!!!!!!!! + self._position = end + if self._creatingPoly: + self._poly.append(end) + if len(self.currentLine) > 42: # 42! answer to the ultimate question + # of life, the universe and everything + self._newLine() + self._update() #count=True) + + def _undogoto(self, entry): + """Reverse a _goto. Used for undo() + """ + old, new, go_modes, coodata = entry + drawing, pc, ps, filling = go_modes + cLI, cL, pl, items = coodata + screen = self.screen + if abs(self._position - new) > 0.5: + print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!") + # restore former situation + self.currentLineItem = cLI + self.currentLine = cL + + if pl == [(0, 0), (0, 0)]: + usepc = "" + else: + usepc = pc + screen._drawline(cLI, pl, fill=usepc, width=ps) + + todelete = [i for i in self.items if (i not in items) and + (screen._type(i) == "line")] + for i in todelete: + screen._delete(i) + self.items.remove(i) + + start = old + if self._speed and screen._tracing == 1: + diff = old - new + diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 + nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) + delta = diff * (1.0/nhops) + for n in range(1, nhops): + if n == 1: + top = True + else: + top = False + self._position = new + delta * n + if drawing: + screen._drawline(self.drawingLineItem, + (start, self._position), + pc, ps, top) + self._update() + if drawing: + screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), + fill="", width=ps) + # Turtle now at position old, + self._position = old + ## if undo is done during crating a polygon, the last vertex + ## will be deleted. if the polygon is entirel deleted, + ## creatigPoly will be set to False. + ## Polygons created before the last one will not be affected by undo() + if self._creatingPoly: + if len(self._poly) > 0: + self._poly.pop() + if self._poly == []: + self._creatingPoly = False + self._poly = None + if filling: + if self._fillpath == []: + self._fillpath = None + print("Unwahrscheinlich in _undogoto!") + elif self._fillpath is not None: + self._fillpath.pop() + self._update() #count=True) + + def _rotate(self, angle): + """Turns pen clockwise by angle. + """ + if self.undobuffer: + self.undobuffer.push(("rot", angle, self._degreesPerAU)) + angle *= self._degreesPerAU + neworient = self._orient.rotate(angle) + tracing = self.screen._tracing + if tracing == 1 and self._speed > 0: + anglevel = 3.0 * self._speed + steps = 1 + int(abs(angle)/anglevel) + delta = 1.0*angle/steps + for _ in range(steps): + self._orient = self._orient.rotate(delta) + self._update() + self._orient = neworient + self._update() + + def _newLine(self, usePos=True): + """Closes current line item and starts a new one. + Remark: if current line became too long, animation + performance (via _drawline) slowed down considerably. + """ + if len(self.currentLine) > 1: + self.screen._drawline(self.currentLineItem, self.currentLine, + self._pencolor, self._pensize) + self.currentLineItem = self.screen._createline() + self.items.append(self.currentLineItem) + else: + self.screen._drawline(self.currentLineItem, top=True) + self.currentLine = [] + if usePos: + self.currentLine = [self._position] - Example: - >>> turtle.write('The race is on!') - >>> turtle.write('Home = (0, 0)', True) - """ - x, y = self._position - x = x-1 # correction -- calibrated for Windows - item = self._canvas.create_text(x, y, - text=str(text), anchor="sw", - fill=self._color) - self._items.append(item) - if move: - x0, y0, x1, y1 = self._canvas.bbox(item) - self._goto(x1, y1) - self._draw_turtle() + def filling(self): + """Return fillstate (True if filling, False else). - def fill(self, flag): - """ Call fill(1) before drawing the shape you - want to fill, and fill(0) when done. + No argument. - Example: - >>> turtle.fill(1) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.fill(0) - """ - if self._filling: - path = tuple(self._path) - smooth = self._filling < 0 - if len(path) > 2: - item = self._canvas._create('polygon', path, - {'fill': self._color, - 'smooth': smooth}) - self._items.append(item) - self._path = [] - self._filling = flag - if flag: - self._path.append(self._position) + Example (for a Turtle instance named turtle): + >>> turtle.begin_fill() + >>> if turtle.filling(): + turtle.pensize(5) + else: + turtle.pensize(3) + """ + return isinstance(self._fillpath, list) + +## def fill(self, flag=None): +## """Call fill(True) before drawing a shape to fill, fill(False) when done. +## +## Optional argument: +## flag -- True/False (or 1/0 respectively) +## +## Call fill(True) before drawing the shape you want to fill, +## and fill(False) when done. +## When used without argument: return fillstate (True if filling, +## False else) +## +## Example (for a Turtle instance named turtle): +## >>> turtle.fill(True) +## >>> turtle.forward(100) +## >>> turtle.left(90) +## >>> turtle.forward(100) +## >>> turtle.left(90) +## >>> turtle.forward(100) +## >>> turtle.left(90) +## >>> turtle.forward(100) +## >>> turtle.fill(False) +## """ +## filling = isinstance(self._fillpath, list) +## if flag is None: +## return filling +## screen = self.screen +## entry1 = entry2 = () +## if filling: +## if len(self._fillpath) > 2: +## self.screen._drawpoly(self._fillitem, self._fillpath, +## fill=self._fillcolor) +## entry1 = ("dofill", self._fillitem) +## if flag: +## self._fillitem = self.screen._createpoly() +## self.items.append(self._fillitem) +## self._fillpath = [self._position] +## entry2 = ("beginfill", self._fillitem) # , self._fillpath) +## self._newLine() +## else: +## self._fillitem = self._fillpath = None +## if self.undobuffer: +## if entry1 == (): +## if entry2 != (): +## self.undobuffer.push(entry2) +## else: +## if entry2 == (): +## self.undobuffer.push(entry1) +## else: +## self.undobuffer.push(["seq", entry1, entry2]) +## self._update() def begin_fill(self): - """ Called just before drawing a shape to be filled. - Must eventually be followed by a corresponding end_fill() call. - Otherwise it will be ignored. + """Called just before drawing a shape to be filled. - Example: + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") >>> turtle.begin_fill() - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) + >>> turtle.circle(60) >>> turtle.end_fill() """ - self._path = [self._position] - self._filling = 1 + if not self.filling(): + self._fillitem = self.screen._createpoly() + self.items.append(self._fillitem) + self._fillpath = [self._position] + self._newLine() + if self.undobuffer: + self.undobuffer.push(("beginfill", self._fillitem)) + self._update() + def end_fill(self): - """ Called after drawing a shape to be filled. + """Fill the shape drawn after the call begin_fill(). - Example: + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") >>> turtle.begin_fill() - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) - >>> turtle.left(90) - >>> turtle.forward(100) + >>> turtle.circle(60) >>> turtle.end_fill() """ - self.fill(0) - - def circle(self, radius, extent = None): - """ Draw a circle with given radius. - The center is radius units left of the turtle; extent - determines which part of the circle is drawn. If not given, - the entire circle is drawn. - - If extent is not a full circle, one endpoint of the arc is the - current pen position. The arc is drawn in a counter clockwise - direction if radius is positive, otherwise in a clockwise - direction. In the process, the direction of the turtle is - changed by the amount of the extent. - - >>> turtle.circle(50) - >>> turtle.circle(120, 180) # half a circle + if self.filling(): + if len(self._fillpath) > 2: + self.screen._drawpoly(self._fillitem, self._fillpath, + fill=self._fillcolor) + if self.undobuffer: + self.undobuffer.push(("dofill", self._fillitem)) + self._fillitem = self._fillpath = None + self._update() + + def dot(self, size=None, *color): + """Draw a dot with diameter size, using color. + + Optional argumentS: + size -- an integer >= 1 (if given) + color -- a colorstring or a numeric color tuple + + Draw a circular dot with diameter size, using color. + If size is not given, the maximum of pensize+4 and 2*pensize is used. + + Example (for a Turtle instance named turtle): + >>> turtle.dot() + >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50) """ - if extent is None: - extent = self._fullcircle - frac = abs(extent)/self._fullcircle - steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac) - w = 1.0 * extent / steps - w2 = 0.5 * w - l = 2.0 * radius * sin(w2*self._invradian) - if radius < 0: - l, w, w2 = -l, -w, -w2 - self.left(w2) - for i in range(steps): - self.forward(l) - self.left(w) - self.right(w2) - - def heading(self): - """ Return the turtle's current heading. + #print "dot-1:", size, color + if not color: + if isinstance(size, (str, tuple)): + color = self._colorstr(size) + size = self._pensize + max(self._pensize, 4) + else: + color = self._pencolor + if not size: + size = self._pensize + max(self._pensize, 4) + else: + if size is None: + size = self._pensize + max(self._pensize, 4) + color = self._colorstr(color) + #print "dot-2:", size, color + if hasattr(self.screen, "_dot"): + item = self.screen._dot(self._position, size, color) + #print "dot:", size, color, "item:", item + self.items.append(item) + if self.undobuffer: + self.undobuffer.push(("dot", item)) + else: + pen = self.pen() + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + try: + if self.resizemode() == 'auto': + self.ht() + self.pendown() + self.pensize(size) + self.pencolor(color) + self.forward(0) + finally: + self.pen(pen) + if self.undobuffer: + self.undobuffer.cumulate = False + + def _write(self, txt, align, font): + """Performs the writing for write() + """ + item, end = self.screen._write(self._position, txt, align, font, + self._pencolor) + self.items.append(item) + if self.undobuffer: + self.undobuffer.push(("wri", item)) + return end + + def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")): + """Write text at the current turtle position. + + Arguments: + arg -- info, which is to be written to the TurtleScreen + move (optional) -- True/False + align (optional) -- one of the strings "left", "center" or right" + font (optional) -- a triple (fontname, fontsize, fonttype) + + Write text - the string representation of arg - at the current + turtle position according to align ("left", "center" or right") + and with the given font. + If move is True, the pen is moved to the bottom-right corner + of the text. By default, move is False. - Example: - >>> turtle.heading() - 67.0 + Example (for a Turtle instance named turtle): + >>> turtle.write('Home = ', True, align="center") + >>> turtle.write((0,0), True) """ - return self._angle + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + end = self._write(str(arg), align.lower(), font) + if move: + x, y = self.pos() + self.setpos(end, y) + if self.undobuffer: + self.undobuffer.cumulate = False - def setheading(self, angle): - """ Set the turtle facing the given angle. + def begin_poly(self): + """Start recording the vertices of a polygon. - Here are some common directions in degrees: + No argument. - 0 - east - 90 - north - 180 - west - 270 - south + Start recording the vertices of a polygon. Current turtle position + is first point of polygon. - Example: - >>> turtle.setheading(90) - >>> turtle.heading() - 90 - >>> turtle.setheading(128) - >>> turtle.heading() - 128 + Example (for a Turtle instance named turtle): + >>> turtle.begin_poly() """ - self._angle = angle - self._draw_turtle() + self._poly = [self._position] + self._creatingPoly = True - def window_width(self): - """ Returns the width of the turtle window. + def end_poly(self): + """Stop recording the vertices of a polygon. - Example: - >>> turtle.window_width() - 640 - """ - width = self._canvas.winfo_width() - if width <= 1: # the window isn't managed by a geometry manager - width = self._canvas['width'] - return width + No argument. - def window_height(self): - """ Return the height of the turtle window. + Stop recording the vertices of a polygon. Current turtle position is + last point of polygon. This will be connected with the first point. - Example: - >>> turtle.window_height() - 768 + Example (for a Turtle instance named turtle): + >>> turtle.end_poly() """ - height = self._canvas.winfo_height() - if height <= 1: # the window isn't managed by a geometry manager - height = self._canvas['height'] - return height + self._creatingPoly = False - def position(self): - """ Return the current (x, y) location of the turtle. + def get_poly(self): + """Return the lastly recorded polygon. - Example: - >>> turtle.position() - [0.0, 240.0] + No argument. + + Example (for a Turtle instance named turtle): + >>> p = turtle.get_poly() + >>> turtle.register_shape("myFavouriteShape", p) """ - x0, y0 = self._origin - x1, y1 = self._position - return [x1-x0, -y1+y0] + ## check if there is any poly? -- 1st solution: + if self._poly is not None: + return tuple(self._poly) - def setx(self, xpos): - """ Set the turtle's x coordinate to be xpos. + def getscreen(self): + """Return the TurtleScreen object, the turtle is drawing on. - Example: - >>> turtle.position() - [10.0, 240.0] - >>> turtle.setx(10) - >>> turtle.position() - [10.0, 240.0] - """ - x0, y0 = self._origin - x1, y1 = self._position - self._goto(x0+xpos, y1) + No argument. - def sety(self, ypos): - """ Set the turtle's y coordinate to be ypos. + Return the TurtleScreen object, the turtle is drawing on. + So TurtleScreen-methods can be called for that object. - Example: - >>> turtle.position() - [0.0, 0.0] - >>> turtle.sety(-22) - >>> turtle.position() - [0.0, -22.0] + Example (for a Turtle instance named turtle): + >>> ts = turtle.getscreen() + >>> ts + + >>> ts.bgcolor("pink") """ - x0, y0 = self._origin - x1, y1 = self._position - self._goto(x1, y0-ypos) + return self.screen - def towards(self, *args): - """Returs the angle, which corresponds to the line - from turtle-position to point (x,y). + def getturtle(self): + """Return the Turtleobject itself. - Argument can be two coordinates or one pair of coordinates - or a RawPen/Pen instance. + No argument. + + Only reasonable use: as a function to return the 'anonymous turtle': Example: - >>> turtle.position() - [10.0, 10.0] - >>> turtle.towards(0,0) - 225.0 + >>> pet = getturtle() + >>> pet.fd(50) + >>> pet + + >>> turtles() + [] """ - if len(args) == 2: - x, y = args - else: - arg = args[0] - if isinstance(arg, RawPen): - x, y = arg.position() - else: - x, y = arg - x0, y0 = self.position() - dx = x - x0 - dy = y - y0 - return (atan2(dy,dx) / self._invradian) % self._fullcircle + return self + + getpen = getturtle + + + ################################################################ + ### screen oriented methods recurring to methods of TurtleScreen + ################################################################ + +## def window_width(self): +## """ Returns the width of the turtle window. +## +## No argument. +## +## Example (for a TurtleScreen instance named screen): +## >>> screen.window_width() +## 640 +## """ +## return self.screen._window_size()[0] +## +## def window_height(self): +## """ Return the height of the turtle window. +## +## No argument. +## +## Example (for a TurtleScreen instance named screen): +## >>> screen.window_height() +## 480 +## """ +## return self.screen._window_size()[1] + + def _delay(self, delay=None): + """Set delay value which determines speed of turtle animation. + """ + return self.screen.delay(delay) - def goto(self, *args): - """ Go to the given point. + ##### event binding methods ##### - If the pen is down, then a line will be drawn. The turtle's - orientation does not change. + def onclick(self, fun, btn=1, add=None): + """Bind fun to mouse-click event on this turtle on canvas. - Two input formats are accepted: + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + num -- number of the mouse-button defaults to 1 (left mouse button). + add -- True or False. If True, new binding will be added, otherwise + it will replace a former binding. - goto(x, y) - go to point (x, y) + Example for the anonymous turtle, i. e. the procedural way: - goto((x, y)) - go to point (x, y) + >>> def turn(x, y): + left(360) - Example: - >>> turtle.position() - [0.0, 0.0] - >>> turtle.goto(50, -45) - >>> turtle.position() - [50.0, -45.0] + >>> onclick(turn) # Now clicking into the turtle will turn it. + >>> onclick(None) # event-binding will be removed """ - if len(args) == 1: - try: - x, y = args[0] - except: - raise Error("bad point argument: %r" % (args[0],)) - else: - try: - x, y = args - except: - raise Error("bad coordinates: %r" % (args[0],)) - x0, y0 = self._origin - self._goto(x0+x, y0-y) - - def _goto(self, x1, y1): - x0, y0 = self._position - self._position = (float(x1), float(y1)) - if self._filling: - self._path.append(self._position) - if self._drawing: - if self._tracing: - dx = float(x1 - x0) - dy = float(y1 - y0) - distance = hypot(dx, dy) - nhops = int(distance) - item = self._canvas.create_line(x0, y0, x0, y0, - width=self._width, - capstyle="round", - fill=self._color) - try: - for i in range(1, 1+nhops): - x, y = x0 + dx*i/nhops, y0 + dy*i/nhops - self._canvas.coords(item, x0, y0, x, y) - self._draw_turtle((x,y)) - self._canvas.update() - self._canvas.after(self._delay) - # in case nhops==0 - self._canvas.coords(item, x0, y0, x1, y1) - self._canvas.itemconfigure(item, arrow="none") - except tkinter.TclError: - # Probably the window was closed! - return - else: - item = self._canvas.create_line(x0, y0, x1, y1, - width=self._width, - capstyle="round", - fill=self._color) - self._items.append(item) - self._draw_turtle() - - def speed(self, speed): - """ Set the turtle's speed. + self.screen._onclick(self.turtle._item, fun, btn, add) + self._update() + + def onrelease(self, fun, btn=1, add=None): + """Bind fun to mouse-button-release event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + num -- number of the mouse-button defaults to 1 (left mouse button). + + Example (for a MyTurtle instance named joe): + >>> class MyTurtle(Turtle): + def glow(self,x,y): + self.fillcolor("red") + def unglow(self,x,y): + self.fillcolor("") + + >>> joe = MyTurtle() + >>> joe.onclick(joe.glow) + >>> joe.onrelease(joe.unglow) + ### clicking on joe turns fillcolor red, + ### unclicking turns it to transparent. + """ + self.screen._onrelease(self.turtle._item, fun, btn, add) + self._update() - speed must one of these five strings: + def ondrag(self, fun, btn=1, add=None): + """Bind fun to mouse-move event on this turtle on canvas. - 'fastest' is a 0 ms delay - 'fast' is a 5 ms delay - 'normal' is a 10 ms delay - 'slow' is a 15 ms delay - 'slowest' is a 20 ms delay + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + num -- number of the mouse-button defaults to 1 (left mouse button). - Example: - >>> turtle.speed('slow') - """ - try: - speed = speed.strip().lower() - self._delay = speeds.index(speed) * 5 - except: - raise ValueError("%r is not a valid speed. speed must be " - "one of %s" % (speed, speeds)) + Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + Example (for a Turtle instance named turtle): + >>> turtle.ondrag(turtle.goto) - def delay(self, delay): - """ Set the drawing delay in milliseconds. + ### Subsequently clicking and dragging a Turtle will + ### move it across the screen thereby producing handdrawings + ### (if pen is down). + """ + self.screen._ondrag(self.turtle._item, fun, btn, add) - This is intended to allow finer control of the drawing speed - than the speed() method - Example: - >>> turtle.delay(15) + def _undo(self, action, data): + """Does the main part of the work for undo() """ - if int(delay) < 0: - raise ValueError("delay must be greater than or equal to 0") - self._delay = int(delay) - - def _draw_turtle(self, position=[]): - if not self._tracing: - self._canvas.update() + if self.undobuffer is None: return - if position == []: - position = self._position - x,y = position - distance = 8 - dx = distance * cos(self._angle*self._invradian) - dy = distance * sin(self._angle*self._invradian) - self._delete_turtle() - self._arrow = self._canvas.create_line(x-dx,y+dy,x,y, - width=self._width, - arrow="last", - capstyle="round", - fill=self._color) - self._canvas.update() - - def _delete_turtle(self): - if self._arrow != 0: - self._canvas.delete(self._arrow) - self._arrow = 0 - - -_root = None -_canvas = None -_pen = None -_width = 0.50 # 50% of window width -_height = 0.75 # 75% of window height -_startx = None -_starty = None -_title = "Turtle Graphics" # default title - -class Pen(RawPen): + if action == "rot": + angle, degPAU = data + self._rotate(-angle*degPAU/self._degreesPerAU) + dummy = self.undobuffer.pop() + elif action == "stamp": + stitem = data[0] + self.clearstamp(stitem) + elif action == "go": + self._undogoto(data) + elif action in ["wri", "dot"]: + item = data[0] + self.screen._delete(item) + self.items.remove(item) + elif action == "dofill": + item = data[0] + self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)), + fill="", outline="") + elif action == "beginfill": + item = data[0] + self._fillitem = self._fillpath = None + if item in self.items: + self.screen._delete(item) + self.items.remove(item) + elif action == "pen": + TPen.pen(self, data[0]) + self.undobuffer.pop() + + def undo(self): + """undo (repeatedly) the last turtle action. + + No argument. + + undo (repeatedly) the last turtle action. + Number of available undo actions is determined by the size of + the undobuffer. + + Example (for a Turtle instance named turtle): + >>> for i in range(4): + turtle.fd(50); turtle.lt(80) + + >>> for i in range(8): + turtle.undo() + """ + if self.undobuffer is None: + return + item = self.undobuffer.pop() + action = item[0] + data = item[1:] + if action == "seq": + while data: + item = data.pop() + self._undo(item[0], item[1:]) + else: + self._undo(action, data) - def __init__(self): - global _root, _canvas - if _root is None: - _root = tkinter.Tk() - _root.wm_protocol("WM_DELETE_WINDOW", self._destroy) - _root.title(_title) + turtlesize = shapesize - if _canvas is None: - # XXX Should have scroll bars - _canvas = tkinter.Canvas(_root, background="white") - _canvas.pack(expand=1, fill="both") +RawPen = RawTurtle - setup(width=_width, height= _height, startx=_startx, starty=_starty) +### Screen - Klasse ######################## - RawPen.__init__(self, _canvas) +class Screen(TurtleScreen): - def _destroy(self): - global _root, _canvas, _pen - root = self._canvas._root() - if root is _root: - _pen = None - _root = None - _canvas = None - root.destroy() + _root = None + _canvas = None + _title = _CFG["title"] -def _getpen(): - global _pen - if not _pen: - _pen = Pen() - return _pen + # Borg-Idiom -class Turtle(Pen): - pass + _shared_state = {} -"""For documentation of the following functions see - the RawPen methods with the same names -""" + def __new__(cls, *args, **kwargs): + obj = object.__new__(cls, *args, **kwargs) + obj.__dict__ = cls._shared_state + return obj -def degrees(): _getpen().degrees() -def radians(): _getpen().radians() -def reset(): _getpen().reset() -def clear(): _getpen().clear() -def tracer(flag): _getpen().tracer(flag) -def forward(distance): _getpen().forward(distance) -def backward(distance): _getpen().backward(distance) -def left(angle): _getpen().left(angle) -def right(angle): _getpen().right(angle) -def up(): _getpen().up() -def down(): _getpen().down() -def width(width): _getpen().width(width) -def color(*args): _getpen().color(*args) -def write(arg, move=0): _getpen().write(arg, move) -def fill(flag): _getpen().fill(flag) -def begin_fill(): _getpen().begin_fill() -def end_fill(): _getpen().end_fill() -def circle(radius, extent=None): _getpen().circle(radius, extent) -def goto(*args): _getpen().goto(*args) -def heading(): return _getpen().heading() -def setheading(angle): _getpen().setheading(angle) -def position(): return _getpen().position() -def window_width(): return _getpen().window_width() -def window_height(): return _getpen().window_height() -def setx(xpos): _getpen().setx(xpos) -def sety(ypos): _getpen().sety(ypos) -def towards(*args): return _getpen().towards(*args) - -def done(): _root.mainloop() -def delay(delay): return _getpen().delay(delay) -def speed(speed): return _getpen().speed(speed) - -for methodname in dir(RawPen): - """ copies RawPen docstrings to module functions of same name """ - if not methodname.startswith("_"): - eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__ - - -def setup(**geometry): - """ Sets the size and position of the main window. - - Keywords are width, height, startx and starty: - - width: either a size in pixels or a fraction of the screen. - Default is 50% of screen. - height: either the height in pixels or a fraction of the screen. - Default is 75% of screen. - - Setting either width or height to None before drawing will force - use of default geometry as in older versions of turtle.py - - startx: starting position in pixels from the left edge of the screen. - Default is to center window. Setting startx to None is the default - and centers window horizontally on screen. - - starty: starting position in pixels from the top edge of the screen. - Default is to center window. Setting starty to None is the default - and centers window vertically on screen. - - Examples: - >>> setup (width=200, height=200, startx=0, starty=0) - - sets window to 200x200 pixels, in upper left of screen - - >>> setup(width=.75, height=0.5, startx=None, starty=None) - - sets window to 75% of screen by 50% of screen and centers - - >>> setup(width=None) - - forces use of default geometry as in older versions of turtle.py - """ + def __init__(self): + if Screen._root is None: + Screen._root = self._root = _Root() + self._root.title(Screen._title) + self._root.ondestroy(self._destroy) + if Screen._canvas is None: + width = _CFG["width"] + height = _CFG["height"] + canvwidth = _CFG["canvwidth"] + canvheight = _CFG["canvheight"] + leftright = _CFG["leftright"] + topbottom = _CFG["topbottom"] + self._root.setupcanvas(width, height, canvwidth, canvheight) + Screen._canvas = self._root._getcanvas() + self.setup(width, height, leftright, topbottom) + TurtleScreen.__init__(self, Screen._canvas) + Turtle._screen = self + + def setup(self, width=_CFG["width"], height=_CFG["height"], + startx=_CFG["leftright"], starty=_CFG["topbottom"]): + """ Set the size and position of the main window. + + Arguments: + width: as integer a size in pixels, as float a fraction of the screen. + Default is 50% of screen. + height: as integer the height in pixels, as float a fraction of the + screen. Default is 75% of screen. + startx: if positive, starting position in pixels from the left + edge of the screen, if negative from the right edge + Default, startx=None is to center window horizontally. + starty: if positive, starting position in pixels from the top + edge of the screen, if negative from the bottom edge + Default, starty=None is to center window vertically. + + Examples (for a Screen instance named screen): + >>> screen.setup (width=200, height=200, startx=0, starty=0) + + sets window to 200x200 pixels, in upper left of screen + + >>> screen.setup(width=.75, height=0.5, startx=None, starty=None) + + sets window to 75% of screen by 50% of screen and centers + """ + if not hasattr(self._root, "set_geometry"): + return + sw = self._root.win_width() + sh = self._root.win_height() + if isinstance(width, float) and 0 <= width <= 1: + width = sw*width + if startx is None: + startx = (sw - width) / 2 + if isinstance(height, float) and 0 <= height <= 1: + height = sh*height + if starty is None: + starty = (sh - height) / 2 + self._root.set_geometry(width, height, startx, starty) + + def title(self, titlestring): + """Set title of turtle-window + + Argument: + titlestring -- a string, to appear in the titlebar of the + turtle graphics window. + + This is a method of Screen-class. Not available for TurtleScreen- + objects. + + Example (for a Screen instance named screen): + >>> screen.title("Welcome to the turtle-zoo!") + """ + if Screen._root is not None: + Screen._root.title(titlestring) + Screen._title = titlestring - global _width, _height, _startx, _starty + def _destroy(self): + root = self._root + if root is Screen._root: + Turtle._pen = None + Turtle._screen = None + Screen._root = None + Screen._canvas = None + TurtleScreen._RUNNING = True + root.destroy() - width = geometry.get('width',_width) - if width >= 0 or width is None: - _width = width - else: - raise ValueError("width can not be less than 0") + def bye(self): + """Shut the turtlegraphics window. - height = geometry.get('height',_height) - if height >= 0 or height is None: - _height = height - else: - raise ValueError("height can not be less than 0") + Example (for a TurtleScreen instance named screen): + >>> screen.bye() + """ + self._destroy() - startx = geometry.get('startx', _startx) - if startx >= 0 or startx is None: - _startx = _startx - else: - raise ValueError("startx can not be less than 0") + def exitonclick(self): + """Go into mainloop until the mouse is clicked. - starty = geometry.get('starty', _starty) - if starty >= 0 or starty is None: - _starty = starty - else: - raise ValueError("startx can not be less than 0") + No arguments. + Bind bye() method to mouseclick on TurtleScreen. + If "using_IDLE" - value in configuration dictionary is False + (default value), enter mainloop. + If IDLE with -n switch (no subprocess) is used, this value should be + set to True in turtle.cfg. In this case IDLE's mainloop + is active also for the client script. - if _root and _width and _height: - if 0 < _width <= 1: - _width = _root.winfo_screenwidth() * +width - if 0 < _height <= 1: - _height = _root.winfo_screenheight() * _height + This is a method of the Screen-class and not available for + TurtleScreen instances. - # center window on screen - if _startx is None: - _startx = (_root.winfo_screenwidth() - _width) / 2 + Example (for a Screen instance named screen): + >>> screen.exitonclick() - if _starty is None: - _starty = (_root.winfo_screenheight() - _height) / 2 + """ + def exitGracefully(x, y): + """Screen.bye() with two dummy-parameters""" + self.bye() + self.onclick(exitGracefully) + if _CFG["using_IDLE"]: + return + try: + mainloop() + except AttributeError: + exit(0) - _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty)) -def title(title): - """Set the window title. +class Turtle(RawTurtle): + """RawTurtle auto-crating (scrolled) canvas. - By default this is set to 'Turtle Graphics' + When a Turtle object is created or a function derived from some + Turtle method is called a TurtleScreen object is automatically created. + """ + _pen = None + _screen = None + + def __init__(self, + shape=_CFG["shape"], + undobuffersize=_CFG["undobuffersize"], + visible=_CFG["visible"]): + if Turtle._screen is None: + Turtle._screen = Screen() + RawTurtle.__init__(self, Turtle._screen, + shape=shape, + undobuffersize=undobuffersize, + visible=visible) + +Pen = Turtle - Example: - >>> title("My Window") +def _getpen(): + """Create the 'anonymous' turtle if not already present.""" + if Turtle._pen is None: + Turtle._pen = Turtle() + return Turtle._pen + +def _getscreen(): + """Create a TurtleScreen if not already present.""" + if Turtle._screen is None: + Turtle._screen = Screen() + return Turtle._screen + +def write_docstringdict(filename="turtle_docstringdict"): + """Create and write docstring-dictionary to file. + + Optional argument: + filename -- a string, used as filename + default value is turtle_docstringdict + + Has to be called explicitely, (not used by the turtle-graphics classes) + The docstring dictionary will be written to the Python script .py + It is intended to serve as a template for translation of the docstrings + into different languages. + """ + docsdict = {} + + for methodname in _tg_screen_functions: + key = "Screen."+methodname + docsdict[key] = eval(key).__doc__ + for methodname in _tg_turtle_functions: + key = "Turtle."+methodname + docsdict[key] = eval(key).__doc__ + + f = open("%s.py" % filename,"w") + keys = sorted([x for x in docsdict.keys() + if x.split('.')[1] not in _alias_list]) + f.write('docsdict = {\n\n') + for key in keys[:-1]: + f.write('%s :\n' % repr(key)) + f.write(' """%s\n""",\n\n' % docsdict[key]) + key = keys[-1] + f.write('%s :\n' % repr(key)) + f.write(' """%s\n"""\n\n' % docsdict[key]) + f.write("}\n") + f.close() + +def read_docstrings(lang): + """Read in docstrings from lang-specific docstring dictionary. + + Transfer docstrings, translated to lang, from a dictionary-file + to the methods of classes Screen and Turtle and - in revised form - + to the corresponding functions. + """ + modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()} + module = __import__(modname) + docsdict = module.docsdict + for key in docsdict: + try: +# eval(key).im_func.__doc__ = docsdict[key] + eval(key).__doc__ = docsdict[key] + except: + print("Bad docstring-entry: %s" % key) + +_LANGUAGE = _CFG["language"] + +try: + if _LANGUAGE != "english": + read_docstrings(_LANGUAGE) +except ImportError: + print("Cannot find docsdict for", _LANGUAGE) +except: + print ("Unknown Error when trying to import %s-docstring-dictionary" % + _LANGUAGE) + + +def getmethparlist(ob): + "Get strings describing the arguments for the given object" + argText1 = argText2 = "" + # bit of a hack for methods - turn it into a function + # but we drop the "self" param. +## if type(ob)==types.MethodType: +## fob = ob.im_func +## argOffset = 1 +## else: +## fob = ob +## argOffset = 0 + # Try and build one for Python defined functions + argOffset = 1 +## if type(fob) in [types.FunctionType, types.LambdaType]: +## try: + counter = ob.__code__.co_argcount + items2 = list(ob.__code__.co_varnames[argOffset:counter]) + realArgs = ob.__code__.co_varnames[argOffset:counter] + defaults = ob.__defaults__ or [] + defaults = list(map(lambda name: "=%s" % repr(name), defaults)) + defaults = [""] * (len(realArgs)-len(defaults)) + defaults + items1 = list(map(lambda arg, dflt: arg+dflt, realArgs, defaults)) + if ob.__code__.co_flags & 0x4: + items1.append("*"+ob.__code__.co_varnames[counter]) + items2.append("*"+ob.__code__.co_varnames[counter]) + counter += 1 + if ob.__code__.co_flags & 0x8: + items1.append("**"+ob.__code__.co_varnames[counter]) + items2.append("**"+ob.__code__.co_varnames[counter]) + argText1 = ", ".join(items1) + argText1 = "(%s)" % argText1 + argText2 = ", ".join(items2) + argText2 = "(%s)" % argText2 +## except: +## pass + return argText1, argText2 + +def _turtle_docrevise(docstr): + """To reduce docstrings from RawTurtle class for functions """ + import re + if docstr is None: + return None + turtlename = _CFG["exampleturtle"] + newdocstr = docstr.replace("%s." % turtlename,"") + parexp = re.compile(r' \(.+ %s\):' % turtlename) + newdocstr = parexp.sub(":", newdocstr) + return newdocstr + +def _screen_docrevise(docstr): + """To reduce docstrings from TurtleScreen class for functions + """ + import re + if docstr is None: + return None + screenname = _CFG["examplescreen"] + newdocstr = docstr.replace("%s." % screenname,"") + parexp = re.compile(r' \(.+ %s\):' % screenname) + newdocstr = parexp.sub(":", newdocstr) + return newdocstr + +## The following mechanism makes all methods of RawTurtle and Turtle available +## as functions. So we can enhance, change, add, delete methods to these +## classes and do not need to change anything here. + + +for methodname in _tg_screen_functions: + pl1, pl2 = getmethparlist(eval('Screen.' + methodname)) + if pl1 == "": + print(">>>>>>", pl1, pl2) + continue + defstr = ("def %(key)s%(pl1)s: return _getscreen().%(key)s%(pl2)s" % + {'key':methodname, 'pl1':pl1, 'pl2':pl2}) +## print("Screen:", defstr) + exec(defstr) + eval(methodname).__doc__ = _screen_docrevise(eval('Screen.'+methodname).__doc__) + +for methodname in _tg_turtle_functions: + pl1, pl2 = getmethparlist(eval('Turtle.' + methodname)) + if pl1 == "": + print(">>>>>>", pl1, pl2) + continue + defstr = ("def %(key)s%(pl1)s: return _getpen().%(key)s%(pl2)s" % + {'key':methodname, 'pl1':pl1, 'pl2':pl2}) +## print("Turtle:", defstr) + exec(defstr) + eval(methodname).__doc__ = _turtle_docrevise(eval('Turtle.'+methodname).__doc__) + + +done = mainloop = TK.mainloop +#del pl1, pl2, defstr + +if __name__ == "__main__": + def switchpen(): + if isdown(): + pu() + else: + pd() - global _title - _title = title - -def demo(): - reset() - tracer(1) - up() - backward(100) - down() - # draw 3 squares; the last filled - width(3) - for i in range(3): - if i == 2: - fill(1) - for j in range(4): - forward(20) - left(90) - if i == 2: - color("maroon") - fill(0) + def demo1(): + """Demo of old turtle.py - module""" + reset() + tracer(True) up() - forward(30) + backward(100) down() - width(1) - color("black") - # move out of the way - tracer(0) - up() - right(90) - forward(100) - right(90) - forward(100) - right(180) - down() - # some text - write("startstart", 1) - write("start", 1) - color("red") - # staircase - for i in range(5): - forward(20) - left(90) - forward(20) - right(90) - # filled staircase - fill(1) - for i in range(5): - forward(20) - left(90) - forward(20) - right(90) - fill(0) - tracer(1) - # more text - write("end") - -def demo2(): - # exercises some new and improved features - speed('fast') - width(3) - - # draw a segmented half-circle - setheading(towards(0,0)) - x,y = position() - r = (x**2+y**2)**.5/2.0 - right(90) - pendown = True - for i in range(18): - if pendown: + # draw 3 squares; the last filled + width(3) + for i in range(3): + if i == 2: + begin_fill() + for _ in range(4): + forward(20) + left(90) + if i == 2: + color("maroon") + end_fill() up() - pendown = False - else: + forward(30) down() - pendown = True - circle(r,10) - sleep(2) - - reset() - left(90) - - # draw a series of triangles - l = 10 - color("green") - width(3) - left(180) - sp = 5 - for i in range(-2,16): - if i > 0: - color(1.0-0.05*i,0,0.05*i) - fill(1) - color("green") - for j in range(3): - forward(l) - left(120) - l += 10 - left(15) - if sp > 0: - sp = sp-1 - speed(speeds[sp]) - color(0.25,0,0.75) - fill(0) - - # draw and fill a concave shape - left(120) - up() - forward(70) - right(30) - down() - color("red") - speed("fastest") - fill(1) - for i in range(4): - circle(50,90) + width(1) + color("black") + # move out of the way + tracer(False) + up() right(90) - forward(30) + forward(100) right(90) - color("yellow") - fill(0) - left(90) - up() - forward(30) - down(); - - color("red") - - # create a second turtle and make the original pursue and catch it - turtle=Turtle() - turtle.reset() - turtle.left(90) - turtle.speed('normal') - turtle.up() - turtle.goto(280,40) - turtle.left(24) - turtle.down() - turtle.speed('fast') - turtle.color("blue") - turtle.width(2) - speed('fastest') - - # turn default turtle towards new turtle object - setheading(towards(turtle)) - while ( abs(position()[0]-turtle.position()[0])>4 or - abs(position()[1]-turtle.position()[1])>4): - turtle.forward(3.5) - turtle.left(0.6) - # turn default turtle towards new turtle object + forward(100) + right(180) + down() + # some text + write("startstart", 1) + write("start", 1) + color("red") + # staircase + for i in range(5): + forward(20) + left(90) + forward(20) + right(90) + # filled staircase + tracer(True) + begin_fill() + for i in range(5): + forward(20) + left(90) + forward(20) + right(90) + end_fill() + # more text + + def demo2(): + """Demo of some new features.""" + speed(1) + st() + pensize(3) + setheading(towards(0, 0)) + radius = distance(0, 0)/2.0 + rt(90) + for _ in range(18): + switchpen() + circle(radius, 10) + write("wait a moment...") + while undobufferentries(): + undo() + reset() + lt(90) + colormode(255) + laenge = 10 + pencolor("green") + pensize(3) + lt(180) + for i in range(-2, 16): + if i > 0: + begin_fill() + fillcolor(255-15*i, 0, 15*i) + for _ in range(3): + fd(laenge) + lt(120) + end_fill() + laenge += 10 + lt(15) + speed((speed()+1)%12) + #end_fill() + + lt(120) + pu() + fd(70) + rt(30) + pd() + color("red","yellow") + speed(0) + begin_fill() + for _ in range(4): + circle(50, 90) + rt(90) + fd(30) + rt(90) + end_fill() + lt(90) + pu() + fd(30) + pd() + shape("turtle") + + tri = getturtle() + tri.resizemode("auto") + turtle = Turtle() + turtle.resizemode("auto") + turtle.shape("turtle") + turtle.reset() + turtle.left(90) + turtle.speed(0) + turtle.up() + turtle.goto(280, 40) + turtle.lt(30) + turtle.down() + turtle.speed(6) + turtle.color("blue","orange") + turtle.pensize(2) + tri.speed(6) setheading(towards(turtle)) - forward(4) - write("CAUGHT! ", move=True) - - - -if __name__ == '__main__': - demo() - sleep(3) + count = 1 + while tri.distance(turtle) > 4: + turtle.fd(3.5) + turtle.lt(0.6) + tri.setheading(tri.towards(turtle)) + tri.fd(4) + if count % 20 == 0: + turtle.stamp() + tri.stamp() + switchpen() + count += 1 + tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right") + tri.pencolor("black") + tri.pencolor("red") + + def baba(xdummy, ydummy): + clearscreen() + bye() + + time.sleep(2) + + while undobufferentries(): + tri.undo() + turtle.undo() + tri.fd(50) + tri.write(" Click me!", font = ("Courier", 12, "bold") ) + tri.onclick(baba, 1) + + demo1() demo2() - done() + exitonclick() diff --git a/Misc/ACKS b/Misc/ACKS index eb2a651..7e2c340 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -411,6 +411,7 @@ Christopher Lindblad Bjorn Lindqvist Per Lindqvist Eric Lindvall +Gregor Lingl Nick Lockwood Stephanie Lockwood Anne Lord -- cgit v0.12