EINFÜHRUNG |
Bisher verstehst du den Computer so, dass er Anweisung um Anweisung ausführt. Er kann auch auf Grund von Bedingungen den Ablauf verändern und Wiederholschleifen durchlaufen. Die entsprechenden Programmstrukturen heissen Sequenz, Selektion und Iteration. Bereits 1966 habe Böhm und Jacopini in einem berühmten Artikel bewiesen, dass sich alle Rechenverfahren (Algorithmen) mit diesen drei Strukturen programmieren lassen. Dies gilt allerdings nur, solange man keine äusseren Einflüsse mit einbezieht. Beispielsweise kann man ein Programm auch abbrechen, indem man in irgend einem Moment mit der Maus auf die Schaltfläche "Schliessen" (close-Button) klickt. Solche Vorgänge brauchen ein neues Programmierkonzept: die Ereignissteuerung (event handling). Das Grundprinzip hast du bereits im Kapitel Turtlegrafik/Ereignissteuerung kennengelernt. Es handelt sich dabei um Abläufe der Art: "Wann immer das Ereignis E auftritt, führe die Funktion f aus". Die Implementierung ist einfach und seit den Anfängen der Computertechnik in den Fünfzigerjahren des letzten Jahrhunderts bekannt. Man definiert eine Funktion f (damals Interruptroutine genannt), welche vom eigenen Programm gar nie aufgerufen wird. Sie schläft sozusagen, bis ein bestimmtes Ereignis E auftritt und sie durch diesen äusseren Einfluss vom System automatisch aufgerufen wird. Heute nennt man eine solche Funktion Callback und sagt anschaulich, dass der Callback f durch den Event E "gefeuert" wird. Oft werden Callbacks mit Parameterwerten aufgerufen, die wichtige Informationen über den Event enthalten, beispielsweise, welcher Mausknopf gedrückt wurde oder wo sich die Maus befindet. |
AUF EINEN MAUSEVENT REAGIEREN |
Wie in der Turtlegrafik kannst du auch im GPanel Mausevents verwenden. Im ersten Beispiel wird beim Drücken der linken oder rechten Maustaste an der aktuellen Mausposition ein grüner Kreis gezeichnet. Du gehst wie folgt vor:
Um deinen Callback zu registrieren, verwendest du einen benannten Parameter von makeGPanel(), der mousePressed heisst. from gpanel import * def onMousePressed(x, y): move(x, y) fillCircle(0.02) makeGPanel(mousePressed = onMousePressed) setColor("green") |
MEMO |
Ein Callback wird nicht durch das eigene Programm aufgerufen, sondern automatisch, wenn der Event ausgelöst wird. Die Registrierung des Callbacks erfolgt durch einen benannten Parameter. Das Drücken einer Maustaste kannst du mit zwei verschiedenen Callbacks erfassen: einem Click-Event oder einem Press-Event. Der Click-Event wird erst ausgelöst, nachdem die Taste wieder losgelassen wird, der Press-Event bereits beim Drücken der Taste. |
MAUSBEWEGUNG ERFASSEN |
from gpanel import * def onMouseMoved(x, y): move(x, y) setColor("red") fillCircle(.04) setColor("black") circle(.04) makeGPanel(mouseMoved = onMouseMoved) |
MEMO |
Der Callback onMouseMoved(x, y) wird durch den benannten Parameter mouseMoved registriert. |
FREIHANDZEICHNEN MIT GEDRÜCKTER MAUSTASTE |
from gpanel import * def onMousePressed(x, y): move(x, y) def onMouseDragged(x, y): draw(x, y) makeGPanel(mousePressed = onMousePressed, mouseDragged = onMouseDragged) |
MEMO |
Man kann mehrere Callbacks mit benannten Parameter gleichzeitig registrieren. Die Reihenfolge der Parameter ist unwesentlich. |
LINKE UND RECHTE MAUSTASTE |
from gpanel import * def onMousePressed(x, y): if isLeftMouseButton(): pixColor = getPixelColor(x, y) if pixColor == makeColor("white"): return clear() setColor(pixColor) move(5, 5) fillCircle(2) if isRightMouseButton(): for i in range(5): move(9, 2 * i + 1) if i == 0: setColor("deep pink") if i == 1: setColor("green") if i == 2: setColor("yellow") if i == 3: setColor("deep sky blue") if i == 4: setColor("dark violet") fillRectangle(2, 2) makeGPanel(0, 10, 0, 10, mousePressed = onMousePressed) move(5, 5) fillCircle(2) |
MEMO |
Die registrierten Maus-Callbacks werden mit der linken und rechten Maustaste ausgelöst. Du kannst mit isLeftMouseButton() bzw. isRightMouseButton() herausfinden, welche Taste es war. |
GUMMIBANDLINIEN |
Um dieses Problem zu lösen, muss man im Press-Callback die bereits vorhandene Zeichnung abspeichern (man sagt auch "retten"). Das Löschen der temporären Gummibandlinie geschieht jetzt so, dass man diese "alte" Zeichnung wiederherstellt. Du kannst die Zeichnung mit storeGraphics() abspeichern und mit recallGraphics() wiederherstellen. from gpanel import * def onMousePressed(x, y): global x1, y1, x2, y2 storeGraphics() x1 = x y1 = y x2 = x1 y2 = y1 setColor("red") def onMouseDragged(x, y): global x2, y2 recallGraphics() x2 = x y2 = y line(x1, y1, x2, y2) def onMouseReleased(x, y): setColor("white") if not (x1 == x2 and y1 == y2): line(x1, y1, x2, y2) else: recalGraphics() x1 = 0 y1 = 0 x2 = 0 y2 = 0 makeGPanel(mousePressed = onMousePressed, mouseDragged = onMouseDragged, mouseReleased = onMouseReleased) title("Press And Drag To Draw Lines") bgColor("blue") setColor("white") lineWidth(2) |
MEMO |
Merke dir das Prinzip zum Zeichnen von Gummibandlinien: Beim Press-Event werden die Endpunkte der Linie initialisiert und die Grafik gespeichert. Beim Drag-Event wird die gespeicherte Grafik wiederhergestellt, die temporäre Linie mit dem neuen Endpunkt gezeichnet und der neue Endpunkt gespeichert. Beim Release-Event wird die Linie definitiv gezeichnet, allerdings nur, falls die Maus überhaupt bewegt wurde. |
AUFGABEN |
|
ZUSATZSTOFF |
CALLBACKS REGISTRIEREN MIT DECORATORS |
Statt benannte Parameter von makeGPanel() zu verwenden, um einen Callback zu registrieren, kann eine beliebig genannte Funktion mit zwei Parameter x und y durch eine vorangestellte Zeile, die mit einem At-Symbol @ eingeleitet wird, so "dekoriert" werden, dass sie von TigerJython automatisch als Callback registriert und beim Eintreten des entsprechenden Events aufgerufen wird. Als Decorators stehen zur Verfügung:
Das oben gezeigte Programm, welches beim Drücken einer Maustaste eine Kreis zeichnet, kannst du unter Verwendung eines Decorators auch so schreiben: from gpanel import * @onMousePressed def doIt(x, y): move(x, y) fillCircle(0.02) makeGPanel() setColor("green") |