Swing Wie bekomme ich eine Referenz auf ein Objekt das im EDT erzeugt wird? Und anderes...

P@u1

Aktives Mitglied
Hallo zusammen,
ich habe mal wieder Probleme mit Swing...
Ich habe eben gelesen, dass alles was mit GUI zu tun hat (etwas grob, wenn ihrs genauer wisst wären Erklärungen hilfreich) im Event Dispatcher Thread ausgeführt werden sollte, entweder durch invokeAndWait oder invokeLater.
Jetzt wollte ich eine GUI so in der Art erzeugen:
Java:
private static void createAndShowGUI()
	{
		SwingUtilities.invokeLater(new Runnable()
		{
			public void run()
			{
				JFrame form = new JFrame("blablub");
				form.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                                             View v = new PlotPanel();
                                             form.add(v);
                                             //ist das so richtig, oder sollte ich besser auf die Contenpane 
                                             //adden und nicht auf das teil direkt?
				form.setVisible(true);
			}
		});
	}

Das Problem ist, dass ich danach die form und v noch brauche.
Ich hatte mir jetzt überlegt, bei Main statische variablen dafür einzuführen und die zu benutzen, was zwar prinzipiell funktioniert, ich aber sehr unschön finde...
Was gibt es für da für gute Alternativen?
Das Problem ist ja, dass ich von InvokeLater keine Rückgabe bekomme.
Außerdem ist im Code noch eine Frage (als Kommentar) zur ContentPane.
Und dann zu guter letzt noch:
Wie sieht es aus mit invokeAndWait oder invokeLater?
Wo prinzipiell der Unterschied ist weiß ich (bei Wait wird solange noch blockiert, bis es fertig ist).
Ich habe aber gelesen, dass man fast immer invokeLater benutzen sollte.
Jetzt ist das bei mir so, dass dieser PlotPanel folgendes Attribut hat, in das ich reinmalen will und beim paintComponent wird dann das BufferedImage auf den Panel gezeichnet:
private BufferedImage offScreenImage;
Wenn ich invokeLater benutze kann es ja passieren, dass das teil noch gar nicht existiert, wenn ich dann nachher dareinzeichnen will...
Wenn ich außerhalb vom EDT auf diese offScreenImage male, sollte das doch unproblematisch sein, oder?
Dann kann es zwar sein, dass paintComponent aufgerufen wird, während das offScreenImage manipuliert wird, das sollte aber eigentlich nicht so schlimm sein, weil eh dauernd neugezeichnet wird, so dass das nicht sonderlich auffallen sollte, wenn "halb veränderte" bilder gemalt werden.
Oder kann es vll sein, dass das irgendwelche anderen Probleme verursachen kann?
Muss ich dieses
private BufferedImage offScreenImage;
dann eigentlich als volatile definieren, da ja dann sowohl der EDT als auch ein anderer Thread darauf zugreifen soll?

Vielen Dank schonmal für eure Hilfe!
 
mal anders gefragt: an wen soll denn zurückgegeben werden, wer kommt 'danach' noch dran?

weiterer normaler Code in der Methode createAndShowGUI()?
durch die Nebenläufigkeit ist die Methode doch längst beendet bevor der Event-Thread sich bequemt, genau dieses Runnable auszuführen,

was immer du weiter machen möchtest kannst du auch einfach in das Runnable hinter Zeile 13 schreiben,
von dort kannst du auch Methoden des umgebenden Objektes aufrufen

wenn du das erzeugte Objekt längerfristig brauchst und statisch nicht gut ist, dann in einem Klassenattribut speichern,
ab wann andere Threads darauf zugreifen können ist aber wie gesagt ungewiss

---

Contentpane oder direkt ist egal
 
Danke für die Antwort.

Wenn ich das Teil in einem Klassenattribut speichern will, also ich nehme mal an du meinst in einem Attribut eines instanzierten Objekts, dann brauche ich sicht auf dieses Objekt, was in der run-Methode aber nicht so ist.
Wenn ich die weiteren Operationen in run machen will, habe ich genau das gleiche Problem, weil ich dafür Sicht aus Sachen für main brauche.
Und eine Referenz auf die Form brauche ich, weil ich später evtl. weitere PlotPanels auf die Form adden will und das geht doch nur, wenn ich noch ein Referenz habe.

Ich beschreibe jetzt mal etwas genauer, was ich vorhabe, vielleicht kannst du mir dann etwas besser helfen.

Am Anfang erstelle ich einen JFrame mit einem PlotPanel drauf.
Außerdem habe ich einen DataGenerator, der nichts direkt mit der GUI zu tun hat, aber eine Referenz auf den Plot panel braucht, weil die generierten Daten dargestellt werden sollen.

Ich möchte das so machen, dass ich zur Laufzeit neue PlotPanels erzeugen kann (z.B. durch einen Button auf dem "add View" oder ähnliches steht oder so) und auch löschen können.
Außerdem kann es sein, dass auch neue Datengeneratoren dazu kommen.
Ein Datengenerator kann eine beliebige Anzahl an views haben.
Ein view stellt aber immer nur die Daten von je einem Generator dar.

Also jetzt wieder zum Anfang: Ich will einen JFrame und einen Plotpanel erstellen und einem DataGenerator, den ich vorher schon erstellt hab den PlotPanel als view hinzufügen.

Dazu rufe ich erstmal createAndShowGUI auf, dass im EDT den JFrame und den PlotPanel erzeugt, dann den PlotPanel zum JFrame hinzufüügt.
Jetzt will ich dem Datagenerator, den ich in Main erzeugt habe diesen PlotPanel als View hinzufügen.
Also brauche ich
-eine Referenz auf den erzeugten PlotPanel in main
oder
-eine Referenz auf den DataGenerator in run

Soweit ich weiß kriege ich solche referenzen nur über statische sachen oder irgendwie noch über final hin, bei final weiß ich nicht genau wie und warum das funktioniert und ob das gut ist.

Also suche ich jetzt einen Weg um dieses Problem möglichst elegant zu lösen und natürlich muss später das hinzufügen oder entfernen von views und Generatoren ohne Probleme möglich sein, also muss ich mir wohl eine Referenz auf den JFrame speichern oder ich muss später bei diesen buttonClick events o.Ä. eine Referenz auf darauf erhalten, falls die dabei irgendwie mitgeliefert wird.

Wie mach ich das ganze jetz am geschicktesten?

Im moment sieht meine Main.java so aus:
(Noch einige Fehler drin)
Java:
public class Main
{
	public JFrame form;
	public PlotPanel v;
	public static void main(String[] args)
	{
		Main main = new Main();
		createAndShowGUI();
		DataGenerator sinus = new DataGenerator(1000);
		Timer dataTimer = new Timer();
		DataTimerTask dataTimerTask = new DataTimerTask(sinus);
		sinus.connect();
		dataTimerTask.addView(main.v);
		dataTimer.scheduleAtFixedRate(dataTimerTask, 0, 5);
	}

	private static void createAndShowGUI()
	{
		try
		{
			SwingUtilities.invokeAndWait(new Runnable()
			{
				public void run()
				{
					main.form = new JFrame("EKG Plot");
					main.form.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
					main.form.setVisible(true);
					main.v = new PlotPanel();
					main.form.add(v);
				}
			});
		} catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
Die ganzen Zugriffe beim run auf main funktionieren nicht, weil run dieses Objekt nicht sehen kann, dass muss ich noch irgendwie ändern.
Auch finde ich es unschön Main zu instanzieren, sollte ich vll eine neue Klasse GUI anlegen, die die Elemente der GUI speichert?
Und mir ist auch klar, dass man public nicht verwenden sollte, dass ist nur im Moment so, weil ich erstmal ein funktionierendes Konzept suche.
 
main oder ein anderes Objekt könnte als Parameter an createAndShowGUI() übergeben werden,
wenn als final deklariert, dann ist es im Runnable sichtbar

invokeAndWait(new Runnable()) könnte in einer nicht-statischen Methode ausgeführt werden, dann hat man Zugriff auf die umschließende Instanz

schließlich ist es bestimmt auch denkbar, das JFrame vor dem Runnable zu erzeugen,
ich glaube nur für den setVisible(true);-Aufruf ist das invokeLater() wichtig

auch wichtig in jedem Fall: setVisible(true); erst am Ende aufrufen, danach nichts mehr adden()
 
reicht es, wenn die Paramter als final deklariert sind, aber das übergeben objekt selbst nicht als final deklariert ist?
Und wie meinst du das mit nach setVisible nichts mehr adden, ich will doch später zur Laufzeit noch was adden, dass muss doch gehen, oder?
 
Zuletzt bearbeitet:
> reicht es, wenn die Paramter als final deklariert sind, aber das übergeben objekt selbst nicht als final deklariert ist?
es gibt innerhalb der Methode nur den Parameter, was der Aufrufer der Methode vorher oder nachher anstellt ist egal, falls du das meinst

> ich will doch später zur Laufzeit noch was adden, dass muss doch gehen, oder?
später geht gewiss, aber 3 Zeilen später ist ja unnötig

wenn man später etwas hinzufügt empfehle ich die Aufrufe
validate();
repaint();
am JFrame

vergleiche es mit dem Auto:
vor losfahren() sollten alle einsteigen() 😉
was 30 min. später passiert ist ein anderes Thema, aber der direkte Start betrachtet
 
Wenn ich das static wegnehme, kennt er immer noch this.form nicht, möglicherweise weil this jetzt auf das new Runnable() bezieht, oder woran liegt das?

Edit: Hatte noch nen Denkfehler, den ersten Teil meines Posts habe ich also gelöscht.
 
Zuletzt bearbeitet:
danke, funktioniert jetzt.
Wusste gar nicht das das so geht.

Jetzt ist noch die Frage, wie ich das vom Design her am besten mache, noch ne GUI-Klasse, oder doch Main instanzieren, oder sonst noch ne andere gute Lösung?

Edit:
Und noch ne wahrscheinlich relativ einfache Frage:
Wie kann ich eine Kompente (z.B. meinen PlotPanel) wieder entfernen?
Edit2:
Habs schon rausgefunden: Bei dem Container remove aufrufen, genauso wie das adden funktionierte.
Edit3:
Was passiert, wenn ich etwas versuche zu removen, dass gar nicht erst geadded wurde?
Einfach nichts, oder fliegt eine Exception?

Und dann noch zu meiner GUI-Klasse die ich jetzt erstellt hab:
Ich habe jetzt z.B. diese Methode hier erstellt:
Java:
	public void addView(final PlotPanel p)
	{
		views.add(p);
		try
		{
			SwingUtilities.invokeAndWait(new Runnable()
			{
				public void run()
				{
					GUI.this.mainWindow.add(p);
				}
			});
		} catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
Ist das hierfür wirklich notwendig den EDT zu benutzen?
 
Zuletzt bearbeitet:

Zurück
Oben