# repaint(), EDT-Warteschlange und Threads



## ernst (18. Mai 2007)

Hallo allerseits,
um mehrfach hintereinander z.B. Kreise auf dem Bildschirm auszugeben, genügt es nicht, in einer Schleife immer wieder repaint aufzurufen.
Grund:
Jeder Aufruf von repaint() sorgt nur dafür, dass ein paint in die EDT-Warteschlange (EventDispatcher-Thread =  Thread, der die graphische Oberfläche zeichnet und Events herumschickt) gestellt wird.
Ein durch einen alten repaint-Aufruf in die EDT-Warteschlange gestelltes paint wird durch ein neueres (durch einen neuen repaint-Aufruf) in die EDT-Warteschlange gestelltes paint
ersetzt  (ein paint() zieht ein paintComponent(...) nach sich). Man sagt dazu auch "coalescing" (verschmelzen, vereinigen). Mehrere Aufrufe werden zu einem vereinigt.
Das sieht man z.B. auch auf dem Bildschirm: es wird nur der letzte Kreise dargestellt, weil er der aktuellste ist.
So weit ist mir das alles noch verständlich.
"kleiner Held" hat mir dankenswerterweise (unten) ein Programm vorgestellt, mit dem man das Problem lösen kann. Dieses Programm benutzt "synchronized", mit dem verhindert werden soll, dass mehrere Threads gleichzeitig auf die Variable myimg zugreifen können.

Frage:
In der EDT-Warteschlange befinden sich (durch mehrere repaint() Aufrufe verursachte) paint (ein paint() zieht ein paintComponent(...) nach sich)) Aufrufe (wobei einige durch "coalescing" vereinigt wurden).
kurz:
paint1()
paint2()
paint3()
....
Das Wort "Warteschlange" in EDT-Warteschlange bedeutet doch, dass die einzelnen paint() _hintereineinander_ ausgeführt werden müssen, aber dann braucht man doch _kein_ synchronized, da dies dann nicht nebenläufig, also _nicht_ parallel erfolgt (und es dann keine Probleme beim Zugriff verschiedener, parallel laufender Threads auf _eine_ Variable gibt).
Oder wo ist da mein Denkfehler?

mfg
Ernst



```
package de;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class MainVerzoegertZeichnen4 {
     public static void main(String[] args){
       JFrame f = new JFrame();
       f.setSize(550,550);
       Diagramm diagramm = new Diagramm(550, 550);
       f.getContentPane().add(diagramm);
       Thread t = new Thread(diagramm);
       t.start();
       f.setVisible(true);
     }
   }

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i;
  private Image myimg;
 

  public Diagramm(int xpAnz, int ypAnz){
    i=0;
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;   
    myimg = new BufferedImage(xpAnz, ypAnz, BufferedImage.TYPE_INT_RGB);
  }
 
  //@Override
/*  
  public Dimension getPreferredSize()
  {
     return new Dimension(xpAnz, ypAnz);
  }
*/ 
 
  protected void paintComponent(Graphics g){
     synchronized(myimg){
        g.drawImage(myimg,0,0,null);
     }
  }
   
  public void addKreis(){
     synchronized(myimg){
        Graphics myg = myimg.getGraphics();
        myg.setColor(Color.red);
        myg.drawOval(i , 2*i+15, 20, 20);
        myg.dispose();
     }
  }

  public void run(){
//    while(i<200){
  	while(true){    	
      try{
        //Thread.sleep(500);
      }
      catch(Exception e){
      }
      i = i+20;
      addKreis();
      this.repaint();
    }
  }
}
```


----------



## SlaterB (18. Mai 2007)

bei dir greifen nebenläufig der Diagramm-Thread (addKreis()) und paintComponent (drawImage) auf das Bild zu,
deshalb wahrscheinlich das synchronized


----------



## kleiner_held (18. Mai 2007)

SlaterB hat gesagt.:
			
		

> bei dir greifen nebenläufig der Diagramm-Thread (addKreis()) und paintComponent (drawImage) auf das Bild zu, deshalb wahrscheinlich das synchronized


So ist es.


----------



## ernst (18. Mai 2007)

kleiner_held hat gesagt.:
			
		

> SlaterB hat gesagt.:
> 
> 
> 
> ...


>
1)
Das kann ich nachvollziehen und habe zum Testen gleich das Programm (siehe unten) geschrieben.
Aber warum werden dort nicht
diagramm.setI(i) und diagramm.repaint()
synchronisiert?
Ich habe doch jeweils in 
setI(i) und paintComponent mit
synchronized(this) veranlaßt dass nicht gelichzeitig auf das Objkt diagramm zugegriffen werden kann?

2)
warum kann man eigentlich nicht 
synchronized(i){
....
}
schreiben, wobei i ein integer ist?
Aber 
synchronized(myimg){
....
}
wobei myimg den Datentyp Image hat?



```
package de;
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen901{
     public static void main(String[] args){
        int k,i;
        i=0;
        k = 0;
       JFrame f = new JFrame();
       f.setSize(550,550);
       Diagramm diagramm = new Diagramm(550, 550);
       f.getContentPane().add(diagramm);
       f.setVisible(true);       
       while(k<10){
       	diagramm.setI(i);     	
       	diagramm.repaint();
       	i=i+20;
         k=k+1;
       }
     }
   }

class Diagramm extends JPanel{
  private int xpAnz;
  private int ypAnz;
  private int i;
  private Graphics myg;
  private int sx;
  private int sy;
 
  public Diagramm(int xpAnz, int ypAnz){ 
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;
  }
 
  public void paintComponent(Graphics g){
  	synchronized(this){  	
  		g.setColor(Color.red);
  		g.drawOval(i , i, 20, 20);
  	}
  	System.out.println("i in paintComponent="+i);
  }

  public void setI(int pi){
  	synchronized(this){
  	  	i=pi;  		
  	}
  	System.out.println("i in setI="+i);  	
  }
}
```


mfg
Ernst


----------



## SlaterB (18. Mai 2007)

> Aber warum werden dort nicht 
> diagramm.setI(i) und diagramm.repaint() 
> synchronisiert? 

grob betrachtet sieht es für mich so aus, als wenn da synchronisiert wird,
was veranlasst dich denn zu der Vermutung, dass es nicht so ist?

--------

primitive Datentypen wie int sind den Objekten in mancher Weise unterlegen,
z.B. haben sie keinen Monitor für synchronized


----------



## ernst (18. Mai 2007)

>grob betrachtet sieht es für mich so aus, als wenn da synchronisiert wird,
>was veranlasst dich denn zu der Vermutung, dass es nicht so ist?
>
Die Ausgabe auf der Konsole:
i in setI=0
i in setI=20
i in setI=40
i in setI=60
i in setI=80
i in setI=100
i in setI=120
i in setI=140
i in setI=160
i in setI=180
i in paintComponent=180
i in paintComponent=180
und die auf dem Bildschirm (es wird nur ein Kreis gezeichnet)

>primitive Datentypen wie int sind den Objekten in mancher Weise unterlegen,
>z.B. haben sie keinen Monitor für synchronized
>
Aber beliebige selbstgebastelte Objekte dürfen mit synchroized verwendet werden?

mfg
Ernst


----------



## SlaterB (18. Mai 2007)

ja dürfen,

und das andere liegt doch am Zusammenfassen der paints,
es wird erst am Ende ein paint ausgeführt,

und das Zusammenfassen geschieht auch nicht erst im Laufe des paints, wenn der synchronized-Block schon geöffnet ist,
oder was immer du dir denkst,

erst 10x setI, dann paintComponent, bei dir anscheinend 2x, 
unabhängig von synchronized, sonder allein abhängig von der Enscheidung Swings, zu zeichnen oder nicht,

habe deinen Code 1:1 bei mir ausgeführt und unterschiedliche Ergebnisse,
darunter auch:

i in paintComponent=0
i in setI=0
i in paintComponent=0
i in setI=20
i in paintComponent=20
i in setI=40
i in paintComponent=40
i in setI=60
i in paintComponent=60
i in setI=80
i in paintComponent=80
i in setI=100
i in paintComponent=100
i in setI=120
i in paintComponent=120
i in setI=140
i in paintComponent=140
i in setI=160
i in paintComponent=160
i in setI=180
i in paintComponent=180

alles nichtdeterministisch


----------



## ernst (18. Mai 2007)

>und das andere liegt doch am Zusammenfassen der paints,
>es wird erst am Ende ein paint ausgeführt,
>
>
In der EDT-Warteschlange befinden sich (durch mehrere repaint() verursacht: --> paint() --> paintComponent(...)):
paintComponent1(...)
paintComponent2(...)
paintComponent3(...)
....
Wenn ich das so sehe wie du, ist mir das zwar klar, aber:
Frage:
Warum werden diese paintComponent(...)-Aufrufe bei dem Programm von "kleinerHeld" (Programm: siehe Anfangsposting von mir) nicht genauso vereinigt.
Was hat er _gemacht_, dass diese Vereinigung nicht passiert?

mfg
Ernst


----------



## SlaterB (18. Mai 2007)

man, dass ich ich dich immer alles nachfragen muss,
wenn du Ausgaben hast, dann poste sie doch gleich dazu..

also: wie kommst du darauf, dass dort die Vereinigung nicht passiert?
bei mir passiert sie, wenn ich
System.out.println("i: " + i);
in paint einfüge und die Schleife netterweise nur bis 200 und nicht ins Unendliche laufen lasse,
dann ist die Ausgabe
i: 200

wiederum kommt immer genau eine Ausgabe, wenn die Schleife VOR dem setVisible(true) liegt,
warum dürfte ja inzwischen geklärt sein 

startet der Thread erst später, habe ich mal eine Ausgabe, mal mehrere, mal alle,
ganz wie im unteren Programm

--------

und noch ein Tipp:
baue bitte
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
in alle deine Programme ein, besonders wenn da while-true-Threads drin sind, die unendlich weiterlaufen..,

mit diesem Befehl wird das Programm beendet, wenn das Fenster geschlossen wird


----------



## ernst (18. Mai 2007)

>wenn du Ausgaben hast, dann poste sie doch gleich dazu..
>
da alle Kreise ausgegeben wurden, war ich so überzeugt ...
sorry.
>
>also: wie kommst du darauf, dass dort die Vereinigung nicht passiert?
>bei mir passiert sie, wenn ich
>System.out.println("i: " + i);
>in paint einfüge und die Schleife netterweise nur bis 200 und nicht ins Unendliche laufen lasse,
>dann ist die Ausgabe
>i: 200
>
Du hast recht!!
Da alle Kreise ausgegeben wurden, war ich so überzeugt, dass die Vereinigung nicht gemacht wird.
Deswegen habe ich die Ausgabe von i nicht mehr angeschaut...
Es wird also vereinigt! Ich habe mich getäuscht.

Frage:
Was muss ich aber dann machen, wenn ich _alle_ Bilder (alle paintComponent()-Aufrufe im EDT sollen realisiert werden) haben will, wenn also die Vereinigung verhindert werden soll?



>und noch ein Tipp:
>baue bitte
>f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
als letzter Befehl in main()?

mfg
Ernst


----------



## SlaterB (18. Mai 2007)

> Was muss ich aber dann machen, wenn ich _alle_ Bilder (alle paintComponent()-Aufrufe im EDT sollen realisiert werden) haben will, wenn also die Vereinigung verhindert werden soll? 

also ich kenne mich da nicht allzu sehr aus, 
aber 1 Mio. repaint pro Sekunde sind sicherlich zu viel 
wenn du da auf unter die typischen Frames Per Second von Computerspielen (z.B. 100 pro Sekunde) kommst, sieht es vielleicht besser aus,  

andererseits ist es im Bereich von 10ms schwer, genau zu timen., 
also entweder so viel repaint wie es gut passt, und nicht weiter stören, wenn welche wegfallen (würde der Betrachter eh nicht sehen)
oder ordentlich Abstand lassen, bei 1 paint pro Sekunde kann nix schiefgehen 

------

> als letzter Befehl in main()? 

schon mal gar nicht schlecht, allein schon der Ordnung halber am besten im Bereich von f.setSize(550,550);
und VOR f.setVisible(true)

setVisible(true) ist zumindest bei mir immer der Abschluss,
da wird das Frame auf die Welt losgelassen und muss nicht mehr duch unnötig spät plazierte Kommandos genervt werden


----------



## ernst (18. Mai 2007)

>also ich kenne mich da nicht allzu sehr aus, 
>aber 1 Mio. repaint pro Sekunde sind sicherlich zu viel 
>wenn du da auf unter die typischen Frames Per Second von 
>Computerspielen (z.B. 100 pro Sekunde) kommst, sieht es vielleicht besser aus,  
>andererseits ist es im Bereich von 10ms schwer, genau zu timen., 
>also entweder so viel repaint wie es gut passt, und nicht weiter stören, 
>wenn welche wegfallen (würde der Betrachter eh nicht sehen)
>oder ordentlich Abstand lassen, bei 1 paint pro Sekunde kann nix schiefgehen 
>
>
Was hältst du von diesem Vorschlag?

```
package de;
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen3 {
	  public static void main(String[] args){
	    JFrame f = new JFrame();
	    f.setSize(550,550);
	    Diagramm diagramm = new Diagramm(550, 550);
	    f.getContentPane().add(diagramm);
	    Thread t = new Thread(diagramm);
	    t.start();
	    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 	    
	    f.setVisible(true);
	  }
	}

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i,j,k; 
  private Image myimg;
  private Graphics myg;
  private int sx; 
  private int sy;
  private boolean ichMaleGerade;
  Object lockForAccessToFlag = new Object();
  
  public Diagramm(int xpAnz, int ypAnz){  
    i=0;
    k=0;
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;    
    myimg=null;
    synchronized(lockForAccessToFlag){
    	ichMaleGerade=true;
    }
  }
  
  public void paintComponent(Graphics g){
  	if(myimg==null){
  	    sx = this.getSize().width;  
  	    sy = this.getSize().height;
  	    myimg = createImage(sx, sy);
  	    myg = myimg.getGraphics();
  	}
  	myg.setColor(Color.red);
 	myg.drawOval(i , j, 20, 20);
    synchronized(lockForAccessToFlag){ 	
    	ichMaleGerade=false;
      	System.out.println("k in paintComponent="+k);    	
  		lockForAccessToFlag.notify();    	
    }
  	g.drawImage(myimg,0,0,null);
  }
	
  public void run(){
  	j=11;
  	i=11;
//    while(true){    	
  	while(k<100){    	
  		System.out.println("k in run="+k);
  		this.repaint();
      synchronized(lockForAccessToFlag){      
	      while(ichMaleGerade==true){
	      	try{
	      		lockForAccessToFlag.wait();
	      	}
	      	catch(Exception e){
	      		System.out.println("Hallo");
	      	}
	      }
	      ichMaleGerade=true;
      }
      try{
      	//Thread.sleep(5);
      }
      catch(Exception e){}
  
      i = i+20;
      if(i>400){
      	j=j+5;
      	i=11;
      }
      if(j>400)
      	j=11;
      k=k+1;
  	}
  }
}
```


mfg
Ernst


----------



## SlaterB (18. Mai 2007)

ich bin kein Fan von synchronized und co.,
dein ganzes Programm mag ich aber auch gar nicht, da das wohl den  PC die ganze Zeit arbeiten lässt,

wenn du aber so viel malen willst mag es so gehen,
kenne mich da aber wirklich nicht aus


----------



## Wildcard (18. Mai 2007)

SlaterB hat gesagt.:
			
		

> ich bin kein Fan von synchronized und co.,


Was heißt du bist kein Fan? Wie synchronisierst du denn dann?


----------



## ernst (18. Mai 2007)

>ich bin kein Fan von synchronized und co.,
>dein ganzes Programm mag ich aber auch gar nicht, 
>da das wohl den  PC die ganze Zeit arbeiten lässt,
>
du hast recht.
Wenn ich aber mit sleep(...) irgend eine noch so kleine oder große Verzögerung einbaue, brauche ich nicht 100% CPU-Zeit, sondern entsprechend (der Verzögerung von sleep) weniger _und_ habe eine Garantie, dass nicht ein paar painComponent(...) Aufrufe durch "coalescing" vereinigt wurden, und deshalb ein paar Bilder verloren gingen.
>
>wenn du aber so viel malen willst mag es so gehen,
>kenne mich da aber wirklich nicht aus[/quote]
>
ich kenne mich auch nicht aus. Ich probiere eben in ein paar Demoprogrammen ein paar Sachen aus.

mfg
Ernst


----------



## Wildcard (18. Mai 2007)

Wenn du die Kontrolle darüber brauchst wann und wie oft gezeichnet werden muss, kannst du diesen Weg nicht gehen, daher sehe ich keinen Sinn in deinen Versuchen.


----------



## ernst (18. Mai 2007)

>Wenn du die Kontrolle darüber brauchst wann und wie oft gezeichnet werden muss, 
>kannst du diesen Weg nicht gehen, 
>
1) warum kann ich diesen Weg (Programm von mir) nicht gehen (was ist falsch)?
2)zeige mir einen gehbaren Weg, eine Alternative.

mfg
Ernst


----------



## Wildcard (18. Mai 2007)

Weil AWT entscheidet wann gezeichnet wird und wann nicht. repaint ist nur eine freundliche Bitte. Brauchst du die volle Kontrolle über die Zeichenroutinen musst du zu Active-Rendering greifen.


----------



## ernst (18. Mai 2007)

>Weil AWT entscheidet wann gezeichnet wird und wann nicht. 
>
Bei mir ist das doch Swing, oder ?
>
>repaint ist nur eine freundliche Bitte. 
>
Und diese freundliche Bitte gestalte ich mit meinem letzten Programm zu einer vollen Kontrolle (keine Bilder gehen verloren) oder ist das eher eine "Vergewaltigung" von repaint?

>Brauchst du die volle Kontrolle über die Zeichenroutinen musst du zu Active-Rendering greifen.
>
Davon habe ich leider Null Ahnung.
Kannst du mir ein paar Tipps, Links und vielleicht ein einfachstes Programm geben ?

mfg
Ernst


----------



## Wildcard (18. Mai 2007)

> Bei mir ist das doch Swing, oder ?


Swing ist ein Lightweight Toolkit das auf AWT aufsetzt. Im Klartext das 'echte' Zeichnen macht AWT.


> Und diese freundliche Bitte gestalte ich mit meinem letzten Programm zu einer vollen Kontrolle


Nein, du kannst weder deterministisch festlegen *wann*, noch *wie oft* gezeichnet wird.


> Kannst du mir ein paar Tipps, Links und vielleicht ein einfachstes Programm geben ?


http://java.sun.com/docs/books/tutorial/extra/fullscreen/rendering.html


----------



## ernst (19. Mai 2007)

1)
>>Und diese freundliche Bitte gestalte ich mit meinem letzten Programm zu einer vollen Kontrolle
>Nein, du kannst weder deterministisch festlegen *wann*, noch *wie oft* gezeichnet wird.
>
"wann " ist mir klar
"wie oft":
Meinst du, dass man dies nicht wegen des  "coalescing" (verschmelzen, vereinigen) - d.h. mehrere Aufrufe werden zu einem vereinigt - machen kann?
>
2)
>http://java.sun.com/docs/books/tutorial/extra/fullscreen/rendering.html
>
Danke, ich werde mir das mal anschauen.

3)
Eine Verständnisfrage habe ich noch:
In den Programmen in meinen letzten Postings habe ich immer eine Variable von Datentyp Image benutzt:
private Image myimg;
mit den zugehörigen Methoden
protected void paintComponent(Graphics g){
   synchronized(myimg){
   g.drawImage(myimg,0,0,null);
   }
   System.out.println("i: " + i);      
}

public void addKreis(){
   synchronized(myimg){
      Graphics myg = myimg.getGraphics();
      myg.setColor(Color.red);
      myg.drawOval(i , 2*i+15, 20, 20);
      myg.dispose();
}

4)
Jetzt habe ich mal probiert, dies ohne Image zu machen, sondern nur mit Graphics g:
protected void paintComponent(Graphics g){
    g.drawOval(i , 2*i+15, 20, 20);
     System.out.println("i: " + i);      
}

```
package de;
import java.awt.*;
import javax.swing.*;

public class MainTest2 {
     public static void main(String[] args){
       JFrame f = new JFrame();
       f.setSize(550,550);
       Diagramm diagramm = new Diagramm(550, 550);
       f.getContentPane().add(diagramm);
       f.setVisible(true);       
       Thread t = new Thread(diagramm);
       t.start();
//       f.setVisible(true);
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       
     }
   }

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i;
  private Image myimg;
 

  public Diagramm(int xpAnz, int ypAnz){
    i=0;
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;   
  }
 
  protected void paintComponent(Graphics g){
    g.drawOval(i , 2*i+15, 20, 20);
     System.out.println("i: " + i);      
  }


  public void run(){
    while(i<200){
//  	while(true){    	
      try{
       }
      catch(Exception e){
      }
      i = i+20;
      this.repaint();
    }
  }
}
```

Dann wurde nur noch _ein_ Kreis auf dem Bildschirm ausgegeben.
Meine Erklärung:
a) 
Durch "coalescing" werden einige (bei mir: alle bis auf einen) paintComponent-Aufrufe vereinigt.
dadurch werden nicht mehr alle 
g.drawOval(i , 2*i+15, 20, 20);
Aufrufe (in paintComponent(...)) ausgeführt und deswegen fehlen die dazugehörigen Bilder auf dem Bildschirm.

b)
Im anderen Programm dagegen werden durch 
addKreis()
die ganzen Kreise in das Image myimg gezeichnet.
Wenn dann mal ein Bild wegen "coalescing" auf dem Bildschirm verloren geht, macht das nichts aus, weil das nächste Bild (speziell das letzte Bild alle Kreis enthält) immer einen Kreis mehr enthält als das vorige.
Im schlimmste Fall - wenn nämlich nur das letzte Bild auf dem Bildschirm ausgegeben wird - enthält dieses sämtliche Kreise und der Anwender sieht nicht, dass ein paar Bilder verloren gingen.
Er würde es merken, wenn das nächste Bild einen Kreis weniger als das vorige enthalten würde.
Ist meine Erklärung richtig?
Oder braucht man das Image auch noch aus anderen Gründen (wegen des Flackerns auf dem Bildschirm; habe ich irgendwo mal gelesen), oder stimmt das nicht?

mfg
Ernst


----------



## Wildcard (19. Mai 2007)

Du hast schon ein ganz grundlegendes Verständnisproblem mit der Art wir Passive-Rendering funktioniert.
Du 'verlierst' nie Bilder (wenn du dein Programm richtig schreibst) weil paint stateless ist.
Du bildest nur den aktuellen Zustand deiner Daten ab und gehst davon aus das noch nie irgendwas gezeichnet wurde.
Solange man paint/paintComponent also so verwendet wie es gedacht ist, ist völlig egal wie oft sich AWT dazu entscheidet neu zu zeichnen. Du musst nur wissen das in paint/paintComponent der aktuelle Zustand des Modells visualisiert wird und that's it.


----------



## ernst (19. Mai 2007)

Wildcard hat gesagt.:
			
		

> Du hast schon ein ganz grundlegendes Verständnisproblem mit der Art wir Passive-Rendering funktioniert.
> Du 'verlierst' nie Bilder (wenn du dein Programm richtig schreibst)


Das ist der Punkt...
In dem Programm mit Image wurde das Programm richtig geschrieben (alle Kreise sind in myimg) und in der anderen Version sind in g nicht alle Kreise mehr enthalten und deswegen ist es falsch geschrieben. 
Oder siehst du das anderst?
[/quote]weil paint stateless ist.
Du bildest nur den aktuellen Zustand deiner Daten ab und gehst davon aus das noch nie irgendwas gezeichnet wurde.
Solange man paint/paintComponent also so verwendet wie es gedacht ist, ist völlig egal wie oft sich AWT dazu entscheidet neu zu zeichnen. Du musst nur wissen das in paint/paintComponent der aktuelle Zustand des Modells visualisiert wird und that's it.[/quote]
Und wenn paint nicht stateless sein soll, man also die verschiedenen Zustände auf dem Bildschirm darstellen will, dann braucht man entweder die Notlösung von mir oder den professionellen Vorschlag von dir.

mfg
Ernst


----------



## Wildcard (19. Mai 2007)

ernst hat gesagt.:
			
		

> Und wenn paint nicht stateless sein soll, man also die verschiedenen Zustände auf dem Bildschirm darstellen will, dann braucht man entweder die Notlösung von mir oder den professionellen Vorschlag von dir.


Zeichnen sollte *immer* von der Logik getrennt sein. Das eine hat mit dem anderen nichts zu tun.
Passive Rendering:
Die Anwendung rutscht auf den Daten rum und bildet diese ab wann immer gezeichnet werden soll.

Active Rendering:
Die Anwendung rutscht auf den Daten rum und zeichnet in regelmäßigen Abständen selbst.

In beiden Fällen muss es für das Modell völlig egal sein wie oft nun konkret gerendert wurde.


----------



## ernst (19. Mai 2007)

>Zeichnen sollte *immer* von der Logik getrennt sein. 
>Das eine hat mit dem anderen nichts zu tun.
>
Okay, das ist OOP (da kenne ich mich nicht so gut aus)

Nochmals zu meiner vorigen Frage (wichtig für mein Verständnis):
...
Oder braucht man das Image auch noch aus anderen Gründen (wegen des Flackerns auf dem Bildschirm; habe ich irgendwo mal gelesen), oder stimmt das nicht? 

mfg
Ernst


----------



## Wildcard (19. Mai 2007)

ernst hat gesagt.:
			
		

> Oder braucht man das Image auch noch aus anderen Gründen (wegen des Flackerns auf dem Bildschirm; habe ich irgendwo mal gelesen), oder stimmt das nicht?


Um Flackern zu vermeiden besteht *eine* Möglichkeit in sog. DoubleBuffering. Dabei wird ein Offscreen Image verwendet (meistens ein Compatible- oder VolatileImage) das zum Rendern verwendet wird und das anschließend auf den Bildschirm kopiert wird.
Damit werden sichtbare Artefakte (Flackern) vermieden.

Bleibt man im normalen 'Applikations' Modus, d.h. Passive Rendering, ist dieses Vorgehen nur für AWT von Nöten. Medium Weight Container des Swing Toolkits (JFrame, JDialog, JWindow) verwenden standardmäßig eine BufferStrategy die Flackern verhindert.


----------



## ernst (22. Mai 2007)

Bei einem Lösungsvorschlag von euch wurde mir geraten, einfach eine Verzögerung zwischen zwei repaints einzubauen.

1)
Im folgenden Programm habe ich das gemacht. 
Allerdings habe ich dazu keinen Thread erzeugt, sondern nur mit Thread.sleep(100) verzögert. 
100 ms reichen offensichtlich noch nicht aus, um die Ausgabe zu synchronisieren.
(sieht man, wenn man sich die Ausgabe auf dem Bildschirm anschaut)
i in main=20
i=20
i=20
i in main=40
i=40
i=40
i in main=60
i=60
i in main=80
i=80
i in main=100
i=100
i in main=120
i in main=140
i=140
i=140
i in main=160
i=160
i in main=180
i=180
i in main=200


```
package de;
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen4 {
	public static void main(String[] args){
	  	int k, i;
	  	i = 0;
	  	k = 0;
	  	JFrame f = new JFrame();
	  	f.setSize(550,550);
	  	Diagramm diagramm = new Diagramm(550, 550);
	  	f.getContentPane().add(diagramm);
	  	f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	  	f.setVisible(true);	  	
     
	  	while(k<10){
	  		try{
	  			Thread.sleep(100);
	  		}
	  		catch(Exception e){}
	  		diagramm.repaint();
	  		k=k+1;
	  		i = i+20;
	      	System.out.println("i in main="+i);	  		
	  		diagramm.setI(i);
       }
	}
} 

class Diagramm extends JPanel{
	  private int xpAnz;
	  private int ypAnz;
	  private int i;
	  private Image myimg;
	  private Graphics myg;
	  private int sx;
	  private int sy;
	 
	  public Diagramm(int xpAnz, int ypAnz){ 
	    i=0;
	    this.xpAnz=xpAnz;
	    this.ypAnz=ypAnz;   
	    myimg=null; 
	  }

	  public void setI(int pi){
	  	i = pi;
	  }
	  
	  public void paintComponent(Graphics g){
	     if(myimg==null){
	         sx = this.getSize().width; 
	         sy = this.getSize().height;
	         myimg = createImage(sx, sy);
	         myg = myimg.getGraphics();
	     }
	     myg.setColor(Color.red);
	     myg.drawOval(i , i, 20, 20);
	     System.out.println("i="+i);
	     synchronized(myimg){
	     	g.drawImage(myimg,0,0,null);
	    }
	  }
}
```

2)
Im folgenden Programm habe ich einen Thread erzeugt und dann in run mit Thread.sleep(100) verzögert.
100 ms reichen hier offensichtlich auch noch nicht aus, um die Ausgabe zu synchronisieren.
(sieht man, wenn man sich die Ausgabe auf dem Bildschirm anschaut)
i in run=20
i in run=40
i in run=60
i in paintComponent=60
i in paintComponent=60
i in paintComponent=60
i in run=80
i in paintComponent=80
i in run=100
i in paintComponent=100
i in run=120
i in paintComponent=120
i in run=140
i in paintComponent=140
i in run=160
i in paintComponent=160
i in run=180
i in paintComponent=180
i in run=200
i in paintComponent=200


```
package de;
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen5 {
	  public static void main(String[] args){
	    JFrame f = new JFrame();
	    f.setSize(550,550);
	    Diagramm diagramm = new Diagramm(550, 550);
	    f.getContentPane().add(diagramm);
	    Thread t = new Thread(diagramm);
	    t.start();
	    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 	    
	    f.setVisible(true);
	  }
	}

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i; 
  private Image myimg;
  private Graphics myg;
  private int sx; 
  private int sy;

  public Diagramm(int xpAnz, int ypAnz){  
    i=0; 
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;    
    myimg=null;  
  }

/*  
  public void setI(int pi){
  	i = pi;
  }
*/  
  
  public void paintComponent(Graphics g){
  	if(myimg==null){
  	    sx = this.getSize().width;  
  	    sy = this.getSize().height;
  	    myimg = createImage(sx, sy);
  	    myg = myimg.getGraphics();
  	}
  	myg.setColor(Color.red);
 	myg.drawOval(i , i, 20, 20);
  	g.drawImage(myimg,0,0,null);
  	System.out.println("i in paintComponent="+i);
  }
	
  public void run(){
  	int k;
  	k=0;
  	i=0;
    while(k<10){
    	k=k+1;
    	i=i+20;
      	System.out.println("i in run="+i);   	
    	this.repaint();
    	try{
    		Thread.sleep(100);
    	}
    	catch(Exception e){}
    }
  }
}
```


Fragen:
1) Ist es _unbedingt nötig_, einen Thread zu verwenden, bzw. welchen Nachteil hat es, wenn man ihn nicht verwendet oder kann man auf ihn verzichten?

2) Sind nur die 100 ms zu wenig (um alle Zeichnungen auf den Bildschirm zu bringen) oder habe ich einen Denkfehler in meinem Programm ?

mfg
Ernst


----------

