NAS

Java Swing, wo ist/kommt die "Hauptschleife" hin?

DaysShadow

Admiral
Registriert
Jan. 2009
Beiträge
9.269
Hallo,

ich schreibe zur Zeit an einem Swing-Programm, welches dauerhaft nicht-Gui Objekte updaten muss.

Da ich sonst nur Spiele und keine Gui-Anwendungen schreibe und man bei Spielen in aller Regel eine Hauptschleife hat in der alles geupdatet und dann gezeichnet wird, weiß ich gerade nicht wo ich damit hin soll.
Bisher update ich in der render-Methode meines Canvas(Libgdx/Lwjgl), was ich aber umschreiben möchte, da nicht dauerhaft die render-Methode des Canvas aufgerufen werden soll, da das Fenster des Canvas hauptsächlich verdeckt bleibt und ich daher das rendern unterbinde, damit dann momentan aber auch das updaten besagter Objekte.

Also, wohin kann ich das packen?
Einfach in main eine Schleife schreiben oder irgendwo anders?
In main funktioniert es, aber ist das in Ordnung bzw. wie macht man es normalerweise?

Und noch eine Frage wo ich schonmal dabei bin: Ich habe einen Timer(java.util.Timer, nicht als Daemon), der jede Sekunde etwas macht.
Wenn ich jetzt aber das Programm beende, läuft es noch weiter und wird nicht geschlossen.
Bei DISPOSE_ON_CLOSE wird es geschlossen und läuft weiter und bei EXIT_ON_CLOSE bleibt es hängen, also das Fenster geht nicht weg, reagiert aber auch nicht mehr.

Wo könnte da der Fehler liegen?

Bin dankbar für jede Hilfe!
 
Für Swing nutzt man keine Hauptschleife, das ganze System ist eventbasiert. Ein Event wird ausgelöst und bearbeitet und so zeichnet sich auch die Swing-GUI automatisch immer neu.

Solltest du trotzdem aus irgendwelchen Gründen so etwas brauchen, schau mal nach "invokeLater". Du würdest einfach einen Runnable dem Swing-EventDispatcher übergeben, der deine Sachen macht. Ist dieser fertig erzeugt er sich neu und fügt sich wieder in den Dispatcher ein.
 
DaysShadow schrieb:
Bisher update ich in der render-Methode meines Canvas(Libgdx/Lwjgl), was ich aber umschreiben möchte, da nicht dauerhaft die render-Methode des Canvas aufgerufen werden soll, da das Fenster des Canvas hauptsächlich verdeckt bleibt und ich daher das rendern unterbinde, damit dann momentan aber auch das updaten besagter Objekte.

Also, wohin kann ich das packen?

In einen eigenen Thread oder Timer. Wenn sich das irgendwie auf die GUI auswirkt, immer darauf achten, dass Änderungen nur im EventDispatch-Thread (EDT) ausgeführt werden dürfen. Gibt extra Klassen hierfür, die Dir Arbeit abnehmen können (javax.swing.Timer, javax.swing.SwingWorker).


DaysShadow schrieb:
Einfach in main eine Schleife schreiben oder irgendwo anders? In main funktioniert es, aber ist das in Ordnung bzw. wie macht man es normalerweise?

Das kommt wohl immer auf das Programm an. Solche Dinge sollten aber immer in einem eigenen Thread laufen und beim Zugriff auf die GUI entsprechend synchronisieren.


DaysShadow schrieb:
Und noch eine Frage wo ich schonmal dabei bin: Ich habe einen Timer(java.util.Timer, nicht als Daemon), der jede Sekunde etwas macht. Wenn ich jetzt aber das Programm beende, läuft es noch weiter und wird nicht geschlossen.

Warum verwendest Du keinen Daemon? Ist doch genau hierfür da?

java.util.Timer ist übrigens eher eine Legacy-Klasse. Man nimmt heute üblicherweise java.util.concurrent.ScheduledExecutorService
 
Zuletzt bearbeitet: (Fehlerhaftes Quoting)
Ich bin mir an dieser Stelle nicht sicher, ob der Ansatz der richtige ist, vor allem, da du die "Hauptschleife" bei spielen erwähnst.
Dein GUI sollte nicht dafür verantwortlich sein, dauerhaft nicht-GUI Objekte zu updaten (wie man es über die Schleife bei Spielen tun würde).
 
DaysShadow schrieb:
Und noch eine Frage wo ich schonmal dabei bin: Ich habe einen Timer(java.util.Timer, nicht als Daemon), der jede Sekunde etwas macht.
Wenn ich jetzt aber das Programm beende, läuft es noch weiter und wird nicht geschlossen.
Bei DISPOSE_ON_CLOSE wird es geschlossen und läuft weiter und bei EXIT_ON_CLOSE bleibt es hängen, also das Fenster geht nicht weg, reagiert aber auch nicht mehr.

Wo könnte da der Fehler liegen?

Wie wäre es, wenn du den Timer einfach sauber beendest, wenn man das Fenster schließt? (Ich fange normalerweise das Fensterschliessen ab, frage den Benutzer(sind sie sich sicher blahblah) und führe dann eine ,,Aufräummethode'' aus, die Verbindungen zur DB oder so schließt oder wartet bis eine Datei fertig geschrieben wurde etc. . Ich weiß ja nicht, was dein Timer da so für Aufgaben jede Sekunde hat.

Wenn du was ähnliches möchtest, dann solltest du hier mal nachschauen.
http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/event/WindowAdapter.html
http://www.java2s.com/Code/Java/Event/DemonstratingtheWindowListenerwithaWindowAdapter.htm



Zum Loop:
Wenn du nur rendern willst, wenn das Canvas auch sichtbar ist, dann sollte der Loop auch nur das umschließen. Allerdings gibt es jetzt dazu verschiedene Möglichkeiten wie man es am geschicktesten macht.
Ich würde vermutlich, wenn es ein extra Fenster ist, in Richtung FokusEvent gehen. Event wird ausgelöst, wenn das Fenster Fokus erhält, rendern und überprüft dann nach dem Rendern, ob das Fenster weiterhin im Fokus liegt (je nach Aufgabe dann weiter rendern oder fertig nichts tun, dann brauchst du es auch nicht mehr Abfragen und eigentlich kein Loop). (Nachteile natürlich: Kein Fokus kein Rendern).

Das GUI selbst oder so brauchst du nicht in eine Schleife packen.
 
Zuletzt bearbeitet:
@ice-breaker, soares: Danke, ich schaue mir eure Vorschläge mal an. Den Timer als Daemon starten löst schonmal das nicht-beenden-Problem bei DISPOSE_ON_CLOSE.

@carom: Die Gui soll das ja auch gar nicht tun, ich habe nur sonst eben nie mit Guis wie Swing gearbeitet, wo alles eventbasiert abläuft, wo eben nur dann etwas gemacht wird wenn es benötigt ist.
Bisher hatte ich immer eine Hauptschleife in der alles gemacht wurde, daher die Ratlosigkeit.

@DasBoeseLebt: Das Fenster ist ein modal-Dialog und sobald ich weiß wo ich hin muss mit meiner "Hauptschleife", macht der Canvas bzw. dessen Methoden auch nichts anderes mehr als zeichnen.

Danke an alle erstmal.
 
DasBoeseLebt schrieb:
Ich würde vermutlich, wenn es ein extra Fenster ist, in Richtung FokusEvent gehen. Event wird ausgelöst, wenn das Fenster Fokus erhält, rendern und überprüft dann nach dem Rendern, ob das Fenster weiterhin im Fokus liegt (je nach Aufgabe dann weiter rendern oder fertig nichts tun, dann brauchst du es auch nicht mehr Abfragen und eigentlich kein Loop). (Nachteile natürlich: Kein Fokus kein Rendern).

Klingt irgendwie ziemlich kompliziert. Swing hat doch die renderComponent()-Methoden, die man überschreiben kann. Also einfach den Code zum Rendern eines Zustandes in die Methode des Canvas reinmachen und Swing wird die Methode aufrufen, wenn neu gerendert werden muss. Minimalster Aufwand.

Wenn Swing erkennt, dass das Rendern nicht nötig ist (Anwendung minimiert, verdeckt) verbraucht man auch keine CPU-Zyklen. Sollte sie aber weiterhin teilweise sichtbar sein, wird Swing sich um das Rendern kümmern.



Diese Endlosschleifen in den ganzen Game-Tutorials finde ich aber einfach auch immer schlecht. Man kann es so lösen, bzw. ist es am einfachsten, wenn man alles selbst baut. Aber in dem Moment wo man ein Framework für ein Spiel verwendet, sollte es so sein, dass man nur noch beschreibt wie das gerenderte Aussehen soll (Kiste auf 10,5; Sonne auf 4,6) und der Renderer kümmert sich darum das effizient auf nen Canvas oder whatever zu malen. Denn der kann dann auch viel effizienter arbeiten wie z.B. nur geänderte Bereiche neuzeichnen usw.
 
Zuletzt bearbeitet:
ice-breaker schrieb:
Klingt irgendwie ziemlich kompliziert. Swing hat doch die renderComponent()-Methoden, die man überschreiben kann. Also einfach den Code zum Rendern eines Zustandes in die Methode des Canvas reinmachen und Swing wird die Methode aufrufen, wenn neu gerendert werden muss. Minimalster Aufwand.

Wenn Swing erkennt, dass das Rendern nicht nötig ist (Anwendung minimiert, verdeckt) verbraucht man auch keine CPU-Zyklen. Sollte sie aber weiterhin teilweise sichtbar sein, wird Swing sich um das Rendern kümmern.

Das ist natürlich noch besser. *gleich mal für die Zukunft merken, falls man es irgendwann brauchen sollte* ;)
 
Naja der Canvas den ich momentan nutze ist eben ein Lwjgl-Canvas, der dann mit 60 FPS oder was man auch immer eingestellt hat die Bilder rendert.

Eigentlich kann man dem Renderer von Libgdx per setContinuousRendering mitteilen, ob er dauerhaft rendern soll oder eben nur dann wenn man es anfordert oder bestimmte Events gefeuert werden, nur will das im Canvas irgendwie nicht ganz funktionieren, sodass ich jetzt einfach in der Render-Methode zuerst abfrage ob isContinuousRendering == false und in dem Falle returne.
ContinuousRendering setze ich eben immer entsprechend wenn das Fenster des Canvas geöffnet/geschlossen wird.

Ist ohnehin sicher nicht die beste Variante, aber wie das nunmal so ist mit neuen Sachen, muss ich mich da erstmal einlernen ;)

Aber danke auf jeden Fall.
 
Zurück
Oben