Fokus im Multithreading

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Antworten
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Fokus im Multithreading

Beitrag von Jan »

In meinem hier bereits ausführlich besprochenen und von Euch tatkräftig unterstützten Projekt 8) arbeite ich nun erfolgreich mit mehreren Threads. Dabei ist mir folgenes aufgefallen: Sobald ich ein anderes Programm starte oder über den Taskmanager bzw. die Taskleiste dahin wechsle, dann über Taskleiste etc. wieder zu "meinem" Programm zurückkehre, liegt der Fokus immer auf dem Hauptthread des Programmes. Auch wenn ich das Programm auf einem anderen Thread (=Dialog) verlassen habe.

Wie kann ich das vermeiden? Mir ist es wichtig, daß ich nach einem Taskwechsel wieder an der Stelle in meinem Programm lande, wo ich es verlassen habe.

Jan
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,

Xbase++ merkt sich nicht welches XbpObject den Fokus zuletzt verloren hat. Ich habe deshalb eine Instanzvariabel bei meinen Fenstern hinterlegt und in der LostInputFocus Methode wird dann das SELF in der Variablen des Fensters hinterlegt. Du bräuchtest wahrscheinlich 2 Variablen, am besten Statics in einer Funktion, eine für das Fenster und eine für das Xbp.

Eventuell geht es ja auch einfacher, mal sehen was die anderen wissen ?

Schöner wäre es natürlich, Xbase++ würde das selbst erledigen ;-)
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Hallo Hubert,

danke für den Hinweis. Da ich gerade erst mit Threads angefangen habe ist das allerdings ziemlich kryptisch für mich. Ich werde mal schauen ob ich das hinbekomme, ansonsten geh ich Dir noch mal damit auf die Nerven 8)
Hubert hat geschrieben:Schöner wäre es natürlich, Xbase++ würde das selbst erledigen
Ja, leider ist es so, daß Xbase++ einem zum Glück viele Freiheiten lässt. Aber eben manchmal, wie in diesem Fall, keine vernünftige Standardeinstellung vorgibt. Es ist ja toll, daß man so einfach mit Threads arbeiten kann, sogar ich habe das immerhin auf Anhieb hinbekommen, das will schon was heißen. Aber das, was ich hier möchte, sollte eigentlich normal "einfach so" funktionieren. Und wenn ich es denn doch anders haben möchte, gibt mir Xbase++ ja die Möglichkeiten, das zu ändern.

Jan
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 Hubert,
wieso sollte man sich das Fenster merken?
Ein SetAppFocus() auf das Objekt, das den Fokus verloren hat, reicht doch aus - egal ob Sle oder Dialog oder...
Oder nicht?

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
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,

das hat mit Threads erstmal gar nichts zu tun.
Geh mal ins MDI Demo Programm.
Setzte den Cursor in ein anderes SLE.
Wechsle in einanderes überlappendes Programm und geh zurück.
Der Focus sitzt wieder in Nachname.
Das Programm hat sich nicht das letzte SLE gemerkt, sondern geht immer in Nachname.
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 »

Martin Altmann hat geschrieben:Ein SetAppFocus() auf das Objekt, das den Fokus verloren hat, reicht doch aus - egal ob Sle oder Dialog oder...
Oder nicht?
eventuell ist das in 1.90 schon verbessert worden, aber wenn in create
nur ein SetAppFocus(oSLE) machte, hatte dieses zwar den Focus, aber die Titelleiste des Fensters oben wurde nicht hervorgehoben angezeigt.

Daher mache ich immer erst setappfocus(oWin) und dann setappfocus(oXbp).
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Hallo Martin,

und ich dachte, es wäre genau andersherum: Ich setze den Fokus auf den Thread (und damit den Dialog), und damit ist das zuletzt aktive Objekt aktiv. In meinem Fall wäre das meist ein Sle oder ein Browse.

Den Fokus auf ein Objekt zu setzen funktioniert bei mir nicht wirklich, weil die oft gleich heißen (z. B. oBrowse)- was aber natürlich änderbar wäre. Außerdem: Funktioniert SetAppFocus() denn Thread-Übergreifend? Also SetAppFocus(oBrowse) sucht sich automatisch DEN Thread, in dem genau dieses Objekt existiert?

Jan
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 Hubert,
stimmt - das ist unschön, aber das Objekt hat trotzdem den Fokus :D
Jan,
Du speicherst ja die Objektadresse im Memory, wenn Du ein self zuweist - insofern müßte das ganze also egal sein, ob da mehrere Threads laufen oder alle Objekte gleich heißen (auch wenn ich von letzterem aufgrund der einfacheren Wartbarkeit abrate)!
Oder täusche ich mich da??

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
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Martin,

jetzt wird mir das ganze zu konfus.

Das ganze Problem ist doch, daß mein Programm nach einem Wechsel zu einer anderen Anwendung nicht mehr weiß, welcher Thread bzw. welcher Dialog denn nun der aktuelle sein soll. Woher soll denn meine Anwendung wissen, welcher z. B. oBrowse der gewünschte ist?

Und wie setze ich den Fokus, wenn ich über den Taskmanager auf meine Applikation zurückkehre? Woher weiß mein Programm, das nun sie wieder im Vordergrund ist?

Angesehen davon komme ich auch mit dem Tipp von Hubert nicht weiter. Ich sitze fest :cry: HILFE!!!

Jan
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 Jan,
Huberts Tipp war, in jedem Control den :killInputFocus()-Slot so zu benutzen, dass dort eine Variable (am Besten global) mit dem Objekt:Self belegt wird.
Und in dem :SetInputFocus()-Slot Deines XbpDialog() setzt Du einfach per SetAppFocus() zurück auf diese globale Variable.
Achtung: Du musst natürlich beim Schliessen Deines XbpDialogs() in der Variablen Dein Hauptfenster ablegen!

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
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Ja, aber den Slot welchen XBPDialoges? Den des 1. Threads?

Vielleicht zur näheren Info: Der 1. Thread des Programmes ist der Hauptbildschirm, von dem aus man per Butttons oder über das Menü weitere Dialoge aufrufen kann. Jeder Dialog startet dabei in einer neuen Tabpage in einem eigenen Thread. Aktiv sein kann dabei jeder Tab, da man ja per Maus zwischen den einzelnen Tabs wechseln kann. Beim Schließen eines Tabs wird der Focus autoatisch auf den höchsten belegten Tab gesetzt.

Jan
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 Jan,
das sollte egal sein! Du müsstest in dem :SetInputFocus()-Slots Deines Hauptfensters den SetApFocus() auf Deine Variable unterbringen können - einfach versuchen :D

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
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,

jedes SLE muss den Erhalt oder den Verlust des Focus an eine Variable melden, d.h. sich selbst dort speichern. Ich ziehe den setInputFocus vor.
Wenn du mehrere Threads hast müsste das dennoch passen, da ja das Fenster im jeweiligen Thread ist und die eventloop auch darin läuft.
Threads selbst haben keinen Focus, nur Fenster und Controls haben den.
Der Anwender wählt also den Thread über das Fenster beim Anklicken aus.

Die Namen sind übrigens gleichgültig, da du ja das Object selbst abspeichert und später wieder mit setappfocus() aktivierst.

Meine Ableitung von XbpDialog, hat eine Instanzvariable ::currentcontrol und eine Methode :setDisplayFocus()

Code: Alles auswählen

*----------------------------------------------------------------------------
METHOD HB_Dialog:setDisplayFocus()  
   if ! Isnil(::HauptAlias)           
      DbSelectArea( ::HauptAlias )
   endif
   IF ::windowMenu # NIL
      ::windowMenu:checkItem( self )
   ENDIF
   ::ShowControls()
   IF ::currentControl <> NIL
      SetAppFocus( ::currentControl )
   ENDIF
RETURN self
Immer wenn ein Fenster also den Focus erhält, und ::currentControl ein Controll enthält, wird dieses Controll aktiviert. Insoweit braucht man sich das Fenster wirklich nicht merken, da dieses ja schon eine setAppFocus() erhalten hat, als der User es anklickte.

So gesehen muss jedes Fenster (im eigenen Thread) genauso wie hier nur das letzte controll dieses Fensters und damit automatisch dieses Threads merken. Und das geht so:

Code: Alles auswählen

in init oder create ...
::currentControl := ::oSleMeinStandard // grundsätzlich
oSleName:setInputFocus := {|n1,n2,oXbp| ::currentControl := oXbp }
Auf diese Art und Weise muß man jedes der Controls bestücken, wenn man eine flexible Version will. Oder man macht sich eine Zwischenklasse jedes Controls, dass dies macht und leitet von der ab.

Ich muß gestehen, dass ich zwar den Mechanismus vorgesehen habe (in meinem HBDialog), tatsächlich aber nicht einsetze. Bisher hat sich keiner beschwert ;-)
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,

ich war langsamer beim Schreiben ...

Jan, gibst du den Focus auf eines deiner Unterfenster ?

Ja, wenn du im Programm eines direkt anzeigst, ohne dass der Anwender auf eines im Hintergrund geklickt hat. Oder als Reaktion auf ein Menü oder Pushbutton.

Was passiert dann ?
Das Fenster führt seine setdisplayfocus Methode aus und zeigt alles an. Dabei erhalten auch die SLE setdisplayfocus Events. (noch hat niemand was getan nach dem Start) Auch ein resize und einige weitere events laufen durch. Nun kommt im crate ein setappfocus() auf ein sle, nun erhält dieses den inputfocus. In dem Moment ruft dieses auch gleich die Methode setinputfocus auf und speichert sich selbst in der ::currentcontrol Variable seines Fensters ! Beide müssen nichts wissen und es ist ihnen auch egal ob weitere Threads am laufen sind. Die Beziehung heist hier Fenster zu Xbp und Xbp zu Fenster.

Nun klickst du auf ein anderes Programm. Deines erhält und erzeugt eine Menge events, die uns jetzt nicht interessieren. Nun Schließt du das andere Programm und Windows müsste das vorherige Fenster anzeigen.

Oder du klickst es direkt an.

In beiden Fällen weden wieder die events des setinputfocus setdisplayfocus aktiv und der Cursor steht im letzten Control.

Zumindest gilt dies für eine sauber programmierte GUI Anwendung.
Nun kann es sein, dass die owner oder parent Beziehungen nicht genau stimmen oder aus anderen Gründen anders gewählt wurden. Dann stimmt manchmal der interne Wert für das letzte angezeigte Fenster nicht.
SetAppWindow() hängt damit auch noch irgendwie zusammen ...
Wenn dann noch Threads hinzukommen, muss man zu dem was oben steht noch eine Threadübergreifende Variable haben, die sich das letzte Dialogfenster gemerkt hat.

Nach gleichen Muster. Entweder man verwendet im SetDisplayFocus() noch ein SetAppWindow(self), oder man erstellt eine eigene Funktion (die müssten doch threadübergreifen sein oder ?)

Code: Alles auswählen

function MyLastActiveWindow(oXbp)
     static oLastActiveWindow := NIL
     if oXbp # nil
        oLastActiveWindow := oXbp
     endif
return oLastActiveWindow
im jeweiligen Fenster dann ein MyLastActiveWindow(self) in die SetDisplayFocus() einfügen.

In der obigen SetDisplayFocus Methode müsste dann noch eingefügt werden:

Code: Alles auswählen

     oWin :=   MyLastActiveWindow() // kann nil sein !
     if oWin # nil
        SetAppFocus(oWin)
     endif
     ...
     nun noch das control setzen...
     
Durch die Auswahl oder setAppFocus(oWin) eines Fensters wird - soweit ich das beurteilen kann - auch der jeweilige Thread automatisch mit gewechselt, denn beide hängen aneinander.
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,

ich habe es jetzt mal in einem meiner Programme scharf geschaltet,
und habe ein zweites gefunden, in dem es scharf war.
Schließlich muss ich mir damals ja was dabei gedacht haben, als ich
die Basisklasse von XbpDialog so erweitert habe :D
Gruß
Hubert
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,
Jan hat geschrieben: jetzt wird mir das ganze zu konfus.
hihi ... zu viele "Fenster" ... :)
Jan hat geschrieben: Das ganze Problem ist doch, daß mein Programm nach einem Wechsel zu einer anderen Anwendung nicht mehr weiß, welcher Thread bzw. welcher Dialog denn nun der aktuelle sein soll. Woher soll denn meine Anwendung wissen, welcher z. B. oBrowse der gewünschte ist?
wieso ? hat dein XbpDialog kein :title ? oder kein :tasklist := .T. ?

Ich hab das "Gefühl" wir reden hier von verschiedenen "Fenstern" :

Code: Alles auswählen

1.) oDlg := XbpDialog():new( oParent,     , aPos, Size, , .F.)
2.) oDlg := XbpDialog():new(        ,oOwner , aPos, Size, , .F.)

3.) oDlg := XbpDialog():new( AppDesktop(),      , aPos, Size, , .F.)
4.) oDlg := XbpDialog():new( AppDesktop(),oOwner , aPos, Size, , .F.)
EVL := Eventloop

ad 1.) "normaler" MDI Client. hat keinen :titlebar / :menu. kein EVL
ad 2.) "owned" MDI Client, meistens "angedockt". kein EVL
ad 3.) "externer" MDI Client, kann :titlebar / :menu haben, MUSS EVL
ad 4.) "externer" MDI Client, ist abhängig vom oOwner, KANN EVL haben

während 1 & 2 "im" oParent (:drawingArea) "Fenster" liegen, also mit
setAppFocus(oPart), muss man bei 3 & 4 erst mit setAppWindow(oFenster)
das "richtige Fenster" wählen bevor man mit setAppFocus(oPart) den
Focus auf den gewünschten "oPart" setzen kann.
Jan hat geschrieben: Und wie setze ich den Fokus, wenn ich über den Taskmanager auf meine Applikation zurückkehre? Woher weiß mein Programm, das nun sie wieder im Vordergrund ist?
Im Taskmanager "siehst" du deine "Fenster" nur dann wenn du
XbpDialog:title := "hier der Title" und o:tasklist := .T. gesetzt hast.
Jan hat geschrieben: Ich sitze fest :cry: HILFE!!!
nicht verzweifeln ... es gibt doch hier das Forum :)

gruss by OHR
Jimmy
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Hi Jimmy,

schönen guten Morgen an den audiophilen Forenteilnehmer 8)

Ohne jetzt irgendetwas von Martin, Hubert, oder Dir Probiert zu haben:
hihi ... zu viele "Fenster" ...
Witzbold! Aber um das zu toppen habe ich ja nun auch aus jedem Fenster einen eigenen Thread gemacht. Ich will es Euch ja nicht zu einfach machen.
wieso ? hat dein XbpDialog kein :title ? oder kein :tasklist := .T. ?
:title -> nein. :tasklist := .T. -> ja. Die Funktion QuisyDialog() ist geblieben wie Du sie kennst.
Ich hab das "Gefühl" wir reden hier von verschiedenen "Fenstern" :
Parent ist der Tab, Owner leer. Wobei die Tabs über oDlg:ChildFromName() als Parent AppDesktop() hat.
nicht verzweifeln ... es gibt doch hier das Forum
Ich weiß 8) Und wenn ich nicht wüßte, das ich hier gut aufgehoben wäre, würde ich nicht solche Fragen stellen. Auch wenn ich mir dabei manchmal etwas bescheuert vorkomme. Aber es (=ich) kann ja nur besser werden!

Oh mann, ist das spät. Hoffentlich habe ich das jetzt alles ordentlich auf die Reihe bekommen. :?

Jan
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,
Jan hat geschrieben:
Ich hab das "Gefühl" wir reden hier von verschiedenen "Fenstern" :

Parent ist der Tab, Owner leer.
versuche mal als Owner auch den oParent zu nehmen.

gruss by OHR
Jimmy
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14655
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Hallo Jimmy,

ändert nichts. Kann ja auch nicht. Denn wenn man den Parent leer lässt, dann wird automatisch der Owner dafür eingetragen, wenn ich mich richtig erinnere.

Jan
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 Jan,

sieh dir oben meine letzte Mail an.

Wenn du keine 'normalen' Fenster hast, muß sich dein Programm zentral das letzte merken und wieder einstellen.
Also hast du doppelte Arbeit. Meine letzten beiden Antworten zusammen sollten eigentlich helfen.
Gruß
Hubert
Antworten