Event-Loop: Unterschied zwischen den Versionen
Georg (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Georg (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
Zeile 4: | Zeile 4: | ||
Während bei "reinem" Clipper die einzige Interaktion mit der Tastatur war (es gab Zusatzbibliotheken wie die Frankie.LIB, mit der auch Mausfunktionen verfügbar wurden), gibt es unter Windows jede Menge Events (Ereignisse), die an ein Programm übermittelt werden, und auf die das Programm reagieren kann oder muss. | Während bei "reinem" Clipper die einzige Interaktion mit der Tastatur war (es gab Zusatzbibliotheken wie die Frankie.LIB, mit der auch Mausfunktionen verfügbar wurden), gibt es unter Windows jede Menge Events (Ereignisse), die an ein Programm übermittelt werden, und auf die das Programm reagieren kann oder muss. | ||
Ein Ereignis kann ein Tastatur-Anschlag oder ein Mausklick sein, | Ein Ereignis kann ein Tastatur-Anschlag oder ein Mausklick sein, oder der Moment, wo der Mauszeiger einen bestimmten Bereich betritt, oder das Vergrössern eines Fensters. | ||
Jedem dieser Ereignisse sind eigene Codes zugeordnet, und Objekte, auf die sich die Ereignisse beziehen. | Jedem dieser Ereignisse sind eigene Codes zugeordnet, und Objekte, auf die sich die Ereignisse beziehen. |
Version vom 16. Oktober 2013, 11:45 Uhr
Ereignis vs. Tastatur
Während bei "reinem" Clipper die einzige Interaktion mit der Tastatur war (es gab Zusatzbibliotheken wie die Frankie.LIB, mit der auch Mausfunktionen verfügbar wurden), gibt es unter Windows jede Menge Events (Ereignisse), die an ein Programm übermittelt werden, und auf die das Programm reagieren kann oder muss.
Ein Ereignis kann ein Tastatur-Anschlag oder ein Mausklick sein, oder der Moment, wo der Mauszeiger einen bestimmten Bereich betritt, oder das Vergrössern eines Fensters.
Jedem dieser Ereignisse sind eigene Codes zugeordnet, und Objekte, auf die sich die Ereignisse beziehen.
Ähnlich dem InKey() unter Clipper "erfragt" eine Xbase-Anwendung ein Ereignis vom System. Der Einfachheit halber gehen wir davon aus, dass wir es hier erst einmal nur mit einem Thread zu tun haben.
Der Event-Loop
LOCAL nEvent, mp1, mp2, oXbp nEvent := xbe_None WHILE nEvent <> xbeP_Close nEvent := AppEvent(@mp1, @mp2, @oXbp) oXbp:handleEvent(nEvent, mp1, mp2) END
Der einfachste Event-Loop benötigt vier Variablen. Eine Variable für die Entgegennahme des aktuellen Event, zwei Variablen für zusätzliche Informationen (Message Parameters), sowie eine Variable für das Objekt, in dessen Kontext das Event fällt.
Damit die Funktion AppEvent() mehr als eine Information zurückgeben kann, müssen die Parameter by Reference übergegeben werden.
Jedes Xbase-Part hat eine Methode :handleEvent(), die in der Lage ist, mit den übergebenen Parametern auf das Event zu reagieren. Das betroffene Objekt muss nicht als Parameter an :handleEvent() übergeben werden, da das betroffene Objekt aus dem Kontext von :handleEvent() über die Instanzvariable self ansprechbar ist.
zeitgesteuerter Event-Loop
LOCAL nEvent, mp1, mp2, oXbp, nWait nEvent := xbe_None nWait := 1000 WHILE nEvent <> xbeP_Close nEvent := AppEvent(@mp1, @mp2, @oXbp, nWait) IF nEvent = xbe_None // ELSE oXbp:handleEvent(nEvent, mp1, mp2) ENDIF END
AppEvent() akzeptiert einen vierten Parameter. Dieser Parameter gibt an, nach wievielen Millisekunden ohne Event das Warten auf ein Event beendet werden soll. Nach Ablauf der Zeitspanne ohne jedes Event wird xbe_None generiert und - in unserem Beispiel - abgefragt mit der Option, hier bestimmte Aktionen auszuführen. Andernfalls wird das Event wie bisher verarbeitet.
Bei Einsatz dieses Parameters muss immer berücksichtigt werden, dass selbst die Mausbewegung über einem Fenster des Programms ein Event auslöst und (nach Abarbeiten von :handleEvent()) die Wartezeit neu beginnt.
Erweiterungen des Event-Loop
Angenommen, wir haben den Event-Loop eines Browse. Mit Drücken der Taste F5 soll der Browse aktualisiert werden, und mit Drücken der Enter-Taste soll ein Satz zur Bearbeitung angezeigt werden:
LOCAL nEvent, mp1, mp2, oXbp nEvent := xbe_None WHILE nEvent <> xbeP_Close nEvent := AppEvent(@mp1, @mp2, @oXbp) DO CASE CASE nEvent = xbeP_Keyboard DO CASE CASE mp1 = xbeK_ENTER EditBrowseRecord(oXbp) CASE mp1 = xbeK_F5 BrowseRefresh(oXbp) OTHERWISE oXbp:handleEvent(nEvent, mp1, mp2) ENDCASE OTHERWISE oXbp:handleEvent(nEvent, mp1, mp2) ENDCASE END
Nachdem ein Event empfangen wurde, wird geprüft, ob es sich um ein Tastatur-Event handelt. Wenn nicht (und das dürfte die Mehrzahl der Fälle betreffen), wird in den OTHERWISE-Zweig verzweigt und das Event nach Vorgabe verarbeitet.
Wichtig ist auch, dass oXbp:handleEvent() zweimal vorkommt: einmal im OTHERWISE-Zweig des äusseren DO CASE (immer dann, wenn kein Tastatur-Event aufgetreten ist), und einmal im OTHERWISE-Zweig des inneren DO CASE: dort behandeln wir zwei Spezialfälle, F5 und ENTER. Alle anderen Tastatur-Eingaben würden "übersehen" ohne das oXbp:handleEvent() im OTHERWISE-Zweig.
Hierzu ein Ablaufbeispiel: Wir unterstellen, dass ein "a" eingetippt wird. Die Funktion AppEvent() liefert folgende Werte zurück:
nEvent = xbeP_Keyboard mp1 = "a" mp2 = NIL oXbp = betroffenes Objekt CASE nEvent = xbeP_Keyboard => diese Abfrage ist wahr, die Anweisungen in diesem CASE-Zweig werden ausgeführt: DO CASE => es wird eine neue CASE-Gruppe ausgeführt CASE mp1 = xbeK_ENTER => mp1 hat den Wert "a", Bedingung ist nicht erfüllt CASE mp1 = xbeK_F5 => mp1 hat den Wert "a", Bedingung ist nicht erfüllt OTHERWISE => da keine der vorhergehenden Bedingungen zutrifft, wird der Standard-Eventhandler ausgeführt und somit landet im Normalfall das Zeichen "a" z.B. in einem XbpSLE() ! würde der OTHERWISE-Zweig fehlen, würde die Eingabe des "a" nicht verarbeitet und ginge somit "verloren".
Da der Event-Loop das "Herz" eines Thread darstellt, muss darauf geachtet werden, dass hier ein schneller Ablauf möglich ist. Dazu gehört auch, dass die Abfragen entsprechend aufgebaut werden, dass ihre Abarbeitung schnell vorgenommen werden kann.
Man könnte den obigen Ablauf auch so formulieren:
DO CASE CASE nEvent = xbeP_Keyboard .AND. mp1 = xbeK_ENTER EditBrowseRecord(oXbp) CASE nEvent = xbeP_Keyboard .AND. mp1 = xbeK_F5 BrowseRefresh(oXbp) OTHERWISE oXbp:handleEvent(nEvent, mp1, mp2) ENDCASE
Es sollte klar sein, dass die zweite Variante zeitaufwändiger ist, denn in der Mehrzahl aller Fälle müssen zwei Abfragen ausgeführt werden, ehe ein Event, das nicht F5 oder ENTER ist, bearbeitet werden kann.
Granularität der Events
An dem vorhergehenden Beispiel erkennen wir, dass Events auch in Gruppen gemeldet werden (xbeP_Keyboard): nicht jede Taste hat ein eigenes Event, sondern die Taste wird als Message Parameter 1 zurückgemeldet. Dies gilt für eine Vielzahl von Events, so dass gegebenenfalls die Dokumentation befragt werden muss, wenn der Event-Loop erweitert werden soll.
Threads
Theoretisch kann man mehr als einen Event-Loop in einem Thread haben, aber es wird dazu führen, dass möglicherweise (immer) der falsche Event-Loop ausgeführt wird.
Getrennte, parallele Event-Loops sind nur dann möglich, wenn auch getrennte Threads vorliegen, da jeder Thread eine eigene Event-Queue hat.