XbpBrowse und :keyboard-Callback

Grafische Primitive, XbaseParts und Darstellungsfragen allgemein.

Moderator: Moderatoren

Antworten
michaelAC

XbpBrowse und :keyboard-Callback

Beitrag von michaelAC »

Hallo,

ich habe ein Problem bei der Verwendung eines Browsers vom Typ XbpBrowse. Der verwendete Code beruht auf Beispielcode, der entsprechend angepasst wurde und ansonsten seinen Zweck erfüllt.

Ich möchte jedoch die im Browser ausgewählte Zeile nicht nur mittels Doppelklick und Return-Taste die unter :itemSelected definierte Aktion ausführen lassen, sondern auch noch die Benutzung der Space-Taste separat (und überhaupt) berücksichtigen. Der Gedanke war nun, analog zu :itemSelected ein :keyboard zu verwenden und dort als ersten Test eine simple Debug-Meldung auszugeben. Das klappt leider nicht; es scheint, als ob der :keyboard-Callback komplett ignoriert wird.

Die aus meiner Sicht wichtigen Elemente des Codes lesen sich so:

Code: Alles auswählen

FUNCTION GuiArrayBrowseWZA( oParent, aPos, aSize, aPresParam, aArray )

   LOCAL oBrowse
   oBrowse := XbpBrowse():new( oParent, ,aPos, aSize, aPresParam )
   oBrowse:cargo         := aArray
   oBrowse:cursorMode    := XBPBRW_CURSOR_ROW
   oBrowse:skipBlock     := {|n,obj| SkipArray( n, obj:cargo ) }
   oBrowse:goTopBlock    := {|obj| obj:cargo[ ARR_NO ] := 1 }
   oBrowse:goBottomBlock := {|obj| obj:cargo[ ARR_NO ] := Len( obj:cargo[ ARR_SRC ] ) }
   oBrowse:posBlock      := {|obj| obj:cargo[ ARR_NO ]  }
   oBrowse:phyPosBlock   := {|obj| obj:cargo[ ARR_SORT, obj:cargo[ ARR_NO ] ] }
   oBrowse:lastPosBlock  := {|obj| Len( obj:cargo[ ARR_SRC ] ) }
   oBrowse:firstPosBlock := {|obj| 1  }
   oBrowse:keyboard      := {|n,x,obj| WZAEventHandler()}
   //oBrowse:itemSelected  := {|n,x,obj| iif(n[1] <= Len(obj:cargo[ARR_SRC]), (ShowArtikelDetail (obj:cargo[ARR_SRC][n[1]][1])),) }
   oBrowse:create()
   
RETURN oBrowse
Die Funktion WZAEventHandler() gibt momentan lediglich eine Meldung aus, um zu signalisieren, ob sie aufgerufen wird.

Anschließend werden noch die einzelnen Spalten gesetzt:

Code: Alles auswählen

   oBrowse = GuiArrayBrowseWZA (oParent, aPos, aSize, aPresParam, aArrayWZA)
   
   for i = 1 to Len (aColumnStruct)
      oBrowse:addColumn (DirBlock (aArrayWZA, i), aColumnStruct[i,3], aColumnStruct[i,1])
   next
Wenn ich das so einsetze, kann ich zwar mit den Cursortasten navigieren, bei Druck auf Space oder Enter geschieht nichts. Entferne ich den Kommentar vor :itemSelected, wird zusätzlich nur Enter berücksichtigt.

Ich denke, ich bin nicht der einzige mit diesen Anforderungen an einen Browser, daher kann es sich eigentlich nur um eine kleine Sache handeln, oder?

Danke & viele Grüße
Michael
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

Hallo Michael,

einfach in den Eventloop setzen:

Code: Alles auswählen

DO WHILE nEvent <> xbeP_Close 

  nEvent := AppEvent( @mp1, @mp2, @oXbp ) 

  oXbp:handleEvent( nEvent, mp1, mp2 ) 

  if nEvent == xbeP_Keyboard .and. mp1 == xbeK_SPACE
    ZeileMarkieren( oBrowse )
  endif

ENDDO 
Tschüs,
Sören
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

also ich habe hier mit 1.90 (dürfte aber keine Rolle spielen) im Beispiel:
C:\ALASKA\XPPW32\SOURCE\samples\basics\GuiBrow\DBBROWSE.prg
folgende Zeilen geändert:

Code: Alles auswählen

FUNCTION GuiBrowseDB( oParent, aPos, aSize )
   LOCAL oBrowse
    oBrowse := XbpBrowse():new( oParent,, aPos, aSize,, .F. ):create()
nach

Code: Alles auswählen

FUNCTION GuiBrowseDB( oParent, aPos, aSize )
   LOCAL oBrowse
    oBrowse := XbpBrowse():new( oParent,, aPos, aSize,, .F. )
    oBrowse:keyboard := {|x1,y2,oXbp|msgbox("Taste : "+str(x1,3))} 
    oBrowse:create()
geändert und im Beispiel reagiert nun der Browser auf die Space-Taste und gibt die Zahl 32 für Blank aus.
Dein Problem müsste woanders liegen.

@Sören,

bei deinem Beispiel wird bei jeder Spacetaste die Funktion aufgerufen.
Man müsste da zumindest prüfen ob der Browser das Ereignis ausgelöst hat.
Aber der Keyboard-Slot ist sicher die bessere Lösung.
Gruß
Hubert
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

Hallo Hubert,

du hast natürlich recht mit Deinem Einwand. Das sollte ja auch nur ein (unvollständiges) Beispiel sein.

In der Funktion mache ich dann z.B. Folgendes:

Code: Alles auswählen

FUNCTION ZeileMarkieren( oBrowse )

  if SetAppFocus() == oBrowse 

    // Markieren ...

    oBrowse:refreshCurrent()
    oBrowse:down()
    oBrowse:refreshCurrent()

  endif

RETURN NIL
Tschüs,
Sören
michaelAC

Beitrag von michaelAC »

Also der Hinweis auf den Event-Loop hat mich noch nicht ans Ziel gebracht, aber schon auf etwas gestoßen, was vermuten lässt, dass das Problem etwas verschachtelter ist.

Denn in MAIN gibt es keinen eigenen Event-Loop. Es werden zwar diverse Eingabefelder erzeugt, aber am Ende der Prozedur wird ein XbpGetController-Objekt erzeugt. Zusammengefasst sieht das so aus:

Code: Alles auswählen

oDlg := XbpDialog():new( AppDesktop(), , aPos, aPrgSize, , .F.)

// Alle darzustellenden Objekte der Anwendung werden mit oDlg:drawingArea verbunden und dem Array aEditControls zugefügt (siehe unten)

oCtrl := XbpGetController():new( oDlg )

bKeyHandler := {|nKey,x,obj| DlgKeyhandler( nKey, obj, aEditControls, oDlg ) }
AEval( aEditControls, {|o| o:keyBoard := bKeyHandler } )

oCtrl:read()
Laut Kommentar läuft in oCtrl:read() der Event-Loop, der mir bislang aber verschlossen bleibt...

Viele Grüße
Michael
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

in der Verarbeitungsschleife mache ich nur Sachen, die für alle gelten sollen. Außerdem kosten Funktionsaufrufe mehr Zeit als Vergleiche und diese Schleife wird viele tausendmal je Sekunde durchlaufen.
Da muss man schon etwas aufpassen. Keyboardevents sind auch wirklich häufig. Wenn du also allen Browsern das Verhalten beibringen willst, ohne diese abzuleiten (was sinnvoller wäre), dann ist der Ort richtig gewählt aber nur eine Abfrage zusätzlich erspart tausende von unnötigen Funktionsaufrufen...

Code: Alles auswählen

if oXbp:isDerivedFrom("XbpBrowse") .and. ;
   nEvent == xbeP_Keyboard .and. mp1 == xbeK_SPACE 
   ZeileMarkieren( oBrowse ) 
endif
Gruß
Hubert
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

Hallo Michael,

Huberts Weg ist schon der bessere (auch was die Performance betrifft, obwohl das bei mit überhaupt nicht spürbar ist).

Mach es so, wie Hubert vorschlug:

Code: Alles auswählen

oBrowse:keyboard := { |x1,y2,oXbp| iif( x1 == xbeK_SPACE, ZeileMarkieren( oBrowse ), NIL ) }
Tschüs,
Sören
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

michaelAC hat geschrieben:

Code: Alles auswählen

AEval( aEditControls, {|o| o:keyBoard := bKeyHandler } )
Diesen XbpGetController() habe ich zwar noch nie gesehen (gibt es in Xbase++ Standard auch nicht), aber die Zeile oben überschreibt deine vorher durchgeführte KEYBOARD Zuweisung mit dem allgemeinen bKeyHandler, wenn der Browser auch in dem Array aEditControls vorhanden ist. Nutzt du Zusatzbibliotheken ?
Wenn nein, muss der Eventloop in deinem Quellcode in dieser Klasse verborgen sein.
Gruß
Hubert
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

@Hubert

oder so...

(Du bist zu schnell für mich :? )

Sören
michaelAC

Beitrag von michaelAC »

Hmm, hier zeichnen sich gerade erstaunliche GUI-Unkenntnis und entsprechender Newbie-Alarm ab...

Da es zum verwendeten XbpGetController keinen Eintrag in der Alaska-Hilfe gab, habe ich im Quelltext entdeckt, dass die Klasse lesbar implementiert ist. Dort wird u.a. gesagt, dass man durch Parameterauswahl der Method :read() festlegen kann, ob ein interner oder externer Event-Loop verwendet werden soll.

Im vorliegenden Programm ist es so, dass es mindestens zwei Browser geben wird, die sich natürlich unterschiedlich verhalten sollen, wenn der Nutzer dort die Spacetaste drückt. Die Frage, die ich mir stellen, ist nun, ob und wo ich was am sinnvollsten einbaue. Den nicht existierenden Event-Loop müsste ich wahrscheinlich eh nachrüsten, nur wo?

Viele Grüße
Michael
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

ich habe mal 10 Finger Schreibmaschinen schreiben gelernt (die Note war eine Katastrophe, aber beim PC gibt es ja die Löschtaste ;-) ).

Die Funktion hat mein Editor auch gefunden, sie stammt von Alaskas \source\solution\xbpget\...
Gruß
Hubert
michaelAC

Beitrag von michaelAC »

brandelh hat geschrieben: ich habe mal 10 Finger Schreibmaschinen schreiben gelernt (die Note war eine Katastrophe, aber beim PC gibt es ja die Löschtaste ;-) ).
@alleNicht10FingerSchreiber: So ein VHS-Kurs ist wirklich jedem zu empfehlen - ich kann mich Huberts Statement zu 100% anschließen :-)

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

Beitrag von brandelh »

Hi,

im Kopf der Datei XbpGetc.PRG ist dies ausgeführt.

du must deine Zeile oCtrl:read() gegen den externen loop austauschen und wohl auch die Browser nicht dem Array aEditControls zufügen, damit deine eigenen Keyboard Routinen greifen.

Ich selbst habe dieses XbpGet nie benutzt, da meine Programme sich wie richtige Windowsprogramme verhalten sollen. Einigen SLE habe ich mehr beigebracht (Datentypen Datum, Nummerisch, spezial etc.) aber ich habe in letzter Zeit auch viel Gutes über SLEPIC (Klasse mit SLE + Picture) gelesen (gibt es z.B. von Phils Seite). Dennoch muss es auch mit deiner Klasse funktionieren, wenn die SLE nicht MODAL arbeiten.
Gruß
Hubert
michaelAC

Beitrag von michaelAC »

Soweit ich es erkennen konnte, wird der Browser nicht zu aEditControls hinzugefügt. Ich habe mir auch den DlgKeyHandler() genauer angeschaut, aber der wird offensichtlich gar nicht beim Browser ausgewertet.

Ich versuche daher mal als nächstes, den Event-Loop des oCtrl extern zu implementieren und hoffe, dass sich zumindest die bisherige Anwendung noch genauso verhält... Prinzipiell hätte ich nichts dagegen, wenn die Anwendung sich wie eine richtige Windows-App verhielte, aber die Codebasis ist doch schon sehr umfangreich, so dass ich mich zuerst nur an die nötigsten Änderungen wagen wollte.

Grüße
Michael
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

ich habe mir mal die Beispiele zu dem XbpGet angesehen, sehen schon gut aus. Anwender die Clipper Programme gewohnt sind werden es lieben und die anderen müssen ja nicht meckern.
Gruß
Hubert
michaelAC

Beitrag von michaelAC »

Ich bin weder Clipper-Programmierer noch habe ich großartig andere GUI-Erfahrung - daher stehe ich nur ratlos vor dem großen Codeberg ;-)

Wenn ich jetzt den Event-Loop extern in seiner einfachsten Variante implementiere (wie im obigen Beispiel von Sören), erhalte ich für die Zeile

Code: Alles auswählen

oXbp:handleEvent( nEvent, mp1, mp2 )
die Fehlermeldung, dass der Parameter den falschen Typ hat, "Operation: :Motion"

Offenbar geht es um die Maussteuerung, um die ich mich ja gar nicht kümmern möchte. Auch wenn ich die Maus nicht anrühre, komme ich beim Programmstart nicht über diese Stelle hinaus. Meine Vermutung ist jetzt, dass ich den Event vom falschen Objekt "handlen" lasse, aber auch mit oDlg klappt es spontan nicht.

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

Re: XbpBrowse und :keyboard-Callback

Beitrag von AUGE_OHR »

hi,
michaelAC hat geschrieben:

Code: Alles auswählen

   oBrowse:keyboard      := {|n,x,obj| WZAEventHandler()}
Zum Verständniss des Xbase++ Eventhandler :

Code: Alles auswählen

   nEvent := AppEvent( @mp1, @mp2, @oXbp, nTimeOut )
Man benötigt beim Keyboard 2 Parameter :

Code: Alles auswählen

   CASE nEvent == xbeP_Keyboard .and. mp1 == xbeK_SPACE 
da du die aber nicht an WZAEventHandler() übergibst wird kein
Eventloop darauf reagieren. versuche es mal mit :

Code: Alles auswählen

   oBrowse:keyboard      := {|nKeyCode, uNIL, self| WZAEventHandler(nKeyCode)}
damit müsste es gehen.

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

Beitrag von brandelh »

Hallo Michael,

du must den externen Eventhandler nutzten, der in
\source\solution\xbpget\xbpgetc.prg beschrieben ist.
Die Variablen namen müssen eventuell angepaßt werden.
Wenn du es nicht hinbekommst, dann kopiere die komplette Main Routine hier rein.
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

wobei da nichts wirklich neues steht...

Code: Alles auswählen

oCtrl:read() 
->
oCtrl:read(,.f.) 
do while Bedingung
    nEvent := AppEvent(@mp1,@mp2,@oXbp)
    oXbp:handleEvent(nEvent,mp1,mp2)
enddo
vermutlich wirst du oben hinter PROC MAIN noch
local mp1:=NIL, mp2:=NIL, oXbp:=NIL, nEvent
einfügen.
Gruß
Hubert
michaelAC

Beitrag von michaelAC »

@Jimmy: Ich habe fast alle Kombinationen der Callback-Definition ausprobiert, also in dieser Zeile:

Code: Alles auswählen

oBrowse:keyboard      := {|nKeyCode, uNIL, self| WZAEventHandler(nKeyCode)}
Die beiden Event-Zeilen kamen bislang mangels Event-Loop nicht zum Einsatz, da die Events irgendwie anders abgehandelt wurden.


@Hubert: Den von Dir genannten Code habe ich auch unten in MAIN eingefügt:

Code: Alles auswählen

   oCtrl:read( , .F. )
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( mp1, mp2, oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO
Der Compiler meldet auch keinen Fehler; der Aufschrei bezüglich ":Motion" erfolgt erst zur Laufzeit. Daher gehe ich davon aus, dass oXbp an dieser Stelle des Codes vom falschen Typ ist.

Ich habe es testhalber mal mit folgendem probiert:

Code: Alles auswählen

   oXbp = XbpCrt():new()
Im Resultat fährt die Anwendung hoch, zeigt ihr Bild, reagiert aber auf gar nichts mehr, selbst nicht den X-Klick oben rechts...

Den MAIN-Quelltext wollte ich nicht direkt posten, da die Prozedur gute 2000 Zeilen lang ist... Eventuell reduziere ich den Code auf ein paar Demo-Elemente und poste ihn dann bzw. stelle ihn bereit.

Ich werde mich allerdings wahrscheinlich erst wieder Ende der Woche bzw. nächsten Dienstag intensiv hiermit befassen, da ich momentan noch auf einer anderen Baustelle meine Zeit verbrate(n muss)...

Auf jeden Fall großen Dank für die vielen hilfsbereiten Beiträge!

Viele Grüße
Michael
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16517
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Michael,
michaelAC hat geschrieben:

Code: Alles auswählen

   oCtrl:read( , .F. )
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( [color=red]@[/color]mp1, [color=red]@[/color]mp2, [color=red]@[/color]oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO
Du musst die Parameter an AppEvent per Referenz übergeben!
Ich habe meine Korrekturen oben rot eingefärbt.

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12909
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 46 Mal

Beitrag von AUGE_OHR »

hi,

also ich hab mir den source des XbpGetController noch mal angesehen
( C:\ALASKA\XPPW32\SOURCE\samples\solution\xbpget\xbpgetc.prg )

Es gibt die "METHOD XbpGetController:read" welches einen Eventloop
hat (bei lModal := .T. ) den du wohl benutzt, den ohne Eventloop geht
bei GUI gar nichts.

Es gibt aber auch eine "METHOD XbpGetController:keyboard()" welche
von der XbpGET Class genutzt wird. Dieser "fängt" nun deine Keyboard
Eingaben ab
der Aufschrei bezüglich ":Motion" erfolgt erst zur Laufzeit
hm ... bei XbpGET gib es kein ":Motion" ... das muss "woanders" her
kommen ... (XbpBrowse ? )

solange du eine "Modales Read" (Variante 1) benutzt wird das wohl nicht,
also versuche mal die Variante 2

Code: Alles auswählen

    oController:read( , .F. )
    DO WHILE Bedingung
        // copy/paste aus dem Source OHNE @
*       nEvent := AppEvent( mp1, mp2, oXbp )

       nEvent := AppEvent( @mp1, @mp2, @oXbp )
       oXbp:handleEvent( nEvent, mp1, mp2 )
    ENDDO
gruss by OHR
Jimmy
p.s. vergiss XbpGET und nimm SLEPic (von James Loughner)
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

p.s. vergiss XbpGET und nimm SLEPic (von James Loughner)
Hi Jimmy,

ich vermute, dass die Anwendung recht ausführlich ist, wenn es schon 2000 Zeilen in der MAIN sind. Da würde ich an seiner Stelle beim aktuellen Kenntnisstand nichts ändern.

Hi Michael,

sieh dir die ROTEN @ in Martins Posting an (ich hatte sie auch drin).
Damit müsste zumindest das aktuelle Programm machen was es soll.
Dann erzeuge die Fehlermeldung und sieh in der XPPERROR.LOG nach wo die fehlerhaften Zeilen sind. Bei diesen Zeilen musst du suchen. Wenn dort nichts steht, muss vorher was schief gelaufen sein.

Wenn du kein Clipper Programmierer warst und in Xbase++ kaum eingearbeitet ist es aber auch schwer. Nur den Kopf nicht hängen lassen, wir haben schon ganz anderes gemeistert ;-)
Gruß
Hubert
Antworten