Eventloop

Eigentlich ist mir die Frage peinlich, aber es kann sonst niemand helfen ... :)

Moderator: Moderatoren

Antworten
ab-software
UDF-Programmierer
UDF-Programmierer
Beiträge: 51
Registriert: Di, 18. Okt 2005 12:35
Wohnort: 41747 Viersen
Kontaktdaten:

Eventloop

Beitrag von ab-software »

Hallo,

ich versuche mich gerade mal näher mit der Eventloop zu beschäftigen und versuche von einem Fenster zum nächsten Nachrichten zu schicken.
Laut Dokumentation muss man dazu "nur" ein neues Event erzeugen und diesem die Absenderadresse des Xbase Parts mitteilen das das Event "empfangen" soll.

Versuche ich das ganze aber mit einem Fenster kommt die Nachricht im Fenster an das Sie loschickt, egal an welches Fenster sie adressiert ist.

Hier mal mein Code:

Code: Alles auswählen

#DEFINE _MyEvent xbeP_User + 1

PROCEDURE APPSYS()
RETURN

PROCEDURE MAIN()
	LOCAL ;
		   nEvent := 0 , ;
			mp1    := 0 , ;
			mp2    := 0 , ;
			oXbp   := NIL , ;
			oButton     := NIL , ;
			oLabel      := NIL , ;
			oLBLMouse   := NIL , ;
			drawingArea := NIL


	* Fenster erzeugen und anzeigen
	oDlg         := XbpDialog():new( ,, {700,300}, {300,200} )
   oDlg:title   := "Erstes Fenster"
	oDlg:taskList := .T.
	oDlg:create()

	drawingArea := oDlg:drawingArea

	oLabel := XbpStatic():new( drawingArea ,, {50,100}, {180,30} )
   oLabel:caption := 'Text Fenster 1'
   oLabel:create()

	oButton := XbpPushButton():new( drawingArea ,, {50,50}, {180,30} )
   oButton:caption := "Fenster öffnen"
   oButton:create()
   oButton:activate := {|| fenster( @oDlg)  }

	oDlg:show()

	* Event loop
   nEvent := 0
	DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )

		IF nEvent == _MyEvent
			oLabel:setCaption(mp1)
		ENDIF

      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO

RETURN

FUNC RegisterXClass
RETURN "****************"  

PROCEDURE fenster( oParentEvent)
	LOCAL ;
		   nEvent := 0 , ;
			mp1    := 0 , ;
			mp2    := 0 , ;
			oXbp   := NIL , ;
			oButton     := NIL , ;
			drawingArea := NIL


	* Fenster erzeugen und anzeigen
	oDlg         := XbpDialog():new( ,, {200,300}, {300,200} )
   oDlg:title   := "Zweites Fenster"
	oDlg:taskList := .T.
	oDlg:create()

	drawingArea := oDlg:drawingArea

	oButton := XbpPushButton():new( drawingArea ,, {50,50}, {180,30} )
   oButton:caption := "Message senden"
   oButton:create()
   oButton:activate := {|| PostAppEvent( _MyEvent, 'Text aus Fenster 2', ,oParentEvent) }

	oDlg:show()

	* Event loop
   nEvent := 0
	DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )

		IF nEvent == _MyEvent
			MsgBox("Doch im Zweiten Fenster gelandet")
		ENDIF

      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO

RETURN
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hallo,

in deinem Programm hast du einen Thread (wenn ich das richtig sehe) und willst 2 Eventloops haben. Das geht aber nicht. EIN Thread kann immer nur EIN Programmteil abarbeiten. Somit bist du mit allen Fenstern und Controlls immer genau in einer Eventloop (welche von beiden das gerade ist weiß ich jetzt nicht genau, ich vermute die 1. Eventloop, solange kein 2. Fenster aufgemacht wurde, danach nur noch in der des letzten 2 Fensters).

Eine Eventloop reicht aber auch völlig aus, da diese nur die Events abfragt und dem jeweiligen Controll den Auftrag gibt darauf zu reagieren. In deinem jeweiligen Fenster musst du dann eine Routine haben die darauf reagiert.

Ein weiteres Problem in deinem Programm ist die Verwendung der Variablen oLabel. Diese wird immer das Object enthalten, das zuletzt mit Ihr erzeugt wurde (XppFD Funktionscode nutzt eine Variable für viele Objecte !) Wenn du möchtest, dass alle Meldungen genau dort in die Caption gehen, dann ist das richtig.

Wenn ich dich richtig verstanden habe, soll also in oLabel der Test 'Ich bin Fenster 1 / 2" erscheinen, je nachdem auf welchen Button du geklickt hast. In deinem Beispiel wird aber der Text nur versandt, wenn du auf ein 2. Fenster klickst und da in dieser die Eventloop aktiv ist und nur nach deinem Event abfragt erscheint die MSGBOX(). Bei einem klick ins erste Fenster erscheinen nur 2. Fenster.

Zur Frage der Eventloops wurde schon viel geschrieben, such mal danach.

Das ZIEL des Events wird ja in oXBP übergeben. Das Fenster selbst kann aber zunächst mit dem _MyEvent nix anfangen. Entweder du bringst diesem bei was es machen soll (wie bin ich jetzt auf die Schnelle überfragt) oder - einfacher - du machst gleich was du willst.

In deinem Beispiel OHNE die zweite Eventloop müsste der Text auf dem ersten Fenster geändert werden in 2. Fenster...
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Ich habe mal dein Programm abgeändert, dass beide den Text ändern können:

Code: Alles auswählen

#include "Appevent.ch"


#DEFINE _MyEvent xbeP_User + 1

PROCEDURE APPSYS()
RETURN

PROCEDURE MAIN()
   LOCAL ;
         nEvent := 0 , ;
         mp1    := 0 , ;
         mp2    := 0 , ;
         oXbp   := NIL , ;
         oButton     := NIL , ;
         oLabel      := NIL , ;
         oLBLMouse   := NIL , ;
         drawingArea := NIL


   * Fenster erzeugen und anzeigen
   oDlg         := XbpDialog():new( ,, {700,300}, {300,200} )
   oDlg:title   := "Erstes Fenster"
   oDlg:taskList := .T.
   oDlg:create()

   drawingArea := oDlg:drawingArea

   oLabel := XbpStatic():new( drawingArea ,, {50,100}, {180,30} )
   oLabel:caption := 'Text Fenster 1'
   oLabel:create()

   oButton := XbpPushButton():new( drawingArea ,, {10,50}, {100,30} )
   oButton:caption := "Fenster öffnen"
   oButton:create()
   oButton:activate := {|| fenster( @oDlg)  }

   oButton := XbpPushButton():new( drawingArea ,, {120,50}, {100,30} )
   oButton:caption := "Message senden"
   oButton:create()
   oButton:activate := {|| PostAppEvent( _MyEvent, 'Text aus Fenster 1' ) }


   oDlg:show()

   * Event loop
   nEvent := 0
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )

      IF nEvent == _MyEvent
         oLabel:setCaption(mp1)
      ENDIF

      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO

RETURN

PROCEDURE fenster( oParentEvent)
   LOCAL ;
         nEvent := 0 , ;
         mp1    := 0 , ;
         mp2    := 0 , ;
         oXbp   := NIL , ;
         oButton     := NIL , ;
         drawingArea := NIL


   * Fenster erzeugen und anzeigen
   oDlg         := XbpDialog():new( ,, {200,300}, {300,200} )
   oDlg:title   := "Zweites Fenster"
   oDlg:taskList := .T.
   oDlg:create()

   drawingArea := oDlg:drawingArea

   oButton := XbpPushButton():new( drawingArea ,, {50,50}, {180,30} )
   oButton:caption := "Message senden"
   oButton:create()
   oButton:activate := {|| PostAppEvent( _MyEvent, 'Text aus Fenster 2', ,oParentEvent) }

   oDlg:show()

RETURN
Gruß
Hubert
ab-software
UDF-Programmierer
UDF-Programmierer
Beiträge: 51
Registriert: Di, 18. Okt 2005 12:35
Wohnort: 41747 Viersen
Kontaktdaten:

Beitrag von ab-software »

Hallo,

danke so funktioniert es wunderbar ... das man nur eine Eventloop haben kann wusste ich garnicht ...
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Ich hab mal ganz früher, als ich mit Xbase++ und Windowsprogrammierung angefangen habe, in jedem Dialog einen eigenen Event-Loop gehabt. Als ich es noch nicht besser wußte. Das hat funktioniert. Aber sehr oft zu unvorhergesehenen Ergebnissen geführt. Bis ich hier im Forum gelernt habe, daß das nicht wirklich der richtige Weg ist.

Jan
ab-software
UDF-Programmierer
UDF-Programmierer
Beiträge: 51
Registriert: Di, 18. Okt 2005 12:35
Wohnort: 41747 Viersen
Kontaktdaten:

Beitrag von ab-software »

Hi,

wie machst du es den jetzt wenn du in 2 Dialogen unterschiedlich auf Tastatureingaben reagieren willst.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Naja, da gibt es (soweit ich das inzwischen weiß, und das hat nicht sooo viel zu sagen) 2 Möglichkeiten:

- Du packst das alles zusammen in den einen Event Loop, und fragst da ab, woher das denn kommt. Du kannst ja z. B. abfragen ob der Event von einem Pushbutton kommt. Ich selber habe mir das in dem Fall z. B. so umgeschrieben, daß nicht nur die Leertaste, sondern auch Enter den auslösen. Genauso kannst Du ja im Dialog den Cargo-Slot mit einem eindeutigen Wert belegen und den dann abfragen.

- Du packst das in 2 Threads. Und da jeder Thread seinen eigenen Event Lopp hat, geht das über den Weg. Das ist eine Lösung, die ich in meinen Projekten im Moment favorisiere. Ich habe mir eine Standard-Lösung entwickelt (bzw. bin noch dabei), wo jeder Hauptdialog ein eigener Thread ist. Das war ein harter Kampf, und ohne ausführliche Hilöfe des Forums und der XUG Osnabrück hätte das niemals geklappt.

Aber pass auf: Alles, was Du mit Maus oder Tatastur machst, läuft durch den Event Loop. Und wenn Du da zu viel reinpackst, dann wird der natürlich immer langsamer. Wenn der jedesmal durch X Abfragen durch muß.

Aber: Warum möchtest Du denn in verschiedenen Dialogen verschiedenen auf Tastatureingaben reagieren? Verwirrt das nicht den Benutzer, wenn das nicht einheitlich ist?

Jan
hschmidt
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 164
Registriert: Mo, 09. Jan 2006 17:06
Wohnort: Paderborn
Hat sich bedankt: 2 Mal
Kontaktdaten:

Beitrag von hschmidt »

Hallo,
Hi,

wie machst du es den jetzt wenn du in 2 Dialogen unterschiedlich auf Tastatureingaben reagieren willst.
so etwas sollte man überhaupt nicht in der Eventschleife abfragen, sondern dafür gibt es die :keyboard()-Callbackmethoden.

Am besten öffnest Du in der Hilfe von xbpDialog mal den Klassenbaum und siehst Dir die Callbacks an. Da ist eigentlich recht gut beschrieben, wie Du (oder besser gesagt Dein Dialog) auf unterschiedliche Events (Tastatur, Maus..) reagieren kannst.

Hans
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

wobei bei Xbase++ eigentlich die SLE, MLE etc. auf die Tastatureingaben reagieren und eher selten das Fenster selbst.
Gruß
Hubert
Benutzeravatar
Rolf Ramacher
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1930
Registriert: Do, 09. Nov 2006 10:33
Wohnort: Bergheim
Danksagung erhalten: 3 Mal
Kontaktdaten:

Beitrag von Rolf Ramacher »

Hallo zusammen,

also ich habe auch für jeden Dialog einen eigenen Event-Loop. Dies scheint für mich die beste Lösung zu sein, da sich meine Fenster bei Menü oder buttons neu aufrufen und die vorherige Dialog gesperrt wird und nach Rückkehr wieder aktiviert wird.
Gruß Rolf

Mitglied der Gruppe XUG-Cologne
www.xug-cologne.de
ab-software
UDF-Programmierer
UDF-Programmierer
Beiträge: 51
Registriert: Di, 18. Okt 2005 12:35
Wohnort: 41747 Viersen
Kontaktdaten:

Beitrag von ab-software »

*hmm* ok das klingt alles eigentlich recht gut.
Aber wenn ich jetzt versuche den Keyboard Callbackslot eines Xbase Fensters abzufragen, bekomme ich von ihm nicht alle Tasten zurückgegeben. Ich bekomme nur die Tasten vom Fenster zurück die auch in der AppEvent.ch stehen. Tasten wie F1 oder die Buchstaben kann ich von meinem Fenster selber nicht auswerten lassen. Wieso? Wie kann ich das ändern?

Gruß und vielen Dank schonmal

Hier mein "Programm" mit dem ich das getestet habe:

Code: Alles auswählen

CLASS absWinTest FROM xbpDialog

  EXPORTED:
			METHOD ;
					 Keyboard(nKey)

ENDCLASS


METHOD absWinTest:Keyboard(nKey)
	MsgBox("Key: " + str(nKey))
RETURN


PROCEDURE MAIN()
	LOCAL ;
		oDlg     := NIL , ;
		nEvent   := 0   , ;
		mp1      := 0   , ;
		mp2      := 0   , ;
		oXbp     := NIL

   oDlg  := absWinTest():new( ,, {50,50}, {1024,786} )
   oDlg:title   := "Kalkulation"
   oDlg:create()


	oDlg:show()

	* Event loop
	nEvent := 0
	DO WHILE nEvent <> xbeP_Close
		nEvent := AppEvent( @mp1, @mp2, @oXbp )


		oXbp:handleEvent( nEvent, mp1, mp2 )
	ENDDO

RETURN
[/code]
ab-software
UDF-Programmierer
UDF-Programmierer
Beiträge: 51
Registriert: Di, 18. Okt 2005 12:35
Wohnort: 41747 Viersen
Kontaktdaten:

Beitrag von ab-software »

Selbst wenn ich die Klasse um die unten folgende Methode erweitere bekomme ich keine Tasten zurück. Aber über die HandleEvent Methode sollte ich doch eigentlich alle Events verarbeiten können, oder?

Code: Alles auswählen

METHOD absWinTest:handleEvent( nEvent, mp1, mp2)
	IF nEvent == xbeP_Keyboard
		MsgBox("HandleEvent - Key: " + str(mp1))
        ELSE
           ::XbpWindow:handleEvent(nEvent, mp1, mp2)
	ENDIF
RETURN
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

hi,
ab-software hat geschrieben:

Code: Alles auswählen

METHOD absWinTest:handleEvent( nEvent, mp1, mp2)
	IF nEvent == xbeP_Keyboard
    	    MsgBox("HandleEvent - Key: " + str(mp1))
               ELSE
                   ::XbpWindow:handleEvent(nEvent, mp1, mp2)
	ENDIF
RETURN
grundsätzlich solle es gehen, ABER die Frage ist wo du das abfragst.
ab-software hat geschrieben: Laut Dokumentation muss man dazu "nur" ein neues Event erzeugen und diesem die Absenderadresse des Xbase Parts mitteilen das das Event "empfangen" soll.
Ja, aber ein XbpDialog ist in diesem Fall kein Xbase Part in dem Sinne.
Vielmehr leitet man ja meistens das :keyboard auf die Xbparts der
Childlist().

Code: Alles auswählen

   oDlg := XbpDialogEx():new(AppDesktop(),,aPos,aSize,,.F.)
...  
   oWMP := XbpActiveXControl():new(oAnzeig,,{0,0},aSize,aPP)
   AADD(aControls,oWMP)     // control to keyhandler
...
   //
   // install keyboard handler for each aControl Element
   //
   bKeyHandler := {| nKey,uNIL,obj | DlgKEYS(nKey,obj ...)}
   AEVAL(aControls,{| o | o:keyBoard := bKeyHandler})
...
   DO WHILE .NOT. SP_MainExit()
      nEvent := AppEvent(@mp1,@mp2,@oXbp,nTimeout)
      //
      // gilt für gesamte Application
      //
      DO CASE
            CASE nEvent == xbe_None // timeout
            OTHERWISE
                     oXbp:handleEvent(nEvent,mp1,mp2)
      ENDCASE 
   ENDDO

PROCEDURE DlgKEYS(nKey,oXbp ....)
   //
   // gilt nur für den zugewiesenen XbpDialog
   // 
   DO CASE
      CASE nKey == xbeK_ESC
      CASE nKey == xbeK_F1

   ENDCASE
RETURN
Es wird also unterschieden zwischen der "Main loop" (die möglist klein
sein sollte) und dem :keyboard Slot für jeden einzelnen XbpDialog.

gruss by OHR
Jimmy
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hallo,

Die Taste F1 erzeugt zunächst einen xbeP_HelpRequest. Tasten die zur Steuerung in einem Control benutzt werden (Tab, Text etc.) werden meines Wissens standardmäßig auch nicht an den Codeblock im Keyboard-Slot weitergegeben, sondern vorher verarbeitet. Darum verwende ich abgeleitete Methoden für meine speziellen SLEs. Das kann man aber umschalten: :autoKeyboard damit habe ich aber keine Erfahrung.

Im Eventloop frage ich aber auch nach F1 ab:

Code: Alles auswählen

   case nEvent = xbeP_Keyboard .and. mp1 == xbeK_F1
          ...  Eigene Hilferoutine ...
Im Prinzip kann man so jede Taste abfragen, allerdings sind die normalen Tasten nicht in der AppEvent.ch hinterlegt (man muss die Codes zuerst ermitteln) und wenn man es übertreibt wird das Programm sehr langsam !!!

Wenn man mehrere Tasten abfragen möchte muss man dringend dafür sorgen, dass nur noch Keyboard events diese Abfragebäume durchlaufen...

Code: Alles auswählen

case nEvent = xbeP_Keyboard // ist es überhaupt ein Keyboard-Event - meist nein !
              do case 
                   case mp1 == xbeK_F1
                    ....
          
Und nochmal zum Grundsätzlichen:

Alle Events die ein XbPart selbst verarbeiten kann, werden nicht an das Fenster weitergereicht !!!
Gruß
Hubert
ab-software
UDF-Programmierer
UDF-Programmierer
Beiträge: 51
Registriert: Di, 18. Okt 2005 12:35
Wohnort: 41747 Viersen
Kontaktdaten:

Beitrag von ab-software »

Hi,

nehmen wir z.B. die F1 Taste als Beispiel ... da die ja ein Standart Event erzeugt, wird mein Keyboard slot meines Fensters nicht ausgelöst und ich kann die Tasten in dem Fenster nicht sepperat behandeln.

Wie kann ich diese Standart Events ausschalten das alle Tastaturereignisse an mein Fenster gesendet werden und ich die dort im Keyboardslot auswerten kann?

Es ist unpraktisch und kompliziert einen großteil der Tasten im Keyboardslot auswerten zu können und einen Teil in der Eventloop (wobei da ja dann noch je nach gerade aktivem Fenster eine Unterscheidung gemacht werden muss welche Aktion denn nun ausgeführt wird)

Hat jemand in der Richtung schon erfahrungen und kann mir weiterhelfen?

Grüße
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

hi,
ab-software hat geschrieben: nehmen wir z.B. die F1 Taste als Beispiel
nicht sehr schönes Beispiel den F1 wird gewöhnlich für Hilfe eingesetzt.
Da kann schonmal eine Application den F1 Event abfangen
ab-software hat geschrieben: ... da die ja ein Standart Event erzeugt, wird mein Keyboard slot meines Fensters nicht ausgelöst und ich kann die Tasten in dem Fenster nicht sepperat behandeln.
wieso ? so lange du einen Event nicht abfängst ist er in der Queue.
ab-software hat geschrieben: Wie kann ich diese Standart Events ausschalten das alle Tastaturereignisse an mein Fenster gesendet werden und ich die dort im Keyboardslot auswerten kann?
Wenn du einen :keyboard Slot eines XbpDialog belegt und das Fenster
den Focus bekommt werden alle :keyboard Event an das Fenster geleitet.
ab-software hat geschrieben: Es ist unpraktisch und kompliziert einen großteil der Tasten im Keyboardslot auswerten zu können und einen Teil in der Eventloop (wobei da ja dann noch je nach gerade aktivem Fenster eine Unterscheidung gemacht werden muss welche Aktion denn nun ausgeführt wird)
Nope, genau das macht es ja überhaupt er möglich das du MDI Fenster
haben kannst. In den Main Eventloop kommen NUR die für die gesammte
Application gültige Keys. Im günstigsten Fall ist die Event loop "leer" bzw
hat nur UserDef Events.

gruss by OHR
Jimmy
Antworten