# Aus Swing Oberfläche Bild erzeugen



## mephi (22. Feb 2007)

Ist es möglich zB aus einem JFrame direkt ein Bild zu generieren?

Meine Idee bisher war es, die Position und größe des Fensters auszulesen und davon dann ein Screenshot zu machen, aber vielleicht geht dies ja einfacher?


----------



## Wildcard (22. Feb 2007)

Von einem Bild ein graphics Objekt holen und die Komponenten darauf zeichnen lassen.


----------



## mephi (22. Feb 2007)

hier mal ein ganz einfacher versuch:


```
public MyFrame() {
		this.setSize(400, 300);
		this.setVisible(true);
		BufferedImage i = new BufferedImage(this.getWidth(), this.getHeight(), 1);
		Graphics g = i.getGraphics();
		this.paintComponents(g);
	}
```

ist das so richtig? und wie gehts dann weiter?

edit:
irgendwie komm ich da nie ganz klar. ich komm durch rumprobieren und api studieren irgendwie auf keinen vernünftigen nenner..


----------



## thE_29 (22. Feb 2007)

Naja und das schreibst via ImageIO.write(i, "png", new File("C:\\output.png")); raus!

Oder nimm jpg (obwohl das net so super ist).


Achja, für deinen obigen Befehl da: So würde ich das nur machen, wenn dein JFrame "nicht am Bildschirm ist"

Ich habe mir manchmal geholfen, wenn ich was ausdrucken wollte indem ich nen JDialog gemacht habe und den auf -1000,-1000 positioniert habe  Ist zwar net gerade die feine Methode aber es klappt!

Jedenfalls bei Screnncapturen, könntest du auch einfach Robot.createScreenCapture(Rectangle screenRect)  nehmen!

Wobei das rect halt von deinem Frame, etc ist...


----------



## The_S (22. Feb 2007)

png is imho besser, da verlustfrei und in manchen Fällen sogar kleiner als jpg


----------



## mephi (22. Feb 2007)

ok so klappts nun fast

```
public MyFrame() {
		this.setSize(400, 300);
		this.setVisible(true);
		BufferedImage i = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics g = i.getGraphics();
		this.paintComponents(g);
		try {
			ImageIO.write(i, "png", new File("C:\\output.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
```

nur ist die farbe etwas komisch d.h. was eigentlich so typisch java blau(unter win) sein sollte ist dann schwarz so wie ich es momentan mache. welches imageType brauch ich denn?


----------



## Wildcard (22. Feb 2007)

Versuch mal paintAll oder printAll


----------



## thE_29 (22. Feb 2007)

BufferedImage.TYPE_INT_ARGB

Oder probier das Robot.createScreenn... (ist aber keine statische Methode, dh du musst Robot zuerst instanzieren)


----------



## mephi (22. Feb 2007)

ok. mit dem imageType geht es zwar nun doch gibt andere fehler wenn ich zB ein JTextPane zum test hinzufüge. 

warum ist die erste variante denn nur zu empfehlen wenn das fenster "nicht am bildschirm" ist

ich brauch das ganze für ein jspwiki plugin um aus zB soetwas : http://www.freshcookies.org/jtreemap/iso-example.png einfach ein bild zu machen

so ein plugin läuft natürlich im hintergrund ab. also es wird nie wirklich etwas angezeigt. denke da fällt die createScreenCapture variante weg


----------



## thE_29 (22. Feb 2007)

Naja, createScreenCapture macht dir halt vom derzeit angezeigten ein Bild!

Und das halt so groß wie du es angibst! Probier das mal!


----------



## mephi (22. Feb 2007)

kann ich heute als plugin selbst leider nicht mehr testen.
aber man kann sich dass so vorstellen dass ein user verschiedene daten auf einer HP eingibt, dann wird serverseitig etwas erzeugt. dachte bisher an ein jframe(vielleicht gehts aber auch anders) und daraus wird ein bild erstellt. also es wird nie wirklich etwas angezeigt


----------



## The_S (23. Feb 2007)

mephi hat gesagt.:
			
		

> ok so klappts nun fast
> 
> ```
> public MyFrame() {
> ...



Hierzu mal ne Frage: Wie schaffe ich das denn mit einem Component, der nicht angezeigt wird?


----------



## Wildcard (23. Feb 2007)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Hierzu mal ne Frage: Wie schaffe ich das denn mit einem Component, der nicht angezeigt wird?


Das macht doch keinen Unterschied  ???:L


----------



## The_S (23. Feb 2007)

Doch, weil die Componente so keine Größe und scheinbar auch keinen Inhalt hat.


----------



## thE_29 (23. Feb 2007)

Doch 

Wenn die Komponente NICHT angzeigt wird, malt der nix rauf..

Trick: Du machst dir nen JDialog und den setzt du auf setUndecorated(true) und dann haust du dort die Komponenten rauf was du abspeichern willst!

Vorher sagst du noch setLocation(-1000,-1000); und dann setVisible(true); und dann kannst du es via paintComponents(g) malen lassen


----------



## Wildcard (23. Feb 2007)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Doch, weil die Componente so keine Größe und scheinbar auch keinen Inhalt hat.


Meinst du jetzt mit einer Komponente die *nie* sichtbar war?
Da ist wohl manuelles Layout, aber nachdringliches Bitten beim Manager angesagt.


----------



## The_S (23. Feb 2007)

@thE_29

Hm, das wäre natürlich ein Work Around ... werde das erstmal so realisieren, aber das geht doch bestimmt auch noch anders oder?


----------



## thE_29 (23. Feb 2007)

Nein geht nicht... (also am Wissenstand der 1.4er API )

Java ist so intelligent und verschleudert keine Resourcen/Rechenaufwand, etc. für Komponenten die nicht angezeigt werden!

Wozu auch?!


----------



## The_S (23. Feb 2007)

Wildcard hat gesagt.:
			
		

> Hobbit_Im_Blutrausch hat gesagt.:
> 
> 
> 
> ...



Nie sichtbar war und auch nie sichtbar sein wird  .


----------



## thE_29 (23. Feb 2007)

Was mir gerade einfällt, vielleicht würde es gehen wenn du die isVisible, isShowing, etc. Methoden überschreibst


----------



## The_S (23. Feb 2007)

Vom Component? Geht leider nicht. Ich schreibe gerade an einer möglichst einfachen API um mit iText (ja, iText is schon verdammt einfach, aber es gibt halt Leute, denen kanns nicht einfach genug sein :roll: ) PDFs generieren zu können. Unter anderem ist es Anforderung, dass man eine beliebige Komponente (egal ob swing oder awt) übergeben kann, welche dann im PDF dargestellt wird. Vondaher ist überschreiben wohl eher nicht geeignet  .


----------



## Wildcard (23. Feb 2007)

Ich hätte jetzt erwartet das man das mit invalidate und validate hinbekommt.
Eine Komponente muss nicht angezeigt werden um Zeichnen zu können wie man an den Tree/Cell-Renderern erkennt.


----------



## The_S (23. Feb 2007)

Wildcard hat gesagt.:
			
		

> Ich hätte jetzt erwartet das man das mit invalidate und validate hinbekommt.
> Eine Komponente muss nicht angezeigt werden um Zeichnen zu können wie man an den Tree/Cell-Renderern erkennt.



nope, so einfach isses leider nicht :cry:


----------



## Wildcard (23. Feb 2007)

Proof of Concept (Muss auch irgendwie besser gehen):

```
public class Test{
	
	public static void main(String[] args) {
		JLabel label = new JLabel("Test");
		BufferedImage image = new BufferedImage(100,100,BufferedImage.TYPE_INT_ARGB);
		Graphics g = image.getGraphics();
		label.addNotify();
		label.setBounds(50,50,50,50);
		label.paint(g);
		try {
			ImageIO.write(image, "png", new File("test.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		g.dispose();
	}
}
```


----------



## Wildcard (23. Feb 2007)

Statt addNotify funktioniert auch setVisible, allerdings vermute ich das es hier zu Thread-Problemen kommen kann, also nachfolgenden Code sicherheitshalber über invokeLater ausführen.


----------



## The_S (23. Feb 2007)

Hm, nicht schlecht. Schonmal ein Anfang. Bleibt noch die Frage, wie ich die optimale größe bestimmen kann!?


----------



## Wildcard (23. Feb 2007)

Mit einem Layout Manager zum Beispiel.


----------



## Wildcard (23. Feb 2007)

```
public static void main(String[] args) {
		JLabel label = new JLabel("Test");
		label.setVisible(true);
		label.setSize(label.getPreferredSize());
		BufferedImage image = new BufferedImage(label.getWidth(),label.getHeight(),BufferedImage.TYPE_INT_ARGB);
		Graphics g = image.getGraphics();
		label.paint(g);
		try {
			ImageIO.write(image, "png", new File("test.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		g.dispose();
	}
```


----------



## thE_29 (23. Feb 2007)

Tjo und setVisible true setzt eben intern diese Schalter auf true, sodaß das painting hinhaut 

Die Frage ist, wenn man ein JPanel zeichnen will, muss man da alle Kindkomponenten auch setVisible(true) sagen oder reicht das Hauptpanel aus?!


Aber, wie man sieht gehts auch anders  Jedenfalls hatte ich recht, das es solange man es nicht anzeigt (setVisible(true)) eben nichts gezeichnet wird über die paint Methode!


----------



## Wildcard (23. Feb 2007)

thE_29 hat gesagt.:
			
		

> Aber, wie man sieht gehts auch anders  Jedenfalls hatte ich recht, das es solange man es nicht anzeigt (setVisible(true)) eben nichts gezeichnet wird über die paint Methode!


Nein, das wird ja nicht wirklich angezeigt.
Wie oben gezeigt geht es ja auch wenn man addNotify direkt aufruft.
Soll man aber nicht machen, also im zweiten Source setVisible.


> Die Frage ist, wenn man ein JPanel zeichnen will, muss man da alle Kindkomponenten auch setVisible(true) sagen oder reicht das Hauptpanel aus?!


Afaik nicht, da es nur darum geht das AWT die Verbindung zu einem OS Handle herstellt.
Wenn ein Label in einen Container eingefügt wird kann es dessen Handle verwenden.


----------



## The_S (23. Feb 2007)

Also Test mit einem JPanel und 2 Buttons darauf verlief leider erfolglos. D. h. man müsste auf einen Container überprüfen und dann die selbe prodzedur noch mit den subcomponenten ausführen ...


----------



## Wildcard (23. Feb 2007)

```
public static void main(String[] args) {
		JPanel panel = new JPanel(new FlowLayout());
		
		JLabel label = new JLabel("Test");
		panel.add(label);
		panel.setSize(panel.getPreferredSize());
		panel.setVisible(true);
		panel.doLayout();
		
		BufferedImage image = new BufferedImage(panel.getWidth(),panel.getHeight(),BufferedImage.TYPE_INT_ARGB);
		Graphics g = image.getGraphics();
		panel.paint(g);
		try {
			ImageIO.write(image, "png", new File("test.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		g.dispose();
	}
```


----------



## The_S (23. Feb 2007)

Hm, funzt bei mir mit setVisible(true) nicht. Nur mit addNotify. kA warum! Aber es funzt. Herzlichen Dank!


----------



## Wildcard (23. Feb 2007)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Hm, funzt bei mir mit setVisible(true) nicht. Nur mit addNotify. kA warum! Aber es funzt. Herzlichen Dank!


Wie gesagt, bei setVisible sehe ich Thread Probleme, also sollte alles nachfolgende mit invokeLater in die Queue gesetzt werden.


----------



## The_S (23. Feb 2007)

Prob is dann aber, dass es evtl. zu spät ausgeführt wird. Aber mit addNotify gehts ausgezeichnet!


----------



## Wildcard (23. Feb 2007)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Prob is dann aber, dass es evtl. zu spät ausgeführt wird. Aber mit addNotify gehts ausgezeichnet!


Zu spät? Was meinst du damit?
addNotfiy ist AFAIK nicht ganz ungefährlich. Wenn du die Wahl hast benutz es lieber nicht, wenn du es doch benutzt, pass auf das du es nur *einmal* aufrufst.
Ansonsten hast du im best case wohl ein Speicherleck und im worst-case gehen evtl. dem OS die Widgets aus  ???:L
EDIT:
noch was: wenn du es doch benutzt, unbedingt removeNotify aufrufen.


----------



## The_S (23. Feb 2007)

Wildcard hat gesagt.:
			
		

> Zu spät? Was meinst du damit?



Weil diese Operation in einer anderen Methode ausgelagert ist und ich mit einem invokeLater nicht mehr garantieren kann, dass "doLayout" ausgeführt wurde bevor das Image generiert wird.



			
				Wildcard hat gesagt.:
			
		

> addNotfiy ist AFAIK nicht ganz ungefährlich. Wenn du die Wahl hast benutz es lieber nicht, wenn du es doch benutzt, pass auf das du es nur *einmal* aufrufst.
> Ansonsten hast du im best case wohl ein Speicherleck und im worst-case gehen evtl. dem OS die Widgets aus  ???:L



öhm ... joa ... glaub ich dir jetzt einfach mal. Ich hab nämlich keine Ahnung was das genau macht, das Einzige was ich momentan darüber weis ist, dass es funktioniert  (wobei ich eigentlich schon gerne weis, was ich eigentlich da zusammenprogrammier).



			
				Wildcard hat gesagt.:
			
		

> noch was: wenn du es doch benutzt, unbedingt removeNotify aufrufen.



Danke für den Tipp, wird gemacht!


----------



## Lim_Dul (23. Feb 2007)

Gibt doch auch noch invokeAndWait.


----------



## The_S (23. Feb 2007)

Jup, hilft aber auch net, kA warum ... denk mal wir bewegen uns hier auf ner ziemlich "abstrakten" Ebene


----------



## The_S (4. Apr 2007)

Zur Ergänzung nochmal eine viel einfachere Möglichkeit:


```
SwingUtilities.paintComponent(graphics2d, component, container, rectangle);
```


----------

