Wie haben uns schon stapelorientierte Programmierung angeschaut. Vergegenwärtige dir, dass Programme stapelorientiert sein können, wobei diese gestartet werden, etwas machen und dann stoppen, oder ereignisgesteuert sind, wobei sie starten, auf Ereignisse warten und nur dann stoppen, wenn man es ihnen sagt - also durch ein Ereignis. Wie erzeugen wir ein ereignisgesteuertes Programm? Wir werden das auf zwei Arten betrachten - zuerst werden wir eine Ereignisumgebung simulieren, dann werden wir ein sehr einfaches GUI-Programm erzeugen, das Betriebsystem und - Umgebung verwendet, um Ereignisse zu erzeugen.
Jedes ereignisgesteuerte Programm hat irgendwo eine Schleife, die eintretende Ereignisse abfängt und sie behandelt. Diese Ereignisse können durch das Betriebssystem, wie es scheinbar in allen GUI-Pprogrammen geschieht, erzeugt werden oder das Programm sucht selbst nach Ereignissen, wie es oft der Fall in eingebetteten Kontrollsystemen ist, so wie es in Kameras verwendet wird usw.
Wir werden ein Programm erzeugen, das genau nur einen Ereignistyp beobachtet - Tastatureingabe - und das Ergebnis so lange bearbeitet, bis ein Ereignis zum Aufhören empfangen wird. In unserem Fall ist das Abbruchereignis die Leertaste. Wir werden die ankommenden Ereignisse auf eine einfache Art und Weise bearbeiten - wir werden einfach nur den ASCII-Code für die jeweilige Taste ausgeben. Dazu werden wir BASIC verwenden, weil es dafür eine schöne, leicht zu verwendende Funktion zum Lesen von einmal gedrückten Tasten besitzt - INKEY$.
Zuerst implementieren wir den Rumpf des Hauptprogrammes, das einfach die Schleife zur Ereignisaufnahme startet und die Subroutinen zur Ereignisbehandlung aufruft, wenn ein gültiges Ereignis erkannt wird.
' Deklarieren der Subroutinen, die die Ereignisse behandeln DECLARE SUB tastenereignis (tastendruck AS STRING) DECLARE SUB abbruchereignis (tastendruck AS STRING) ' Reinige zuerst den Bildschirm von allem Mist und sage dem Anwender, ' wie er das programm abbrechen kann CLS PRINT "Drücke Leertaste zum beenden..." PRINT ' Nun eine Endlosschleife WHILE 1 ky$ = INKEY$ laenge = LEN(ky$) IF laenge <> 0 THEN ' sende ereignisse zu den Ereignisbehandlungsfunktionen IF ky$ <> " " THEN CALL tastenereignis(ky$) ELSE CALL abbruchereignis(ky$) END IF END IF WEND
Beachte, was wir mit den Ereignissen tun, die uninteressant für den Hauptteil sind; die ereignisse werden einfach gesammelt und an die ereignisbehandler übergeben. Diese Unabhängigkeit zwischen Ereignisabfang und ereignisbehandlung ist die Schlüsseleigenschaft der ereignisgesteuerten programmierung.
Jetzt können wir die 2 Ereignisbehandler implementieren. Der erste, tastenereignis, druckt einfach den ASCII-Wert der gedrückten Taste aus:
SUB tastenereignis (tastendruck AS STRING) ' drucke gültige Tastenanschläge laenge = LEN(tastendruck) IF laenge = 1 THEN 'es ist einfach ASCII PRINT ASC(tastendruck) ELSE IF laenge = 2 THEN 'es ist nicht alphanumerisch, verwende also das 2te Zeichen PRINT ASC(MID$(tastendruck, 2, 1)) END IF END IF END SUB
Das abbruchereignis ist trivial - es STOPt lediglich das Programm!
SUB abbruchereignis (tastendruck AS STRING) STOP END SUB
Wenn dies als Grundgerüst für den Gebrauch in mehreren Projekten erzeugen wollten, müssten wir möglicherweise noch den Aufruf einer Intitialisierungsfunktion am Anfang und eine Bereinigungsfunktion am Ende einfügen. Der Programmierer könnte dann den Schleifenteil verwenden, um seine eigenen Initialisierung-, Prozess- und Bereinigungsfunktionen damit auszustatten.
Das ist genau das, was GUI-Typ-Umgebungen tun, in denen der Schleifenteil in der Operationsumgbung oder ein Grundgerüst eingebettet ist und die Applikationen sind verbindlich verpflichtet die Ereinisbehandlungsfunktionen zu unterstützen und diese irgendwie in die Ereignisschleife einzuhaken.
Laß uns das in Aktion sehen, indem wir Python's Tkinter GUI-Bibliothek untersuchen.
Zu dieser Übung werden wir den Python Tkinter-Werkzeugsatz verwenden. Dies ist eine Python - Hülle um das Tk-Toolkit, das ursprünglich als eine Erweiterung zu Tcl geschrieben wurde und auch für Perl erhältlich ist. Die Python-Version ist ein objekt-orientiertes Rahmenwerk, welches, meiner Meinung nach, erheblich einfacher zu bedienen ist, als die originale prozedurale Tk - Version. Ich werde mich nicht lange mit den GUI-Aspekten dieses Werkzeugs aufhalten, vielmehr möchte ich den Blick auf den Stil des Programmierens richten - das Verwenden von Tkinter, um die Ereignisschleife zu handhaben, den Programmierer das dazu erforderliche GUI erzeugen lassen und dann die Ereignisse bearbeiten, wie sie ankommen.
In dem Beispiel erzeugen wir eine Anwendungsklasse TastenAnwend, welche das GUI in der __init__ Methode erzeugt und die Leertaste an die abbruchereignis - Methode bindet. Die Klasse definiert auch die erforderliche abbruchEreignis - Methode.
Das GUI selbst besteht aus einem Texteingabe-Widget, dessen voreingestelltes Verhalten es ist, alle Tastetatureingaben auf der Anzeige zu wiederholen.
Das Erzeugen einer Anwendungsklasse ist ganz üblich in OO ereignisgesteuerten Umgebungen, weil es viele Übereinstimmungen zwischen den Konzepten besteht, ob ein Ereignis an ein Programm oder eine Nachricht an ein Objekt gesendet wird. Die beiden Konzepte decken sich ziemlich gut. Eine Ereignisbehandlungsfunktion wird zu einer Methode der Anwendungsklasse.
Haben wir die Klasse definiert, erzeugen wir einfach eine Instanz von ihr und senden sie an die mainloop - Mitteilung.
Der Code sieht so aus:
# Verwende from X import * , um alle Eventualitäten abzusichern # von tkinter.xxx from Tkinter import * # erzeuge die anwendungsklasse, die das GUI definiert # und die Ereignisbehandlungsmethode class TastenAnwend(Frame): def __init__(self): Frame.__init__(self) self.txtBox = Text(self) self.txtBox.bind("", self.abbruchEreignis) self.txtBox.pack() self.pack() def abbruchEreignis(self,event): import sys sys.exit() # erzeuge jetzt eine Instanz und starte den Ablauf der Ereignisschleife meineAnwend = TastenAnwend() meineAnwend.mainloop()
In der BASIC - Version haben wir den ASCII-Code aller Tasten ausgegeben, wohingegen wir hier nur die alphanumerischen Versionen der druckbaren Tasten ausgeben. Es gibt nichts, was uns daran hindert, alle Tastendrucke einzufangen und dann das selbe zu tun. Dazu werden wir die folgende Zeile zur __init__- Methode hinzufügen:
self.txtBox.bind("< Key >", self.tastenEreignis)
und die folgende Methode, um das Ereignis zu bearbeiten:
def tastenereignis(self,event): str = "%d\n" % event.keycode self.txtBox.insert(END, str) return "break"
Anmerkung 1: Der Tastenwert ist gespeichert im keycode - Feld des Ereignisses. Ich musste im Quellcode von Tkinter.py nachschauen, um dies herauszufinden... Erinnerst du, dass dies ein Schlüsselattribut eines Programmierers ist?
Note 2: return "break" ist ein magisches Signal, um Tkinter zu sagen, dass es nicht den standardmäßig festgelegten Ereignisprozess für dieses Widget aufrufen soll. Ohne diese Zeile würde die Textbox den ASCII-Code gefolgt von der eben gedrückten Taste anzeigen, was wir hier nicht möchten.
Das sei zunächst genug. Dies ist nicht als ein Lehrgang über Tkinter gedacht ; sieh dazu auf der Referenzseite nach einem link zu einer guten Online-Quelle. Es gibt auch verschiedene Bücher, die TK und Tkinter verwenden. Ich werde dennoch zu Tkinter zurückkehren, und zwar in der Fallstudie, wo ich einen Weg darstellen werde, wie man ein batchartiges Programm in ein GUI eingekapselt wird, um seine Brauchbarkeit zu erhöhen.
(Anmerkung des Übersetzers: während der Arbeit an der übersetzung dieses Textes, hat Alan ein eigenes kapitel über GUI-Programmierung mit Tkinter dazugefügt !)
Falls du Fragen oder Hinweise hast, wende dich bitte per E-Mail: auf Enlisch an alan.gauld@yahoo.co.uk oder auf Deutsch an bup.schaefer@freenet.de