Growbook, Cannabis, Notizbuch, Growbuch, Pflanzenzucht, Samenbank, Seedbank, Software, Stammbaum, Kreuzen, züchten,growen,anbauen,News,GIB,Lux,Omega,Mars, Dünger,BioBizz,Hesi,Athena,
die Software für deinen Anbau

Willkommen

Tutorial 15: EventHooks

The MaxGUI Beginner Tutorial Series - Tutorial 15:  EventHooks
(c) Assari Jan 13 2005
Ins Deutsche übersetzt von simi

Ich dachte eigentlich nicht, dass ich dieses Thema schon jetzt behandeln würde. Denn EventHooks ist kein Beginnerthema. Aber es gab ein paar Anfragen für so ein Tutorial, und so kam dieses Tutorial zu Stande. Los gehts....

Das BlitzMax-Eventsystem benutzt WaitEvent(), um auf einen Event zu warten. Aber WaitEvent() hat die Einschränkung, dass du nur dann einen Event verarbeiten kannst, wenn der Benutzer zum Beispiel die Maustaste losgelassen hat.

Lass uns ein einfaches Beispiel ansehen, um dies zu verdeutlichen:

    SuperStrict
Import MaxGui.Driver
Local x:Int=(GadgetWidth(Desktop())-400)/2
Local y:Int=(GadgetHeight(Desktop())-400)/2

Local MyWindow:TGadget=CreateWindow("EventHook-Beispiel", x,y,400,400)
Global MyCanvas:TGadget=CreateCanvas(0,0,380,360,MyWindow)

Local SmallWindow:TGadget=CreateWindow("Beweg Mich!", x+125, y+150, 150, 100, MyWindow, WINDOW_TITLEBAR)

Repeat
  WaitEvent()
  Select EventID()
  Case EVENT_WINDOWCLOSE
     End
  Case EVENT_GADGETPAINT  
    UpDateCanvas()
   End Select
Forever
End

Function UpdateCanvas:Int()
     SetGraphics CanvasGraphics(MyCanvas)
     Cls
     SetColor Rnd(255),Rnd(255),Rnd(255)
     DrawRect 10,10,100,150
     Flip
End Function


Wenn du jetzt das kleine Fenster bewegst, wirst du einen Pfad von Bildern sehen, die nicht gelöscht werden, bevor du die linke Maustaste losgelassen hast. Das ist deshalb, weil die WaitEvent-Funktion keinen Event empfängt, bevor du die Maustaste losgelassen hast.

    Tut15 1
Tut15 2


Bevor wir fortfahren, lass uns sicherstellen, dass du das obige Programm verstehst.

Mit dieser Formel unten, wir das 400x400-Fenster mit den x und y-Koordinaten im Zentrum unseres Desktops erstellt.

    Local x:Int=(GadgetWidth(Desktop())-400)/2
Local y:Int=(GadgetHeight(Desktop())-400)/2

 
Dann erstellen wir unser Fenster, das Canvas und das kleine Fenster, das über unserem Canvas erscheint.

    Local MyWindow:TGadget=CreateWindow("EventHook-Beispiel", x,y,400,400)
Global MyCanvas:TGadget=CreateCanvas(0,0,380,360,MyWindow)

Local SmallWindow:TGadget=CreateWindow("
Beweg Mich!", x+125, y+150, 150, 100, MyWindow, WINDOW_TITLEBAR)


Das ist unsere gewöhnliche Schleife, die entweder auf das Beenden des Programmes wartet oder auf das Neuzeichnen des Canvas. Dies hängt von der ID ab, die uns WaitEvent() zurückgibt.

    Repeat
  WaitEvent()
  Select EventID()
  Case EVENT_WINDOWCLOSE
     End
  Case EVENT_GADGETPAINT  
    UpDateCanvas()
   End Select
Forever
End


Wenn wir das Canvas updaten, zeichnen wir ein 100x150-Rechteck mit zufälliger Farbe.
(Ich hätte auch einfach nichts machen können, aber ich dachte wir können etwas sehen, wenn wir verschiedenfarbige Rechtecke zeichnen.)

    Function UpdateCanvas:Int()
     SetGraphics CanvasGraphics(MyCanvas)
     Cls
     SetColor Rnd(255),Rnd(255),Rnd(255)
     DrawRect 10,10,100,150
     Flip
End Function



EVENTHOOK

So einen Pfad zu haben, wenn wir das Fenster bewegen, ist nicht gerade schön. Hier ist es, wo EventHooks ihre Fähigkeiten ausspielen können. Lass uns das obige Programm abändern und einen EventHook hinzufügen (siehe die fetten Ergänzungen).

    SuperStrict
Import MaxGui.Driver
Local x:Int=(GadgetWidth(Desktop())-400)/2
Local y:Int=(GadgetHeight(Desktop())-400)/2

Local MyWindow:TGadget=CreateWindow("EventHook-Beispiel", x,y,400,400)
Global MyCanvas:TGadget=CreateCanvas(0,0,380,360,MyWindow)

Local SmallWindow:TGadget=CreateWindow("
Beweg Mich!", x+125, y+150, 150, 100, MyWindow, WINDOW_TITLEBAR)

AddHook EmitEventHook, MyHook

Repeat
  WaitEvent()
  Select EventID()
  Case EVENT_WINDOWCLOSE
     End
  Case EVENT_GADGETPAINT  
    UpDateCanvas()
   End Select
Forever
End

Function MyHookobject(iId:Int,tDataobject,tContextobject)
  Local Event:TEvent=TEvent(tData)

  If Event.source=MyCanvas And Event.ID=EVENT_GADGETPAINT
     UpdateCanvas()
     Return Null
  EndIf

  Return tData
End Function

Function UpdateCanvas:Int()
     SetGraphics CanvasGraphics(MyCanvas)
     Cls
     SetColor Rnd(255),Rnd(255),Rnd(255)
     DrawRect 10,10,100,150
     Flip
End Function


Wie wir jetzt sehen können, hinterlässt unser kleines Fenster keine Spur mehr zurück. Unsere Eventhook-Funktion ist das zu verdanken.

    Tut15 1
Tut15 2


Lass uns versuchen zu verstehen, was passiert ist. Lass uns die zusätzlichen Zeilen in unserem Programm analisieren.

Diese Zeile unten erlaubt uns, "einen Event zu erhalten, wenn er tief im Systeminnern passiert".

    AddHook EmitEventHook, MyHook


Die Syntax für AddHook wird in der Hilfe wie folgt angegeben:

Function AddHook( id,func:Object( id,data:Object,context:Object ),context:Object=Null,priority=0 )


Für uns können wir im Moment die Syntax so vereinfachen:

Function AddHook( id,func)


mit id = EmitEventHook und func, den Namen der Hookfunktion, die ausgeführt wird, wenn ein Event "gefunden" wird.

Die Hook-Funktion

Lass uns jetzt die Hook-Funktion selbst anschauen.

Es ist nötig, dass die Hook-Funktion immer wie folgt definiert sein muss:

    Function MyHook:Object(iId:Int,tDataobject,tContextobject)


Mit der einzigen Freiheit, dass wir den Funktionsnamen MyHook in alles was wir wollen (natürlich unter den Bedingungen für die Funktionsnamenvergabe von BlitzMax) ändern können und auch die Variablen können wir nennen, wie wir wollen.


Die folgende Linie castet die tData-Variable (welches irgendein Objekt ist) in ein Event-Objekt.

      Local Event:TEvent=TEvent(tData)


Wir müssen dann überprüfen, ob der Event auch UNSER Event ist. Wie im Beispiel, dass der Event von MyCanvas kommt und dass er ein EVENT_GADGETPAINT-Event ist.

      If Event.source=MyCanvas And Event.ID=EVENT_GADGETPAINT


Ist dies der Fall, können wir unser Canvas neu zeichnen.

         UpdateCanvas()


Wie schon gesagt, war der Event vielleicht für etwas anderes vorgesehen, womöglich auch für unser WaitEvent()-Funktion. Dass unser Canvas nicht zwei Mal neu gezeichnet wird, geben wir null zurück. So wird die nächste Funktion unser Canvas nicht mehr updaten.

          Return Null


Wenn dies nicht unser Event war, müssen wir das Objekt tData zurücksenden, wie wenn unsere Funktion diesen Event nie angeschaut hätte. Die nächste Funktion kann dann diesen Event verarbeiten.

      Return tData


Phu. Das ist das Ende der Erklärungen. Ich hoffe das war klar und verständich.

Zum sicherstellen, dass wir alles richtig gelernt haben, lass uns uns eine zweite Hookfunktion namens SecondHook erstellen.

    SuperStrict
Import MaxGui.Driver
Local x:Int=(GadgetWidth(Desktop())-400)/2
Local y:Int=(GadgetHeight(Desktop())-400)/2

Local MyWindow:TGadget=CreateWindow("EventHook-Beispiel", x,y,400,400)
Global MyCanvas:TGadget=CreateCanvas(0,0,380,360,MyWindow)

Local SmallWindow:TGadget=CreateWindow("
Beweg Mich!", x+125, y+150, 150, 100, MyWindow, WINDOW_TITLEBAR)

AddHook EmitEventHook, MyHook

AddHook EmitEventHook, SecondHook


Repeat
  WaitEvent()
  Select EventID()
  Case EVENT_WINDOWCLOSE
     End
  Case EVENT_GADGETPAINT  
    UpDateCanvas()
   End Select
Forever
End

Function SecondHookobject(iId:Int,tDataobject,tContextobject)
  Local Event:TEvent=TEvent(tData)

 
If event=Null Return Null

  If Event.source=MyCanvas And Event.ID=EVENT_MOUSEDOWN
     Notify "Klick Mich!"
     Return Null
  EndIf

  Return tData
End Function

Function MyHookobject(iId:Int,tDataobject,tContextobject)
  Local Event:TEvent=TEvent(tData)

  If Event.source=MyCanvas And Event.ID=EVENT_GADGETPAINT
     UpdateCanvas()
     Return Null
  EndIf

  Return tData
End Function

Function UpdateCanvas:Int()
     SetGraphics CanvasGraphics(MyCanvas)
     Cls
     SetColor Rnd(255),Rnd(255),Rnd(255)
     DrawRect 10,10,100,150
     Flip
End Function


Abgesehen von der neuen AddHook-Funktion, müssen wir auch noch eine andere Hookfunktion haben. Beachte, dass der Name geändert wurde, und dass überprüft wird, ob der Event ein null-Event ist oder nicht. Es wäre auch nicht schlecht, wenn wir dies in unserer früheren Funktion machen würden.

    Function SecondHook:Object(iId:Int,tDataobject,tContextobject)
  Local Event:TEvent=TEvent(tData)

  If event=Null Return Null


Wir überprüfen dann ob ein Mausklick auf unser Canvas stattgefunden hat und reagieren entsprechend.

      If Event.source=MyCanvas And Event.ID=EVENT_MOUSEDOWN
     Notify "Klick Mich!"
     Return Null
  EndIf


Im obigen Szenario würde es besser sein, nur eine Hookfunktion zu haben, und mit select/case die verschiedenen Events zu unterscheiden. Doch dieses Beispiel sollte die Möglichkeit zeigen, mehr als eine Hookfunktion in Serie zu haben.

Weiterführende Erklärungen
Es gibt ein paar interessante Threads im englischen Forum über EventHooks, aber unglücklicherweise sind die meisten Beispiele sehr komplex; nur ein oder zwei Beispiele waren simpel genug, um davon zu lernen.


Wenn du mehr darüber erfahren willst, kannst du diese Links auschecken:

  • BlitzMax Forums/BlitzMax Beginners Area/MaxGUI menu over a canvas - Dieser Thread war derjenige, der mir die Idee zu diesem Tutorial und dem Beispiel gab (link)
  • BlitzMax Forums/BlitzMax Beginners Area/Events / Hooks and Stuff - Dies ist eine Erklärung von Mark Sibly selbst über EventHooks (siehe unten) Schau dir auch den Slidercode von René und auch den von Pertubatio am Ende an. (link)
  • BlitzMax Forums/BlitzMax Beginners Area/events - Dies hat mich verwirrt, aber könnte nützlich zum Lesen sein. (link)
  • BlitzMax Forums/BlitzMax Programming/MaxGui Program Flow - Eine Anregung, wie man EventHooks in Verbindung mit Gadgets und dem Programmablauf brauchen könnte. Fortgeschrittenes Zeug. (link)

Das hat Mark über Eventhooks geschrieben:

* Die "MyEventHook"-Funktion wird jedesmal aufgerufen, wenn ein Event stattfindet (von WaitEvent aus - oder präziser, von WaitSystem, was bei WaitEvent aufgerufen wird).

* Hooks sollten den "data"-Parameter überprüfen, um sicherzustellen, dass es verfügbar ist. In diesem Beispiel wird es immer so sein, aber es ist grundsätzlich eine gute Idee.

*Hooks sollten generell den "data"-Parameter zurückgeben, denn sie bekommen haben - das ist so, dass auch andere Hooks die Daten ansehen können. Wenn du andere Hooks "blocken" willst, kannst null zurückgeben.

Polled Input (KeyDown, KeyHit etc.) und der Eventqueue werden am Anfang der Hooks erstellt - schau im Quellcode, um dich selbst zu überzeugen!

Warum Hooks benutzen? Warum nicht einfach mit WaitEvent die Events behandeln? Also, Hooks sind hauptsächlich nützlich um mit "modal actions" umzugehen. "modal actions" sind Aktionen, bei denen der Benutzer die Maustaste drückt und irgendetwas tut, wie die Fenstergrösse verändern oder eine Scrollbar bewegen.

Wenn der Benutzer eine "modal-action" ausführt, geht das Betriebssystem in eine Schleife und informiert dein Programm über eine "Callback-Funktion" über die Events. Hooks sind nur eine Generalisierung von solchen Callback-Funktionen.

BlitzPlus kam um das herum, weil ich ein komplexes System von "mirrors und pulleys" dafür anwandte, aber ich dachte, denselben Trick auf mehreren Plattformen zu benutzen, sei zu gefärlich/mache zu viel Probleme.

Ich hoffe, das hilft ein bisschen!

Zum Schluss...

Ich bin sicher, es können viel clevere Sachen mit Eventhooks gemacht werden. Aber auf dem einfachsten Level, ist es nichts mehr als Events aus dem Eventsystem zu holen, was dem Programmierer erlaubt, mit "modal actions" umzugehen.

Um einen Eventhook zu erstellen, müssen wir

  • Addhook aufrufen, um MaxGUI mitzuteilen welche Funktion die Hookfunktion ist.
  • die Hookfunktion schreiben, unter Beachtung der Vorgaben.
  • beachten, dass MaxGUI auf Events basiert. Die Events müssen erzeugt werden, damit sich auch verarbeitet werden können (zum Beispiel muss bei einem Panel der PANEL_ACTIVE-Style aktiviert sein, so dass Events erzeugt werden).

Das ist alles, was für etwas einfaches zu tun ist. Man kann dies natürlich auch noch mit Types und OOP machen smile