Seite 1 von 1

tastenevent

Verfasst: Mo, 08. Aug 2011 12:10
von Benz
Hi,
ich könnte wetten ich hab das schonmal irgendwo gelesen ich finde jetzt aber leider nichts im Forum....

Und zwar:
Wie kann ich das Event einer speziellen Taste abfangen?
Beispielsweise die F1-Taste oder das "z" auf der Tastatur.

Gibt es außerdem eine Liste dafür ? In der Hilfe habe ich nur die Klasse XbpActiveXControl gefunden und ich weiß nicht, ob das so richtig ist.

Liebe Grüße und danke schonmal im Voraus

Re: tastenevent

Verfasst: Mo, 08. Aug 2011 12:20
von AUGE_OHR
Benz hat geschrieben:Wie kann ich das Event einer speziellen Taste abfangen?
Beispielsweise die F1-Taste oder das "z" auf der Tastatur.
das kommt nur wieder darauf an "wem" die Taste "zugeordnet" wird.

wenn du die z.b. einem XbpBrowse() "zuordnen" will würde ich den o:Keyboard Slot von XbpBrowse() benutzten.

wenn du es "allgemein" handhaben willst musst du wohl eine Abfrage in deine Event Loop schreiben.

Code: Alles auswählen

nEvent := AppEvent( @mp1, @mp2, @oXbp)
DO CASE
     CASE nEvent == xbeP_Keyboard .AND. mp1 == xbeK_F1
     CASE nEvent == xbeP_Keyboard .AND. mp1 == CHR("z")
Benz hat geschrieben:Gibt es außerdem eine Liste dafür ? In der Hilfe habe ich nur die Klasse XbpActiveXControl gefunden und ich weiß nicht, ob das so richtig ist.
c:\ALASKA\XPPW32\Include\appevent.ch

Re: tastenevent

Verfasst: Mo, 08. Aug 2011 12:32
von Tom
wenn du es "allgemein" handhaben willst musst du wohl eine Abfrage in deine Event Loop schreiben.
Code:
nEvent := AppEvent( @mp1, @mp2, @oXbp)
DO CASE
CASE nEvent == xbeP_Keyboard .AND. mp1 == xbeK_F1
Wenn man es wirklich allgemein halten will, muss man in der Eventloop abfangen, welches Part das Event auslöst bzw. den Fokus hat:

Code: Alles auswählen

CASE nEvent == xbeP_Keyboard .AND. mp1 == xbeK_F1 .AND. oXpb:IsDerivedFrom("XbpCellGroup")
Hier würde auf "F1" reagiert werden, wenn ein Browse den Fokus hat.

Auf diese Weise baut man sich übrigens nach und nach einen eigenen Eventhandler, der bestimmt, wie sich die Applikation grundsätzlich verhält. Die Slots sollte man nur in Spezialfällen benutzen. Oder Klassen bauen, die die Slots jeweils bestücken.

Re: tastenevent

Verfasst: Mo, 08. Aug 2011 13:17
von AUGE_OHR
Tom hat geschrieben:Wenn man es wirklich allgemein halten will, muss man in der Eventloop abfangen, welches Part das Event auslöst bzw. den Fokus hat:

Code: Alles auswählen

CASE nEvent == xbeP_Keyboard .AND. mp1 == xbeK_F1 .AND. oXpb:IsDerivedFrom("XbpCellGroup")
Hier würde auf "F1" reagiert werden, wenn ein Browse den Fokus hat.
"allgemein" deshalb weil ich ja zuvor auf o:KeyBoard hingewiesen habe wenn man es einem "speziellen" XbPart zuweisen will.

Re: tastenevent

Verfasst: Mo, 08. Aug 2011 13:23
von Tom
Hallo, Jimmy.

"Allgemein" wäre im Handler, wenn man etwa will, dass alle Browses in der Applikation auf Bild-abwärts mit dem Scrollen um exakt 10 Datensätze reagieren. Oder auf Buchstabentasten durch Sprung zum nächsten Eintrag in der jeweiligen Spalte, dessen Inhalt mit diesem Buchstaben beginnt. Oder auf das "Wheel"-Event mit Scrollen um einen abweichenden Wert von dem, der betriebssystemseitig gewählt ist (SysParametersInfoA()), was ich beispielsweise mache.

"Speziell" geht entweder über den Keyboard-Slot des soeben erzeugten Browses - oder in einer abgeleiteten Klasse, die den Slot bestückt. "Speziell" geht ansonsten natürlich auch über einen Handler, der nur an dieser Stelle benutzt wird. Es kommt immer darauf an, wie weit es gültig sein soll. Am "allgemeinsten" ist aber definitiv der allgemeine Handler, der dann auch abfragt, welches Objekt das Event ausgelöst hat.

Re: tastenevent

Verfasst: Mo, 08. Aug 2011 13:54
von AUGE_OHR
hi

deine Definition von "Allgemein" ist exakter als meine, allerdings wollte ich "verhindern" das Benz dies als Lösung benutzten wenn man "Speziell" Lösungen hat.

wenn man die "Allgemein" Lösung benutzt gilt es ja für alle XbParts, deshalb ist wäre o:IsDerivedFrom(XbParts) notwendig um es auf einen Type zu "begrenzen"

wenn ich nun ein "bestimmtest" XbPart ansprechen will "sollte" man die "Speziell" Lösung benutzten.
bei einem XbpBrowse "würde" man den o:Keyboard Slot benutzen ... den "Speziell" Codeblock kann man dann auch für "andere" XbpBrowse nutzen.

Re: tastenevent

Verfasst: Mo, 08. Aug 2011 17:23
von brandelh
Hi,

die F1 Taste ist aber ein Spezialfall !
Diese wird normalerweise einen xbeP_HelpRequest ausgelöst !
Wie schon mehrfach geschrieben, sollte man sich die appevent.ch bzw. inkey.ch (GET System ohne Maus) nachsehen.
Ansonsten kann man ja diese Variable (nEvent) und Parameter ausgeben.

Re: tastenevent

Verfasst: Di, 16. Aug 2011 9:26
von Benz
Ich hab in meinem kompletten Programm nur eine einzgige Eventschleife.
Im Prinzip muss ich ja jetzt für jedes Fenster eine eigene Eventloop erstellen?!
Mit was für einer Bedingung muss ich diese dann aufrufen, wenn ich die Schleife in einem Childfenster der Anwendung platziere?

Bisher habe ich nämlich nur diese Event-loop:

Code: Alles auswählen

   
DO WHILE nEvent <> xbeP_Quit
      nEvent := AppEvent( @mp1, @mp2, @oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO
Das ist ja aber schlecht, da diese Schleife ja existiert, bis die komplette Anwendung geschlossen wird?! Oder habe ich jetzt einen Denkfehler drin ?

Re: tastenevent

Verfasst: Di, 16. Aug 2011 9:38
von Jan
Jens,

nein, normalerweise hat jeder Thread nur einen einzigen Eventloop. Das bedeutet, wenn Du selber keine weiteren Threads startest, dann hast Du auch nur den einen Eventloop. In dem passiert dann alles, was Du brauchst.

Um den nicht zu überlasten (denn dann geht die Gesamtperformance in die Knie), ist es sinnvoll, wohl überlegte Schleifen da reinzubauen. Wenn Du z. B. nur einen tastenevent ausfiltern möchtest, dann wäre es sinnlos, alle Events abzufragen, auch die der Maus. Also erstmal abfragen, ob das überhaupt ein Tastendruck war. Danach kannst Du z. B. abfragen, ob der Event in dem relevanten XBPart geschehen ist. Usw.

Bei mir sieht das dann z. B. so aus (ich möchte in diesem Fall, das ein Pushbutton nicht nur Windowskonform über die Leertaste ausgelöst wird, sondern auch wie bauchmäßig gewöhnt über Enter, ähnlich bei anderen XBParts)

Code: Alles auswählen

DO WHILE .T.
   nEvent := AppEvent(@mp1, @mp2, @oEventXbp)

   // Für einige XBase-Parts ein anderes Tastaturverhalten einsetzen
   IF nEvent == xbeP_Keyboard //Ist das überhaupt ein Tastaturevent? Ansonsten aus Performancegründen die ganze Schleife überspringen
      IF mp1 = xbeK_ENTER                                     // Ist das ein Enter? Darum geht es schließlich.
         IF oEventXbp:isDerivedFrom("XBPPUSHBUTTON")          // Ist das ein Pushbutton?
            PostAppEvent(xbeP_Activate, NIL, NIL, oEventXbp)  // Dann auslösen, wie mit Leertaste
          ELSEIF oEventXbp:isDerivedFrom("XbpBrowse")         // Ist das eine Browse-Spalte?
            PostAppEvent(xbeK_RIGHT, NIL, NIL, oEventXbp)     // Dann in die nächste Spalte springen, wie mit PfeilRechts
          ELSEIF oEventXbp:isDerivedFrom("XbpCheckBox")       // Ist das eine Checkbox?
            PostAppEvent(xbeK_TAB, NIL, NIL, oEventXbp)       // Dann verlasse, wie mit Tab
          ELSEIF oEventXbp:isDerivedFrom("XbpComboBox")       // Ist das eine Combobox?
            PostAppEvent(xbeK_TAB, NIL, NIL, oEventXbp)       // Dann verlasse, wie mit Tab
          ELSEIF oEventXbp:isDerivedFrom("XbpSle")            // Ist das ein SLE?
            PostAppEvent(xbeK_TAB, NIL, NIL, oEventXbp)       // Dann verlasse, wie mit Tab
         ENDIF
       ELSEIF mp1 = xbeK_ALT_F4                               // Möchte jemand das Programm mit Alt F4 beenden?
         mp1 := xbeK_ALT                                      // Das Menü aktivieren
      ENDIF
   ENDIF

   oEventXbp:handleEvent(nEvent, mp1, mp2)
ENDDO
Das ist jetzt natürlich nur ein Beispiel. Du must das an Deine Gegebenheiten anpassen. Also z. B. die Zeile, in der ich ALT-F4 abfrage, auf Deine abzufragende Taste umschreiben, und dann entsprechend reagieren.

Jan

Re: tastenevent

Verfasst: Di, 16. Aug 2011 9:50
von Benz
ok soweit so gut.
Kann ich das jetzt auch irgendwie für einzelne Childfenster ändern?
Weil ich hätte das gerne so, dass im einen Childfenster der Anwendung beispielsweise F8 das Childfesnter beendet, jedoch in einem anderen Childfenster der Anwendung die Taste F8 z.b. ein Browse öffnet.

Re: tastenevent

Verfasst: Di, 16. Aug 2011 10:44
von Jan
Das kannst Du regeln, indem Du das Fenster abfragst.

Aber mal ehrlich: Willst Du das wirklich machen? Du solltest Dir Regeln überlegen, die im gesamten Programm die gleichen sind. Wenn Du, um bei dem Beispiel zu bleiben, F8 in jedem Dialog anders belegst, dann wird der Nutzer doch total irre.

Jan

Re: tastenevent

Verfasst: Di, 16. Aug 2011 13:58
von AUGE_OHR
hi,
Benz hat geschrieben:Bisher habe ich nämlich nur diese Event-loop
und dabei würde "ich" es lassen.

Du kannst zwar, wie Jan sagt, alle "Abfragen" in deine Event-loop packen aber "ich" würde es nicht "so" machen.

jedes XbPart, von XbpWindow(), hat den o:keyboard Slot welche zu "dem" XbPart gehört.
Wenn du 100 "gleiche" XbParts hättest heisst es ja nicht das du auch bei allen 100 die "selbe" Aktion haben willst.

deshalb sprach ich von "allgemeinen" und "speziellen" Keyboard Events.

***

bei Xbase++ "schreibst" du ja "manuell" deinen Code oder du nimmst den Formdesigner.
Wenn du dir nun "andere" RAD Systeme ansiehst gibt es da nur "Property" und "Trigger" die du "ausfüllen" kannst/musst.

im Prinzip müsste man den Xbase++ Formdesigner "aufbohren" sodass man "im" XppFD auch "alle" Slot der SubClass hätte und es "dort" eintragen.

***

Jedes Fenster hat ja eine o:drawingArea und die hat wieder o:keyboard welche dann für "das" Fenster gilt.
Ein XbpBrowse() hat ebenfalls ein o:Keyboard wo "ich" die Keyboard Events für "das" XbpBrowse verarbeiten würde.

stelle dir mal einen Data-Driven Code vor wo deine "Aktion" in einer DBF steht welche du zur Laufzeit den Codeblock des Slot zuweist.
wenn ich da was verändern will mache ich das in der DBF und nicht im Source Code.

Fazit : "vergiss" das es eine Event-loop gibt und fülle nur die Slot´s mit deiner "Aktion".

Re: tastenevent

Verfasst: Di, 16. Aug 2011 14:11
von brandelh
AUGE_OHR hat geschrieben:Fazit : "vergiss" das es eine Event-loop gibt und fülle nur die Slot´s mit deiner "Aktion".
grundsätzlich stimme ich den Aussagen zu, aber wenn du eine Taste im ganzen Programm einer Aktion zuordnen oder unterdrücken willst, dann ist die Event-Loop sehr nützlich ;-)

Re: tastenevent

Verfasst: Do, 18. Aug 2011 17:15
von Benz
So leid es mir tut ich kann damit einfach nichts anfangen... Wie belege ich diesen Slot?

so?

Code: Alles auswählen

Pushbutton1:keyboard(xbeK_F1)
Pushbutton1:activate:= {|| irgendeineaktion() }
oder so?

Code: Alles auswählen

Pushbutton1:keyboard:=xbeK_F1
Pushbutton1:activate:= {|| irgendeineaktion() }
oder so?

Code: Alles auswählen

Pushbutton1:keyboard:= {||xbeK_F1}
Pushbutton1:activate:= {|| irgendeineaktion() }
oder ganz anders?

Re: tastenevent

Verfasst: Do, 18. Aug 2011 17:39
von brandelh
Hi,

der KeyBoard - Slot ist eine Variable, Variablen werden so belegt:

oPushButton:keyBoard := {| nKeyCode, uNIL, oXbp | IchBinDieKeyhandlerfunktion(oXbp,nKeyCode) }

in meinem Beispiel wird dem KeyBoard-Slot des Controls (hier wohl ein PushButton) ein Codeblock zugeordnet,
der ausgeführt wird in dem Moment wo ...
1. Der PushButton den Eingabefokus besitzt (also nicht mit der Maus !), das kann man erkennen am geänderten Rahmen z.B. mit TAB, falls das erlaubt ist.
2. Eine Taste gedrückt wird, die das Control nicht automatisch abarbeitet, denn dann kommt es hier nicht mehr an.
Bei der Ausführung dieses Codeblockes, wird das ausführende Control selbst als 3. Parameter (hier oXbp; SELF wäre hier schlecht ! ) übergeben, im ersten der Key-Code (=> appevent.ch)
Der 2. Parameter wird nicht benutzt und ist immer NIL, daher uNil (unbenutzt NIL) als Variablenname, der kann natürlich frei gewählt werden.

Die Parameter von {|...| KANN man nun z.B. an eine Funktion (hier IchBinDieKeyhandlerfunktion(oXbp,nKeyCode, self ) ) übergeben.
In diesem Beispiel kümmert soll sich diese Funktion um mehrere Controls kümmen, somit muss man das aufrufende Control und den Keycode übergeben.
Das könnte man natürlich auch anders machen, aber ich ziehe es vor pro Fenster eine Methode zu haben die sich um die Tastatureingaben kümmert.
Beispiel-Funktion:

Code: Alles auswählen

function IchBinDieKeyhandlerfunktion(oXbp,nKeyCode,oWin)
   do case 
      case oXbp:isDerivedFrom( "XbpSle" )
             do case 
                  case nKeyCode = xbeK_ENTER 
                         PostAppEvent( xbeK_TAB , NIL, NIL, oXbp ) // sollte in das nächste Eingabefeld springen ...
                  case ...
             end

      case oXbp = oWin:oPushButton
             do case 
                  case nKeyCode = xbeK_ENTER // geht nur wenn KEIN DEFAULT PushButton definiert wurde !
                         msgbox("Enter im oPushButton")
                         PostAppEvent( xbeP_Activate, NIL, NIL, oXbp )
                  case ...
             end
...
Als Methode braucht man das Fenster nicht zu übergeben, da es ja per SELF bekannt ist.

Code: Alles auswählen

method ...:IchBinDieKeyhandlerfunktion(oXbp,nKeyCode)
   do case 
      case oXbp:isDerivedFrom( "XbpSle" )
             do case 
                  case nKeyCode = xbeK_ENTER 
                         PostAppEvent( xbeK_TAB , NIL, NIL, oXbp ) // sollte in das nächste Eingabefeld springen ...
                  case ...
             end

      case oXbp = ::oPushButton
             do case 
                  case nKeyCode = xbeK_ENTER // geht nur wenn KEIN DEFAULT PushButton definiert wurde !
                         msgbox("Enter im oPushButton")
                         PostAppEvent( xbeP_Activate, NIL, NIL, oXbp )
                  case ...
             end
...
:!: NUR wenn man selbst eine Klasse von einem Control ableitet, kann man auch die METHODE keyBoard(nKeyCode) umdefinieren. :!:

Re: tastenevent

Verfasst: Do, 18. Aug 2011 18:00
von AUGE_OHR
Benz hat geschrieben:oder ganz anders?
... ganz anders ...

Code: Alles auswählen

o:keyBoard := {| nKeyCode, uNIL, oSelf | MYKey(nKeyCode,oSelf) }
steht im Help File wobei
Parameter
<nKeyCode>
<nKeyCode> ist der numerische Tastencode der gedrückten Taste.
Die Taste "F1" ist nun ein Sonderfall deshalb verwende ich zur weiteren Betrachtung die Taste "F2" und "ENTER"

Code: Alles auswählen

PROCEDURE MyKey(nKeyCode,oSelf)
   DO CASE
        CASE nKeyCode = xbeK_F2
              ShowMore()               
       
        CASE nKeyCode = xbeK_ENTER
              // Pushbutton per ENTER "aktivieren"
              PostAppEvent(xbeP_Activate,,,oSelf)
   ENDCASE
RETURN
wenn du nun "mehrere" XbParts mit dem o:Keyboard Slot "bestücken" willst solltest du alle XbParts in einem Array "sammeln"

Code: Alles auswählen

   oPB1 := := XbpPushButton():new()
   ...
   oPB1:Create()
   AADD(aControl,oPB1)
 
   oPB2 := := XbpPushButton():new()
   ...
   oPB2:Create()
   AADD(aControl,oPB2)

   bKeyHandler := {| nKeyCode, uNIL, oSelf | MYKey(nKeyCode,oSelf) }
   AEVAL(aControls,{| o | o:keyBoard := bKeyHandler})

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 11:59
von Benz
Ich habs jetzt mal so versucht, leider funktioniert es noch nicht so richtig wie ich will:

Code: Alles auswählen

f2_name:destroy()
f2_name:caption  := f2_str
//IF f2_akt="close_this"
   f2_name:activate := {|| close_this(fenster) }
//ENDIF
f2_name:keyboard := {| nKeyCode, uNil, oSelf | MyKey(nKeyCode,oSelf) }
f2_name:create()
//IF empty(f2_akt)
//   f2_name:disable()
//ENDIF
f2_name ist ein Button..
Und hier dann noch die Procedure MyKey:

Code: Alles auswählen

PROCEDURE MyKey(nKeyCode,oSelf)
  DO CASE
      CASE nKeyCode = xbeK_F2
         ConfirmBox(,"HALLOO")
  ENDCASE
RETURN

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 12:24
von brandelh
Hi,

du hast vergessen zu erwähnen was nicht geht ;-)

PS: Kann es sein, dass F2 diesen Button auslösen soll und dieses meist nicht geschieht ?

Das hatte ich aber oben erwähnt, dass dieser Button nur wenn er selbst ausgewählt ist die Tasten verarbeiten kann. ;-)

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 14:36
von Benz
okay, ja das ist das Problem.
Also ich habs dann jetzt so gelöst, dass das Fenster das Objekt des :keyboard Slots ist. So ist es doch richtig oder ? ;-)

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 14:42
von Benz
AUGE_OHR hat geschrieben:Die Taste "F1" ist nun ein Sonderfall deshalb verwende ich zur weiteren Betrachtung die Taste "F2" und "ENTER"
Ich kann doch diese Taste trotzdem irgendwie abfangen oder nicht ? Nur mit xbeK_F1 funktioniert es ja nicht ?!

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 14:43
von brandelh
Hi,

das funktioniert, solange du keine Taste belegen willst, die schon vom aktiven Control selbst verarbeitet wird. F2 müsste so funktionieren, F1 nicht, da das ja einen Help-Event auslöst ;-)

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 15:13
von Benz
das heißt ich hab jetzt keine Möglichkeit auf F1 zuzugreifen ? ;)

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 15:30
von brandelh
Hi,

versuche doch mal was passiert wenn du F1 drückst ... KEIN KEYBOARD EVENT !
F1 erzeugt den xbe_Help oder so ähnlich ;-)
Man kann ja schließlich eine standard Hilfedatei hinterlegen und dann braucht man diese Taste ...

Re: tastenevent

Verfasst: Fr, 19. Aug 2011 17:11
von Benz
Ja schon, aber wenn ich das gar nicht will ? ;-) Und wenn ich F1 dann nicht ungenutzt lassen will ? Gibts da keine Möglichkeit?

Re: tastenevent

Verfasst: Sa, 20. Aug 2011 2:50
von AUGE_OHR
Benz hat geschrieben:Ja schon, aber wenn ich das gar nicht will ? ;-) Und wenn ich F1 dann nicht ungenutzt lassen will ? Gibts da keine Möglichkeit?
siehe mal unter "helpRequest" nach wofür F1 "gedacht" ist.

die Lösung für F1 wäre nun "SET KEY" wie im Beispiel ReadVar()
"SET KEY" / SetKey() sind aber nicht Event gesteuert dafür brauchst du dann SetAppEvent()

Code: Alles auswählen

#include "Appevent.ch"

PROCEDURE Main
LOCAL mp1, mp2, oXbp, nEvent, bBlock

   SetMouse(.T.)
   CLS
   ? "F1 Taste prüft SetAppEvent()"
   ? "Abbrechen mit rechter Maustaste"

   SetAppEvent( xbeK_F1, ;
               {|mp1,mp2,obj| CheckEvent(mp1,mp2,obj) } )


   DO WHILE .T.
      nEvent := AppEvent( @mp1, @mp2, @oXbp, 0 )

      IF (bBlock := SetAppEvent(nEvent)) <> NIL
          Eval( bBlock, mp1, mp2, oXbp )

      ELSEIF nEvent < xbeB_Event
          ?? Chr( nEvent )

      ELSEIF nEvent == xbeM_RbClick
         QUIT

      ENDIF
   ENDDO

RETURN


PROCEDURE CheckEvent( mp1, mp2, oXbp )

   ? "Erster  Message-Parameter:", mp1
   ? "Zweiter Message-Parameter:", mp2

   IF Valtype( oXbp ) == "O"
      ? "Xbase Part:", oXbp:className()
   ENDIF

RETURN
p.s. mit /PM:PM linken