2.11 TURTLEOBJEKTE

 

 

EINFÜHRUNG

 

In der Natur ist eine Schildkröte ein Individuum mit seiner ganz spezifischen Identität. In einem Zoogehege könntest du jeder Schildkröte einen eigenen Namen geben, z.B. Pepe oder Maya. Schildkröten habe aber auch Gemeinsamkeiten: Sie sind Lebewesen aus der Tierklasse der Schildkröten. Solche Beschreibungen haben sich derart gut bewährt, dass sie in der Informatik als grundlegendes Konzept eingeführt wurden, das sich Objektorientierte Programmierung (OOP) nennt. Mit der Turtlegrafik ist es für dich spielerisch leicht, die Grundprinzipien der OOP kennen zu lernen.

PROGRAMMIERKONZEPTE: Klasse, Objekt, Objektorientierte Programmierung, Konstruktor, Klone

 

 

TURTLEOBJEKT ERZEUGEN

 

Bei der bisher verwendeten Turtle handelt es sich um ein anonymes Objekt, für das wir keinen Namen brauchten. Wenn du mehrere Turtles gleichzeitig verwenden willst, musst du aber jeder Turtle mit einem Namen eine eigene Identität geben. Den Namen kannst du als Variablennamen verwenden.

Mit der Anweisung maya = Turtle() erzeugst du eine Turtle mit dem Namen maya.
Mit der Anweisung pepe = Turtle() erzeugst du eine Turtle mit dem Namen pepe.

Du kannst die benannten Turtles mit den dir bekannten Befehlen steuern, aber du musst natürlich nun immer sagen, welche der Turtles du meinst. Dazu stellst du dem Befehl den Turtlenamen durch einen Punkt getrennt voran, z.B. maya.forward(100).

Im ersten Beispiel zeichnet maya eine Leiter. Die Zeile makeTurtle() brauchst du nicht mehr, da du ja die Turtle selbst erzeugst.

 
from gturtle import *

maya = Turtle()
maya.setColor("red")
maya.setPenColor("green")
maya.setPos(0, -200)

repeat 7:
    repeat 4:
        maya.forward(50)
        maya.right(90)
    maya.forward(50)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Gleichartige Objekte werden in Klassen zusammengefasst. Ein Objekt einer Klasse wird erzeugt, indem man den Klassennamen mit einer Parameterklammer verwendet. Wir nennen dies den Konstruktor der Klasse.

Funktionen, die zu einer bestimmten Klasse gehören, nennen wir in Zukunft Methoden.

 

 

MEHRERE TURTLEOBJEKTE ERZEUGEN

 

Es liegt ja nun auf der Hand, dass du auf die beschriebene Art im gleichen Programm mehrere Turtles verwenden kannst. Willst du maya und pepe erzeugen, so schreibst du
maya = Turtle() und pepe = Turtle()

Allerdings befinden sich diese dann jeweils in ihrem eigenen Turtlefenster. Du kannst sie aber ins gleiche Turtlegehege setzen, indem du auch das Gehege als ein Objekt der Klasse TurtleFrame erzeugst:
tf = TurtleFrame()
und dieses bei der Erzeugung der Turtles angibst.
Während maya die gleiche Leiter wie vorhin baut, soll der schwarze pepe gleichzeitig eine horizontale schwarze Leiter bauen.

 
from gturtle import *

tf = TurtleFrame()

maya = Turtle(tf)
maya.setColor("red")
maya.setPenColor("red")
maya.setPos(0, -200)

pepe = Turtle(tf)
pepe.setColor("black")
pepe.setPenColor("black")
pepe.setPos(200, 0)
pepe.left(90)

repeat 7:
    repeat 4:
        maya.forward(50)
        maya.right(90)
        pepe.forward(50)
        pepe.left(90)
    maya.forward(50)    
    pepe.forward(50)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Wenn du mehrere Turtles in das gleiche Fenster setzen willst, musst du ein TurtleFrame erzeugen und dieses als Konstruktorparameter der Turtle angeben. Die Turtles kollidieren nicht miteinander, sondern bewegen sich sozusagen übereinander, wobei die eben sich bewegende Turtle immer über allen anderen zu liegen kommt.

 

 

TURTLEPARAMETER

 

Beide Turtles zeichnen eine gleichartige Leiter. Dafür wird derselbe Code verwendet. Daher ist es eleganter, dazu eine Funktion step() zu definieren, der man mitteilt, welche Turtle die Zeichnungen ausführen soll.  Dazu wird die jeweilige turtle als Parameter an die Funktion step() übergeben.

Als Parameterbezeichner kannst du irgendeinen Namen verwenden, beispielsweise lediglich t, kurz für irgendeine Turtle. Du übergibst dann beim Aufruf das eine Mal maya und das andere Mal pepe.
 
from gturtle import *

def step(t):
    repeat 4:
        t.forward(50)
        t.right(90)
    t.forward(50)
    
tf = TurtleFrame()

maya = Turtle(tf, "sprites/beetle.gif")
maya.setPenColor("green")
maya.setPos(0, -150)
pepe = Turtle(tf, "sprites/cuteturtle.gif")
pepe.setPos(200, 0)
pepe.left(90)

repeat 7:
    step(maya)
    step(pepe)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Du kannst für jede Turtle ein eigenes Bild verwenden, wenn du beim Erzeugen der Turtle den Pfad auf die Bilddatei angibst. Hier verwendest du die beiden Bilddateien beetle.gif und cuteturtle.gif, die sich in der Distribution von TigerJython befinden.

 

 

KÄFERPROBLEME MIT GEKLONTER TURTLE

 

Beim berühmten Käferproblem [mehr... Bei den Verfolgungskurven handelt es sich nach dem Mathematiker
Brocard um logarithmische Spiralen mit dem Polygonzentrum als Pol
] starten n Käfer in den Ecken eines regulären n-Ecks und verfolgen sich gegenseitig mit konstanter Geschwindigkeit. Dabei wird die Lage der Käfer in gleichen Zeitschritten fixiert und jeder Käfer in die Richtung zum Käfer an der nächsten Polygonecke gedreht. Nachher bewegen sich alle Käfer geradlinig um eine immer gleiche Schrittweite vorwärts.

Du kannst dieses Problem sehr elegant lösen, indem du zuerst mit der namenlosen (globalen) Turtle das Polygon zeichnest und an jeder Ecke ein geklontes Turtleobjekt erstellst. Du wählst hier ein Viereck und erstellst mit clone() die Turtleklons t1, t2, t3, t4. Ein Klone ist ein neues Turtle-Objekt mit identischen Eigenschaften.

Nachher stellst du in einer Endlosschleife mit setHeading() ihre Blickrichtung ein und schiebst sie um die Schrittweite 5 vorwärts. Die Zeichnung wird besonders schön, wenn du noch die Verbindungsgeraden zwischen den jeweils sich verfolgenden Turtles einzeichnest.

Am einfachsten definierst du dazu die Funktion drawLine(a, b), mit welcher die Turtle a mit moveTo() eine Spur zur Turtle b zeichnet und wieder zurück springt.

 
from gturtle import *

s = 360

makeTurtle()
setPos(-s/2, -s/2)

def drawLine(a, b):
    ax = a.getX()
    ay = a.getY()
    ah = a.heading()
    a.moveTo(b.getX(), b.getY())
    a.setPos(ax, ay)
    a.heading(ah)
    
# generate Turtle clone
t1 = clone() 
t1.speed(-1)
forward(s)
right(90)
t2 = clone()
t2.speed(-1)
forward(s)
right(90)
t3 = clone()
t3.speed(-1)
forward(s)
right(90)
t4 = clone()
t4.speed(-1)
forward(s)
right(90)
hideTurtle()

while True:
    t1.setHeading(t1.towards(t2))
    t2.setHeading(t2.towards(t3))
    t3.setHeading(t3.towards(t4))
    t4.setHeading(t4.towards(t1))
   
    drawLine(t1, t2)
    drawLine(t2, t3)
    drawLine(t3, t4)
    drawLine(t4, t1)

    t1.forward(5)
    t2.forward(5)
    t3.forward(5)
    t4.forward(5)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Erzeugst du mit clone() aus der globalen Turtel eine neue Turtle, so hat diese die gleiche Position, die gleiche Blickrichtung und die gleiche Farbe (bei Verwendung von benutzerdefinierten Turtlebildern hat sie das gleiche Turtlebild) [mehr... Die Verwendung von Klons entspricht einem modernen Programmierparadigma: der Prototypenbasierten Programmierung] .

Die Funktion drawLine() kann vereinfacht werden, wenn man Position und Blickrichtung der Turtle mit pushState() abspeichert und den Zustand mit popState() wieder zurückholt:

def drawLine(a, b):
    a.pushState()
    a.moveTo(b.getX(), b.getY())
    a.popState()

Die Verfolgungskurve lässt sich mathematisch berechnen (siehe).

 

 

AUFGABEN

 

1.


Drei Turtles sollen abwechlungsweise Zack-um-Zack einen fünfzackigen Stern zeichnen. Die Turtles haben die Farben cyan (Standardfarbe), rot und grün. Die Turtlefarbe kann als zusätzlicher Parameter des Konstruktors angegeben werden.




2.

Eine grüne Mutterturtle bewegt sich mit grüner Stiftfarbe ständig auf einem Kreis. Eine rote Kindturtle ist zuerst weit von der Mutter entfernt und bewegt sich dann mit roter Stiftfarbe in Richtung zur Mutter.

(Das Kind child kann mit direction = child.towards(mother.getX(), mother.getY)) die Richtung zur Mutter bestimmen.)

 

3.
 


Die Turtle laura zeichnet (nicht gefüllte) Quadrate. Nach jedem gezeichneten Quadrat springt eine zweite Turtle hinein und färbt es grün.

Verwende für die beiden Turtles verschiedene Turtlebilder. Im TigerJython stehen die Bilder beetle.gif, beetle1.gif, beetle2.gif und spider.png zur Verfügung. Du kannst aber euch eigene Bilder verwenden. Du musst diese im Unterverzeichnis sprites des Verzeichnisses, in dem sich dein Programm befindet oder im Unterverzeichnis /bin/sprites des Installationsordners speichern.


4.

Erstelle ähnlich wie im Beispiel "Käferprobleme" eine Verfolgungsgrafik für 6 Turtles, die in den Ecken eines regelmässigen 6-Eck die Verfolgung starten.


 

 

 

ZUSATZSTOFF


 

TURTLES MIT MAUSKLICK ERZEUGEN

 

Dein Programm erzeugt bei jedem Mausklick an der Stelle des Mauscursors eine neue Turtle, die unabhängig von den bereits vorhandenen Turtles einen Stern zeichnet. Dabei erlebst du die volle Tragweite und die Eleganz der Objektorientierten Programmierung sowie der Ereignissteuerung.

Um den Mausklick zu erfassen, definierst du eine Funktion drawStar(). Damit diese beim Drücken der linken Maustaste vom System aufgerufen wird, verwendest du im Konstruktor von TurtleFrame den benannten Parameter mouseHit und übergibst ihm den Namen dieser Funktion.

 
from gturtle import *

def drawStar(x, y):
    t = Turtle(tf)
    t.setPos(x, y)
    t.fillToPoint(x, y)
    for i in range(6):
        t.forward(40)
        t.right(140)
        t.forward(40)
        t.left(80)

tf = TurtleFrame(mouseHit = drawStar)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

In der OOP werden Objekte mit gleichen Fähigkeiten und gleichen Eigenschaften in Klassen zusammengefasst. Mit dem Konstruktor erzeugt man einzelne Objekte (Instanzen).

Um einen Mausklick zu verarbeiten, schreibst du eine Funktion mit beliebigem Namen (aber zwei Parametern x und y) und übergibt diesen Funktionsnamen im Konstruktor von TurtleFrame dem benannten Parameter mouseHit.

x und y liefern die Koordinaten des Mausklicks.