[ERLEDIGT] SetAppFocus() bockt

Grafische Primitive, XbaseParts und Darstellungsfragen allgemein.

Moderator: Moderatoren

Antworten
georg
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2824
Registriert: Fr, 08. Feb 2008 21:29
Hat sich bedankt: 95 Mal
Danksagung erhalten: 13 Mal

[ERLEDIGT] SetAppFocus() bockt

Beitrag von georg »

Guten Morgen -


in einem kleinen Dialog-Programm soll der Focus beim Aufruf im zweiten XbpSLE stehen, was auch funktioniert. Ich "sammle" alle Dialog-Elemente in einem Array, aEntries. Das Setzen des Focus funktioniert. Danach kommt ein Event-Loop, verbunden mit einer über einen XbpPushButton ausgelösten Aktion. Nach dieser Aktion soll der Focus auf das erste Dialog-Elment in aEntries gesetzt werden, für das noch kein Wert vorliegt:

Code: Alles auswählen

   SetAppFocus(aEntries[2])
Nach dem Erstellen des Dialogs wird mit dieser Anweisung der Focus auf das 2. Element gesetzt, was funktioniert: wenn der Dialog sichtbar wird, steht der Cursor im 2. Element.

Code: Alles auswählen

   oXbp := XbpPushButton():new(oDlg, oDlg, aPos, aSize)
   oXbp:caption := "Browse Files"
   oXbp:tabStop := .T.
   oXbp:activate := {|uNIL1, uNIL2, self| SearchCoverImage(aEntries, self)}
   oXbp:create()
   AAdd(aEntries, oXbp)
Hier erstelle ich den XbpPushButton, der das Suchen (und Einfügen) eines Cover-Bildes steuert, und hier der Quellcode der entsprechenden Funktion:

Code: Alles auswählen

STATIC FUNCTION SearchCoverImage(aEntries, oButton)
   Local cOldDrive, cOldDir, cNewFile, cMask, cType
   Local nUpdPos, nLen, nI
   Local oFD

   nLen := Len(aEntries)
   FOR nI := 1 TO nLen
      IF aEntries[nI] == oButton
         nUpdPos := nI - 1
         EXIT
      ENDIF
   NEXT

   IF nUpdPos == NIL
      ConfirmBox(, "Could not locate SLE to place image location", "Error", XBPMB_OK, XBPMB_INFORMATION)
      RETU(.F.)
   ENDIF

   cOldDir := CurDir()
   cOldDrive := CurDrive()
   IF Left(cOldDir, 1) <> "\"
      cOldDir := "\" + cOldDir
   ENDIF

   cMask := "*.jpg"

   oFD := XbpFileDialog():new(SetAppWindow(), SetAppWindow())
   oFD:create()
   cNewFile := oFD:open(cMask)

   CurDrive(cOldDrive)
   CurDir(cOldDir)

   IF !Empty(cNewFile)
      aEntries[nUpdPos]:setData(cNewFile)
      nLen := Len(aEntries)
      FOR nI := 1 TO nLen
         cType := aEntries[nI]:className()
         IF cType <> "XbpPushButton"
            IF Empty(aEntries[nI]:getData())
               // ConfirmBox(, "Setting focus to " + cType + " " + Str(nI), "Information", XBPMB_INFORMATION, XBPMB_OK)
               SetAppFocus(aEntries[nI])
               EXIT
            ENDIF
         ENDIF
      NEXT
   ENDIF

RETURN (.T.)
Ziel für die :setData() Operation ist das XbpSLE vor dem XbpPushButton, der diese Funktion aufruft.

Die Abfrage auf den Typ erfolgt, da XbpPushButton keine :getData() Methode besitzt.

Die (derzeit kommentierte) ConfirmBox() hat mir im Test bestätigt, dass der SetAppFocus() z.B. auf das zweite Dialog-Element erfolgt. Danach steht der Focus aber immer noch auf dem XbpPushButton, der diese Funktion aufgerufen hat, und nicht auf dem Dialog-Element, das in SetAppFocus() angegeben wurde.

Was übersehe ich hier?
Liebe Grüsse aus der Eifel,

Georg S. Lorrig
Redakteur der Wiki des Deutschprachigen Xbase-Entwickler e.V.
Dieter
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 237
Registriert: Do, 14. Aug 2008 14:59
Wohnort: Straelen
Hat sich bedankt: 2 Mal
Danksagung erhalten: 3 Mal

Re: SetAppFocus() bockt

Beitrag von Dieter »

Hallo Georg,
ich glaube, du gehst die Sache viel zu kompliziert an.
Beispiel: oXbp:activate := {|uNIL1, uNIL2, self| SearchCoverImage(aEntries, self)
Wieso übergibst du statt aEntries nicht das Dialogobjekt oDlg und sorgst vorher dafür, dass alle Parts als Instanzvariablen des Dialogfensters eingerichtet werden?
In der Function SearchCoverImage(oDlg) könntest du den Focus dann direkt mit SetappFocus(oDlg:sleXY) setzen.
Viele Grüße

Dieter

Was man nicht versteht, besitzt man nicht.
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:

Re: SetAppFocus() bockt

Beitrag von brandelh »

Der zusätzlich eingeblendete Dateiauswahldialog nimmt den Focus auf vom Fenster selbst.
Hier würde ich zunächst setAppFocus(oDlg) machen, den du bei dem Code als Parameter übergeben müsstest.

Dann schreibst du einmal, dass du das nächste LEEREN SLE und einmal dass du das SLE VOR dem Pushbutton haben willst ...
Das SLE Auszuwählen kann man am schnellsten mit aScan() indem du nach oButton im aEntry oder so suchst.

Ob ein Objekt ein SLE ist kann man mit :isDerivedFrom( "XbpSLE" ) abfragen.
Aber egal wie du setAppFocus() einsetzt, die nächste MSGBOX() oder ähnliches wird den Focus wieder nehmen, daher sind die fürs debugging von setAppFocus() ungeeignet, ebenso der debugger selbst ;-)
Gruß
Hubert
georg
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2824
Registriert: Fr, 08. Feb 2008 21:29
Hat sich bedankt: 95 Mal
Danksagung erhalten: 13 Mal

Re: SetAppFocus() bockt

Beitrag von georg »

Hallo -


@Dieter: aEntries enthält alle Xbase-Parts des Dialogs. Alle Xbase-Parts liegen ihrerseits auf XbpStatic, so dass ich - ausgehend von oDlg - immer erst die :childList() des XbpStatic abrufen muss, um an das Xbase-Part zu kommen, was deutlich aufwändiger ist, als aEntries zu durchsuchen.

@Hubert: es sind zwei verschiedene XbpSLE gemeint. Der XbpPushButton startet die Dateiauswahl, und das Ergebnis wird in das XbpSLE geschrieben, das sich - bezogen auf die Anzeigereihenfolge - VOR dem XbpPushButton befindet. Nach dem :setData() ist dieses XbpSLE nicht mehr leer, so dass es rein definitionsgemäss aus der Suche nach einem "leeren" Xbase-Part rausfällt.

DANACH soll der Focus auf das erste XbpSLE (oder XbpMLE) gehen, dessen :editBuffer() leer ist. Da alle in Frage kommenden Xbase-Parts entweder XbpSLE oder XbpMLE sind (sowie drei XbpPushButton), genügt es, die XbpPushButton auszugrenzen und lediglich :getData() abzufragen.
Aber egal wie du setAppFocus() einsetzt, die nächste MSGBOX() oder ähnliches wird den Focus wieder nehmen, daher sind die fürs debugging von setAppFocus() ungeeignet, ebenso der debugger selbst ;-)
Der ConfirmBox()-Aufruf war ein Akt der Verzweiflung, um zu sehen, ob die Routine richtig arbeitet und das korrekte Xbase-Part ermittelt. Nachdem klar war, dass das richtige Xbase-Part ausgewählt wird, habe ich die ConfirmBox()-Anweisung auskommentiert. Sollte eigentlich noch sichtbar sein, der Kommentar.

Und auch ohne Debugger wird der Focus nicht gesetzt, sondern verbleibt beim XbpPushButton.
Hier würde ich zunächst setAppFocus(oDlg) machen, den du bei dem Code als Parameter übergeben müsstest.
Ja, habe ich mal entsprechend angepasst:

Code: Alles auswählen

            IF Empty(aEntries[nI]:getData())
               // ConfirmBox(, "Setting focus to " + cType + " " + Str(nI), "Information", XBPMB_INFORMATION, XBPMB_OK)
               SetAppFocus(oDlgWin)
               SetAppFocus(aEntries[nI])
               EXIT
            ENDIF
Ergebnis: der Focus steht weiterhin auf dem XbpPushButton.

Vorstellen könnte ich mir, dass während des Abarbeiten des :activate Slot der Focus nicht verschoben werden kann. Ich habe alternativ diese beiden Varianten auch noch ausprobiert:

Code: Alles auswählen

               PostAppEvent(xbeP_SetInputFocus, NIL, NIL, aEntries[nI])

Code: Alles auswählen

               PostAppEvent(xbeP_SetDisplayFocus, NIL, NIL, aEntries[nI])
In beiden Fällen: keine Änderung des Zustands.
Liebe Grüsse aus der Eifel,

Georg S. Lorrig
Redakteur der Wiki des Deutschprachigen Xbase-Entwickler e.V.
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:

Re: SetAppFocus() bockt

Beitrag von brandelh »

dass ein Button so hartnäckig den Focus behält habe ich noch nicht erlebt, aber gerade bei solchen Sachen hilft meine DebugPrint() Funktion, weil du online sofort siehst was
abläuft. In einem eigenen Fenster ohne auch nur ein Finger zu bewegen.
Nur an den kritischen Stellen DebugPrint("Ich bin da") mit sinnvollerem Text eingeben.
Zu klären ist, ob überhaupt ein focuswechsel stattfindet ...

* ich meine oben hast du das bejaht !
Wenn er also weg geht, warum kommt er zurück ? Dazu muss es einen anderen Aufruf geben, automatisch macht der das nicht !

* wenn nein ???

Warum bekommt eigentlich der Button den Focus ?
Wenn ich z.B. Drucken oder Sortieren oder ... anklicke bleibt der Focus im SLE !

pbDrucken:pointerFocus := .f.
pointerFocus Falls mit der Maus auf einen Pushbutton geklickt wurde, bekommt er den Eingabefokus.
Attribut: EXPORTED
Datentyp: Logisch (.T.)
Gruß
Hubert
georg
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2824
Registriert: Fr, 08. Feb 2008 21:29
Hat sich bedankt: 95 Mal
Danksagung erhalten: 13 Mal

Re: SetAppFocus() bockt

Beitrag von georg »

Hallo, Hubert -


darum war ich ja auch so "grosszügig" mit meinem Codebeispiel. Neben dem Erstellen des XbpPushButton kommt ein ganz normaler Event-Loop ohne besondere Anweisungen zum Tragen.

Wobei mit dem Anklicken des XbpPushButton der den Focus bekommt. Aus meiner Sicht daran erkennbar, dass der XbpPushButton einen kleinen, inneren Rahmen bekommt, wenn man ihn mit der Maus anklickt. Eventuell besetzt Du die iVar pointerFocus mit .F., in diesem Fall bekommt der Button keinen Focus, wenn man ihn anklickt. Ich verwende den Standardwert, d.h. .T. und somit bekommt der XbpPushButton auch den Focus.

Also, zum Ablauf:

ich rufe den Dialog auf, der Focus steht auf dem 2. Xbase-Part. Ich klicke auf den XbpPushButton, wähle eine Datei aus, der Pfad zur Datei wird in den XbpSLE vor dem XbpPushButton eingetragen, es werden alle Xbase-Parts durchgesehen und eines gefunden, bei dem der Rückgabewert von :getData() leer ist. Ich führe SetAppFocus(aEntries[nI]) aus und verlasse die Funktion. Der Focus steht immer noch auf dem XbpPushButton.
Liebe Grüsse aus der Eifel,

Georg S. Lorrig
Redakteur der Wiki des Deutschprachigen Xbase-Entwickler e.V.
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:

Re: SetAppFocus() bockt

Beitrag von brandelh »

georg hat geschrieben:darum war ich ja auch so "grosszügig" mit meinem Codebeispiel. Neben dem Erstellen des XbpPushButton kommt ein ganz normaler Event-Loop ohne besondere Anweisungen zum Tragen.
nun bei meinen Augen bin ich wohl schon in der Mitte ausgestiegen ;-)
Ein offensichtlicher Fehler ist bis jetzt keinem aufgefallen ...
georg hat geschrieben: Wobei mit dem Anklicken des XbpPushButton der den Focus bekommt. Aus meiner Sicht daran erkennbar, dass der XbpPushButton einen kleinen, inneren Rahmen bekommt, wenn man ihn mit der Maus anklickt.
das stimmt !
georg hat geschrieben: Eventuell besetzt Du die iVar pointerFocus mit .F., in diesem Fall bekommt der Button keinen Focus, wenn man ihn anklickt.
Ich verwende den Standardwert, d.h. .T. und somit bekommt der XbpPushButton auch den Focus.
so ist es, wenn ein Button KEINEN Focus bekommen soll, setzte ich .F.
Was ich damit ausdrücken wollte war zweierlei:
1. was passiert wenn der Button den Focus nicht erhalten kann, ob dann die SLE den Fokus richtig wechseln.
2. Eventuell braucht dein Button keinen Focus, dann hättest du das Problem schon mal weg ;-)
georg hat geschrieben:Hallo, Hubert -
Also, zum Ablauf:
ich rufe den Dialog auf, der Focus steht auf dem 2. Xbase-Part.
Ich klicke auf den XbpPushButton, wähle eine Datei aus, der Pfad zur Datei wird in den XbpSLE vor dem XbpPushButton eingetragen, es werden alle Xbase-Parts durchgesehen und eines gefunden, bei dem der Rückgabewert von :getData() leer ist. Ich führe SetAppFocus(aEntries[nI]) aus und verlasse die Funktion. Der Focus steht immer noch auf dem XbpPushButton.
und genau hier liegt dein Gedankenfehler :!: :!: :!:

1. Du beschreibst, was du auf der Oberfläche siehst (das geht oft aber nicht beim Verhalten von Events).
2. Du gehst davon aus, dass genau das passiert was du gerade im Code / Oberfläche ansiehst. Windows verarbeitet über 1000 Events pro Sekunde !
3. Nur weil du nichts siehst heist es nicht, dass nichts passiert ist.

Dein Button verliert seinen Eingabefocus in dem Moment wo ein anderes Fenster aktiv wird ... genau die Dateiauswahl ...
Noch ist die GUI in dem Code gefangen (aber hinter der Datei Auswahl ist der Button sicher nicht mehr umrandet), du gibts den setAppFocus() Befehl,
vielleicht wird er ausgeführt (die GUI hat ja einen eigenen Thread), vielleicht auch nicht - wer weiß was im inneren abgeht ...
Ganz am Ende wird deine Anwendung die Nachricht erhalten, dass sie wieder den focus hat (display focus / input focus ? ) ...
Xbase++ selbst setzt keinen Inputfocus in SLEs (das habe ich meiner Klasse beigebracht), aber wohl den displayfocus auf das Fenster oder sogar auf den Button ...

Auch wenn zwischenzeitlich der der Inputfocus sogar richtig im SLE WAR (keine Ahnung) nun wird er wieder auf dem Button sein ...

Auch wenn du es nicht glaubst, aber ich habe bei meiner Basisklasse zu den Fenstern damals lange experimentiert.
OHNE set alternate to Datei oder live DebugPrint() oder was auch immer du verwenden willst, kannst du NIE WISSEN was wirklich abgeht !

Ich auch nicht, obwohl ich mir ziemlich sicher bin richtig GERATEN zu haben :badgrin:
Gruß
Hubert
Dieter
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 237
Registriert: Do, 14. Aug 2008 14:59
Wohnort: Straelen
Hat sich bedankt: 2 Mal
Danksagung erhalten: 3 Mal

Re: SetAppFocus() bockt

Beitrag von Dieter »

Hallo Georg,
Georg hat geschrieben:
@Dieter: aEntries enthält alle Xbase-Parts des Dialogs. Alle Xbase-Parts liegen ihrerseits auf XbpStatic, so dass ich - ausgehend von oDlg - immer erst die :childList() des XbpStatic abrufen muss, um an das Xbase-Part zu kommen, was deutlich aufwändiger ist, als aEntries zu durchsuchen.
Hier liegt dein Gedankenfehler: Die xbase-Parts können alle als Instanzvariablen eines xbpDialog mittels Class-Code angelegt werden.
Beispiel:

Code: Alles auswählen

 CLASS meinDialog FROM DKXbpDialog
		EXPORTED:
		VAR tab1, tab2, tab3
		VAR sle1, sle2,...usw
Anschließend kann man von diesem Klassenfenster durch Vererbung das eigentliche Dialog-Fenster erzeugen.
Beispiel:

Code: Alles auswählen

Procedure meinFenster()
Local oDlg 
oDlg := meinDialog():new()
...
oDlg:create()
oDlg:Tab1 := xbpTabpage():new(oDlg:drawingArea)
...
oDlg:Tab1:create()
...
oDlg:sle1 := xbpSle():new(oDlg:Tab1) 
usw.
Man sieht dass, das sle1 nicht in der Drawingarea von oDlg liegt und trotzdem ist es direkt über oDlg:sle1 ansprechbar!
Viele Grüße

Dieter

Was man nicht versteht, besitzt man nicht.
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9358
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 101 Mal
Danksagung erhalten: 361 Mal
Kontaktdaten:

Re: SetAppFocus() bockt

Beitrag von Tom »

Was ist denn, wenn Du das Objekt, auf das der Fokus zu setzen ist, als Rückgabewert der Funktion definierst und das SetAppFocus() nach dem Funktionsaufruf im Activate-Slot des Pushbuttons vornimmst?

Außerdem/alternativ würde ich mal den SetInputFocus-Slot des Buttons belegen und nachschauen, wer ihn auslöst.
Herzlich,
Tom
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

Re: SetAppFocus() bockt

Beitrag von AUGE_OHR »

georg hat geschrieben:Was übersehe ich hier?
das XbpFileDialog() ein "separates" Fenster ist ;)

ich würde auch den Parent (oDlg) übergeben und es so machen

Code: Alles auswählen

SearchCoverImage(aEntries, oButton,oDlg)
...
   oFD:destroy()
   SLEEP(1) 
   SetAppWindow(oDlg)
   SetAppFocus(aEntries[nI])
   EXIT
gruss by OHR
Jimmy
georg
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2824
Registriert: Fr, 08. Feb 2008 21:29
Hat sich bedankt: 95 Mal
Danksagung erhalten: 13 Mal

Re: SetAppFocus() bockt

Beitrag von georg »

Guten Morgen,


also, ich bin den beiden Vorschlägen mal nachgegangen:

@Tom: unverändert, der Focus steht auf dem XbpPushButton
@Jimmy: unverändert, der Focus steht auf dem XbpPushButton
Liebe Grüsse aus der Eifel,

Georg S. Lorrig
Redakteur der Wiki des Deutschprachigen Xbase-Entwickler e.V.
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

Re: SetAppFocus() bockt

Beitrag von AUGE_OHR »

hm ... und bist wirklich sicher das dass XbpFileDialog() Fenster zerstört ist ?

Code: Alles auswählen

   oFD:destroy()
   // warten bis es wirklich zerstört ist
   SLEEP(1) 
   ...
gruss by OHR
Jimmy
georg
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2824
Registriert: Fr, 08. Feb 2008 21:29
Hat sich bedankt: 95 Mal
Danksagung erhalten: 13 Mal

Re: SetAppFocus() bockt

Beitrag von georg »

Hallo,


ich greife das noch einmal auf.

"Error in front of device" - der Fehler lag beim Programmierer.

Zur Erklärung: ich habe die Xbase-Parts mit eigenen überladen und darin auch die SetAppFocus() umgeleitet. Hintergrund ist die Tatsache, dass beim Wechsel von einem anderen Programm zu einem Xbase-Programm das letzte, aktive Xbase-Part den Focus verliert (ich bin jetzt zu faul, das Thema rauszusuchen). Dafür habe ich eine interne Steuerung geschrieben, die das letzte, aktive Xbase-Part im XbpDialog() in einer Instanz-Variablen festhält.

In diesem Fall war es eben der XbpPushButton() - heute hatte ich endlich mal die Zeit, das anzugehen (es nervte mich schon lange) und musste (leider) wieder mal feststellen: "error in front of device".
Liebe Grüsse aus der Eifel,

Georg S. Lorrig
Redakteur der Wiki des Deutschprachigen Xbase-Entwickler e.V.
Antworten