# paintComponent mit repaint() aufrufen



## Falk (7. Dez 2007)

Hallo,

folgendes Problem bekomme ich nicht gelöst. In einer Klasse, die von JPanel erbt, habe ich die Methode *paintComponent(Graphics g)* überschrieben. Über einen Klick auf einen Button soll diese dann aufgerufen werden, um in einer anderen Klasse was zu erledigen. Das ganze habe ich mit *repaint()* versucht, klappt aber nicht.

Danke für eure Hilfe und Gruß
Falk


----------



## SlaterB (7. Dez 2007)

Tipp: der Fehler liegt in deinem Code


----------



## Falk (7. Dez 2007)

Zum besseren Verständnis habe ich einen Code- Ausschnitt bereitgestellt:


```
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class Datenbereich extends JPanel implements ActionListener{
	
	//GUI-Elemente definieren
	//hab ich teilweise gelöscht, da sonst zuviel Text

	static Kontur[] kontur = new Kontur[20];
	static int lfdNr = 0;
	static int indexEndpunkt = 0;
	static int x1, y1, x2, y2;

	public Datenbereich(){ 

	//verschiedene Deklartionen, hab sie gelöscht, da sonst zuviel Text!!!
	}
	
	public void actionPerformed(ActionEvent e){
		
		String befehl = e.getActionCommand();
		String strKontArt = buttongruppe.getSelection().getActionCommand();
		
		if(befehl.equals("Kontur schließen"))
		{
			if(Zeichenbereich.x[0] == Zeichenbereich.x[1] || Zeichenbereich.y[0] == Zeichenbereich.y[1] || Zeichenbereich.n <3 ){
				System.out.println("Kontur unzulässig!");
				
			}
			else{
				
				kontur[lfdNr] = new Kontur(strKontArt, Zeichenbereich.x, Zeichenbereich.y, Zeichenbereich.n);
				kontur[lfdNr].lfdNr = lfdNr;
	
				repaint(); //Hier ist die Stelle an der ich möchte, das paintComponent() aufgerufen wird
				
				//Kontur schließen
				//dazu die eigene überschriebene paint- Fkt aufrufen						
				indexEndpunkt = kontur[lfdNr].anzKnoten - 1;
				x1 = kontur[lfdNr].x[0] * 5;
				y1 = kontur[lfdNr].y[0] * 5;
				x2 = kontur[lfdNr].x[indexEndpunkt] * 5;
				y2 = kontur[lfdNr].y[indexEndpunkt] * 5;
				
				//Zeichenbereich Zähler n nullen
				Zeichenbereich.n = 0;
			}
		}
		if(befehl.equals("Kontur löschen"))
		{
			//Zu tun: Letzte Kontur löschen
			System.out.println("Noch nicht implementiert");
		}
		
		//Zähler Kontur hochzählen
		lfdNr++;
	}
	
	public void paintComponent(Graphics g){

		if(indexEndpunkt > 1){
			Zeichenbereich.KonturSchliessen(g, x1, y1, x2, y2);
		}
		indexEndpunkt = 0;
	}
}
```

Viele Grüße 
Falk


----------



## SlaterB (7. Dez 2007)

und du glaubst, mit Code wie

 x1 = kontur[lfdNr].x[0] * 5; 
            y1 = kontur[lfdNr].y[0] * 5; 
            x2 = kontur[lfdNr].x[indexEndpunkt] * 5; 
            y2 = kontur[lfdNr].y[indexEndpunkt] * 5; 

ist deinem Problem geholfen?
(ich formuliere das so drastisch, damit du die Wichtigkeit entdeckst)

so kann ich das jedenfalls nicht testen und auch nichts erkennen,
nur ein allgemeiner Tipp:

schreibe

System.out.println("gleich kommt repaint");
repaint(); 

+

public void paintComponent(Graphics g){ 
System.out.println("paintComponent");
...
}

---------

wenn beide Meldungen nicht erscheinen, dann liegt es daran,
dass es gar nicht zum repaint()-Aufruf kommt (irgendein if/else)

wenn aber 'gleich kommt repaint' ausgegeben wird und die andere Meldung nicht,
dann wirds interessant, ist es so?


----------



## happy_robot (7. Dez 2007)

ein repaint zeichnet nicht einfach so mal munter drauflos.
du musst vorher ein invalidate() loschicken damit du einen bereich als ungültige darstellung erklärst.


----------



## SlaterB (7. Dez 2007)

da habe ich andere Erfahrungen (falls es nicht um die Millisekunde genau geht)


----------



## Marco13 (8. Dez 2007)

Ein repaint bewirkt ein repaint. (Es geht doch nichts über sprechende Methodennamen). Vielleicht liegt das Problem in der fälschlichen Annahme, dass das paintComponent vom repaint aus aufgerufen wird. Das ist aber NICHT so. Ein repaint ist nur ein Hinweis: ~""Painte mal, sobald sich die Gelegeneheit dafür ergibt"". Es wird also nach dem repaint das "//Kontur schließen "-Zeux ausgeführt, und "irgendwann später" dann auch mal paintComponent. (BTW:Ein invalidate braucht man definitiv NICHT. )


----------



## happy_robot (8. Dez 2007)

Marco13 hat gesagt.:
			
		

> Ein repaint bewirkt ein repaint.


da kann ich aus der praxis sagen daß dies definitiv nicht der fall ist. sonst hätte er doch das problem nicht. 



			
				Marco13 hat gesagt.:
			
		

> ..... und "irgendwann später" dann auch mal paintComponent.


das würde also heissen daß ein repaint auf dem JFrame immer ALLE components neuzeichen würde. das ist sicherlich nicht so.  es werden nur die komponenten gezeichnet die auch "dirty" sind und als solche gekennzeichnet sind. das zu markieren ist explizit notwendig. deswegen hatte ich hier ein invalidate() für den zu zeichnenden bereich empfohlen. ich mache es selber so und es klappt prima. in der win32-api muss mans genau so machen. 



			
				Marco13 hat gesagt.:
			
		

> (BTW:Ein invalidate braucht man definitiv NICHT. )


kann sein daß ich mich hier geirrt habe, aber aus meiner erfahrung heraus ist es notwendig für ein garantiertes zeichnen. das immer alles im container gezeichnet wird ist sicherlich falsch. das wäre ja wohl ein performancetechnisches desaster. hier wäre es jetzt wichtig zu wissen wie man einer component sagt daß sie "dirty" oder "invalid"  ist.

dafür müsste man aber jetzt die sourcen durchschauen und gucken wer das zeichnen aufruft.......


----------



## byte (8. Dez 2007)

happy_robot hat gesagt.:
			
		

> Marco13 hat gesagt.:
> 
> 
> 
> ...


Das ist falsch! repaint() ruft paint(). paint() ruft paintComponent(), paintBorder() und paintChildren() auf. Wenn Du nur einen Teil der Komponente neu zeichnen willst, dann rufst Du repaint(x,y,w,h) auf.




> Marco13 hat gesagt.:
> 
> 
> 
> ...


Du hast da offenbar was mißverstanden. revalidate() / invalidate() bezieht sich ausschließlich auf das Layout von Komponenten. Ändert sich die Größe einer Komponente, so kann man mit invalidate() dafür sorgen, dass das Layout neu berechnet wird. Wenn sich bloß der Inhalt von Komponenten geändert hat aber nicht die Größe, so ist es Blödsinn, vor dem repaint() ein invalidate() aufzurufen. Das zieht nur die Performance runter, weil sinnloserweise das Layout neu berechnet wird.


----------



## Falk (8. Dez 2007)

Oje, da hab ich ja was losgetreten.

An SlaterB:    Ich konnte nicht den ganzen Code reinstelle, das hätte sich keiner angeschaut. Zum Verständnis meines Problems habe ich nur einen Ausschnitt reingestellt. Aus den vielen Antworten kann sicherlich geschlossen werden, das repaint() nicht so trivial funktioniert, wie angenommen.

Eigentlich müsste ja spätestens wenn das Fenster minimiert und anschließend wieder maximiert wird repaint aufgerufen werden, was aber leider nicht immer der Fall ist.

Viele Grüße und danke für euer interesse
Falk


----------



## Guest (8. Dez 2007)

An SlaterB: Ich habe das mit Sysem.out..... etc probiert. Also die Funktion in der repaint() steht wird aufgerufen, paintComponent() aber nicht.

Gruß 
Falk


----------



## Guest (8. Dez 2007)

Wenn in der Funktion die repaint() aufruft beispielsweise die größe des Objektes verändert wird, so wird auch paintComponent() durch repaint() aufgerufen. 

Das ist zwar nicht schön, aber funktioniert jedenfalls.

Gruß
Falk


----------



## Beni (8. Dez 2007)

Falk hat gesagt.:
			
		

> Eigentlich müsste ja spätestens wenn das Fenster minimiert und anschließend wieder maximiert wird repaint aufgerufen werden, was aber leider nicht immer der Fall ist.


Eigentlich ist es nicht sehr kompliziert, happy_robot hat nur ein paar Konzepte durcheinandergemischelt :wink:

"repaint" führt zu einem Eintrag in die EventQueue, dass der ganze Component-Baum ab einer bestimmten Wurzel-Component (eben die Component, auf die "repaint" angewandt wurde) neu gezeichnet werden sollte.
Die Abarbeitung dieses Eintrages folgt irgendwann später durch den EventDispatcherThread.

Es kann passieren, dass irgendwelche Optimierungen geschehen und paint-Befehle gelöscht werden. Z.B. weil eine Component nicht sichtbar ist.

Eine Component kann auch neu gezeichnet werden, wenn kein "repaint" auf die Component angewandt wurde, weil eine Vater-Component neu gezeichnet wird (und es wird ja immer der ganze Baum - abzüglich Optimierungen - neu gezeichnet).

Die "paint"-Methode ruft dann "paintComponent" auf, du selbst musst niergends in deinem Code "paintComponent" aufrufen.

Aber folgendes gilt sicher: "Ein repaint führt zu einem paint, falls das paint nicht wegoptimiert werden kann. Weitere Methodenaufrufe sind für ein paint nicht notwendig."


----------



## Falk (8. Dez 2007)

Danke an euch alle!

Letztendlich schalte ich jetzt hintereinander Objekt.setVisible() auf false und anschließend gleich wieder auf true, somit wird paintComponent() aufgerufen.

Bis der Groschen bei mir letzendlich fällt (bezüglich dieses Themas) dauert es wohl noch ne Weile.


Schönes Wochenende und Gruß
Falk


----------



## Marco13 (9. Dez 2007)

@happy_robot (aber auch byto und Beni) : WAS genau ein "repaint" bewirkt, braucht man (wenn es um eine Antwort auf 'Anfängerfragen' geht) nicht in dieser Tiefe durchzudiskutieren. Swing macht eine ganze Menge im Hintergrund. Bei einem repaint passiert sehr viel, es werden Components als "dirty" markiert, der RepaintManager gibt seinen Senf dazu, es wird eine HashTable mit den Rechtecken der dirty Components gefüllt, die Rechtecke werden ggf. gemergt, wenn sie sich überschneiden, aber irgendwann werden letztendlich doch Dinge getan, die dazu führen, dass schlecht und einfach 'paintComponent' aufgerufen wird. Und das Entscheidende hier war (falls ich mich jetzt nicht irre) schlicht und einfach, dass das eben nicht sofort (durch einen direkten Aufruf von 'repaint' aus) sondern evtl. später passiert. Aber vielleicht irre ich mich auch.  :roll:


----------



## byte (9. Dez 2007)

Marco13 hat gesagt.:
			
		

> @happy_robot (aber auch byto und Beni) : WAS genau ein "repaint" bewirkt, braucht man (wenn es um eine Antwort auf 'Anfängerfragen' geht) nicht in dieser Tiefe durchzudiskutieren.


Und warum nicht? Ist doch ein interessantes Thema und es lesen auch noch andere hier.


----------



## Marco13 (9. Dez 2007)

Ja klar    es ging ja auch nicht um die Sache an sich (dass das Thema interessant ist, und man mal adrüber reden kann, ist klar - ich drifte ja auch häufig bei vermeintlich "einfachen Anfängerfragen" in fast schon philosophisch wirkende Grundsatzdiskussionen ab - und solange das nicht die (hilfreiche) Beantwortung der ursprünglichen Sprache untergräbt, ist da auch nichts dagegen zu sagen - im Gegenteil)

Vielmehr ... "unpassend" fand ich ... ähm - ja, solche argumentfreien Aussagen wie
_>Ein repaint bewirkt ein repaint._
_>>da kann ich aus der praxis sagen daß dies definitiv nicht der fall ist_ <-  ???:L  
(Was bewirkt ein "repaint" denn dann? Dass MineSweeper gestartet und die Festplatte defragmentiert wird?)

Oder eben auch deine Antwort dann
_Das ist falsch! repaint() ruft paint(). paint() ruft paintComponent(), paintBorder() und paintChildren() auf. _
Ja nun, DAS ist falsch. (Oder sollte das Ausrufezeichen eigentlich ein Doppelpunkt sein?  :lol: ). Du weißt vermutlich, dass 'repaint()' eben NICHT 'paint' aufruft. Der Aufruf wird ungesehen an RepaintManager.addDirtyRegion weitergereicht, der damit dann eine Weile rumhantiert (dirty regions berechnet, vereinigt, usw) und dafür sorgt, dass dann irgendwann ein Event in die EventQueue gelegt wird, der bewirkt, dass irgendwann später, indirket, über Umwege 'paint' aufgerufen wird. Also ... WENN man so tief in die Materie einsteigt, dann sollte man auch aufpassen, _entweder_ (eindeutig als solche erkennbare) "unpräzise" Aussagen zu machen, die vielleicht (für den Anfänger und Threadersteller) zum Verständnis beitragen oder verdeutlichen, dass manches nicht so einfach ist, wie es aussieht, _oder_ präzise (und dann meinetwegen auch "absolute") Aussagen zu machen (wie "Das ist falsch") die dann aber ... nach Möglichkeit auch _richtig_ sein sollten. Und dann kann man sich imho auch solche (für einige anscheindend) ""überflüssigen"" Spitzfindigkeiten erlauben, wie etwa die Aussage, dass unsichtbare Components ja DOCH nicht repaintet werden und so :wink:


----------



## byte (10. Dez 2007)

Ist mir jetzt zu spät, um diesen abstrusen Text noch zu verstehen. :roll:

Es kann ja einfach jeder nachlesen: http://java.sun.com/products/jfc/tsc/articles/painting/



> Programs may trigger a future call to paint() by invoking repaint(), but shouldn't call paint() directly.
> 
> [...]
> 
> ...


----------



## SlaterB (10. Dez 2007)

Falk hat gesagt.:
			
		

> An SlaterB:    Ich konnte nicht den ganzen Code reinstelle, das hätte sich keiner angeschaut. Zum Verständnis meines Problems habe ich nur einen Ausschnitt reingestellt. Aus den vielen Antworten kann sicherlich geschlossen werden, das repaint() nicht so trivial funktioniert, wie angenommen.
> 
> +
> 
> An SlaterB: Ich habe das mit Sysem.out..... etc probiert. Also die Funktion in der repaint() steht wird aufgerufen, paintComponent() aber nicht



ein interessantes Problem, welches ich ergründen würde, aber ohne deinen Code geht es nicht,
bei all dem Code den ich mir selber ausdenken könnte klappt das,
also machst du irgendwas anderes, mehrere JPanel-Ebenen dazwischen oder so,


----------



## happy_robot (10. Dez 2007)

will jetzt hier nich wieder die grosse welle machen, aber verlasst euch darauf daß der einfach repaint()-call garantiert nicht das macht was ihr hier erzählt was er denn machen soll.

das update-problem ist in swing nun echt nicht neu und daß was ich da mit invalidate() geschrieben habe werdet ihr auch öfters im netz finden.

wenn ein repaint ausreichen würde gäbe es im netz nicht so viele artikel darüber 
und wenn jeder repaint-call immer alles zeichnen würde wäre die JVM wohl fast nur damit beschäftigt. ich hab jetzt nich wirklich lust die sourcen zu wälzen, aber seid euch sicher daß es aus performancegründen kriterien fürs neuzeichnen gibt (gibts ja auch sonst überall, siehe Win32-API). auch wenn  explizit bereiche angegeben werden.


----------



## Quaxli (11. Dez 2007)

Mal ein Versuch zur Lösung des ursprünglichen Problems:

Schreib mal in die erste Zeile der überschriebenen paintComponent-Methode das das rein:

super.paintComponent(g);


----------



## Marco13 (11. Dez 2007)

_verlasst euch darauf daß der einfach repaint()-call garantiert nicht das macht was ihr hier erzählt was er denn machen soll._
Was haben "wir" hier behauptet, was er macht? 
Was davon macht er nicht?
Was macht er stattdessen?

_das update-problem ist in swing nun echt nicht neu und daß was ich da mit invalidate() geschrieben habe werdet ihr auch öfters im netz finden._
Du wirst im Netz auch oft finden, dass jemand in das Graphics-Object zeichnet, das er mit getGraphics von einer Component bekommen hat, oder dass jemand updateUI aufruft, um ein Neuzeichnen auszulösen. Etwas "irgendwo im Netz" zu finden ist erstmal ziemlich wertfrei.... :roll: 


_und wenn jeder repaint-call immer alles zeichnen würde wäre die JVM wohl fast nur damit beschäftigt._
Falls das aufgrund eines meiner Posts so aufgefaßt werden könnte, hätte er vielleicht ausführlicher sein sollen. Ein repaint bewirkt, dass neu gezeichnet wird. (Punkt). Dass heißt aber NICHT, dass 10 repaints bewirken, dass 10 mal neu gezeichnet wird. (Das hat aber auch niemand behauptet).


----------



## happy_robot (12. Dez 2007)

Marco13 hat gesagt.:
			
		

> Ein repaint bewirkt, dass neu gezeichnet wird. (Punkt). Dass heißt aber NICHT, dass 10 repaints bewirken, dass 10 mal neu gezeichnet wird. (Das hat aber auch niemand behauptet).


ähhhh.....im vorliegenden fall scheint es wohl so zu sein daß immer der erste repaint-call() kein repaint macht. und irgendwo ist der nächste call dann auch immer irgendwie der erste und nix passiert 

folgender code:


```
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class GUI extends javax.swing.JPanel{

	private static int counter = 0;
	private static int lastCounter = counter;
	
	public GUI() {
		setPreferredSize(new java.awt.Dimension(600, 400));
		for(int n = 0; n < 100; n++) {
			add(new JButton("B::"+n));
		}
	}

	@Override
	protected void paintComponent(Graphics graphics) {
		super.paintComponent(graphics);
		int delta = counter-lastCounter; 
		if(delta > 1) {
			System.out.println(delta);
		}
		lastCounter = counter;
	}

	
	public static void main(String[] args) throws InterruptedException {
		JFrame frame = new JFrame();
		frame.getContentPane().add(new GUI());
		frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		frame.pack();
		frame.setVisible(true);

		while(true) {
			counter++;
			frame.repaint();
			Thread.sleep(1);
		}
	}	
}
```

die ausgabe ist die differenz zwischen der repaint-calls (fortlaufend in nem static hochgezählt) des aktuellen paintComponent-Aufrufs und das des vorhergehenden. es wird nur die anzahl der verschluckten "paintComponent"-calls ausgegeben. 
hier sieht man auch daß er sogar zeichnet wenn das fenster minimiert oder im hintergrund ist (zumindest auf meinem linux-system). jetzt stellt sich mir eigentlich die frage ob das "unterbinden" des zeichnens nicht vielleicht doch im Graphics-Object stattfindet, was vielleicht auch sinn machen würde (??). wenn man die 100 buttons herausnimmt verschluckt er deutlich weniger updates.


----------



## happy_robot (12. Dez 2007)

Marco13 hat gesagt.:
			
		

> Etwas "irgendwo im Netz" zu finden ist erstmal ziemlich wertfrei.... :roll:


wie ordnest du denn dann deine postings hier ein?  :autsch:


----------



## Marco13 (12. Dez 2007)

happy_robot hat gesagt.:
			
		

> Marco13 hat gesagt.:
> 
> 
> 
> ...



Genau so. Keiner braucht sich drum zu scheren, aber vielleicht hilft der eine oder andere Beitrag dem einen oder anderen.

Was genau mit dem Beispiel nun gezeigt werden sollte, ist mir aber nicht klar. (Genaugenommen ist mir auch noch nicht klar, was die ursprüngliche Frage bzw. das Problem war, aber ... naja). Das Graphics enthält übrigens auch noch eine "Clipping region", d.h. der bereich, in dem WIRKLICH gezeichnet werden muss, aber wie der berechnet wird bzw. in die Berechnung der zu repaintenden Bereiche winfließt, weiß ich ohne Code-Durchsuchen auch nicht auswendig. Aber wenn paintComponent aufgerufen wird, wird alles gemacht, was auch in paintComponent steht (ob sowas wie g.drawLine(...) dann sofort zurückspringt, weiß man halt erstmal nicht....).


----------



## Quaxli (12. Dez 2007)

Aber doch schön, daß Ihr Euch auch weiterhin um das eigentliche Problem kümmert.


----------



## Marco13 (12. Dez 2007)

Da der Threadersteller nichts mehr dazu geschrieben hat, muss mal es wohl als "erledigt" annehmen....


----------



## byte (13. Dez 2007)

happy_robot hat gesagt.:
			
		

> hier sieht man auch daß er sogar zeichnet wenn das fenster minimiert oder im hintergrund ist (zumindest auf meinem linux-system). jetzt stellt sich mir eigentlich die frage ob das "unterbinden" des zeichnens nicht vielleicht doch im Graphics-Object stattfindet, was vielleicht auch sinn machen würde (??). wenn man die 100 buttons herausnimmt verschluckt er deutlich weniger updates.


Ich kann nur nochmal auf diesen Link verweisen:
http://java.sun.com/products/jfc/tsc/articles/painting/#swing
repaint() ist asynchron, wird also ausgeführt, wenn der EDT bereit dafür ist. Wenn Du ein zweites Mal repaint() aufrufst, bevor der erste abgearbeitet wurde, so wird dieser verschluckt. Genau das passiert in Deinem Beispielcode, weil Du jede Millisekunde repaint() aufrufst. Ich schätze mal, dass dieses "Verschlucken" multipler repaint() Aufrufe im RepaintManager passiert.
Du kannst ja mal Dein Thread-Sleep auf einen sinnvolleren Wert setzen. 1ms ist weit unter der Genauigkeit des OS. Und 100 Buttons müssen auch erstmal gezeichnet werden.


----------

