15 Signal wird ausgelöst

Ziel ist es, dass ein Fußgänger die Ampelschaltung auslösen kann, wenn er die Straße überqueren will. Dazu muss zunächst das Auslösen des Signals erkannt werden. Anschließend müssen Signalgeber und Ampel so verbunden werden, dass die Ampel mit dem Auslösen des Signals schaltet.

Die Frage ist: Wann liegt ein Punkt(x,y) innerhalb einer Figur, von dem man die linke untere Eckposition sowie Weite und Höhe kennt?

[UML-Diagramm der Ampel mit Signal]

Aufgabe 5:

Führe das Testprogramm aus, klicke in das Ausgabefenster und kontrolliere die Ausgabe. Überlege anschließend, welche Bedingungen für x und y wahr sind, wenn (x,y) im grauen Rechteck liegt.

Herausgefunden? Wir brauchen die Bedingungen, um sagen zu können, ob der Schalter des Signalgebers angeklickt wurde.

14 Signalgeber

Die Ampel soll auf Wunsch aktiviert werden, wenn Fußgänger die Straße überqueren wollen. Dazu braucht sie einen Signalgeber, also ein Kästchen, an dem man einen Schalter betätigt, indem man einen Knopf drückt. Der Einfachheit halber nehmen wir die Klasse LICHT, um den Knopf zu bauen. das hat den Vorteil, dass man später die Farbe ändern kann, wenn er angeklickt wird.

Aufgabe 4:

  1. Ergänze die Methode set_schalter() so, dass der Schalter in der Mitte des Signalgebers gezeichnet wird.
  2. Implementiere die Methode anzeige().

Damit sind nun die Ampel und ein Signalgeber fertig. Im nächsten Schritt sollen die beiden Objekte miteinander verbunden werden.

13 Ampelschaltung

Ampeln für Autofahrer schalten ganz typisch. Vergleiche dazu die untenstehende Abbildung der vierphasigen Schaltung.

Abb. 1, File source: https://commons.wikimedia.org/wiki/File:Traffic_lights_4_states.svg

Aufgabe 3.1:

Verändere das Testprogramm so, dass das rote Licht der Ampel angeht, eine Sekunde leuchtet und danach wieder ausgeht.

Aufgabe 3.2:

Implementiere eine Methode steuerung(str) der Klasse AMPEL, so dass sie analog zu den vier Phasen und abhängig von den Werten ’stop‘ (1), ‚bereit‘ (2), ‚los‘ (3) und ‚achtung‘ (4) die richtigen Lichter anschaltet und anzeigt. Vergleiche dazu Abbildung 1 auf dieser Seite.

>> Code: prg_132_ampelsteuerung.py

12 Die Ampel

Je nach Einsatz können Ampeln recht unterschiedlich aussehen. Die häufigste Erscheinung hat drei runde Lichter in rot, gelb und grün.

File source: http://commons.wikimedia.org/wiki/File:Ampel-Goettingen-25a.jpg

Die Grundform der Ampel ist ein Rechteck. Um sie zeichnen zu können, braucht man die Werte der Attribute Position, Weite, Höhe und Farbe.

Zusätzlich besteht die Ampel aus den drei Lichtern.

Die Grundform der Lichter ist ein Kreis. Um sie zeichnen zu können, braucht man jeweils Positionswerte, den Radius und eine Farbe. Die Farben stehen fest, Radius und Positionswerte ergeben sich in Abhängigkeit von Größe und Position der Ampel.

[UML-Diagramm der Ampel]

Näherungsweise Bestimmung der Größen der Lichter in Abhängigkeit von Position (x,y), Weite w und Höhe h der Ampel

Zu den Positionswerten:

Die Positionswerte werden hier als Tupel behandelt, die aus zwei Teilen bestehen. Vergleiche auch die Abbildung „Tupel“.

pos = (100, 10)   # Ein Tupel wird dem Namen pos zugewiesen
print(pos[0])     # -> 100
print(pos[1])     # -> 10

Aufgabe:

  1. Ergänze die Bestimmung der Größen der Lichter in Abhängigkeit von den Werten der Ampel, so dass die Ampel mit den Lichtern gezeichnet werden kann.
  2. Vervollständige die Methode anzeige() der Klasse AMPEL.

11 Ein Licht

Verkehrsampeln bestehen aus roten, gelben und grünen Lichtern, mit denen sie anzeigen, wie sich die Verkehrsteilnehmer verhalten sollen: „Bei Rot sollst Du steh’n, bei Grün darfst Du geh’n.“

Grundlage für den Aufbau eines Ampelsystems ist daher eine Klasse Licht. Ohne mindestens ein Licht funktioniert eine Ampel nicht. Daher ist das Prinzip der Zusammensetzung hier die Komposition.

Aufgabe:

Füge an den Stellen 1 – 5 jeweils den entsprechenden Methodenaufruf der Klasse LICHT ein und bringe das Testprogramm zum Laufen. Im Ergebnis soll es aussehen, als würde das rote Licht angehen und danach wieder ausgehen.

10 Zusammengesetzte Objekte

Objekte kann man zusammensetzen wie die Steine in einem Baukastensystem. Dabei gibt es zwei Möglichkeiten der Zusammensetzung:

  • Komposition bedeutet, dass Objekt A nicht ohne Objekt B funktioniert. Zum Beispiel hat jedes Fahrrad auch Räder.
  • Aggregation bedeutet, dass Objekt A auch ohne Objekt B funktioniert, aber Objekt B enthalten kann. Zum Beispiel gibt es Karteikästen auch ohne Karteikarten.

Diese Sichtweise kann mit graphischen Objekten trainieren. Als Gegenstand der Betrachtung wird hier eine Ampel genommen.

07 Viele bewegte Kreise

Bewegung am Bildschirm ist immer eine Frage der zugrunde liegenden Graphikbibliothek. Die Turtle erlaubt es aber in gewissem Rahmen Animationen mit mehreren Objekten ablaufen zu lassen. Dazu soll die neue Klasse ANIMKREIS verwendet werden.

Die Vorlage zum Programmieren findest Du hier: prg07_vielbewegung.

Bewegung erfolgt am Bildschirm von einer Position (x1;y1) zu einer Position (x2;y2). Ein kleinster Schritt wäre eine Verschiebung um dx = 1 und dy = 1, also z.B. von Position (2;3) zu Position (3;4). Das wäre eine Bewegung im 45°-Winkel um etwa ein Pixel nach rechts oben. Ein größerer Schritt wäre zum Beispiel eine Verschiebung um dx = 5 und dy = 5, also z.B. von Position (2;3) zu Position (7;8). Dem entspräche eine Bewegung im 45°-Winkel um etwa sieben Pixel (Pythagoras) nach rechts oben.

Aufgabe 1

Ein zusätzliche Attribut speed der Klasse ANIMKREIS soll Werte zwischen (-12,-12) und (12,12) annehmen können. Dazu eignet sich ein so genannter „Setter“, eine Methode, die geeignete Werte als Parameter entgegen nimmt und ein bestimmtes Attribut der Klasse mit diesen Werten belegt.

def set_speed(self, ...):
   self.speed = ...

Probiere es aus.

Exkurs: Hilfreiche Zufallszahlen

Hin und wieder sind Zufallszahlen sehr praktisch, zum Beispiel wenn Geschwindigkeitsvektoren zufällig gesetzt werden sollen. Dazu importiert man das Modul random und kann anschließend verschiedene Funktionen des Moduls aufrufen.

import random

a = random.randint(0,12)   # eine Ganzzahl zwischen 0 und 12
print('a: '+str(a))
b = random.random()        # eine Kommazahl zwischen 0 und 1 
print('b: '+str(b))
c = random.randint(-12,12) # eine Ganzzahl zwischen -12 und 12
print('c: '+str(c)) 

Aufgabe 2

Die Bewegung des Kreises soll nicht bei jedem Aufruf von außen bestimmt werden, sondern in Abhängigkeit von dem Attribut speed mit seinen Komponenten dx und dy erfolgen:

def bewege(self):
   self.loesche()
   dx, dy = self.speed
   self.update(dx, dy)
   self.zeichne()

Aufgabe 3

Im Hauptprogramms sollen 5 Kreise an der Position (0;0) erzeugt werden, die alle jeweils andere, zufällige Geschwindigkeiten haben. Eine Schleife soll diese Kreise anschließend 100 mal jeweils um einen Schritt weiterbewegen.

Nächstes Kapitel: Ende der ersten Einheit

06 Etwas Bewegung

Die Stärke der Objektorientierung liegt zweitens in der Möglichkeit durch „Vererbung“ existierende Klassen wiederzuverwenden und dabei nach Bedarf anzupassen.

Beispiel: Aus KREIS wird ANIMKREIS.

Ziel:  Objekte der Klasse Kreis sollen von Position 1 nach Position 2 bewegt werden können.

Frage 1: Wie bewegt man eine Figur im Bild?

Antwort: Man kann eine Figur zeichnen, sie dann an der aktuellen Stelle löschen, und dann den Kreis mit geänderten Positionswerte neu zeichnen. 

Frage 2: Wie löscht man eine Figur?    
Antwort: In dem man die Figur mit der Farbe des Hintergrunds – z.B. ‚white‘ – an derselben Stelle nochmals zeichnet.

Frage 3: Wie zeichnet man die Figur mit geänderten Positionswerten?
Antwort: Erst die Positionswerte ändern, dann zeichnen.

Frage 4: Wie ändert man die Positonswerte?

Antwort:

x,y = self.position # Immer rechts vom = nach links lesen!
x = x + dx
y = y + dy
self.position = (x, y)
Die beiden Klassen KREIS und ANIMKREIS im Diagramm (UML-Klassendiagramm).

Aufgabe:

Implementiere die Methoden loesche() und bewege() in der vorbereiteten Klasse ANIMKREIS: prg06_bewegung.

Nächstes Kapitel: 07 Viele bewegte Kreise