Seite 1 von 4

GUI Programmierung

Verfasst: 01.04.2007 19:00
von TWELVE
Hallo zusammen,

ich bin neu bei PureBasic und auch hier im Forum.Vor einiger Zeit habe ich die Anforderung gehabt, ein Executable für Windows zu programmieren.Aus verschiedenen Erwägungen habe ich dann PureBasic gewählt.Meine letzten Programme davor habe ich auf einem Amiga in Assembler geschrieben.Ist also schon eine Weile her.Ich habe dann also ein kleines Programm in PureBasic getippt ( ~ 700 Zeilen), welches derzeit mit einer Console arbeitet ( welche aber nur für Kontrollzwecke genutzt wird).Um mein Programm flexibler und besser bedienbar zu machen, habe ich mich nun damit beschäftigt, ein GUI dazu zu entwerfen.

Ich habe verschiedene Forms-Editoren genutzt sowie GUI Code von Hand getippt.Das ist nicht das Problem.Ich habe aber scheinbar ein Verständnisproblem, wie die Windows, Event usw. in PureBasic arbeiten.

Mir ist folgendes aufgefallen:

- Loops für Windows Events etc. müssen ständig durchlaufen werden, sonst bleibt das Fenster leer und friert ein

Für mich sieht das so aus, als müßte diese Loop fürs Window ständig laufen, sonst erfolgt kein Refresh und man sieht einen BusyPointer, wenn man mit der Maus drüberfährt.

- da mein "Core" Programm die eigentliche Arbeit macht und nicht das GUI, unterliegt die (zeitliche) Steuerung diesem Core.Das heißt in diesem Hauptprogramm laufen diverse Loops und ich müßte quasi irgendwo mal abzweigen, um die GUI Loop zu bedienen.

Ich habe verschiedene Ansätze probiert, der am besten funktionierende war, mein Core Programm als Procedure zu definieren und vom GUI aus als Thread aufzurufen.Dabei bin ich bei "leidlicher" Funktionalität auf folgende Probleme gestoßen:

- mit GUI frißt mein Programm nun 99% CPU auf

- die Reaktion der Buttons in meinem Fenster ist eher sehr träge.Man muß den Button schon so richtig "reinpressen", ehe das GUI reagiert.

- ich kann einem Thread keine Variablen übergeben.In meinem Fall wird durch einen Button lediglich der Thread gestartet und durch einen anderen der Thread wieder gekillt ( "Start", "Stop").

Das alles ist irgendwie sehr unbefriedigend.Vielleicht habe ich das noch nicht richtig verstanden..? Ich dachte, das das GUI ( Windows, Events, Gadgets etc.) asynchron laufen und vom OS bedient werden.Ich definiere alle Elemente des GUIs und lasse es von mir aus als Hauptprogramm laufen.An entsprechenden Stellen in meinem Code prüfe ich bestimmte Events und reagiere darauf ( also z.B. Schleife läuft und bricht diese abhängig vom Window Event ab).Benötige ich die GUI Elemente nicht mehr, schließe ich alles.Das PureBasic GUI Konzept kommt mir vom Amiga/Intuitions sehr bekannt vor, aber bei diesem mußte man sich nicht um den Refresh der Windows kümmern.

Wer kann mich mal aufklären, wo mein Denkfehler ist..? Wie kann man in Purebasic ein vernünftiges GUI basteln ( Window und ein paar Gadgets), das nicht 99 % der CPU Zeit frißt und nicht träge reagiert..?



Grüße

TWELVE

Verfasst: 01.04.2007 19:07
von Kaeru Gaman
> Wer kann mich mal aufklären, wo mein Denkfehler ist..?

ok.

PB ist keine objekt-orientierte sprache die eine derartige event-bearbeitung zugrundelegt.


- deine event-schleife sollte mit WaitWindowEvent() erstellt werden,
dann brauchst sie keinerlei CPU-zeit solange sie nichts macht.

- dein thread mit dem hauptprogramm sollte ein Delay(1) [mindestens] drin haben,
damit auch zeit für die anderen threads übrigbleibt und das ding nicht auf vollast läuft und eine CPU brät.
dann reagieren auch deine buttons anständig.

- die kommunikation zwischen thread und eventschleife kannst du (easy) über globals lösen,
oder über eigene events, oder über mutexes...

Verfasst: 01.04.2007 20:44
von TWELVE
Hallo,

vielen Dank für Deine Antwort.

Das mit dem WaitWindowEvent hatte ich auch schonmal probiert, aber wenn dies Bestandteil des Hauptprogramms ist, dann steht ja die Bearbeitung an der WaitWindowEvent Stelle, bis ein Event eintrifft.
Das bedeutet, das das Programm in mehreren Threads laufen muß, denn der Hauptteil meines Programms soll nicht auf einen Event warten.

Welche Struktur würde man in diesem Fall am besten benutzen..?

Vielleicht so..?

Hauptprogramm
...
Thread für GUI startet
Event?
Global Variable für Event setzen
...
Thread für Core startet
Event? --> reagiere auf Event
...
Kill Threads
End

Oder eher so..?

Hauptprogramm
...
GUI WaitWindowEvent
Event?
Setze Global Variable für Event oder starte/stoppe Core Thread
...
Thread für Core
...
Kill Thread
End

Es gibt ja immer mehrere Wege, um zum Ziel zu kommen.Gibt es bei PureBasic sowas wie "Best Practices", welche Struktur solche Projekte am besten haben (sollten)..?


Grüße

TWELVE

Verfasst: 02.04.2007 05:54
von Kaeru Gaman
events müssen immer von dem thread verarbeitet werden, der das gadget erzeugt hat.
insofern ist es wichtig, in einem einzigen thread (z.b. im hauptprogramm)
das fenster zu öffnen, alle gadgets zu erzeugen, und alle events zu bearbeiten.
das wäre dann dein GUI-thread.

dein geheimnisvoller Haupttask müßte dann also in einem anderen thread sitzen, damit sich GUI und berechnung nicht blockieren.

falls deine berechnung auch "events" zurückliefern soll,
also die GUI ne fortschrittsanzeigen zeigen soll oder melden wenn was fertig ist,
dann wirds etwas kniffliger.

die einfache lösung wäre, wenn du in der GUI-Schleife ein WaitWindowEvent(20) einbaust, also ein timeout.
damit wäre es möglich, dass der Berechnungsthread statusrückmeldungen über globals tätigt,
die dann vom GUI ausgewertet werden können, auch wenn keine anderen events stattgefunden haben.
darüber ist auch schon einiges geschrieben worden, such mal nach statusbar...

Verfasst: 03.04.2007 21:19
von TWELVE
Hallo,

erstmal vielen Dank für Deine Ausführungen.Ich bin schon einen Schritt weitergekommen, nämlich seit ich WaitWindowEvent() verwende.Damit funktioniert das GUI so, wie ich mir das vorstelle und es reagiert auch flüssig.Leider macht das Programm in der Zeit nichts anderes als warten, so das mein "geheimnisvolles Hauptprogramm" nicht zum Zuge kommt.Die Lösung mit WindowEvent oder WaitWindowEvent(timeout) umgeht das zwar, allerdings kann ich nicht garantieren, das ich nach einer bestimmten Zeit wieder den WindowEvent abfrage ( z.B. warte ich viellleicht ein paar Sekunden auf einen Server), so das mein GUI einschlafen bzw. sehr träge nur reagieren wird.

Hier war ja mein Ansatz, mit Threads zu arbeiten, um ein asynchrones Design zu erreichen.Meine Recherche im Forum bezüglich Threads ist aber leider sehr ernüchternd, da fast durchgängig von der Verwendung von Threads abgeraten wird, da es zu unsicher und zu aufwendig wegen Synchronisierung ist.Ok, das verstehe ich schon, nur was ist die Alternative zu Threads...? Ein schneller Mausklick dauert geschätze 50-100ms, wie kann ich ohne Threads erreichen, das mein Code mindestens in diesem Intervall beim WindowEvent vorbeischaut...? Baue ich WindowEvent Abfragen irgendwo in meinem Core Code ein, in der Hoffnung, der wird schon in regelmäßig bedient...?

Ich kann mir echt nicht vorstellen, das ich der Einzige PB Programmierer bin, der sich mit diesem Problem konfrontiert sieht....

Noch eine weitere Frage in diesem Zusammenhang: Gibt es unter PB so etwas wie einen Timer-Interrupt oder muß man in PB alles mit Delay() Loops timen...? Ich habe bisher nicht allzuviel dazu gefunden...
Grüße


TWELVE

Verfasst: 03.04.2007 21:27
von ts-soft
>> Noch eine weitere Frage in diesem Zusammenhang: Gibt es unter PB so etwas wie einen Timer-Interrupt
Das Forum ist voll von entsprechenden Antworten, z.B.
http://www.purebasic.fr/german/viewtopi ... r&start=10

Verfasst: 04.04.2007 00:23
von TWELVE
@ts-soft: werd ich mir mal ansehen...

Ich habe jetzt mal meinen Code so geändert, das Threads benutzt werden.Das funktioniert schonmal sehr gut, allerdings ist dadurch jetzt ein neues Problem dazugekommen: ich verwende PB Libraries für den seriellen Port ( mvcom) und für Mail/SMTP ( PureSMTP).Im konkreten Fall wird die mvcom von einem Thread aus als Procedure() aufgerufen und scheint da nicht richtig zu funktionieren.Ein String, der eigentlich mit Inhalt aus der Proc innerhalb des Threads zurückkommen kommen sollte, bleibt immer leer.

Ist dieses Problem bekannt..?

Desweiteren bekomme ich eine Fehlermeldung, wenn ich die Option "create thread-safe executable" einschalte:

POLINK: error: Unresolved external Symbol '_PB_StringBasePosition'.

Ich konnte diesen String nirgends in meinem Code finden.Bedeutet dies, das das Symbol in einer Libraries enthalten ist..?


Grüße

TWELVE

Verfasst: 04.04.2007 00:42
von ts-soft
Die userlib ist nicht threadsafe geeignet, entweder den Autoren anhauen,
ob er die Möglichkeit hat eine entsprechende Version zu erstellen, oder
auf API zurückgreifen (OpenComPort() ist undokumentiert in PB4 enthalten,
ansonsten gibts im engl. Forum oder im CodeArchiv entsprechende
CodeSchnippsel.

Verfasst: 04.04.2007 13:54
von bobobo
Bei korrekter Nutzung von RunProgram() sollte eine Kommunikation
zwischen dem geheimnisvollen Hauptprogramm (ghp) mit einem
zweiten GUI-Programm (guip) möglich sein und damit eine hinreichende
Steuerung des ghp sowei eine hinreichende Useranbindung per GUI
über das guip machbar sein.

So entfiele der Zwang zur Anpassung von an sich funktionierenden
Threads.

"Lediglich" die Kommunikation zwschen ghp und guip müsste
angefasst werden weil die funktionierenden Grundfunktionen
des ghp dann wohl bleiben könnten wie sie sind.

Verfasst: 05.04.2007 21:11
von TWELVE
Hallo,

von den beiden bei mir derzeit verwendeten Libraries PureSMTP und mvcom ist wenigstens erstere laut Hilfe Unicode+threadsafe.Nichtdestotrotz habe ich nach einiger Sucherei ein Stück älteren,funktionierenden Code genommen und den GUI da wieder eingebaut - und oh Wunder mvcom funktionierte wieder.Irgendwann hatte ich das Problem gefunden, ich hatte irgendwann vorher eine wichtige Codezeile entfernt, was aber wegen Testbetrieb nicht aufgefallen war.

Ich kann jetzt schonmal folgendes sagen: Das GUI läuft als Hauptprogramm und das Geheimnisvolle als Thread.Alles funktioniert derzeit so, wie ich mir das wünsche.Bislang habe ich noch keine Maßnahmen ergriffen, um die Threads und Hauptprogramm gegeneinander zu schützen.Das Einzige was ich derzeit dafür tue: ich schreibe niemals gleichzeitig in Variablen von einem Thread und dem Hauptprogramm aus.Also eine Seite schreibt immer und die andere Seite liest nur.Und umgekehrt.Abstürze wegen illegalem Memory Access hatte ich auch schon, welche aber nach einer Code Veränderung nicht mehr da sind.Werde ich sicher noch etwas besser machen und mit Mutexes alles gegeneinander abschotten.

Probleme mit dem GUI hatte ich z.B. mit Toggle ButtonGadgets, welche teilweise zwar das Klicken annahmen und ihren Zustand entsprechend umschalteten, aber die Eventabfrage ging leer aus.Dies führte dazu, das die Zustandsvariable des Buttons nun genau invers zum wirklichen Zustand des Buttons war.Nach einer Veränderung des WaitWindowEvent timeouts war das Problem weg.

Das sagt mir, das das Timing für das GUI doch schon sehr wichtig ist und deshalb verstehe ich nicht ganz, wie jemand ein gut reagierendes GUI basteln kann, ohne Threads zu verwenden.Z.B. versendet das Geheimnisvolle ab und zu auch Mails( nur Text-basiert, sehr klein), dies kann je nach Latenz der Verbindung schonmal 10-20 sekunden dauern.Unmöglich, das mein GUI solange wartet, wo es schon sehr sensibel auf timing veränderungen im 100ms Bereich reagiert.

Außerdem kann es von bestimmten Faktoren bedingte Veränderungen von Schleifen-Iterationen geben, einmal läuft sie vielleicht gar nicht durch, ein anderes Mal sind es 100 Durchläufe.Ob letztendlich mein Thread auf den Buttonklick reagiert, ist noch eine ganz andere Frage, aber zumindest muß ich im GUI erstmal den Button klicken können.
Desweiteren muß ich in gewissen Programmteilen Schleifentimings verändern können, ohne damit die Timings des GUIs mit zu verändern.
Im übrigen konnte ich nur mit WaitWindowEvent(timeout) - wie oben von
Kaeru Gaman vorgeschlagen - ein akzeptables GUI basteln, nehme ich WindowEvent mit einem Delay(), dann muß ich erst fünfmal einen Button klicken, ehe er wirklich reagiert.


Insofern verstehe ich echt nicht, warum das Thema Thread so ein heißes Eisen ist.

Dennoch vielen Dank für Eure Beiträge, sie haben mir den entscheidenden Durchbruch gebracht.Weitere Tips&Tricks zu Threads
sind sehr willkommen, andere Tips natürlich auch..!

Grüße

TWELVE