Discussion:
warum keine Schleifen und Logik in paintComponent()?
(zu alt für eine Antwort)
Ernst Baumann
2007-05-23 15:31:41 UTC
Permalink
Hallo allerseits,
im folgenden ein kleines Demoprogramm:

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

public class MainVerzoegertZeichnen3{
public static void main(String[] args){
int k;
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);
diagramm.repaint();
}
}

class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int i,j;
private Graphics myg;
private int sx;
private int sy;

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

public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}
}
}
--------------------------------------------------------------------------
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
man in paintComponent() folgendes Verbot berücksichtigen soll:
- keine Zustände (wie oben das i=i+20) ändern und
- keine Logik machen.
Allerdings habe ich über dieses Verbot in der Doku über paint nichts
gelesen.
http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Wo steht dieses Verbot ?
Welchen Sinn hat dieses Verbot ?

2) Komischer Programmablauf
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.

mfg
Ernst
Sönke Müller-Lund
2007-05-23 15:48:20 UTC
Permalink
Post by Ernst Baumann
package de;
Böse!
Post by Ernst Baumann
import java.awt.*;
import javax.swing.*;
public class MainVerzoegertZeichnen3{
public static void main(String[] args){
int k;
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);
diagramm.repaint();
}
}
class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int i,j;
private Graphics myg;
private int sx;
private int sy;
public Diagramm(int xpAnz, int ypAnz){
i=0;
this.xpAnz=xpAnz;
this.ypAnz=ypAnz;
}
public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}
}
}
--------------------------------------------------------------------------
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
Gegen dieses Gebot verstößt Du nicht.
Post by Ernst Baumann
- keine Logik machen.
Na das ist ja eine Aussage! :)

Es ist vielmehr damit gemeint, dass die Methode paintComponent() nichts
weiter tun soll, als sich zu zeichnen. Du sollst also keine
Datenbankzugriffe, Geschäftsprozesse, Dateizugriffe und ähnliches in
dieser Methode implementieren oder aufrufen, sondern nur malen (und das
am besten so, dass der Zustand der Komponente nicht verändert wird).
Post by Ernst Baumann
Allerdings habe ich über dieses Verbot in der Doku über paint nichts
gelesen.
http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Wo steht dieses Verbot ?
Welchen Sinn hat dieses Verbot ?
Zwei wichtige Gründe:
1.) Du verdirbst das Design.
2.) Du blockierst deine Anwendung, weil das besitzende Fenster erst
wieder für GUI-Events bereit ist, wenn paintComponent() ausgeführt ist
Post by Ernst Baumann
2) Komischer Programmablauf
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
Wenn Du verzögert zeichnen oder Animationen erstellen willst, musst Du
Threads verwenden.

Sönke
Ernst Baumann
2007-05-23 17:21:06 UTC
Permalink
Post by Sönke Müller-Lund
Post by Ernst Baumann
package de;
Böse!
Warum??
Habe ich mit Eclipse erstellt und das package de genannt.
Post by Sönke Müller-Lund
Post by Ernst Baumann
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
Gegen dieses Gebot verstößt Du nicht.
Ich habe aber den Zustand der Exemplarvariable i verändert (i=i+20).
Das soll ich doch nicht, oder?
Post by Sönke Müller-Lund
Es ist vielmehr damit gemeint, dass die Methode paintComponent() nichts
weiter tun soll, als sich zu zeichnen. Du sollst also keine
Datenbankzugriffe, Geschäftsprozesse, Dateizugriffe und ähnliches in
dieser Methode implementieren oder aufrufen, sondern nur malen (und das
am besten so, dass der Zustand der Komponente nicht verändert wird).
_Wo_ steht dieses Verbot ?
Post by Sönke Müller-Lund
2.) Du blockierst deine Anwendung, weil das besitzende Fenster erst
wieder für GUI-Events bereit ist, wenn paintComponent() ausgeführt ist
Es gibt aber in diesem konkreten Beispiel keinen weiteren GUI-Event.
Weder durch explizites Afrufen von repaint(), weil dies genau einmal
im Programm geschieht, noch durch vergrößeren oder verkleinern des
Fensters (ich verändere nichts).
ich verstehe den Grund nicht (siehe oben). Es werden keine weiteren
GUI-Events ausgelöst.
Und selbst wenn:
Wenn paintComponent() öfters vom Betriebssystem aufgerufen wird, ist
das doch egal. Es werden u.U. durch "coalescing" mehrere
repaintComponent() vereinigt (oder auch nicht, dies entscheidet JVM).
Das ist doch egal. Bei jedem paintComponent() werden eben durch die
Schleife bedingt die 10 Kreise gezeichnet.
Post by Sönke Müller-Lund
Post by Ernst Baumann
2) Komischer Programmablauf
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
mfg
Ernst
Jochen Theodorou
2007-05-23 19:24:36 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Ich habe aber den Zustand der Exemplarvariable i verändert (i=i+20).
Das soll ich doch nicht, oder?
sollst du nicht unbedingt, nein, aber das hast du auch nicht.. denn:

class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int i,j;
[...]

da sieht man eine Exemplarvariable i. aber:

public void paintComponent(Graphics g){
int i=0;
int k=0;
[...]

hier definierst du eine lokale Variable i, welche die Exemplarvaribale
verdeckt.

Kann es sein, dass die noch einige Grundkenntnisse in Java fehlen?
Post by Ernst Baumann
Post by Sönke Müller-Lund
Es ist vielmehr damit gemeint, dass die Methode paintComponent() nichts
weiter tun soll, als sich zu zeichnen. Du sollst also keine
Datenbankzugriffe, Geschäftsprozesse, Dateizugriffe und ähnliches in
dieser Methode implementieren oder aufrufen, sondern nur malen (und das
am besten so, dass der Zustand der Komponente nicht verändert wird).
_Wo_ steht dieses Verbot ?
Das ist kein Verbot als solches, es ist Best Practice und sichert eine
Funktionierende Anwendung ohne dass das UI blockiert.
Post by Ernst Baumann
Post by Sönke Müller-Lund
2.) Du blockierst deine Anwendung, weil das besitzende Fenster erst
wieder für GUI-Events bereit ist, wenn paintComponent() ausgeführt ist
Es gibt aber in diesem konkreten Beispiel keinen weiteren GUI-Event.
Weder durch explizites Afrufen von repaint(), weil dies genau einmal
im Programm geschieht, noch durch vergrößeren oder verkleinern des
Fensters (ich verändere nichts).
na dann mach mal deine Animation endlos und füge einen Knopf ein um sie
zu stoppen.. viel Spass! Willst du das nicht gleich richtig lernen?
Post by Ernst Baumann
ich verstehe den Grund nicht (siehe oben). Es werden keine weiteren
GUI-Events ausgelöst.
in diesem, extrem simplen Beispiel vielleicht nicht, sofern die
Rahmenbedingungen stimmen.. Sind das nicht ein bisschen viele Bedingungen?

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-05-24 12:49:33 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Post by Sönke Müller-Lund
Es ist vielmehr damit gemeint, dass die Methode paintComponent() nichts
weiter tun soll, als sich zu zeichnen. Du sollst also keine
Datenbankzugriffe, Geschäftsprozesse, Dateizugriffe und ähnliches in
dieser Methode implementieren oder aufrufen, sondern nur malen (und das
am besten so, dass der Zustand der Komponente nicht verändert wird).
_Wo_ steht dieses Verbot ?
Das ist kein Verbot als solches, es ist Best Practice und sichert eine
Funktionierende Anwendung ohne dass das UI blockiert.
Wo stehen die Empfehlungeen zu "Best Practice" ?

mfg
Ernst
Michael Holtermann
2007-05-24 16:20:28 UTC
Permalink
Moin Ernst!
Post by Ernst Baumann
Wo stehen die Empfehlungeen zu "Best Practice" ?
Leider weniger in Büchern zum Java-lernen, sondern eher in Büchern über
Entwurfsmuster, Refactoring, Style Guides, praktische Erfahrung...

Einige "Fehler", die man als Anfänger macht, sind harmlos und haben keine
Nachteile. Spätestens jedoch, wenn man anfangen möchte, Code
wiederzuverwenden, hapert es dann an solchen Kleinigkeiten.

Grüße, Michael.
Ernst Baumann
2007-05-25 19:24:32 UTC
Permalink
Post by Michael Holtermann
Post by Ernst Baumann
Wo stehen die Empfehlungeen zu "Best Practice" ?
Leider weniger in Büchern zum Java-lernen, sondern eher in Büchern über
Entwurfsmuster, Refactoring, Style Guides, praktische Erfahrung...
Einige "Fehler", die man als Anfänger macht, sind harmlos und haben keine
Nachteile. Spätestens jedoch, wenn man anfangen möchte, Code
wiederzuverwenden, hapert es dann an solchen Kleinigkeiten.
Was heißt Fehler:
Ich denke, dass es ein Fehler von Java ist, die Verzögerungen in
paintComponent einfach _ungefragt_ aufzusummieren und am Schluss auf
einen Schlag alles auf dem Bildschirm auszugeben.
Wenn dies Java einfach _hier_ ungefragt macht, dann kann es ja sein,
dass dies Java auch bei anderen Gelegenheiten wie der Folgenden macht.
Ist Java also _nicht berechechenbar_ ???
Ich habe in der Funktion main diese Schleife (siehe unten) eingebaut
und damit diese "rechenintensive" (statt k < 10 könnte ich auch k <
10000000 schreiben, dann wäre es rechenintensiv) Operation damit
außerhalb des EDT gelegt.
Kann dann auch das passieren, was in paintComponet passiert ist:
d.h 10*100ms warten und dann alles ausgeben (tut es
aber nicht, ich habe es ausprobiert; wenn dies der Fall wäre, dann
wäre Java nicht berechenbar).

----------------------------------------
int main(){
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(100);
}
catch(Exception e){}
}
...
}
----------------------------------------


mfg
Ernst
Michael Paap
2007-05-25 19:41:02 UTC
Permalink
Post by Ernst Baumann
Ich denke, dass es ein Fehler von Java ist, die Verzögerungen in
paintComponent einfach _ungefragt_ aufzusummieren und am Schluss auf
einen Schlag alles auf dem Bildschirm auszugeben.
Wenn dies Java einfach _hier_ ungefragt macht, dann kann es ja sein,
dass dies Java auch bei anderen Gelegenheiten wie der Folgenden macht.
Ist Java also _nicht berechechenbar_ ???
Wie wäre es, du liest dir mal ein paar Grundlagen an, liest *dann* die
diversen Antworten die dir gegeben wurden noch mal und stellst *dann*
weitere Fragen?

Gruß,
Michael
Marcus Woletz
2007-05-25 20:12:12 UTC
Permalink
Hallo Michael,

Michael Paap schrieb:
[...]
Post by Michael Paap
Wie wäre es, du liest dir mal ein paar Grundlagen an, liest *dann* die
diversen Antworten die dir gegeben wurden noch mal und stellst *dann*
weitere Fragen?
Und _wir_ sollten ihm vielleicht noch sagen, dass von außerhalb des EDT
auch nicht auf bereits realisierte Swing/AWT-Objekte zugegriffen werden
soll. Deshalb soll ja auch die Erzeugung der Swing-Komponenten nicht
direkt in main() erfolgen, sondern in einem Runnable, das über
invokeLater aufgerufen wird.
Post by Michael Paap
Gruß,
Michael
ciao

Marcus
Ernst Baumann
2007-05-27 11:31:02 UTC
Permalink
Post by Marcus Woletz
Und _wir_ sollten ihm vielleicht noch sagen, dass von außerhalb des EDT
auch nicht auf bereits realisierte Swing/AWT-Objekte zugegriffen werden
soll.
1)
Weil Swing nicht threadsicher ist, darf man von einem Thread (in dem
die rechenintensiven Operationen ablaufen) nicht auf Teile des
Programms zugreifen, in dem sich die Komponenten von Swing befinden
(Buttons, Labels, usw.). Das habe ich mal gelesen.
Ist das richtig?

2)
Was heißt threadsicher?
Post by Marcus Woletz
Deshalb soll ja auch die Erzeugung der Swing-Komponenten nicht
direkt in main() erfolgen, sondern in einem Runnable, das über
invokeLater aufgerufen wird.
3)
Das trifft doch aber nicht auf das Programm unten zu, da ich keine
Komponenten von Swing (Buttons, Labels, usw.) erzeuge.
Ist das richtig?

--------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}
--------------------------------------------------------------


mfg
Ernst
Jochen Theodorou
2007-05-27 13:53:55 UTC
Permalink
Post by Ernst Baumann
Post by Marcus Woletz
Und _wir_ sollten ihm vielleicht noch sagen, dass von außerhalb des EDT
auch nicht auf bereits realisierte Swing/AWT-Objekte zugegriffen werden
soll.
1)
Weil Swing nicht threadsicher ist, darf man von einem Thread (in dem
die rechenintensiven Operationen ablaufen) nicht auf Teile des
Programms zugreifen, in dem sich die Komponenten von Swing befinden
(Buttons, Labels, usw.). Das habe ich mal gelesen.
Ist das richtig?
2)
Was heißt threadsicher?
threadsicher heisst, dass mehrere Threads auf die gleiche Datenstruktur
zugreifen können, also parallel. Wenn du also den Status eines JButton
von gedrückt auf nicht gedrückt setzt, dann ist es nicht gut das
ausserhalb des EDT zu setzen.
Post by Ernst Baumann
Post by Marcus Woletz
Deshalb soll ja auch die Erzeugung der Swing-Komponenten nicht
direkt in main() erfolgen, sondern in einem Runnable, das über
invokeLater aufgerufen wird.
3)
Das trifft doch aber nicht auf das Programm unten zu, da ich keine
Komponenten von Swing (Buttons, Labels, usw.) erzeuge.
Ist das richtig?
du änderst sie indirekt, deswegen falsch, aber du synchronisierst, daher
richtig. Allerdings solltest du nicht zu viel synchronoized verwenden.
würdest du setI in einem SwingWorker machen, bräuchtest du kein
synchronized. Andererseits könnte man bei setI auch einfach das "Risiko"
eingehen und es einfach ohne jegliche Mechanism verwenden. Die
Wahrscheinlichkeit für einen seltsamen Seiteneffekt sollte relativ
niedrig sein. Im allgemeinen aber ist SwingWorker dein Freund.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-05-31 13:56:51 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Was heißt threadsicher?
threadsicher heisst, dass mehrere Threads auf die gleiche Datenstruktur
zugreifen können, also parallel. Wenn du also den Status eines JButton
von gedrückt auf nicht gedrückt setzt, dann ist es nicht gut das
ausserhalb des EDT zu setzen.
Post by Ernst Baumann
Post by Marcus Woletz
Deshalb soll ja auch die Erzeugung der Swing-Komponenten nicht
direkt in main() erfolgen, sondern in einem Runnable, das über
invokeLater aufgerufen wird.
Da ich in dem Programm Swing verwende, habe ich, ohne explizit einen
Thread zu erzeugen, automatisch zwei Threads:
a) den Main-Thread und
b) den EDT

Wenn nun Daten verwendet werden, die paralel im Main-Thread und
dem EDT verwendet werden, gibt es zwei Möglichkeiten:

1. Möglichkeit: Alles (Programm siehe unten) in den EDT (und deshalb
_ohne_ synchronized)
Die Elemente der GUI (Fenster, Buttons, usw.), die Verzögerung und das
Zeichnen in der Schleife, werden im EDT realisiert und nicht im
Main-Thread.
Damit sich diese Anweisungen alle innerhalb des EDT befinden, wird
invokeLater() verwendet. Bei invokeLater() bzw. der Methode run ist
also kein synchronized nötig (alles befindet sich ja in nur _einem_
Thread)
Dies (invokeLater()) soll deshalb geschehen, weil sonst die zwei
Threads auf gemeinsame Daten, (nämlich den Elementen der GUI)
zugreifen können, aber der Main-Thread und der EDT nicht threadsicher
sind.

Zum Programm:
Das Programm ist ein Testprogramm zum Experimentieren und Lernen, aber
nicht korrekt (aber lauffähig). Ich will den EDT durch das Programm
etwas näher kennenlernen.
Im EDT befindet sich die Event-Wahrnehmung und Verarbeitung, das
Zeichnen (z.B. mit paintComponent(...) und von mir mit invokeLater neu
hinzugekommen der Thread, der die Methode createAndShowGUI() startet.
Ist das richtig, oder habe ich da schon einen Denkfehler?

Das Programm hat folgende Fehler:
a) Es _summiert_ die ganzen Verzögerungen, bevor es einen Kreis
ausgibt.
Den Grund dafür kann ich mir im Moment nicht erklären.
Oder liegt es daran, dass sich jetzt die Eventverarbeitung,
paintComponent() und createAndShowGUI() einen Thread (den EDT) teilen.
D.h. während die Methode createAndShowGUI() abläuft kann dann
paintComponent() nicht ausgeführt werden.
b) paintComponent() wird -laut Ausgabe - nur zweimal ausgeführt
(wahrscheinlich einmal von setVisible(true)).
An was liegt das?

---------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainTest10 {
private static void createAndShowGUI(){
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){
System.out.println("i in
createAndShowGUI()="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){
}
}

}


public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}
}


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;
}
}
}
---------------------------------------------------------------

2. Möglichkeit: Verwendung von synchronized
siehe Programm in meinem letzten Posting.
Die Elemente der GUI (Fenster, Buttons, usw.), die Verzögerung, usw.
werden in main erzeugt und die gemeinsamen Daten werden mit
synchronized einander korrekt zugreifbar gemacht.
Post by Jochen Theodorou
Post by Ernst Baumann
3)
Das trifft doch aber nicht auf das Programm unten zu, da ich keine
Komponenten von Swing (Buttons, Labels, usw.) erzeuge.
Ist das richtig?
du änderst sie indirekt,
wie?
mit setI() werden die Koordinaten des Kreises vom Main-Thread gesetzt
und mit g.drawOval() gezeichnet.
Ist das richtig?
Nebenbei:
Hier schreibt (mit setI) ein Thread und der andere liest (mit
g.drawOval()). Ist bei der Kombination schreiben/lesen unbedingt
synchronized erforderlich oder gilt dies nur für die Kombination
schreiben/schreiben?


mfg
Ernst
Jochen Theodorou
2007-05-31 16:33:12 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Damit sich diese Anweisungen alle innerhalb des EDT befinden, wird
invokeLater() verwendet. Bei invokeLater() bzw. der Methode run ist
also kein synchronized nötig (alles befindet sich ja in nur _einem_
Thread)
damit das was wird darfst du da aber kein Thread.sleep verwenden.
createAndShowGUI wird im EDT ausgeführt, ja? Dort baust du die gesamte
GUI, aber du amchst dort auch deine "Animation". Die Steuerung der
Post by Ernst Baumann
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);
ist ok, aber diese Schleife
Post by Ernst Baumann
while(k<10){
System.out.println("i in
createAndShowGUI()="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){
}
}
while (k<10) {
final value = i;
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
diagramm.setI(value);
diagramm.repaint();
}
});
i=i+20;
k=k+1;
try{
Thread.sleep(500);
} catch(InterruptedException e){}
}
ich hab nicht probiert ob es korrekt ist, aber so in der Art müsste es
stimmen. Hier wird der EDT nicht schlafen gelegt, sondern der main
Thread, der EDT darf auch garnicht schlafen. Darf nicht im Sinne von,
weil das Programm sonst nicht funktioniert.

[...]
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
3)
Das trifft doch aber nicht auf das Programm unten zu, da ich keine
Komponenten von Swing (Buttons, Labels, usw.) erzeuge.
Ist das richtig?
du änderst sie indirekt,
wie?
mit setI() werden die Koordinaten des Kreises vom Main-Thread gesetzt
und mit g.drawOval() gezeichnet.
Ist das richtig?
ja
Post by Ernst Baumann
Hier schreibt (mit setI) ein Thread und der andere liest (mit
g.drawOval()). Ist bei der Kombination schreiben/lesen unbedingt
synchronized erforderlich oder gilt dies nur für die Kombination
schreiben/schreiben?
unbedingt erforderlich kann man allgemein nicht beantworten.
gleichzeitiges lesen und schreiben oder schreiben und schreiben ist im
Allgemeinen eher schlecht, kann aber unter Umständen trotzdem gefahrlos
sein. lesen/lesen ist in der Regel kein Problem.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-06-01 19:46:21 UTC
Permalink
Post by Jochen Theodorou
[...]
Post by Ernst Baumann
Damit sich diese Anweisungen alle innerhalb des EDT befinden, wird
invokeLater() verwendet. Bei invokeLater() bzw. der Methode run ist
also kein synchronized nötig (alles befindet sich ja in nur _einem_
Thread)
1)
Nochmal zu dem letzten Programm:
...
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
...

Mit
javax.swing.SwingUtilities.invokeLater(new Runnable()...
wird innerhalb des EDT ein neuer Thread (ich nenne ihn Tneu) gestartet
und in diesem Thread die Methode createAndShowGUI() aufgerufen.
Dann haben wir 3 Threads:
- Main-Thread
- EDT
- Tneu
Dann dürfte ein sleep in Tneu doch nicht den EDT schlafenlegen (und
Events nicht mehr durchlassen), da dies ja alles _verschiedene_
Threads sind.
Wo ist mein Denkfehler?
Post by Jochen Theodorou
damit das was wird darfst du da aber kein Thread.sleep verwenden.
2)
weil ich den EDT damit beschäftige und damit keine Events mehr erzeugt
werden können (aber dies geschieht doch in Tneu)
Ist das richtig?

3)
Das Programm _summiert_ die ganzen Verzögerungen, bevor es einen Kreis
ausgibt. Wird da auch wieder double buffering gemacht?
Woher weiss man, wann und wo double buffering gemacht wird?
Innerhalb der Methode painComponent -das ist mir jetzt klar.
Aber wo noch?
Stell dir mal vor, ich will verzögert (mit sleep) in einer Animation
hintereinander 10 Kreise ausgeben.
Kann es dann sein dass die VJM meine 10 repaint zu _einem einzigen_
zusammenfasst? Dann gäbe es nur ein Bild und ich hätte keine Animation
mehr.

4)
paintComponent() wird -laut Ausgabe - nur zweimal ausgeführt
(wahrscheinlich einmal von setVisible(true)).
Liegt das daran, dass mehrere repaint zusammengefasst (coaliscing)
werden?


mfg
Ernst
Bernd Eckenfels
2007-06-01 20:11:21 UTC
Permalink
Post by Ernst Baumann
wird innerhalb des EDT ein neuer Thread (ich nenne ihn Tneu) gestartet
und in diesem Thread die Methode createAndShowGUI() aufgerufen.
Nein, es wird im EDT aufgerufen, siehe Javadoc:

# Causes doRun.run() to be executed asynchronously _on_the_AWT_event
# dispatching_thread. This will happen after all pending AWT events have been
# processed.

Gruss
Bernd
Bernd Eckenfels
2007-05-27 15:38:32 UTC
Permalink
Post by Ernst Baumann
Das trifft doch aber nicht auf das Programm unten zu, da ich keine
Komponenten von Swing (Buttons, Labels, usw.) erzeuge.
Ist das richtig?
Du erzeugst ein JPanel und ein JFrame.
Ernst Baumann
2007-05-27 11:31:03 UTC
Permalink
Post by Michael Paap
Post by Ernst Baumann
Ich denke, dass es ein Fehler von Java ist, die Verzögerungen in
paintComponent einfach _ungefragt_ aufzusummieren und am Schluss auf
einen Schlag alles auf dem Bildschirm auszugeben.
Wenn dies Java einfach _hier_ ungefragt macht, dann kann es ja sein,
dass dies Java auch bei anderen Gelegenheiten wie der Folgenden macht.
Ist Java also _nicht berechechenbar_ ???
Wie wäre es, du liest dir mal ein paar Grundlagen an, liest *dann* die
diversen Antworten die dir gegeben wurden noch mal und stellst *dann*
weitere Fragen?
Ich habe schon ein paar Dinge gelesen, doch ist mir immer noch einiges
unklar.

1)
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Das heisst - im Gegensatz zu deiner Meinung - müssten alle Kreise auf
den Bildschirm kommen.
Beim Erstzeichnen ist das der Fall, beim vergrößern/verkleinern
allerdings nicht.
Irrt hier (beim vergrößern/verkleinern) die Doku oder wo habe ich
meinen Denkfehler?

2) Nochmals zu oben:
Java summiert in dem Programm von mir (wo ich Verzögerungen in
paintComponent(..) einbaue) einfach _ungefragt_ diese Verzögeungen auf
und bringt am Schluss alles auf einen Schlag auf den Bildschirm.
Wo steht in der Java-Doku, dass dies Java machen darf.

Wenn dies Java einfach _hier_ ungefragt macht, dann kann es ja sein,
dass dies Java auch bei anderen Gelegenheiten wie der Folgenden macht.
Oder wo steht in der Java-Doku, dass dies Java _nicht_ machen darf.

Da du die Grundlagen beherrscht und die Java-Doku kennst, hoffe ich,
dass du mir da weiterhelfen kannst.

mfg
Ernst
Jochen Theodorou
2007-05-27 14:01:25 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Java summiert in dem Programm von mir (wo ich Verzögerungen in
paintComponent(..) einbaue) einfach _ungefragt_ diese Verzögeungen auf
und bringt am Schluss alles auf einen Schlag auf den Bildschirm.
Wo steht in der Java-Doku, dass dies Java machen darf.
du machst:

public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}}

was soll daran ungefragt sein? Du legst den EDT für 10
Schleifenddurchläufe lahm, und wartest pro Durchlauf 300ms, also 3sec+x
insgesamt. In dieser Zeit malst du 10 Kriese ohne den Hintergrund zu
löschen. So jetzt stell die DoubleBuffering vor als würdest du 2 Bilder
haben. In eines zeichnest du, das andere wird gezeigt. Wenn du mit dem
Zeichnen fertig bist, dann wird das Bild gewechselt und der nächste
Zeichenvorgang findet im anderen Bild statt, während du in das andere
Bild malst. Jetzt stell dir vor das dieser Wechsel stattfindet, wann
immer der EDT einmal durchgelaufen ist. Ist es dann nicht logisch, dass
alle 10 Kreise angezeigt werden? Der EDT ist ja erst fertig wenn alle 10
Kreise da sind und du deine 3s gewartet hast. Wenn du das
DoubleBuffering abschaltest könnte es zu einem anderen Ergebnis kommen.

Wenn du also wissen willst wo steht das Java das machen darf, dann lies
die Doku zu DoubleBuffering.
Post by Ernst Baumann
Wenn dies Java einfach _hier_ ungefragt macht, dann kann es ja sein,
dass dies Java auch bei anderen Gelegenheiten wie der Folgenden macht.
bitte nicht alles durcheinander bringen.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-05-31 13:56:52 UTC
Permalink
Post by Ernst Baumann
public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}}
was soll daran ungefragt sein?
Du hast recht.
Da dies _innerhalb_ der Methode paintComponent() ist, wird dort double
buffering gemacht.
Dagegen wird bei meiner Lösung (wo sich die Verzögerung in main()
befindet) kein double buffering gemacht, weil dort dies _außerhalb_
der Methode paintComponent() gemacht wird.
Ist das so weit richtig?


mfg
Ernst
Jochen Theodorou
2007-05-31 16:35:05 UTC
Permalink
Post by Ernst Baumann
Post by Ernst Baumann
public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}}
was soll daran ungefragt sein?
Du hast recht.
Da dies _innerhalb_ der Methode paintComponent() ist, wird dort double
buffering gemacht.
Dagegen wird bei meiner Lösung (wo sich die Verzögerung in main()
befindet) kein double buffering gemacht, weil dort dies _außerhalb_
der Methode paintComponent() gemacht wird.
Ist das so weit richtig?
double buffering wird in beiden Fällen gemacht, nur wirkt es sich in
einem Fall aus, im anderen nicht. Aber es ist schon richtig, dass du
etwas ausserhalb von paintComponent machen musst, damit sich die
Zeichenvorgänge nicht einfach akkumulieren.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-06-01 19:46:19 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Du hast recht.
Da dies _innerhalb_ der Methode paintComponent() ist, wird dort double
buffering gemacht.
Dagegen wird bei meiner Lösung (wo sich die Verzögerung in main()
befindet) kein double buffering gemacht, weil dort dies _außerhalb_
der Methode paintComponent() gemacht wird.
Ist das so weit richtig?
double buffering wird in beiden Fällen gemacht, nur wirkt es sich in
einem Fall aus, im anderen nicht. Aber es ist schon richtig, dass du
etwas ausserhalb von paintComponent machen musst, damit sich die
Zeichenvorgänge nicht einfach akkumulieren.
Du sagst:
In dem Programm unten wird _außerhalb_ der Methode paintComponent()
double buffering gemacht.
Warum wird dann der Kreis nicht nach jeder Verögerung in den double
buffer geschrieben, so dass sich die Verzögerungszeiten wieder
summieren müssten und erst am Schluss die ganzen Kreise schlagartig
auf dem Bildschirm erscheinen dürften (was aber nicht der Fall ist)?


--------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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);
System.out.println("isOpaque="+diagramm.isOpaque());
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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){
// setOpaque(true);
// setOpaque(false);
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;
}
}
}
--------------------------------------------------------



mfg
Ernst
Wanja Gayk
2007-05-27 15:49:04 UTC
Permalink
Ernst Baumann said...
Post by Ernst Baumann
Post by Michael Paap
Wie wäre es, du liest dir mal ein paar Grundlagen an, liest *dann* die
diversen Antworten die dir gegeben wurden noch mal und stellst *dann*
weitere Fragen?
Ich habe schon ein paar Dinge gelesen, doch ist mir immer noch einiges
unklar.
[..]

Lass mich versuchen Klarheit zu bringen:

Es gibt den EDT, ein Thread, der im Wesentlichen zwei Aufgaben hat:
a) Event-Dispatching
b) Gui-Zeichnen

Das bedeutet:
Wenn die Bearbeitung eines Events (z.B. die
ActionListener#actionPerformed(..) für einen Button oder
MouseListener#mousePressed(..) für irgendeine Komponente) eine Sekunde
braucht, kann währenddessen die GUI nicht neu gezeichent werden und es
können keine weiteren Events angenommen werden: Es scheint, als würde
das Programm für eine Sekunde hängen bleiben.
Wenn das Malen einer Komponente (z.B. JPanel#paintComponent(..)) eine
Sekunde dauern würde, könnten während dieser Sekunde auch keine anderen
Komponenten gemalt werden und keine anderen Events empfangen werden, das
Programm würde also auch scheinbar eine Sekunde still stehen.
Warum? Weil beides in einem Thread statt findet.

Zu b) gibt es jetzt noch eine Besonderheit: DoubleBuffering.
Das heißt der EDT lässt, wann es ihm passt, die GUI neu zeichnen -
Dabei steigt es von der "component root" die geamte
Komponentenhierarchie ab und zeichnet alles, was sichtbar ist.
Das Graphics-Objekt, was du dann in einer #paintComponent(..)-Methode
bekommst, muss aber nicht auf das aktuell angezeigte Bild zeigen,
sondern kann auch auf einen BackBuffer zeigen, was für Swing normal ist.
Das bedeutet: die komplette Komponmentenhierrarchie wird in einen
Backbuffer gezeichnet und dieser wird dann, sobald es abngemessen
erscheint, mit dem aktuellen Bild ausgetauscht.

Was passiert also, wenn du sowas hast?:

void paintComponent(Graphics g){
for(int n = 0; n<100; ++n){
g.drawOval(0,0,n,n);
Thread.sleep(1000);//etc
}
}

Der EDT geht in diese Methode, dort geschieht folegndes:
1.du malst ein oval in den Backbuffer
2.du wartest eine Sekunde
3.du wiederholst das weitere 99 mal.

Du wirst also 100 Sekunden lang malen, aber keinen Fortschritt sehen,
weil alles im Backbuffer landet. Während der 100 Sekunden reagiert dein
Programm scheinbar nicht, weil deine paintComponent-Methode ja im EDT
abgearbeitet wird.
Ist die Schleife durch, wird der Backbuffer mit dem aktuellen Bild
ausgetauscht (geschieht auch im EDT) und du siehst das Gesamtergebnis.
Bummer!

Was ist also die Lösung?

Die Lösung habe ich in einem anderen posting zitiert, sie ist eigentlich
recht einfach:

1. du malst immmer den aktuellen Zustand komplett
2. du hast einen Thread/Timer, der den aktuellen Zustand ändert, sich
kurz schlafen legt, und einen repaint() anfordert.

Nun heißt es aber, das man außerhalb des EDT nicht an realisierten
(sichtbaren) Komponenten rumspielen, wie verträgt sich das?
Ganz einfach: man muss sicherstellen, dass das, was man tut,
"threadsafe" ist.
Threadsafety lässt sich auf zwei Arten erzeugen:
1. Alles in einem Thread machen
2. Sychronisieren

Ich habe in einem anderen Beitrag ein Beispiel gepostet, das Kreise
zeichnet. In diesem Beispiel sind zwei "synchronized"-Statements. Nimmst
du diese raus und wählst eine kleine Verzögerung, bekommst du im
Beispielprogramm ConcurrentModificationExceptions.
Die Synchronisation ist in diesem Fall kein Problem: die Modifikation
(hinzufügen eines Kreises) an der GUI geht schnell.

Was aber, wenn man z.B. ein Apfelmännchen mal will, das dauert ja
schließlich lange!
Die Lösung ist auch hier einfach:
Du malst das Apfelmännchen mit irgendeinem Thread in ein Image und
tauscht das, wenn fertig, mit dem Image, welches von der paintComponent-
Methode gemalt wird, und triggerst den repaint(); Das Austauschen der
Referenz geht schnell, die Synchronisation ist also auch hier kein
Problem.

Sanchronisation ist aber incht immer eine schöne Lösung, denn man hat ja
nicht immer Zugriff auf die inneren Abläufe von Komponenten und man
handelt sich, wenn an was falsch macht eine tödlichen Deadlock ein.

Es gibt also die "Single Thread"-Möglichkeit.
Aber halt: "sollte man den EDT denn nicht unnötg belasten? Wenn ich nun
alles im EDT ausführe, dann ist das doh falsch, denke ich?"
Richtig gedacht! Deswegen führt man nicht alles im EDT aus, sondern nur
das, was schnell geht und das was lange dauert führt man außerhalb aus,
dann muss man auch nicht synchronisieren.

Beispiel:

new Thread(){
public void run(){ //außerdhalb des EDT
Parameters parameters = getParameters(); //holt zufällige Parameter
Image img = createMandelbot(parameters); //Berechnung dauert lange
EventQueue.invokeLater(new Runnable(){
public void run(){ //innerhalb des EDT
myPanel.setImage(img); //Referenz setzen geht schnell
repaint(); //Änderungen bitte malen, wenn Zeit
}
});
//..
Thread.sleep(delay); //warten, dann nächstes Apfelmännchen
//..
}
}.start();

das EventQueue#invokeLater(Runnable) führt die run()-Methode des
übergebenen Runnable im EDT aus.

Ich hoffe damit einige Unklarheiten aufglöst zu haben.

Gruß,
-Wanja-
--
Ada Byron, die Lochkarten für Babbages "Difference Engine" vorbereitete,
gilt als die erste Programmiererin der Weltgeschichte. Sie hat auch das
Verhaltensmuster für alle nachfolgenden Programmierer vorgegeben: Sie
trank und schluckte Drogen.
Ernst Baumann
2007-05-31 13:57:01 UTC
Permalink
Post by Wanja Gayk
a) Event-Dispatching
b) Gui-Zeichnen
Wenn die Bearbeitung eines Events (z.B. die
ActionListener#actionPerformed(..) für einen Button oder
MouseListener#mousePressed(..) für irgendeine Komponente) eine Sekunde
braucht, kann währenddessen die GUI nicht neu gezeichent werden und es
können keine weiteren Events angenommen werden: Es scheint, als würde
das Programm für eine Sekunde hängen bleiben.
Wenn das Malen einer Komponente (z.B. JPanel#paintComponent(..)) eine
Sekunde dauern würde, könnten während dieser Sekunde auch keine anderen
Komponenten gemalt werden und keine anderen Events empfangen werden, das
Programm würde also auch scheinbar eine Sekunde still stehen.
Warum? Weil beides in einem Thread statt findet.
Um den EDT näher kennenzulernen, bzw. zu verstehen, wie er reagiert,
habe ich das Programm unten geschrieben.

Zum Programm:
Das Programm ist ein Testprogramm zum Experimentieren und Lernen, aber
nicht korrekt (aber es ist lauffähig). Ich will den EDT durch das
Programm etwas näher kennenlernen.
Im EDT befindet sich die Event-Wahrnehmung und Verarbeitung, das
Zeichnen (z.B. mit paintComponent(...) und neu hinzugekommen (durch
die Verwendung von invokeLater()) der Thread, der die Methode
createAndShowGUI() startet.
Ist das richtig, oder habe ich da schon einen Denkfehler, oder was hat
sich durch invokeLater() für den EDT geändert?


Das Programm hat folgende Fehler:
a) Es _summiert_ die ganzen Verzögerungen, bevor es einen Kreis
ausgibt.
Den Grund dafür kann ich mir im Moment nicht erklären.
Oder liegt es daran, dass sich jetzt die Eventverarbeitung,
paintComponent() und createAndShowGUI() einen Thread (den EDT) teilen.
D.h. während die Methode createAndShowGUI() abläuft kann dann
paintComponent() nicht ausgeführt werden.
b) paintComponent() wird -laut Ausgabe - nur zweimal ausgeführt
(wahrscheinlich einmal von setVisible(true)).
An was liegt das?

---------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainTest10 {
private static void createAndShowGUI(){
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){
System.out.println("i in
createAndShowGUI()="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){
}
}

}


public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}
}


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;
}
}
}
---------------------------------------------------------------

Frage:
invokeLater() startet einen Thread im EDT. Braucht man in der zu
invokeLater() zugehörigen Methode run() dann keine synchronized
Anweisungen mehr, damit nicht parallel auf gmeinsame Daten zugegriffen
werden kann?
Post by Wanja Gayk
Was ist also die Lösung?
Die Lösung habe ich in einem anderen posting zitiert, sie ist eigentlich
1. du malst immmer den aktuellen Zustand komplett
2. du hast einen Thread/Timer, der den aktuellen Zustand ändert, sich
kurz schlafen legt, und einen repaint() anfordert.
Nun heißt es aber, das man außerhalb des EDT nicht an realisierten
(sichtbaren) Komponenten rumspielen, wie verträgt sich das?
Ganz einfach: man muss sicherstellen, dass das, was man tut,
"threadsafe" ist.
1. Alles in einem Thread machen
2. Sychronisieren
Du verwendest den Begriff "synchronisieren".
Ich vermute, du meinst damit folgendes:
In einem Thread z.B. etwas (zeitaufwendiges) rechnen lassen und wenn
dieses fertig ist sofort zeichnen lassen.
Mit synchronisieren ist also das zeitliche abgestimmmt sein gemeint
von (zeitaufwendigem) rechnen und danach gleich zeichnen?
Hast du das damit gemeint?
Dies hat also nichts mit synchronized zu tun.
Post by Wanja Gayk
Ich habe in einem anderen Beitrag ein Beispiel gepostet, das Kreise
zeichnet. In diesem Beispiel sind zwei "synchronized"-Statements. Nimmst
du diese raus und wählst eine kleine Verzögerung, bekommst du im
Beispielprogramm ConcurrentModificationExceptions.
Die Synchronisation ist in diesem Fall kein Problem: die Modifikation
(hinzufügen eines Kreises) an der GUI geht schnell.
Was aber, wenn man z.B. ein Apfelmännchen mal will, das dauert ja
schließlich lange!
Du malst das Apfelmännchen mit irgendeinem Thread in ein Image und
tauscht das, wenn fertig, mit dem Image, welches von der paintComponent-
Methode gemalt wird, und triggerst den repaint(); Das Austauschen der
Referenz geht schnell, die Synchronisation ist also auch hier kein
Problem.
Was ist hier bei dieser Lösung anders als bei deinem Quellcode unten
mit invokeLater(). Bei allen zwei Möglichkeiten wird doch ein neuer
Thread verwendet, oder wie sieht der Quellcode hier aus (im Gegensatz
zu unten)?
Post by Wanja Gayk
Sanchronisation ist aber incht immer eine schöne Lösung, denn man hat ja
nicht immer Zugriff auf die inneren Abläufe von Komponenten und man
handelt sich, wenn an was falsch macht eine tödlichen Deadlock ein.
Es gibt also die "Single Thread"-Möglichkeit.
Aber halt: "sollte man den EDT denn nicht unnötg belasten? Wenn ich nun
alles im EDT ausführe, dann ist das doh falsch, denke ich?"
Richtig gedacht! Deswegen führt man nicht alles im EDT aus, sondern nur
das, was schnell geht und das was lange dauert führt man außerhalb aus,
dann muss man auch nicht synchronisieren.
new Thread(){
public void run(){ //außerdhalb des EDT
Parameters parameters = getParameters(); //holt zufällige Parameter
Image img = createMandelbot(parameters); //Berechnung dauert lange
EventQueue.invokeLater(new Runnable(){
public void run(){ //innerhalb des EDT
myPanel.setImage(img); //Referenz setzen geht schnell
repaint(); //Änderungen bitte malen, wenn Zeit
}
});
//..
Thread.sleep(delay); //warten, dann nächstes Apfelmännchen
//..
}
}.start();
mfg
Ernst
Wanja Gayk
2007-05-27 12:34:23 UTC
Permalink
Ernst Baumann said...
Post by Ernst Baumann
Post by Michael Holtermann
Einige "Fehler", die man als Anfänger macht, sind harmlos und haben keine
Nachteile. Spätestens jedoch, wenn man anfangen möchte, Code
wiederzuverwenden, hapert es dann an solchen Kleinigkeiten.
Ich denke, dass es ein Fehler von Java ist, die Verzögerungen in
paintComponent einfach _ungefragt_ aufzusummieren und am Schluss auf
einen Schlag alles auf dem Bildschirm auszugeben.
Erm, du hast das Konzept des Double-Buffering nicht verstanden, das ist
kein Fehler.

Stell dir der Einfachheit einfach vor, dass bei paintComponent nur das
Gesamtergebnis am Methodenausgang zählen würde.

Um dir ein wenig zu helfen:

class AnimationExample extends JPanel{

private List<Shape> shapes = new ArrayList<Shape>();
private Timer timer;

public void draw(final int n, final int delay){
if(timer!=null){timer.cancel();}
timer=new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
int done=-1;
public void run(){
++done;
synchronized(shapes){
if(done==0){shapes.clear();}
else if(done==n){timer.cancel();}
else{shapes.add(createShape(done));}
}
repaint();
}
}, 0, delay);
}

private Shape createShape(int n){
return new Ellipse2D.Float(0,0,n,n);
}

public void paintComponent(Graphics g){
super.paintComponent(g);
synchronized(shapes){
for(Shape shape : shapes){
((Graphics2D)g).draw(shape);
}
}
}

public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
AnimationExample ex =new AnimationExample();
ex.setPreferredSize(new Dimension(800, 600));
frame.getContentPane().add(ex);
frame.pack();
frame.setVisible(true);

ex.draw(600, 50);
}
}

Gruß,
-Wanja-
--
Ada Byron, die Lochkarten für Babbages "Difference Engine" vorbereitete,
gilt als die erste Programmiererin der Weltgeschichte. Sie hat auch das
Verhaltensmuster für alle nachfolgenden Programmierer vorgegeben: Sie
trank und schluckte Drogen.
Ernst Baumann
2007-05-31 13:57:03 UTC
Permalink
Post by Wanja Gayk
class AnimationExample extends JPanel{
private List<Shape> shapes = new ArrayList<Shape>();
private Timer timer;
public void draw(final int n, final int delay){
if(timer!=null){timer.cancel();}
timer=new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
int done=-1;
public void run(){
++done;
synchronized(shapes){
if(done==0){shapes.clear();}
else if(done==n){timer.cancel();}
else{shapes.add(createShape(done));}
}
repaint();
}
}, 0, delay);
}
private Shape createShape(int n){
return new Ellipse2D.Float(0,0,n,n);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
synchronized(shapes){
for(Shape shape : shapes){
((Graphics2D)g).draw(shape);
}
}
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
AnimationExample ex =new AnimationExample();
ex.setPreferredSize(new Dimension(800, 600));
frame.getContentPane().add(ex);
frame.pack();
frame.setVisible(true);
ex.draw(600, 50);
}
}
Da sind einige Anweisungen, die ich noch nicht kenne und die ich mir
in Ruhe mal anschauen muss. Vielen Dank

mfg
Ernst
Wanja Gayk
2007-06-01 18:02:35 UTC
Permalink
Ernst Baumann said...
Post by Ernst Baumann
Post by Wanja Gayk
class AnimationExample extends JPanel{
private List<Shape> shapes = new ArrayList<Shape>();
private Timer timer;
public void draw(final int n, final int delay){
if(timer!=null){timer.cancel();}
timer=new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
int done=-1;
public void run(){
++done;
synchronized(shapes){
if(done==0){shapes.clear();}
else if(done==n){timer.cancel();}
else{shapes.add(createShape(done));}
}
repaint();
}
}, 0, delay);
}
private Shape createShape(int n){
return new Ellipse2D.Float(0,0,n,n);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
synchronized(shapes){
for(Shape shape : shapes){
((Graphics2D)g).draw(shape);
}
}
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
AnimationExample ex =new AnimationExample();
ex.setPreferredSize(new Dimension(800, 600));
frame.getContentPane().add(ex);
frame.pack();
frame.setVisible(true);
ex.draw(600, 50);
}
}
Da sind einige Anweisungen, die ich noch nicht kenne und die ich mir
in Ruhe mal anschauen muss. Vielen Dank
[Fullquoe beabsichtigt]

Welche sind das konkret?

Gruß,
-Wanja-
--
Ada Byron, die Lochkarten für Babbages "Difference Engine" vorbereitete,
gilt als die erste Programmiererin der Weltgeschichte. Sie hat auch das
Verhaltensmuster für alle nachfolgenden Programmierer vorgegeben: Sie
trank und schluckte Drogen.
Paul Ebermann
2007-05-23 19:31:47 UTC
Permalink
Post by Ernst Baumann
Post by Sönke Müller-Lund
Post by Ernst Baumann
package de;
Böse!
Warum??
Habe ich mit Eclipse erstellt
Ob du das mit Eclipse oder sonst einem Programm schreibst,
ist egal.
Post by Ernst Baumann
und das package de genannt.
Darum geht es: Es gibt Package-Namensgebungs-Konventionen,
und danach benennt man seine Pakete entsprechend einem
Domain-Namen, über den du verfügen darfst.

Falls du etwa die Domain ernst.baumann.de besitzt, könnte
dein Package
de.baumann.ernst.test
heißen. Oder so ähnlich.

Das wurde gemacht, damit Konflikte zwischen Packages
verschiedener Autoren vermieden werden.

Über den Package-Namen de kannst du offensichtlich nicht
verfügen.
Post by Ernst Baumann
Post by Sönke Müller-Lund
Post by Ernst Baumann
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
Gegen dieses Gebot verstößt Du nicht.
Ich habe aber den Zustand der Exemplarvariable i verändert (i=i+20).
Das soll ich doch nicht, oder?
Du hast in der Methode eine lokale Variable i definiert,
also wird diese verändert. Dass es zusätzlich noch eine
Exemplarvariable gleichen Namens gibt, ist irrelevant.

(Eclipse kann man bestimmt so konfigurieren, dass es in
so einem Fall eine Warnung ausspuckt - allerdings kann
ich dir da nicht helfen, bin kein Eclipse-Nutzer.)


Paul
--
Nun ludigxas: : ()
Ingo R. Homann
2007-05-23 15:51:24 UTC
Permalink
Hi,
Post by Ernst Baumann
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
- keine Logik machen.
Allerdings habe ich über dieses Verbot in der Doku über paint nichts
gelesen.
http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Wo steht dieses Verbot ?
Welchen Sinn hat dieses Verbot ?
Der Grund ist, dass die paint-Methode auch gerne mal vom Betriebssystem
aufgerufen wird, z.B. wenn ein repaint nötig ist, weil das Fenster
zweitweise verdeckt war.

Die Methode kann also nicht wissen, wann oder wie oft sie aufgerufen
wird. paint sollte daher immer nur den aktuellen Status "malen", der
(natürlich außerhalb) der Methode abgespeichert und (in einem separaten
Thread) verändert wird.

Hth,
Ingo
Ernst Baumann
2007-05-23 17:21:11 UTC
Permalink
Post by Ingo R. Homann
Der Grund ist, dass die paint-Methode auch gerne mal vom Betriebssystem
aufgerufen wird, z.B. wenn ein repaint nötig ist, weil das Fenster
zweitweise verdeckt war.
1)
Es gibt aber in diesem _konkreten_ Beispiel keinen weiteren GUI-Event.
Weder durch explizites Afrufen von repaint(), weil dies genau einmal
im Programm geschieht, noch durch vergrößeren oder verkleinern des
Fensters (ich verändere nichts).
Deswegen verstehe ich nicht warum es den komischen Effekt gibt mit
10*300 ms warten

2)
Post by Ingo R. Homann
Die Methode kann also nicht wissen, wann oder wie oft sie aufgerufen
wird.
Aber selbst wenn paintComponent() öfters vom Betriebssystem aufgerufen
wird, ist das doch egal. Es werden u.U. durch "coalescing" mehrere
repaintComponent() vereinigt (oder auch nicht, dies entscheidet JVM).
Das ist doch egal. Bei jedem paintComponent() werden eben durch die
Schleife bedingt die 10 Kreise gezeichnet.

mfg
Ernst
Ingo R. Homann
2007-05-24 06:57:11 UTC
Permalink
Hi,
Post by Ernst Baumann
Post by Ingo R. Homann
Der Grund ist, dass die paint-Methode auch gerne mal vom Betriebssystem
aufgerufen wird, z.B. wenn ein repaint nötig ist, weil das Fenster
zweitweise verdeckt war.
1)
Es gibt aber in diesem _konkreten_ Beispiel keinen weiteren GUI-Event.
Das kannst Du eben nicht wissen, weil - wie ich oben erläutert habe -
das OS gerne mal solche Aufrufe tätigt. Geh mal mit nem Debugger ran
oder las dir Log-Meldungen ausgeben, dann merkst Du es.
Post by Ernst Baumann
Aber selbst wenn paintComponent() öfters vom Betriebssystem aufgerufen
wird, ist das doch egal. Es werden u.U. durch "coalescing" mehrere
repaintComponent() vereinigt (oder auch nicht, dies entscheidet JVM).
Das ist doch egal. Bei jedem paintComponent() werden eben durch die
Schleife bedingt die 10 Kreise gezeichnet.
Ja, und da sollte kein Thread.sleep() drinnen stehen.

Ciao,
Ingo
Kai Schwebke
2007-05-23 15:51:56 UTC
Permalink
Das sleep ist eine ganz schlechte Idee. Schau dir mal für diesen
Zweck den Swing Timer an:
http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html
Post by Ernst Baumann
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
Swing kann Double-Buffering verwenden. Dabei wird Geflacker und
sichtbares Neuzeichnen dadurch vermieden, dass paint() erst mal
alles in einen Puffer zeichnet und dieser erst nach Abschluss
der Methode in einem Rutsch angezeigt wird.
Daher erscheint es so, als ob erst gewartet und dann gezeichnet wird.



Gruß
Kai
--
http://www.whiskycommunity.de/
Ernst Baumann
2007-05-23 17:21:18 UTC
Permalink
Post by Kai Schwebke
Das sleep ist eine ganz schlechte Idee. Schau dir mal für diesen
http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html
1)
Ich glaube dir schon, dass es bessere Möglichkeiten gibt.
Ich will nur wissen, warum die Idee von mir schlecht ist.
Post by Kai Schwebke
Post by Ernst Baumann
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
Swing kann Double-Buffering verwenden. Dabei wird Geflacker und
sichtbares Neuzeichnen dadurch vermieden, dass paint() erst mal
alles in einen Puffer zeichnet und dieser erst nach Abschluss
der Methode in einem Rutsch angezeigt wird.
Daher erscheint es so, als ob erst gewartet und dann gezeichnet wird.
2) Heißt das, dass der Programmierer nicht mehr selbst in einen
offscreen Speicherbereich (kann man z.B. mit der Klasse Image
realisieren) schreiben muss, sondern dies Swing selbst machen _kann_?
3) _Muss_ Swing Double-Buffering verwenden?
Ich vermute folgendes:
a) Wenn ich das sleep(300) auskommentiere und das Fenster vergrößere,
wird mir nur noch 1 Kreis dargestellt. Es wird also nicht mehr der
ganze offscreen Bereich auf den Bildschirm kopiert.
(also kein Double-Buffering)
b) Wenn ich dagegen das sleep(300) nicht auskommentiere und das
Fenster vergrößere, werden wieder alle Kreise dargestellt. Es wird
also der ganze offscreen Bereich auf den Bildschirm kopiert.
(also Double-Buffering)
Das heißt man, kann sich nicht verlassen, dass Swing immer
Double-Buffering verwendet, oder?

mfg
Ernst
Kai Schwebke
2007-05-23 17:59:29 UTC
Permalink
Post by Ernst Baumann
Post by Kai Schwebke
Das sleep ist eine ganz schlechte Idee. Schau dir mal für diesen
http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html
1)
Ich glaube dir schon, dass es bessere Möglichkeiten gibt.
Ich will nur wissen, warum die Idee von mir schlecht ist.
Das Programmierparadigma von Swing (und quasi jeder anderen
GUI-Bibliothek) ist die ereignisgetriebene Verarbeitung.

http://en.wikipedia.org/wiki/Event-driven_programming

Durch das sleep() wird der eine Thread, der Ereignisse abarbeiten
soll, aufgehalten. Wärdend dessen reagiert die GUI auf nichts
mehr (friert ein), und das ist schlecht.
Post by Ernst Baumann
2) Heißt das, dass der Programmierer nicht mehr selbst in einen
offscreen Speicherbereich (kann man z.B. mit der Klasse Image
realisieren) schreiben muss, sondern dies Swing selbst machen _kann_?
3) _Muss_ Swing Double-Buffering verwenden?
ja und nein, Double-Buffering ist eben die Voreinstellung:
http://java.sun.com/docs/books/tutorial/uiswing/painting/concepts.html
Post by Ernst Baumann
a) Wenn ich das sleep(300) auskommentiere und das Fenster vergrößere,
wird mir nur noch 1 Kreis dargestellt. Es wird also nicht mehr der
ganze offscreen Bereich auf den Bildschirm kopiert.
(also kein Double-Buffering)
b) Wenn ich dagegen das sleep(300) nicht auskommentiere und das
Fenster vergrößere, werden wieder alle Kreise dargestellt. Es wird
also der ganze offscreen Bereich auf den Bildschirm kopiert.
(also Double-Buffering)
Das heißt man, kann sich nicht verlassen, dass Swing immer
Double-Buffering verwendet, oder?
Da würde ich eher sagen, wenn man den ereignisverarbeitenden Thread
einfriert und dann die Fenstergröße ändert, ist das Ergebnis
undefiniert und hängt im Detail vielliecht auch noch von der Plattform
ab.



Gruß
Kai
Ernst Baumann
2007-05-24 12:49:36 UTC
Permalink
Post by Kai Schwebke
Post by Ernst Baumann
a) Wenn ich das sleep(300) auskommentiere und das Fenster vergrößere,
wird mir nur noch 1 Kreis dargestellt. Es wird also nicht mehr der
ganze offscreen Bereich auf den Bildschirm kopiert.
(also kein Double-Buffering)
b) Wenn ich dagegen das sleep(300) nicht auskommentiere und das
Fenster vergrößere, werden wieder alle Kreise dargestellt. Es wird
also der ganze offscreen Bereich auf den Bildschirm kopiert.
(also Double-Buffering)
Das heißt man, kann sich nicht verlassen, dass Swing immer
Double-Buffering verwendet, oder?
Da würde ich eher sagen, wenn man den ereignisverarbeitenden Thread
einfriert und dann die Fenstergröße ändert, ist das Ergebnis
undefiniert und hängt im Detail vielliecht auch noch von der Plattform
ab.
1)
Wenn ich das sleep(300) weglasse, und erst dann das Fenster
vergrößere, wird mir nur noch 1 Kreis dargestellt. Es gefriert nichts
ein.
Es wird also nicht mehr der ganze offscreen Bereich auf den Bildschirm
kopiert. Also kein Double-Buffering. Das widerspricht deiner Aussage.

2)
Post by Kai Schwebke
Swing kann Double-Buffering verwenden. Dabei wird
Geflacker und sichtbares Neuzeichnen dadurch vermieden,
dass paint() erst mal alles in einen Puffer zeichnet und dieser
erst nach Abschluss der Methode in einem Rutsch angezeigt wird.
Daher erscheint es so, als ob erst gewartet
und dann gezeichnet wird.
Ich habe in der Funktion main diese Schleife (siehe unten) eingebaut
und damit diese "rechenintensive" (statt k < 10 könnte ich auch k <
10000000 schreiben, dann wäre es rechenintensiv) Operation damit
außerhalb des EDT gelegt.
Kann dann dein obiges Argument "nach Abschluss der Methode in einem
Rutsch angezeigt..." mir auch innerhalb von main im Programmausschnitt
unten passieren, d.h 10*100ms warten und dann alles ausgeben (tut es
aber nicht, ich habe es ausprobiert).
Dann hätte ich mit Swing ja immer das Problem, dass meine
Verzögerungen, egal wo ich mich befinde - aufsummiert werden und dann
sich erst mit einem Schlag bemerkbar machen.
Oder wo _muss_ ich meine rechenintensiven Operationn platzieren
(brauche ich dazu unbedingt einen eigenen Thread)?


----------------------------------------
int main(){
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(100);
}
catch(Exception e){}
}
...
}
----------------------------------------

3)
Was gehört alles zum EDT ?
Ereignisverarbeeitung, paintComponent(), und was noch?


mfg
Ernst
Jochen Theodorou
2007-05-23 17:05:12 UTC
Permalink
Post by Ernst Baumann
Hallo allerseits,
also ergänzend zu den anderen noch ein paar Anmerkungen zu deinem Programm:

[...]
Post by Ernst Baumann
public void paintComponent(Graphics g){
int i=0;
int k=0;
in java kann man Variablen als Teil der for-Schleife deklarieren, also

for(int k=0;k<10;k++){

das i könnte man auch noch reinnehmen, aber ich persönlich mag das
wiederum nicht. Jedenfalls ist es schlechter Stil die Zählvariable nicht
im for zu deklarieren.

[...]
Post by Ernst Baumann
try{
Thread.sleep(300);
}
catch (Exception e){
}
Das hier ist mehrfach böse. gewöhne dir an die Excpetion zu fangen, die
auch geworfen wird und nicht allgemein Exception. Ich meine in diesem
Fall macht es wohl nichts aus, aber im Allgemeinen schon. Dann ausserdem
machst du ein sleep in paintComponent. Das beduetet die componente wird
nicht gezeichnet werden können bis die Schleife komplett ist, was
mindestens 10*300 Millisekunden, also 3 Sekunden dauert. Ich glaube
nicht dass du das so wolltest.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-05-24 12:49:38 UTC
Permalink
Post by Jochen Theodorou
[...]
Post by Ernst Baumann
public void paintComponent(Graphics g){
int i=0;
int k=0;
in java kann man Variablen als Teil der for-Schleife deklarieren, also
for(int k=0;k<10;k++){
das i könnte man auch noch reinnehmen, aber ich persönlich mag das
wiederum nicht. Jedenfalls ist es schlechter Stil die Zählvariable nicht
im for zu deklarieren.
Warum?
In C wurden früher alle Variablen deklariert (und initialisiert).
Warum ist dies heute schlechter Stil?
Post by Jochen Theodorou
[...]
Post by Ernst Baumann
try{
Thread.sleep(300);
}
catch (Exception e){
}
Das hier ist mehrfach böse. gewöhne dir an die Excpetion zu fangen, die
auch geworfen wird und nicht allgemein Exception. Ich meine in diesem
Fall macht es wohl nichts aus, aber im Allgemeinen schon.
Warum?
Kannst du mir ein Beispiel geben?

mfg
Ernst
Achim Peters
2007-05-24 13:19:23 UTC
Permalink
Post by Ernst Baumann
Post by Jochen Theodorou
[...]
Post by Ernst Baumann
public void paintComponent(Graphics g){
int i=0;
int k=0;
in java kann man Variablen als Teil der for-Schleife deklarieren, also
for(int k=0;k<10;k++){
das i könnte man auch noch reinnehmen, aber ich persönlich mag das
wiederum nicht. Jedenfalls ist es schlechter Stil die Zählvariable nicht
im for zu deklarieren.
Warum?
Kleinstmöglicher Scope.
Post by Ernst Baumann
In C wurden früher alle Variablen deklariert (und initialisiert).
Wird sie ja immer noch - nur eben im for, weil sie nur da gebraucht wird.

Bye
Achim
Marcus Woletz
2007-05-24 15:01:48 UTC
Permalink
Hallo Achim,

Achim Peters schrieb:
[...]
Post by Achim Peters
Post by Ernst Baumann
Warum?
Kleinstmöglicher Scope.
ACK. Dabei gibt es allerdings einen Unterschied zwischen C(++) und Java:
In C++ ist die im Schleifenkopf definierte Variable auch außerhalb des
Schleifenkopfes und -Rumpfs sichtbar, in Java nur im Kopf und Rumpf.

Außerdem ist diese Deklarationsstelle für mich eine Ausnahme. Es gibt ja
genügend Style Guides, die die Variablendeklaration zum spätest
möglichen Zeitpunkt vorschreiben. Das ergibt allerdings häufig
unübersichtlichen Code. Werden lokale Variablen gleich zu Beginn des
Methodenrumpes deklariert, dann weiß man sofort, was Sache ist, und
muss, wenn man später irgendwo auf die Variable trifft, nicht lange
suchen, wo diese denn deklariert ist.

[...]
Post by Achim Peters
Bye
Achim
ciao

Marcus
Ingo R. Homann
2007-05-24 16:12:47 UTC
Permalink
Hi,
Post by Marcus Woletz
Außerdem ist diese Deklarationsstelle für mich eine Ausnahme. Es gibt ja
genügend Style Guides, die die Variablendeklaration zum spätest
möglichen Zeitpunkt vorschreiben. Das ergibt allerdings häufig
unübersichtlichen Code. Werden lokale Variablen gleich zu Beginn des
Methodenrumpes deklariert, dann weiß man sofort, was Sache ist, und
muss, wenn man später irgendwo auf die Variable trifft, nicht lange
suchen, wo diese denn deklariert ist.
Suchen? Ein Druck auf "F3" genügt, wenn man eine vernünftige IDE verwendet!

Und eine möglichst "späte" Deklaration kann ungemein bei Refactorings
und Aufräum-Aktionen helfen!

Ciao,
Ingo
Michael Holtermann
2007-05-24 16:18:08 UTC
Permalink
Moin!
Post by Marcus Woletz
Werden lokale Variablen gleich zu Beginn des
Methodenrumpes deklariert, dann weiß man sofort, was Sache ist, und
muss, wenn man später irgendwo auf die Variable trifft, nicht lange
suchen, wo diese denn deklariert ist.
Das sollte jede bessere IDE unterstützen. In Eclipse Strg+linke Maustaste,
F3, oder auch die Markierung für das Auftreten im Code... :-)

Von daher ist das Argument nicht wirklich zugkräftig. Im Gegensatz dazu
könnte man in der selben Methode zwei for-Schleifen nacheinander haben,
wofür man in obigem Paradigma auch 2 Variablen bräuchte, die nach dem
Schleifendurchlauf für alles mögliche andere missbraucht werden könnten...

(Das man bei zwei Schleifen in der selben Methode das Auslagern in eigene
Methoden zumindest prüft, sei mal dahingestellt.)

Grüße, Michael.
Marcus Woletz
2007-05-24 19:06:39 UTC
Permalink
Hallo Michael,

Michael Holtermann schrieb:
[...]
Post by Michael Holtermann
Von daher ist das Argument nicht wirklich zugkräftig. Im Gegensatz dazu
könnte man in der selben Methode zwei for-Schleifen nacheinander haben,
wofür man in obigem Paradigma auch 2 Variablen bräuchte, die nach dem
Schleifendurchlauf für alles mögliche andere missbraucht werden könnten...
Ich hatte ja geschrieben, dass ich bei for-Schleifen das auch so
handhabe, die Variable im Schleifenkopf zu deklarieren. Ansonsten setze
ich die Deklarationen möglichst so, dass das Programm gut lesbar ist.
Ich persönlich sehe solche Stilregeln nicht als Dogmen.
Post by Michael Holtermann
(Das man bei zwei Schleifen in der selben Methode das Auslagern in eigene
Methoden zumindest prüft, sei mal dahingestellt.)
Hmm. sorry wenn ich das jetzt etwas überspitzt formuliere, aber man kann
seinen Code auch zu Tode modularisieren. Vielleicht habe ich nicht das
passende Softwareentwicklerhirn (obwohl ich mit Leib und Seele
entwickle), aber mit zunehmender Tiefe von Methodenaufrufen finde ich
die Programm schwerer nachvollziehbar. Das soll ich jetzt nicht heißen,
dass ich alles in die main() packe ;-) OK, ein
'System.out.println("Hallo Welt!")' vielleicht schon...
Post by Michael Holtermann
Grüße, Michael.
ciao

Marcus
Michael Holtermann
2007-05-26 09:28:22 UTC
Permalink
Post by Marcus Woletz
Ich persönlich sehe solche Stilregeln nicht als Dogmen.
Vernünftige Einstellung, wenn ich das mal so sagen darf :-)

Grüße, Michael, lieber nicht an das Copy'n'Paste von gestern denkend...
Stefan Ram
2007-05-24 17:49:26 UTC
Permalink
Post by Marcus Woletz
In C++ ist die im Schleifenkopf definierte Variable auch
außerhalb des Schleifenkopfes und -Rumpfs sichtbar,
Ich verstehe die Abschnitte 6.5.1 (p2: »... the scope of
the variable that is declared extends from its point of
declaration (3.3.1) to the end of the while statement ...«)
und 6.5.3 von ISO/IEC 14882:2003(E) so, daß dies nicht zutrifft.
Marcus Woletz
2007-05-24 18:58:40 UTC
Permalink
Hallo Stefan,
Post by Stefan Ram
Post by Marcus Woletz
In C++ ist die im Schleifenkopf definierte Variable auch
außerhalb des Schleifenkopfes und -Rumpfs sichtbar,
Ich verstehe die Abschnitte 6.5.1 (p2: »... the scope of
the variable that is declared extends from its point of
declaration (3.3.1) to the end of the while statement ...«)
und 6.5.3 von ISO/IEC 14882:2003(E) so, daß dies nicht zutrifft.
Du hast Recht! Ich habe das mal kurz mit dem aktuellen gcc getestet.
Das Verhalten wurde im neuen ISO-Standard geändert (sagt jedenfalls
cpp). Wusste ich noch nicht.

ciao

Marcus
Ralf Callenberg
2007-05-26 12:05:30 UTC
Permalink
Post by Marcus Woletz
In C++ ist die im Schleifenkopf definierte Variable auch außerhalb des
Schleifenkopfes und -Rumpfs sichtbar, in Java nur im Kopf und Rumpf.
Standard C++ verhält sich hier genau so wie Java. Was Du beschreibst ist
eine Eigenart von Microsofts Visual C++. (Microsoft hatte seinen
Compiler über die Versionen standardkomformer gemacht, schaltet dieses
Verhalten aber nur optional ab, da sonst zuviel existierender Code
Probleme bereiten würde.)

Gruß,
Ralf
Olaf Pohlmann
2007-05-24 14:06:12 UTC
Permalink
Post by Ernst Baumann
Post by Jochen Theodorou
for(int k=0;k<10;k++){
In C wurden früher alle Variablen deklariert (und initialisiert).
Obiges is gueltiger C-Code nach dem C99-Standard. Dem GCC z.B. muss man
allerdings mit der Option -std=c99 sagen, dass er diesen Standard
benutzen soll.


op
Jochen Theodorou
2007-05-25 17:14:45 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
try{
Thread.sleep(300);
}
catch (Exception e){
}
Das hier ist mehrfach böse. gewöhne dir an die Excpetion zu fangen, die
auch geworfen wird und nicht allgemein Exception. Ich meine in diesem
Fall macht es wohl nichts aus, aber im Allgemeinen schon.
Warum?
Kannst du mir ein Beispiel geben?
Ich meinte sowas:

void myActions() throws InterruptedException {
while (todo) {
action()
Thread.sleep(200)
}
}

try {
myActions()
} catch (Exception e){}

jegliche Exception die action() schmeisst wird verschluckt, also auch
NullPointExceptions (NPE) zum Beispiel. Das hilft nicht gerade bei der
Fehlersuche. Ausserdem, sollte der Thread unterbrochen werden fällt die
Behandlung dessen komplett flach

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-05-27 11:33:34 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Kannst du mir ein Beispiel geben?
void myActions() throws InterruptedException {
while (todo) {
action()
Thread.sleep(200)
}
}
try {
myActions()
} catch (Exception e){}
jegliche Exception die action() schmeisst wird verschluckt, also auch
NullPointExceptions (NPE) zum Beispiel.
Das habe ich kapiert.
Post by Jochen Theodorou
Das hilft nicht gerade bei der
Fehlersuche.
ok
Post by Jochen Theodorou
Ausserdem, sollte der Thread unterbrochen werden fällt die
Behandlung dessen komplett flach
Das habe ich nicht verstanden. Was meinst du konkret damit?

mfg
Ernst
Jochen Theodorou
2007-05-27 12:22:46 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Post by Jochen Theodorou
Ausserdem, sollte der Thread unterbrochen werden fällt die
Behandlung dessen komplett flach
Das habe ich nicht verstanden. Was meinst du konkret damit?
wenn ich zum Beispiel irgendwo t1.interrupt() aufrufe, wird die
Ausführung des Threads unterbrochen. Wenn du zu diesem Zeitpunkt
irgendwo anders in einem t1.sleep() machst, dann wird eine
InterruptedException geworfen. Und ein interrupted flag gesetzt. Wenn
jetzt irgendwo Logik auf diesem Flag aufbaut, dann bist du angeschmiert.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Wanja Gayk
2007-05-27 15:49:03 UTC
Permalink
Ernst Baumann said...
Post by Ernst Baumann
In C wurden früher alle Variablen deklariert (und initialisiert).
Da kannstemal sehen, wie C den Stil versaut :-)
Post by Ernst Baumann
Warum ist dies heute schlechter Stil?
Es war schon immer schlechter Stil, wenn du mich fragst.

Die Antwort auf das "warum" nennt sich "information hiding":
Variablen sollten immer im kleinst möglichen Sichtbarkeitsbereich
deklariert werden, denn jede Variable die in einem größeren Umfeld
sichtbar ist als nötig, kann auch in diesem Umfeld dort benutzt werden.
Man sollte also die Anzahl der möglichen "Kandidaten", deren Änderung
man beim Lesen des Codes verfolgen muss, möglichst klein halten, das
schafft mehr Übersicht über den Kontrollfluss einer Methode oder eines
Objekts und erleichtert das Refactoring, bzw, die Analyse des Codes
durch einen Compiler.

Um es deutlich zu machen, erlaube mir bitte eine Art Worst-Case-Szenario
zu zeichnen: Ein Programm in dem alle Variablen Objektvariablen und
public sind.
Ein Compiler müsste hier jede Klasse checken, ob aus ihr ein Zugriff auf
eine Variable zugreift, bevor er entscheiden kann, ob ein inlining oder
eine ähnliche Optimierung legal ist.

Gruß,
-Wanja-
--
Ada Byron, die Lochkarten für Babbages "Difference Engine" vorbereitete,
gilt als die erste Programmiererin der Weltgeschichte. Sie hat auch das
Verhaltensmuster für alle nachfolgenden Programmierer vorgegeben: Sie
trank und schluckte Drogen.
Marcus Woletz
2007-05-27 16:52:24 UTC
Permalink
Hallo Wanja,

Wanja Gayk schrieb:
[...]
Post by Wanja Gayk
Variablen sollten immer im kleinst möglichen Sichtbarkeitsbereich
deklariert werden, denn jede Variable die in einem größeren Umfeld
sichtbar ist als nötig, kann auch in diesem Umfeld dort benutzt werden.
also entweder ich stehe hier im Dunkeln, aber mit "information hiding"
verbinde ich etwas völlig anderes als das zusammenführen von Variablen
im Codeblock. Das "information hiding" bezieht sich doch wohl auf die
semantische Sichtbarkeit, oder nicht? *Total verunsichert*

[...]
Post by Wanja Gayk
Um es deutlich zu machen, erlaube mir bitte eine Art Worst-Case-Szenario
zu zeichnen: Ein Programm in dem alle Variablen Objektvariablen und
public sind.
OK, dann hab ich's doch korrekt verstanden ;-) Und behaupte jetzt gleich
noch frech, dass das nichts mit "Variablen am Blockanfang oder an der
Stelle der Erstverwendung deklarieren" zu tun hat.
[...]
Post by Wanja Gayk
Gruß,
-Wanja-
ciao

Marcus
Wanja Gayk
2007-05-27 18:32:15 UTC
Permalink
Marcus Woletz said...
Post by Marcus Woletz
Hallo Wanja,
[...]
Post by Wanja Gayk
Variablen sollten immer im kleinst möglichen Sichtbarkeitsbereich
deklariert werden, denn jede Variable die in einem größeren Umfeld
sichtbar ist als nötig, kann auch in diesem Umfeld dort benutzt werden.
also entweder ich stehe hier im Dunkeln, aber mit "information hiding"
verbinde ich etwas völlig anderes als das zusammenführen von Variablen
im Codeblock. Das "information hiding" bezieht sich doch wohl auf die
semantische Sichtbarkeit, oder nicht? *Total verunsichert*
Nunja, ich kann nur für mich reden: Ich persönlich verbinde es nur damit
die Sichtbarkeit zu begrenzen, auf welcher Stufe ist mir persönlich
völlig egal. "Information hiding" sagt mir: jeder soll nur das sehen
können, was er sehen braucht.

Also auch das hier:
for(final Object x : xs){
for(final Object y : ys){
doSomething(x,y);
}
// y unsichtbar
}
// x unsichtbar

Im Gegensatz zu:

Object x;
Object y;
for(x : xs){
for( y : ys){
doSomething(x,y);
}
//y sichtbar
}
//x sichtbar

In so kleinen Schleifen mag das untere noch übersichtlich sein, wächst
eine Methode, wird die Kontrollstruktur komplizierter, gewinnt für mich
das erste Beispiel, denn es ist schon hilfreich, wenn man eine Variable
nicht mehr im Auge behalten muss, weil sie schlicht nicht mehr sichtbar
ist.
Post by Marcus Woletz
[...]
Post by Wanja Gayk
Um es deutlich zu machen, erlaube mir bitte eine Art Worst-Case-Szenario
zu zeichnen: Ein Programm in dem alle Variablen Objektvariablen und
public sind.
OK, dann hab ich's doch korrekt verstanden ;-) Und behaupte jetzt gleich
noch frech, dass das nichts mit "Variablen am Blockanfang oder an der
Stelle der Erstverwendung deklarieren" zu tun hat.
[...]
Siehe oben. :-)
"Information hiding" heißt für mich "Sichtbarkeit begrenzen", ob auf
Paketebene, auf Klassenebene, auf Objektebene oder Methodenebene spielt
für mich dabei eine untergeordnete Rolle.

Natüclich muss man dabei nicht zu dogmatisch sein, ein
final Iterator<Foo> xitr = xs.iterator();
while(xitr.hasNext()){
final Foo x = xitr.next();
if(condition(x)){
xitr.remove();
}
}

könnte ma auch so schreiben:
{
final Iterator<Foo> xitr = xs.iterator();
while(xitr.hasNext()){
final Foo x = xitr.next();
if(condition(x)){
xitr.remove();
}
}
}

finde ich persönlich aber unhübsch.

Es ginge auch ein:

for(final Iterator<Foo> xitr = xs.iterator(); xitr.hasNext();){
final Foo x = xitr.next();
if(condition(x)){
xitr.remove();
}
}

Ist in meinrn Augen aber ungewohnt, wäre aber ein ordentlicher
Kompromiss, wie ich finde.
Und weil ich ncht er dogmatiker bin, den eine hier wahrscheinlich
vermuten, erlaube ich mir die kleine Sauerei das While-statement zu
nehmen. :-)

Gruß,
-Wanja-
--
Ada Byron, die Lochkarten für Babbages "Difference Engine" vorbereitete,
gilt als die erste Programmiererin der Weltgeschichte. Sie hat auch das
Verhaltensmuster für alle nachfolgenden Programmierer vorgegeben: Sie
trank und schluckte Drogen.
Marcus Woletz
2007-05-28 08:10:45 UTC
Permalink
Hallo Wanja,

Wanja Gayk schrieb:

[...]
Post by Wanja Gayk
Nunja, ich kann nur für mich reden: Ich persönlich verbinde es nur damit
die Sichtbarkeit zu begrenzen, auf welcher Stufe ist mir persönlich
völlig egal. "Information hiding" sagt mir: jeder soll nur das sehen
können, was er sehen braucht.
for(final Object x : xs){
for(final Object y : ys){
doSomething(x,y);
}
// y unsichtbar
}
// x unsichtbar
OK, passt schon.

[...]
Post by Wanja Gayk
Gruß,
-Wanja-
Marcus Woletz
2007-05-23 18:23:10 UTC
Permalink
Hallo Ernst,

Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
- keine Logik machen.
wer hat das gesagt? ;-)

Das Problem bei paintComponent() ist folgendes: Diese Methode wird
grundsätzlich aus dem Event Dispatch Thread (EDT) aufgerufen bzw.
läuft in diesem. Nun läuft allerdings der komplette Nachrichtenverkehr
in Swing/AWT auch in diesem besagten EDT. Hältst Du Dich also länger in
Deiner paintComponent()-Methode auf, können während dieser Zeit keine
weiteren Ereignisse oder Zeichenoperationen mehr ausgeführt werden. Für
den Anwender scheint Dein Programm zu "hängen". Deshalb die _Regel_,
möglichst keine rechenzeitintensiven Operationen im EDT (also auch in
der paintComponent()-Methode) durchzuführen. Schleifen etc. sind kein
Problem, so lange Du nicht irgend welche Millionen Schleifendurchläufe
mit entsprechender Laufzeit hast.

Dann kommt noch folgendes hinzu: Du möchtest im Abstand von 0,3s Kreise
malen, richtig? Das kann so, wie Du es machst, nicht funktionieren. Die
Ausgabe von drawOval() findet standardmäßig in einen Zwischenspeicher
(den ominösen "Double Buffer") statt. Das ist einfach ein
Speicherbereich, in den genau wie auf den Bildschirm gezeichnet werden
kann. Um diesen Zwischenspeicher musst Du Dich nicht kümmern, das macht
Swing automatisch für Dich. Zu beachten ist jedoch, dass ja irgend wann
einmal dieser Zwischenspeicher auf den Bildschirm kopiert werden muss,
und zwar in einem Rutsch. Und genau diese Kopieraktion findet (ebenfalls
für Dich unsichtbar) am _Ende_ des EDT-Durchlaufs statt, also auf jeden
Fall erst, wenn Deine paintComponent()-Methode verlassen wurde. Deshalb
müsste Dein Programm folgendes Verhalten zeigen: Dein Programm wartet
über 10*300ms = 3s auf die Rückkkehr von sleep. In dieser Zeit müsste
Deine Oberfläche einfrieren (was Du in Deinem Minimalbeispiel vermutlich
nicht bemerkst, für Dich tut sich vermutlich 3s lang nichts).
Dann werden plötzlich alle zehn Kreise auf einen Schlag dargestellt,
nämlich dann, wenn Du in der paintComponent()-Methode fertig bist und
diese verlässt. Dann nämlich wird, wie bereits erwähnt, der
Zwischenpuffer (mit Deinen gezeichneten Kreisen) in das Fenster kopiert.

Wenn Du Double Buffering ausschaltest (suche im API-Index eine Methode
"enableDoubleBuffering" oder so ähnlich und rufe das mit (false) auf dem
JFrame auf),
dann sollten Deine Kreise wie gewünscht schön der Reihe nach angezeigt
werden, da die Zeichenoperationen dann direkt auf dem Bildschirm
stattfinden. Da bin ich mir aber nicht sicher, denn es könnte sein, dass
die paintOval-Aufrufe auch irgendwo gepuffert werden.
Post by Ernst Baumann
Allerdings habe ich über dieses Verbot in der Doku über paint nichts
gelesen.
http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Wo steht dieses Verbot ?
Welchen Sinn hat dieses Verbot ?
2) Komischer Programmablauf
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
Hmm ich seh's gerade: deckt sich 100% mit meiner Beschreibung oben.

Ach ja, die korrekte Lösung ist, zuerst einen Kreis zu malen, dann zwei
... (Irgend wo einen Thread starten, jeweils 300ms warten und dann
repaint() oder paintImmediately() (repaint() ist sauberer) aufrufen.
Bei größeren Anwendungen verwaltet man selbst einen Zwischenspeicher, in
den dann das gewünschte Zeug gezeichnet wird. Das Problem bei repaint
etc. ist, dass normalerweise der Hintergrund gelöscht wird, Du also
immer Dein komplettes Bild neu darstellen musst. Das musst Du auch tun,
denn sonst wird Dein Fenster nicht richtig dargestellt, wenn z.B. zuvor
unsichtbare Bereiche sichtbar werden (darüberliegendes Fenster entfernt
etc.)

Ich weiß, es wirkt zuerst ein wenig mühsam, das ganze Zeug. Das Problem
ist einfach, dass Swing (aus gutem Grund - irgendwo habe ich mal eine
Begründung gelesen) in einem einzigen Thread, dem EDT, läuft.
Aber man gewöhnt sich dran ;-)
Post by Ernst Baumann
mfg
Ernst
ciao

Marcus
Ernst Baumann
2007-05-24 12:49:41 UTC
Permalink
Post by Marcus Woletz
Das Problem bei paintComponent() ist folgendes: Diese Methode wird
grundsätzlich aus dem Event Dispatch Thread (EDT) aufgerufen bzw.
läuft in diesem. Nun läuft allerdings der komplette Nachrichtenverkehr
in Swing/AWT auch in diesem besagten EDT. Hältst Du Dich also länger in
Deiner paintComponent()-Methode auf, können während dieser Zeit keine
weiteren Ereignisse oder Zeichenoperationen mehr ausgeführt werden. Für
den Anwender scheint Dein Programm zu "hängen".
1)
Aha, paintComponent und die Ereignisverarbeitung laufen innerhalb
eines Threads ab und teilen sich somit die CPU-Zeit für diesen Thread.
Das was der eine zuviel braucht, fehlt dem anderen. Ist das richtig?
Post by Marcus Woletz
Deshalb die _Regel_,
möglichst keine rechenzeitintensiven Operationen im EDT (also auch in
der paintComponent()-Methode) durchzuführen. Schleifen etc. sind kein
Problem, so lange Du nicht irgend welche Millionen Schleifendurchläufe
mit entsprechender Laufzeit hast.
2)
Wo - wenn nicht im EDT - soll man dann rechenintensive Operationen
ausführen?

3)
Ich habe in der Funktion main diese Schleife eingebaut und damit diese
"rechenintensive" (statt k < 10 könnte ich auch k < 10000000
schreiben, dann wäre es rechenintensiv) Operation damit außerhalb des
EDT gelegt.
Wäre das dann eine akkzeptable Lösung, oder brauche ich dazu (siehe
unten unbedingt einen eigenen Thread)?
----------------------------------------
int main(){
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(100);
}
catch(Exception e){}
}
...
}
----------------------------------------

4)
Was gehört alles zum EDT ?
Ereignisverarbeeitung, paintComponent(), und was noch?

5)
Post by Marcus Woletz
Ach ja, die korrekte Lösung ist, zuerst einen Kreis zu malen,
dann zwei ... (Irgend wo einen Thread starten,
du meinst, die Malerei in einen Thread zu verlegen?
Post by Marcus Woletz
jeweils 300ms
warten und dann repaint() oder paintImmediately() (repaint()
ist sauberer) aufrufen. Bei größeren Anwendungen verwaltet
man selbst einen Zwischenspeicher,
kann man das z.B. mit einer Variable vom Typ image machen?
Post by Marcus Woletz
in den dann das
gewünschte Zeug gezeichnet wird. Das Problem bei repaint
etc. ist, dass normalerweise der Hintergrund gelöscht wird,
Das ("normalerweise der Hintergrund gelöscht wird") ist laut API-Doku
nicht de Fall:
http://java.sun.com/j2se/1.5.0/docs/api/java/awt/Component.html#repaint()
"Repaints this component.
If this component is a lightweight component, this method causes a
call to this component's paint method as soon as possible. Otherwise,
this method causes a call to this component's update method as soon as
possible. "
Eine JComponent ist lightweight, man landet also bei paint(). Diese
Methode delegiert die Zeichnerei an paintComponent, paintBorder und
paintChildren. Und bei paintComponent findet man unter
http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics)
für abgeleitete Klassen, die diese Methode überschreiben, folgende
Aussage:
"Further, if you do not invoke super's implementation you must honor
the opaque property, that is if this component is opaque, you must
completely fill in the background in a non-opaque color. If you do not
honor the opaque property you will likely see visual artifacts."


mfg
Ernst
Ingo R. Homann
2007-05-24 14:14:30 UTC
Permalink
Hi,
Post by Ernst Baumann
1)
Aha, paintComponent und die Ereignisverarbeitung laufen innerhalb
eines Threads ab und teilen sich somit die CPU-Zeit für diesen Thread.
Das was der eine zuviel braucht, fehlt dem anderen. Ist das richtig?
Jepp. Und vor allem: Wenn das Zeichnen zu lange dauert und/oder den
Objekt-Status ändert, kommt es zu "merkwürdigen" Effekten, wenn das OS /
die JVM die Methode "unerwartet" aufruft (wie Du ja schon festgestellt
hast).
Post by Ernst Baumann
Post by Marcus Woletz
Deshalb die _Regel_,
möglichst keine rechenzeitintensiven Operationen im EDT (also auch in
der paintComponent()-Methode) durchzuführen. Schleifen etc. sind kein
Problem, so lange Du nicht irgend welche Millionen Schleifendurchläufe
mit entsprechender Laufzeit hast.
2)
Wo - wenn nicht im EDT - soll man dann rechenintensive Operationen
ausführen?
In einem anderen als dem EDT! :-) Also im Haupt-Thread, oder in einem
ganz eigenen, extra dafür gestarteten Thread.
Post by Ernst Baumann
4)
Was gehört alles zum EDT ?
Ereignisverarbeeitung, paintComponent(), und was noch?
AFAIK nichts. Das kann dir aber eigentlich auch egal sein, wenn Du die
besagte Regel beherzigst.
Post by Ernst Baumann
Post by Marcus Woletz
jeweils 300ms
warten und dann repaint() oder paintImmediately() (repaint()
ist sauberer) aufrufen. Bei größeren Anwendungen verwaltet
man selbst einen Zwischenspeicher,
kann man das z.B. mit einer Variable vom Typ image machen?
Ja. jawa.awt.Image oder BufferedImage. Auf dessen Graphics kann man
malen, und in der paint-Methode einfach das Image auf die Graphics des
Canvas painten.
Post by Ernst Baumann
Post by Marcus Woletz
in den dann das
gewünschte Zeug gezeichnet wird. Das Problem bei repaint
etc. ist, dass normalerweise der Hintergrund gelöscht wird,
Das ("normalerweise der Hintergrund gelöscht wird") ist laut API-Doku
Also unter AWT machte das AFAIK die update-Methode, die aber implizit
von repaint aufgerufen wird. Wie das unter Swing war, weiss ich nicht mehr.

Ciao,
Ingo
Ernst Baumann
2007-05-25 19:24:30 UTC
Permalink
Post by Ingo R. Homann
Post by Ernst Baumann
2)
Wo - wenn nicht im EDT - soll man dann rechenintensive Operationen
ausführen?
In einem anderen als dem EDT! :-) Also im Haupt-Thread, oder in einem
ganz eigenen, extra dafür gestarteten Thread.
Die Funktion main gehört nicht zum EDT.
Deshalb habe ich diese rechenintensive Operation - zu Testzwecken - in
main eingebaut (siehe unten).
Die Kreise werden verzögert gezeichnet.
Ist dieser Versuch von mir also auch möglich, oder _muss_ es ein
eigener Thread sein?

Siehe folgenden Quellcode:
----------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}

----------------------------------------

1) Was ich dann nicht verstehe ist die folgende Ausgabe des Programms:
i in main=0
i in paintComponent=0 <--
i in paintComponent=0 <--
i in main=20
i in paintComponent=20
i in main=40
i in paintComponent=40
i in main=60
i in paintComponent=60
i in main=80
i in paintComponent=80
i in main=100
i in paintComponent=100
i in main=120
i in paintComponent=120
i in main=140
i in paintComponent=140
i in main=160
i in paintComponent=160
i in main=180
i in paintComponent=180 <--
i in paintComponent=180 <--

Wenn alles schön hintereinander kommen würde,
müssten sich doch die Ausgaben in main und paintComponent abwechseln.
Warum tun sie dies nicht?


2)
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Es wird also nicht der ganze offscreen Bereich auf den Bildschirm
kopiert (also kein Double-Buffering).
Oder wie muss ich mir das erklären?


mfg
Ernst
Bernd Eckenfels
2007-05-25 19:40:01 UTC
Permalink
Post by Ernst Baumann
Ist dieser Versuch von mir also auch möglich, oder _muss_ es ein
eigener Thread sein?
es muss ein anderer sein, main ist ok. Allerdings: main thread sollte man
aus anderen gruenden nicht nehmen, z.b. weil man die Stacksize nicht setzen
kann.

statisches main ist nicht OO, aber zum testen sicher ok.
Post by Ernst Baumann
f.setVisible(true);
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i in main=0
i in paintComponent=0 <--
i in paintComponent=0 <--
Was hier genau der Grund ist fuer den paint event kann ich dir nicht sagen,
ich vermute mal das setVisible, aber du solltest generell nicht davon
ausgehen dass paint nur gerufen wird wenn du es willst. Wenn z.B. ein
Fesnter das deine Anwendung verdeckt geschlossen wird, dann wird auch paint
gerufen.

Schau dir mal die diversen Tutorials zu Animations und Double Buffering an.

Gruss
Bernd
Carsten Wanke
2007-05-26 05:37:37 UTC
Permalink
Hallo Ernst,
Post by Ernst Baumann
i in paintComponent=0 <--
i in paintComponent=0 <--
i in paintComponent=180 <--
i in paintComponent=180 <--
Wenn alles schön hintereinander kommen würde,
müssten sich doch die Ausgaben in main und paintComponent abwechseln.
Warum tun sie dies nicht?
Wenn beispielsweise das Betriebssystem der Meinung ist, ein Fenster müsste
neu gezeichnet werden, dann wird es java davon informieren, und das
triggert dann ein repaint(). Andererseits kann es gut sein, dass trotz
eines mehrfachen repaint()-Aufrufs nur einmal paintComponent() durchlaufen
wird, weil Swing das zusammenfasst. Du kannst also nie wissen, wann das
ist, und das hat gute Gründe.
Post by Ernst Baumann
2)
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Es wird also nicht der ganze offscreen Bereich auf den Bildschirm
kopiert (also kein Double-Buffering).
Oder wie muss ich mir das erklären?
java geht davon aus, dass paintComponent() die komplette Komponente neu
zeichnet. Wenn Du das Fenster vergrößerst, liegt für den neu gezeigten
Bereich noch keine Information vor, und da finde ich es naheliegend, wenn
der Puffer gelöscht wird.

Vielleicht hilft es, wenn Du in Objekten denkst: Dein Diagramm sollte
wissen, was es darstellen soll. Zwei Ideen, wie das gehen könnte:

1) Dem Diagramm wird mitgeteilt, dass es einen weiteren Kreis zeichnen
soll. Alle Kreise werden in einer Liste gespeichert. paintComponent() geht
die Liste durch und zeichnet alle vorgefundenen Kreise. In etwa so:

private List<Integer> kreise = new LinkedList<Integer>();
public void setI(int i){
kreise.add(new Integer(i);
}
public void paintComponent(Graphics g){
for(Integer i : kreise){
g.setColor(Color.red);
g.drawOval(i , i, 20, 20);
}
}

2) Das Diagramm zeichnet direkt in ein Image, das von paintComponent
lediglich abgebildet wird, in etwa so:

private Image bild ......;
public void setI(int i){
Graphics g = bild.getGraphics()
g.setColor(Color.red);
g.drawOval(i , i, 20, 20);
}
public void paintComponent(Graphics g){
g.drawImage(bild ........);
}

Gruß, Carsten
Ernst Baumann
2007-05-27 11:30:57 UTC
Permalink
Post by Carsten Wanke
Wenn beispielsweise das Betriebssystem der Meinung ist, ein Fenster müsste
neu gezeichnet werden, dann wird es java davon informieren, und das
triggert dann ein repaint(). Andererseits kann es gut sein, dass trotz
eines mehrfachen repaint()-Aufrufs nur einmal paintComponent() durchlaufen
wird, weil Swing das zusammenfasst. Du kannst also nie wissen, wann das
ist, und das hat gute Gründe.
Das zusammenfassen nennt man auch coalescing (habe ich zumindest
gelesen)..
Wenn man verzögert zeichnen will, wurde mir geraten, in einer Schleife
einfach zwischen dem Zeichnen Thread.sleep(...) einzubauen.
Beispiel:
while(..){
Thread.sleep(100);
Keis zeichen
}
Wenn man die Verzögerungszeit in sleep(..) entsprechend groß wählt
werden keine paintComponent(..) zusammengefasst (kein coalescing).
Frage:
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Post by Carsten Wanke
Post by Ernst Baumann
2)
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Es wird also nicht der ganze offscreen Bereich auf den Bildschirm
kopiert (also kein Double-Buffering).
Oder wie muss ich mir das erklären?
java geht davon aus, dass paintComponent() die komplette Komponente neu
zeichnet. Wenn Du das Fenster vergrößerst, liegt für den neu gezeigten
Bereich noch keine Information vor, und da finde ich es naheliegend, wenn
der Puffer gelöscht wird.
Du findest es naheliegend, was sagt aber die Doku?
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Also müssten bei vergrössern/verkleinern alle Kreise auf dem
Bildschirm erscheinen.
Irrt hier die Doku oder wo ist mein Denkfehler?

mfg
Ernst
Jochen Theodorou
2007-05-27 14:05:41 UTC
Permalink
Post by Ernst Baumann
Post by Carsten Wanke
Wenn beispielsweise das Betriebssystem der Meinung ist, ein Fenster müsste
neu gezeichnet werden, dann wird es java davon informieren, und das
triggert dann ein repaint(). Andererseits kann es gut sein, dass trotz
eines mehrfachen repaint()-Aufrufs nur einmal paintComponent() durchlaufen
wird, weil Swing das zusammenfasst. Du kannst also nie wissen, wann das
ist, und das hat gute Gründe.
Das zusammenfassen nennt man auch coalescing (habe ich zumindest
gelesen)..
Wenn man verzögert zeichnen will, wurde mir geraten, in einer Schleife
einfach zwischen dem Zeichnen Thread.sleep(...) einzubauen.
while(..){
Thread.sleep(100);
Keis zeichen
}
Wenn man die Verzögerungszeit in sleep(..) entsprechend groß wählt
werden keine paintComponent(..) zusammengefasst (kein coalescing).
aber sicher nicht im EDT!
Post by Ernst Baumann
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Gegenfrage... warum interessiert dich das? Wenn du eine Animation
machst, dann ist die Wartezeit durch die Vorgaben der Animation
bestimmt, nicht durch eine Mindestzeit bei dem Zeichenoperationen
zusammengefasst werden. Sie werden ja auch garnicht wirklich
zusammengefasst, es erscheint nur so, weil eine Änderung nicht unbedingt
sofort dargestellt wird.
Post by Ernst Baumann
Post by Carsten Wanke
Post by Ernst Baumann
2)
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Es wird also nicht der ganze offscreen Bereich auf den Bildschirm
kopiert (also kein Double-Buffering).
Oder wie muss ich mir das erklären?
java geht davon aus, dass paintComponent() die komplette Komponente neu
zeichnet. Wenn Du das Fenster vergrößerst, liegt für den neu gezeigten
Bereich noch keine Information vor, und da finde ich es naheliegend, wenn
der Puffer gelöscht wird.
Du findest es naheliegend, was sagt aber die Doku?
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Also müssten bei vergrössern/verkleinern alle Kreise auf dem
Bildschirm erscheinen.
Irrt hier die Doku oder wo ist mein Denkfehler?
wer sagt das es einen Wechsel des Buffer gibt, wenn du
vergrösserst/verkleinerst?

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ernst Baumann
2007-05-31 13:56:56 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
while(..){
Thread.sleep(100);
Keis zeichen
}
Wenn man die Verzögerungszeit in sleep(..) entsprechend groß wählt
werden keine paintComponent(..) zusammengefasst (kein coalescing).
aber sicher nicht im EDT!
Ich beziehe mich auf das folgende Programm:
-------------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}
-------------------------------------------------------------------
Post by Jochen Theodorou
Post by Ernst Baumann
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Gegenfrage... warum interessiert dich das? Wenn du eine Animation
machst, dann ist die Wartezeit durch die Vorgaben der Animation
bestimmt,
Du meinst, bis ich z.B. das Bild von der Festplatte geladen habe?

Ich will erst mal keine Animation machen, sondern z.B. wie im Programm
oben Kreise zeichnen (oder - was ich mal gemacht habe - den
Lichtstrahl in einem verspiegelten Kreis zeichnen lassen).

Ich will, dass alle repaint() ausgeführt werden und keine
zusammengefasst werden (kein coalescing)
Wenn man die Verzögerungszeit in sleep(..) zu groß wählt,
werden zwar alle Bilder gebracht (kein coalescing), doch damit bewegen
sich die Bilder recht langsam.

Wenn man die Verzögerungszeit in sleep(..) zu klein wählt
werden nicht alle repaint ausgeführt, es werden also Bilder (Kreise)
zusammengefasst (coalescing) und damit nicht _alle_ Bilder (Kreise)
auf den Bildschirm grbracht.

Man könnte jetzt sagen:
Schreib die Kreise alle in ein image und bring dieses image dann
jeweils auf den Bildschirm. Da dort dann nur Kreise hinzukommen, und
die Verzögerungszeit recht klein ist, bekommt der Anwender es nicht
mit, wenn dazwischen irgendwo ein paar Bilder fehlen.
Wenn ich aber trotzdem alle Bilder (Kreise) will:
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Post by Jochen Theodorou
Post by Ernst Baumann
Du findest es naheliegend, was sagt aber die Doku?
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Also müssten bei vergrössern/verkleinern alle Kreise auf dem
Bildschirm erscheinen.
Irrt hier die Doku oder wo ist mein Denkfehler?
wer sagt das es einen Wechsel des Buffer gibt, wenn du
vergrösserst/verkleinerst?
Das war ein Denkfehler von mir.
Aber trotzdem habe ich noch ein Problem.
1)
In meinem Programm oben wird in der Schleife nach jedem Durchgang
durch die Schleife ein neuer Kreis gezeichnet und die alten Kreise
stehen gelassen.
Darf man in Swing davon ausgehen, dass bei jedem Zeichnen, z.B. mit
paintComponent(), der alte Bildschirminhalt _nicht_gelöscht wird,
sondern nur zusätzlich das, was das paintComponent() macht
_zusätzlich_ auf den Bildschirm bringt?

2)
Angenommen nach dem ersten Neuzeichnen der 10 Kreise vergrößern oder
verkleinern der Anwender das Fenster.
Dann zeichnet die JVM einen Kreis (den letzten). Das ist jetzt klar.
Aber warum wird vorher der Bildschirm gelöscht.
Beim Neuzeichnen (der 10 Kreise) wird doch auch nicht nach jedem
Zeichnen eines Kreises vorher der Bildschirm gelöscht (siehe 1)).


mfg
Ernst
Jochen Theodorou
2007-05-31 17:23:04 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Post by Jochen Theodorou
Post by Ernst Baumann
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Gegenfrage... warum interessiert dich das? Wenn du eine Animation
machst, dann ist die Wartezeit durch die Vorgaben der Animation
bestimmt,
Du meinst, bis ich z.B. das Bild von der Festplatte geladen habe?
Nein ich meine wie viele Zeichenaktionen/Bilder du pro Sekunde machen
willst.
Post by Ernst Baumann
Ich will erst mal keine Animation machen, sondern z.B. wie im Programm
oben Kreise zeichnen (oder - was ich mal gemacht habe - den
Lichtstrahl in einem verspiegelten Kreis zeichnen lassen).
Ich will, dass alle repaint() ausgeführt werden und keine
zusammengefasst werden (kein coalescing)
Wenn man die Verzögerungszeit in sleep(..) zu groß wählt,
werden zwar alle Bilder gebracht (kein coalescing), doch damit bewegen
sich die Bilder recht langsam.
mit invokeLater wirst du da eventuell nicht weit kommen, weil das keine
echte Steuerung des EDT und des double buffering erlaubt. Ich würde das
double buffering selber machen.
Post by Ernst Baumann
Wenn man die Verzögerungszeit in sleep(..) zu klein wählt
werden nicht alle repaint ausgeführt, es werden also Bilder (Kreise)
zusammengefasst (coalescing) und damit nicht _alle_ Bilder (Kreise)
auf den Bildschirm grbracht.
du meinst nicht alle Bilder einzeln. Fehlen Kreise?
Post by Ernst Baumann
Schreib die Kreise alle in ein image und bring dieses image dann
jeweils auf den Bildschirm. Da dort dann nur Kreise hinzukommen, und
die Verzögerungszeit recht klein ist, bekommt der Anwender es nicht
mit, wenn dazwischen irgendwo ein paar Bilder fehlen.
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Das hängt von deinem System ab würde ich sagen.

[...]
Post by Ernst Baumann
1)
In meinem Programm oben wird in der Schleife nach jedem Durchgang
durch die Schleife ein neuer Kreis gezeichnet und die alten Kreise
stehen gelassen.
Darf man in Swing davon ausgehen, dass bei jedem Zeichnen, z.B. mit
paintComponent(), der alte Bildschirminhalt _nicht_gelöscht wird,
sondern nur zusätzlich das, was das paintComponent() macht
_zusätzlich_ auf den Bildschirm bringt?
Ich glaube normalerweise sollte man komplett neu zeichnen. Ich denke
wenn das Gebiet vorher zum Beispiel Rot verdeckt war und du jetzt Kreise
zeichnest, würden die vielleicht auf rotem Hintergrund erscheinen.
Letzlich ist es aber glaube ich der Swingimplementierung überlassen ob
der bisherige Inhalt beibehalten wird oder nicht.
Post by Ernst Baumann
2)
Angenommen nach dem ersten Neuzeichnen der 10 Kreise vergrößern oder
verkleinern der Anwender das Fenster.
Dann zeichnet die JVM einen Kreis (den letzten). Das ist jetzt klar.
Aber warum wird vorher der Bildschirm gelöscht.
weil der alte Hintergrund nicht mehr valide ist und neu gezeichnet wird.
Post by Ernst Baumann
Beim Neuzeichnen (der 10 Kreise) wird doch auch nicht nach jedem
Zeichnen eines Kreises vorher der Bildschirm gelöscht (siehe 1)).
weil das nicht unbedingt notwendig ist.. könnte aber so gemacht werden

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Michael Paap
2007-05-31 17:58:18 UTC
Permalink
Post by Jochen Theodorou
Ich glaube normalerweise sollte man komplett neu zeichnen. Ich denke
wenn das Gebiet vorher zum Beispiel Rot verdeckt war und du jetzt Kreise
zeichnest, würden die vielleicht auf rotem Hintergrund erscheinen.
Letzlich ist es aber glaube ich der Swingimplementierung überlassen ob
der bisherige Inhalt beibehalten wird oder nicht.
Wer ist denn "die Swing-Implementierung"? Wie die aussieht, steht im
Quellcode...

Die Methode paintComponent() von JComponent sorgt dafür, dass, wenn die
Methode isOpaque() des Graphics true liefert, der Hintergrund komplett
in der eingestellten Hintergrundfarbe "übermalt" wird.

Wenn man also in der überschreibenden paintComponent() einer
abgeleiteten Klasse super.paintComponent() aufruft, passiert genau dies.
Wenn nicht, dann passiert eben nur das, was man in der überschreibenden
Methode selbst implementiert.

Gruß,
Michael
Ernst Baumann
2007-06-01 16:26:00 UTC
Permalink
Post by Michael Paap
Die Methode paintComponent() von JComponent sorgt dafür, dass, wenn die
Methode isOpaque() des Graphics true liefert, der Hintergrund komplett
in der eingestellten Hintergrundfarbe "übermalt" wird.
In der Doku (Painting in AWT and Swing) steht noch genauer:
"true: The component agrees to paint all of the bits contained within
its rectangular bounds"
Post by Michael Paap
Wenn man also in der überschreibenden paintComponent() einer
abgeleiteten Klasse super.paintComponent() aufruft, passiert genau dies.
Wenn nicht, dann passiert eben nur das, was man in der überschreibenden
Methode selbst implementiert.
In der API-Doku zu paintComponent in JComponent steht:
----------------------------------------------------------------
If you override this in a subclass you should not make permanent
changes to the passed in Graphics.
...
Further, if you do not invoker super's implementation you must honor
the opaque property, that is if this component is opaque, you must
completely fill in the background in a non-opaque color. If you do not
honor the opaque property you will likely see visual artifacts.
----------------------------------------------------------------
Ich übersetze das wie folgt:
Wenn man super() nicht aufruft muss man die opaque Eigenschaft
beachten, d.h., wenn die Komponente undurchlässig ist, muss man den
Hintergrund mit einer nichtundurchlässigen Farbe füllen.
1)
Was soll das heissen?

Was wird aber in dem Fall gemacht, wenn man super() aufruft?
Setzt super() intern isOpaque(true) oder was wird dann gemacht?
2)
Also ich kann mit diesen Aussagen in der Doku nicht viel anfangen
(bzw. dem nicht das gleiche wie du entnehmen), bzw. wie kommst du oben
auf deine Interpretation?
Liegt es an meinem schlechten Englisch oder meiner schlechten
Auffassungsgabe oder an was?


Problem:
Du hast oben geschrieben:
"Die Methode paintComponent() von JComponent sorgt dafür, dass, wenn
die Methode isOpaque() des Graphics true liefert, der Hintergrund
komplett in der eingestellten Hintergrundfarbe "übermalt" wird".

In dem Programm von mir (siehe unten) hat opaque den Wert true
(Beweis: dieser Wert wird auf dem Bildschirm ausgegeben, zusätzlich
habe ich ihn im Konstruktor von Diagramm auf true gesetzt, was aber
nicht nötig gewesen wäre)
In der Schleife werden hintereinander 10 Kreise gezeichnet. Da opaque
gleich true ist, müsste bei jedem Zeichnen eines Kreises der
Hintergrund _neu_ gezeichnet werden und damit die alten Kreise
_übermalt_ werden.
3)
Dem ist aber _nicht_so.
Warum?


Was mich jetzt noch total durcheinanderbringt ist folgendes:
Wenn ich im Konstruktor Diagramm die folgende Anweisung mache:
setOpaque(false);
dann werden die alten Kreise übermalt.
4)
Warum ist das so?
Ich verstehe das nicht.

-------------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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);
System.out.println("isOpaque="+diagramm.isOpaque());
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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){
setOpaque(true);
//setOpaque(false);
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;
}
}
}

-------------------------------------------------------------------

mfg
Ernst
Michael Paap
2007-06-01 17:38:39 UTC
Permalink
Post by Ernst Baumann
1)
Was soll das heissen?
Na, eben, dass man, wenn man *nicht* super.paintComponent() aufruft, man
sich selbst darum kümmern muss, dass opake Komponenten ihren Hintergrund
"löschen", also in der Hintergrundfarbe komplett neu malen. Opak ist das
Gegenteil von transparent.
Post by Ernst Baumann
Was wird aber in dem Fall gemacht, wenn man super() aufruft?
Setzt super() intern isOpaque(true) oder was wird dann gemacht?
Nein. Warum sollt es? *Wenn* die Komponente opak ist, dann zeichnet die
paintCompent() von JComponent den Hintergrund komplett neu. Wenn nicht,
dann nicht. Dann soll die Komponente ja transparent sein.
Post by Ernst Baumann
Also ich kann mit diesen Aussagen in der Doku nicht viel anfangen
(bzw. dem nicht das gleiche wie du entnehmen), bzw. wie kommst du oben
auf deine Interpretation?
Ich lese sinnentnehmend und mit ein bisschen Hintergrundwissen.
Post by Ernst Baumann
Liegt es an meinem schlechten Englisch oder meiner schlechten
Auffassungsgabe oder an was?
Ich weiß es nicht. Deine ganze Herangehensweise, soweit ich sie aus
deinen Fragen schließen kann, erscheint mir gelinde gesagt eigenartig.
Es gibt eine Sorte Leute, denen aus der Ferne etwas zu erklären,
empfinde zumindest ich als etwas quälend. Du könntest dazugehören. :-)
Post by Ernst Baumann
"Die Methode paintComponent() von JComponent sorgt dafür, dass, wenn
die Methode isOpaque() des Graphics true liefert, der Hintergrund
komplett in der eingestellten Hintergrundfarbe "übermalt" wird".
In dem Programm von mir (siehe unten) hat opaque den Wert true
(Beweis: dieser Wert wird auf dem Bildschirm ausgegeben, zusätzlich
habe ich ihn im Konstruktor von Diagramm auf true gesetzt, was aber
nicht nötig gewesen wäre)
In der Schleife werden hintereinander 10 Kreise gezeichnet. Da opaque
gleich true ist, müsste bei jedem Zeichnen eines Kreises der
Hintergrund _neu_ gezeichnet werden und damit die alten Kreise
_übermalt_ werden.
3)
Dem ist aber _nicht_so.
Warum?
Weil du eben *nicht* super.paintComponent() aufrufst. Und damit wird die
Methode paintComponent von JComponent, auf die sich das oben gesagte
bezieht, *nicht* ausgeführt, sondern wir haben genau den Fall, der in
der Doku erwähnt ist:

"if you do not invoker super's implementation you must honor
the opaque property, that is if this component is opaque, you must
completely fill in the background in a non-opaque color."

Gruß,
Michael
Ernst Baumann
2007-06-02 06:58:22 UTC
Permalink
Post by Michael Paap
Post by Ernst Baumann
Also ich kann mit diesen Aussagen in der Doku nicht viel anfangen
(bzw. dem nicht das gleiche wie du entnehmen), bzw. wie kommst du oben
auf deine Interpretation?
Ich lese sinnentnehmend und mit ein bisschen Hintergrundwissen.
Wahrscheinlich braucht man ein gewisses intuitives Erfassen.
Das habe ich leider nicht. Vielleicht fehlen auch sonst noch ein paar
Schaltkreise, die man für dieses Thema braucht (das Thema ist ziemlich
komplex).
Post by Michael Paap
Ich weiß es nicht. Deine ganze Herangehensweise, soweit ich sie aus
deinen Fragen schließen kann, erscheint mir gelinde gesagt eigenartig.
Es gibt eine Sorte Leute, denen aus der Ferne etwas zu erklären,
empfinde zumindest ich als etwas quälend. Du könntest dazugehören. :-)
Es ist mir selbst unangenehm, dass ich das ganze nicht in angemessener
Zeit (vielleicht auch nie 100%) kapiere und damit andere auf die
Nerven falle. Leider kann ich meine Kapsel nicht auswechseln. Trotzdem
Dank an alle, die mir noch antworten.


---------------------------------------------------------------
Ich habe verschiedene Fälle untersucht und mit dem verglichen, was du
beschrieben hast:
(standardmäßig ist Swing auf opak (d.h. undurchlässig also
nicht transparent) eingestellt).

Fall1_1: (Zustand=opak, super.paintComponent() wird aufgerufen):
super.paintComponent() zeichnet Hintergrund komplett neu
Programm macht, was du beschreibst.

Fall1_2 (Zustand=transparent, super.paintComponent() wird aufgerufen):
super.paintComponent() zeichnet Hintergrund nicht neu (sagst du)
Programm macht nicht, was du beschreibst:
Programm zeichnet Hintergrund komplett neu (alte Kreise werden
übermalt).

Fall2_1: (Zustand=opak, super.paintComponent() wird nicht aufgerufen):
Programmierer muss Hintergrund in einer nichtopaken Farbe malen,
sonst bekommt er "visuelle Artifakte" (laut Doku)

Frage:
Was ist eine nichtopake Farbe und was sind "visuelle Artifakte", die
er dann bekommt?

Fall2_2 (Zustand=transparent, super.paintComponent() wird nicht
aufgerufen):

Frage:
Was passiert in diesem Fall?
Bei meinem Programm wird der Hintergrund neu gemalt, also das
_gleiche_gemacht wie im Fall1_1. Warum ist das so, obwohl der Zustand
transparent, also _nicht_ opak ist? (Was sagt die Doku dazu?)
Meiner Meinung nacht, müsste der Hintergrund nicht neu gemalt werden,
da Zustand transparent.

Frage:
Was mir überhaupt nicht klar ist:
Warum wird im
Fall2_1: (Zustand=opak, super.paintComponent() wird nicht aufgerufen)
beim Zeichnen der 10 Kreise die Hintergrundfarbe _nicht_ jedesmal neu
gemalt (also getan, als ob Zustand transparent wäre), aber wenn danach
das Fenster vergrössert/verkleinert wird, wird die Hintergrundfarbe
_neu_ gemalt (und die alten Kreise übermalt)?
Sind das die "visuelle Artifakte", oder warum ist das Verhalten nicht
eindeutig?


Hier ist das Programm dazu, wobei man zum Testen die entsprechenden
Anweisungen auskommentieren kann oder nicht
(setOpaque(true); setOpaque(false); super.paintComponent(g))

-------------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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);
System.out.println("isOpaque="+diagramm.isOpaque());
f.getContentPane().add(diagramm);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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){
// setOpaque(true);
// setOpaque(false);
this.xpAnz=xpAnz;
this.ypAnz=ypAnz;
}

public void paintComponent(Graphics g){
super.paintComponent(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;
}
}
}
-------------------------------------------------------------------


mfg
Ernst

Ernst Baumann
2007-06-01 19:46:23 UTC
Permalink
Post by Jochen Theodorou
Post by Ernst Baumann
Wenn man die Verzögerungszeit in sleep(..) zu klein wählt
werden nicht alle repaint ausgeführt, es werden also Bilder (Kreise)
zusammengefasst (coalescing) und damit nicht _alle_ Bilder (Kreise)
auf den Bildschirm grbracht.
du meinst nicht alle Bilder einzeln. Fehlen Kreise?
Es könnten ja Kreise fehlen, denn wenn ich ich verzögert (mit sleep)
in einer Schleife durch 10 repaints 10 Kreise ausgeben will, könnte
die JVM ja einfach hergehen und 9 zusammenfassen, so dass dann nur
noch _ein einziger_ ausgegeben wird und meine kleine Demoanimation
wäre futsch.
Wie kann ich das unterbinden?

mfg
Ernst
Marcus Woletz
2007-05-27 17:00:31 UTC
Permalink
Hallo Ernst,

Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Das zusammenfassen nennt man auch coalescing (habe ich zumindest
gelesen)..
Wenn man verzögert zeichnen will, wurde mir geraten, in einer Schleife
einfach zwischen dem Zeichnen Thread.sleep(...) einzubauen.
while(..){
Thread.sleep(100);
Keis zeichen
}
Wenn man die Verzögerungszeit in sleep(..) entsprechend groß wählt
werden keine paintComponent(..) zusammengefasst (kein coalescing).
das zusammenfassen von paint-Ereignissen hat damit nichts zu tun. Du
befindest Dich doch schon im EDT! Zusammenfassen findet auf völlig
anderer Ebene statt: Wenn Du mehrmals kurz hintereinander z.B. repaint
aufrufst, dann kann der RepaintManager diese Aufrufe zusammenfassen und
einen einzigen paint-Aufruf an die betroffenen Komponenten daraus
generieren.
Post by Ernst Baumann
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Das ist für Dich völlig ohne Belang und lediglich eine Möglichkeit für
Swing, den Aufwand zum zeichnen der Komponenten zu minimieren.

[...]
Post by Ernst Baumann
Du findest es naheliegend, was sagt aber die Doku?
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Also müssten bei vergrössern/verkleinern alle Kreise auf dem
Bildschirm erscheinen.
Irrt hier die Doku oder wo ist mein Denkfehler?
Wenn Du hier vielleicht nochmal kurz ein Beispielprogramm postest, kann
Dir vielleicht geholfen werden. Aber nur dadurch, dass du jetzt bald
verstehen wirst, wie Swing im Hintergrund arbeitet, wird Dein geplantes
Vorgehen nicht korrekter, und wir alle brauchen uns über lahme
Java-Anwendungen nicht wundern ;-)
Post by Ernst Baumann
mfg
Ernst
ciao

Marcus
Ernst Baumann
2007-05-31 13:56:58 UTC
Permalink
Post by Marcus Woletz
Post by Ernst Baumann
}
Wenn man die Verzögerungszeit in sleep(..) entsprechend groß wählt
werden keine paintComponent(..) zusammengefasst (kein coalescing).
das zusammenfassen von paint-Ereignissen hat damit nichts zu tun. Du
befindest Dich doch schon im EDT! Zusammenfassen findet auf völlig
anderer Ebene statt: Wenn Du mehrmals kurz hintereinander z.B. repaint
aufrufst, dann kann der RepaintManager diese Aufrufe zusammenfassen und
einen einzigen paint-Aufruf an die betroffenen Komponenten daraus
generieren.
Ich beziehe mich auf das folgende Programm:
-------------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}
-------------------------------------------------------------------

Ich will, dass alle repaint() ausgeführt werden und keine
zusammengefasst werden (kein coalescing)
Wenn man die Verzögerungszeit in sleep(..) zu groß wählt,
werden zwar alle Bilder gebracht (kein coalescing), doch damit bewegen
sich die Bilder recht langsam.

Wenn man die Verzögerungszeit in sleep(..) zu klein wählt
werden nicht alle repaint ausgeführt, es werden also Bilder (Kreise)
zusammengefasst (coalescing) und damit nicht _alle_ Bilder (Kreise)
auf den Bildschirm grbracht.

Man könnte jetzt sagen:
Schreib die Kreise alle in ein image und bring dieses image dann
jeweils auf den Bildschirm. Da dort dann nur Kreise hinzukommen, und
die Verzögerungszeit recht klein ist, bekommt der Anwender es nicht
mit, wenn dazwischen irgendwo ein paar Bilder fehlen.
Wenn ich aber trotzdem alle Bilder (Kreise) will:
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Post by Marcus Woletz
Post by Ernst Baumann
Du findest es naheliegend, was sagt aber die Doku?
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Also müssten bei vergrössern/verkleinern alle Kreise auf dem
Bildschirm erscheinen.
Irrt hier die Doku oder wo ist mein Denkfehler?
Wenn Du hier vielleicht nochmal kurz ein Beispielprogramm postest, kann
Dir vielleicht geholfen werden.
-----------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}
-----------------------------------------------------
Das war ein Denkfehler von mir.
Aber trotzdem habe ich noch ein Problem.
1)
In meinem Programm oben wird in der Schleife nach jedem Durchgang
durch die Schleife ein neuer Kreis gezeichnet und die alten Kreise
stehen gelassen.
Darf man in Swing davon ausgehen, dass bei jedem Zeichnen, z.B. mit
paintComponent(), der alte Bildschirminhalt _nicht_gelöscht wird,
sondern nur zusätzlich das, was das paintComponent() macht
_zusätzlich_ auf den Bildschirm bringt?

2)
Angenommen nach dem ersten Neuzeichnen der 10 Kreise vergrößert oder
verkleinert der Anwender das Fenster.
Dann zeichnet die JVM einen Kreis (den letzten). Das ist jetzt klar.
Aber warum wird vorher der Bildschirm gelöscht.
Beim Neuzeichnen (der 10 Kreise) wird doch auch nicht nach jedem
Zeichnen eines Kreises vorher der Bildschirm gelöscht (siehe 1)).


mfg
Ernst
Carsten Wanke
2007-05-31 18:53:51 UTC
Permalink
Hallo Ernst,
Post by Ernst Baumann
Ich will, dass alle repaint() ausgeführt werden und keine
zusammengefasst werden (kein coalescing)
Offensichtlich (das haben in diese Thread bislang alle gesagt) hat man
darauf keinen Einfluss. Was möchtest Du erreichen? Bei mir hat bislang
keine Animation deswegen geruckelt.

Du könntest das natürlich 'mal testen:

public class PaintTest2 {
public static void main(String[] args) {
new PaintTest2();
}
int count = 0;
PaintTest2(){
JFrame f = new JFrame("PaintTest");
JPanel p = new JPanel(){ // warum nimmst Du eigentlich immer JPanel?
protected void paintComponent(Graphics arg0) {
super.paintComponent(arg0);
count++;
}
};
f.add(p);
f.setSize(100,100);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
int i = 0;
count=0;
while (count<2 && i < 100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();}
count=0;
f.repaint();
try {
Thread.sleep(i++);
} catch (InterruptedException e) {e.printStackTrace();}
f.repaint();
}
System.out.println("Sleep: "+i);
System.exit(0);
}
}

Schön ist das zwar nicht, zeigt aber, dass die Werte für die Wartezeit sehr
unterschiedlich sein können. Also, mit sleep() erreichst Du das Ziel
bestimmt nicht.

Gruß, Carsten
Ernst Baumann
2007-06-01 19:46:24 UTC
Permalink
Post by Carsten Wanke
Offensichtlich (das haben in diese Thread bislang alle gesagt) hat man
darauf keinen Einfluss.
Das glaube ich gerne.
Post by Carsten Wanke
Was möchtest Du erreichen? Bei mir hat bislang
keine Animation deswegen geruckelt.
Stell dir mal vor, ich will verzögert (mit sleep) in einer Animation
hintereinander 10 Kreise ausgeben.
Kann es dann sein dass die VJM meine 10 repaint zu _einem einzigen_
zusammenfasst? Dann gäbe es nur ein Bild und ich hätte keine Animation
mehr.
Wie kann ich das unterbinden?

mfg
Ernst
Carsten Wanke
2007-06-02 06:42:25 UTC
Permalink
Hallo Ernst,
Post by Ernst Baumann
Post by Carsten Wanke
Was möchtest Du erreichen? Bei mir hat bislang
keine Animation deswegen geruckelt.
Stell dir mal vor, ich will verzögert (mit sleep) in einer Animation
hintereinander 10 Kreise ausgeben.
Kann es dann sein dass die VJM meine 10 repaint zu _einem einzigen_
zusammenfasst? Dann gäbe es nur ein Bild und ich hätte keine Animation
mehr.
Und, siehst Du eine Animation oder nicht?

Gruß, Carsten

P.S.: Mir ist schon klar, das die Diskussion ziemlich viele Enden hat, aber
dennoch finde ich es irgendwie unangenehm, die identischen Äußerungen auch
an anderer Stelle zu lesen. Du bist doch hoffentlich nich von der Telekom?
;-)
Marcus Woletz
2007-06-01 01:41:39 UTC
Permalink
Hallo Ernst,


Ernst Baumann schrieb:

[...]
Post by Ernst Baumann
Ich will, dass alle repaint() ausgeführt werden und keine
zusammengefasst werden (kein coalescing)
nein, das willst Du nicht. _Falls_ Du das trotzdem willst, leidet Dein
Programm an einem Designfehler. Dieses Zusammenfassen findet zur
Optimierung der Darstellungsqualität und der Laufzeiteigenschaften des
Grafiksystems statt. Wieso möchtest Du diese Optimierungen, die Dir das
Laufzeitsystem gratis bietet, mit brachialer gewalt aushebeln?

[...]
Post by Ernst Baumann
Wenn man die Verzögerungszeit in sleep(..) zu klein wählt
werden nicht alle repaint ausgeführt, es werden also Bilder (Kreise)
zusammengefasst (coalescing) und damit nicht _alle_ Bilder (Kreise)
auf den Bildschirm grbracht.
Dann hat Dein Programm einen Fehler.
Post by Ernst Baumann
Schreib die Kreise alle in ein image und bring dieses image dann
jeweils auf den Bildschirm. Da dort dann nur Kreise hinzukommen, und
die Verzögerungszeit recht klein ist, bekommt der Anwender es nicht
mit, wenn dazwischen irgendwo ein paar Bilder fehlen.
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Was genau soll denn der Anwender Deines Programms zu Gesicht bekommen?
Ich habe mir jetzt Dein Programm nicht nochmal angesehen, deshalb die Frage.

[...]
Post by Ernst Baumann
mfg
Ernst
ciao

Marcus
Ernst Baumann
2007-06-01 19:46:26 UTC
Permalink
Post by Marcus Woletz
Post by Ernst Baumann
Ich will, dass alle repaint() ausgeführt werden und keine
zusammengefasst werden (kein coalescing)
nein, das willst Du nicht. _Falls_ Du das trotzdem willst, leidet Dein
Programm an einem Designfehler. Dieses Zusammenfassen findet zur
Optimierung der Darstellungsqualität und der Laufzeiteigenschaften des
Grafiksystems statt. Wieso möchtest Du diese Optimierungen, die Dir das
Laufzeitsystem gratis bietet, mit brachialer gewalt aushebeln?
Stell dir mal vor, ich will verzögert (mit sleep) in einer Animation
hintereinander 10 Kreise ausgeben.
Kann es dann sein dass die VJM meine 10 repaint zu _einem einzigen_
zusammenfasst? Dann gäbe es nur ein Bild und ich hätte keine Animation
mehr.
Wie kann ich das unterbinden?

mfg
Ernst
Carsten Wanke
2007-05-27 17:40:29 UTC
Permalink
Hallo Ernst,
Post by Ernst Baumann
Das zusammenfassen nennt man auch coalescing (habe ich zumindest
gelesen)..
Wenn man verzögert zeichnen will, wurde mir geraten, in einer Schleife
einfach zwischen dem Zeichnen Thread.sleep(...) einzubauen.
while(..){
Thread.sleep(100);
Keis zeichen
}
Ja, wenn Du etwas zeichen willst, teilst Du das der Komponente genau dann
mit, wenn Du es angezeigt haben möchtest, und triggerst anschließend mit
repaint() ein neuzeichnen.
Post by Ernst Baumann
Wenn man die Verzögerungszeit in sleep(..) entsprechend groß wählt
werden keine paintComponent(..) zusammengefasst (kein coalescing).
Gibt es in der Doku die Angabe einer Mindeszeit (in sleep), ab der
nicht mehr zusammengefasst (nicht mehr gecoalescingt) wird, oder sind
dies Erfahrungswerte?
Geh' einfach davon aus, das die Zeit so klein ist, dass Du sie nicht
bemerkst. Bei einem repaint() wird aus Sicht des Anwenders sofort
gezeichnet.
Post by Ernst Baumann
Post by Carsten Wanke
java geht davon aus, dass paintComponent() die komplette Komponente neu
zeichnet. Wenn Du das Fenster vergrößerst, liegt für den neu gezeigten
Bereich noch keine Information vor, und da finde ich es naheliegend, wenn
der Puffer gelöscht wird.
Du findest es naheliegend, was sagt aber die Doku?
In "Painting in AWT and Swing" steht bei Paint Processing, dass wenn
double Bufferung eingestellt ist (und das ist es standardmässig bei
Swing), dass der offscreen image auf den Bildschirm kopiert wird.
Also müssten bei vergrössern/verkleinern alle Kreise auf dem
Bildschirm erscheinen.
Irrt hier die Doku oder wo ist mein Denkfehler?
Hm, mach' Dir nicht so viele Gedanken über die Pufferung. Der wesentliche
Vorteil ist der, dass das Bild nicht flackert, wenn in paintComponent()
viele Elemente gezeichet werden müssen. Dann werden sie nämlich erst in den
Puffer gezeichnet, der am Ende auf einen Schlag angezeigt wird. Davon
merkst Du aber ansonsten nix.

Diese internen Dinge scheinen insgesamt eher zur Verwirrung zu führen als
zum Verständnis beizutragen. Du stellst die dritte Frage, obwohl Du die
erste Antwort noch nicht umgesetzt hast und verstrickst Dich dabei in
Probleme, die nicht entstehen sollten. Experimentiere doch 'mal mit
folgender Struktur:

Die Komponente speichert intern ihren Zustand, also, welche Kreise
gezeichnet werden sollen. Dazu werden geeignete Methoden implementiert
(z.B. setI).
paintComponent() wird nur vom EDT aufgerufen. Hier wird auf der Basis des
Zustandes der Komponente das komplette gewünschte Bild gezeichnet. Solange
es sich dabei um einen überschaubaren Satz geometrischer Figuren handelt,
sollte das kein Problem sein. Falls Du ein Apfelmännchen berechnen willst,
solltest Du das allerdings in einem eigenen Thread tun... ;-) Und, um der
Frage vorzubeugen: Du brauchst erst anfangen, über die Auslagerung in einen
separaten Thread nachzudenken, wenn sich die Oberfläche irgendwie lahm
anfühlt.

Gruß, Carsten
Michael Paap
2007-05-27 18:23:28 UTC
Permalink
Post by Carsten Wanke
Ja, wenn Du etwas zeichen willst, teilst Du das der Komponente genau dann
mit, wenn Du es angezeigt haben möchtest, und triggerst anschließend mit
repaint() ein neuzeichnen.
Im Normalfall teilst du es nicht der Komponente mit, sondern einem
Objekt, welches von der Komponente als Datenquelle zum Zeichnen
verwendet wird.
Post by Carsten Wanke
Geh' einfach davon aus, das die Zeit so klein ist, dass Du sie nicht
bemerkst. Bei einem repaint() wird aus Sicht des Anwenders sofort
gezeichnet.
... außer du blockierst den EDT durch närrisches Treiben. ;-)
Post by Carsten Wanke
paintComponent() wird nur vom EDT aufgerufen.
Vom Fenstersystem, im EDT.
Post by Carsten Wanke
Hier wird auf der Basis des
Zustandes der Komponente das komplette gewünschte Bild gezeichnet. Solange
es sich dabei um einen überschaubaren Satz geometrischer Figuren handelt,
sollte das kein Problem sein. Falls Du ein Apfelmännchen berechnen willst,
solltest Du das allerdings in einem eigenen Thread tun... ;-) Und, um der
Frage vorzubeugen: Du brauchst erst anfangen, über die Auslagerung in einen
separaten Thread nachzudenken, wenn sich die Oberfläche irgendwie lahm
anfühlt.
Wenn du kontinuierliche Änderungen hast (Animation), dann sollte das
Triggern der Zustandsänderungen ebenso wie das des Neuzeichnens in
eigenen Threads stattfinden. Wenn du das nicht deutlich sagst, wird der
OP vermutlich doch wieder alles im EDT abwickeln.
Post by Carsten Wanke
Diese internen Dinge scheinen insgesamt eher zur Verwirrung zu führen als
zum Verständnis beizutragen. Du stellst die dritte Frage, obwohl Du die
erste Antwort noch nicht umgesetzt hast und verstrickst Dich dabei in
Probleme, die nicht entstehen sollten.
Dem kann ich mich nur anschließen. @ Ernst: Ich nehme die Aufforderung
zur Lektüre von Grundlagen zurück, das scheint alles nur schlimmer zu
machen. Schau dir stattdessen den Quellcode einer funktionierenden
simplen Animation an und verstehe, wie die funktioniert, das sollte helfen.

Gruß,
Michael
Ernst Baumann
2007-05-31 13:57:00 UTC
Permalink
Post by Michael Paap
Wenn du kontinuierliche Änderungen hast (Animation), dann sollte das
Triggern der Zustandsänderungen ebenso wie das des Neuzeichnens in
eigenen Threads stattfinden. Wenn du das nicht deutlich sagst, wird der
OP vermutlich doch wieder alles im EDT abwickeln.
Um den EDT näher kennenzulernen, bzw. zu verstehen, wie er reagiert,
habe ich das Programm unten geschrieben.

Zum Programm:
Das Programm ist ein Testprogramm zum Experimentieren und Lernen, aber
nicht korrekt (aber lauffähig). Ich will den EDT durch das Programm
etwas näher kennenlernen.
Im EDT befindet sich die Event-Wahrnehmung und Verarbeitung, das
Zeichnen (z.B. mit paintComponent(...) und von mir mit invokeLater neu
hinzugekommen der Thread, der die Methode createAndShowGUI() startet.
Ist das richtig, oder habe ich da schon einen Denkfehler, oder was hat
sich durch invokeLater() für den EDT geändert?

Das Programm hat folgende Fehler:
a) Es _summiert_ die ganzen Verzögerungen, bevor es einen Kreis
ausgibt.
Den Grund dafür kann ich mir im Moment nicht erklären.
Oder liegt es daran, dass sich jetzt die Eventverarbeitung,
paintComponent() und createAndShowGUI() einen Thread (den EDT) teilen.
D.h. während die Methode createAndShowGUI() abläuft kann dann
paintComponent() nicht ausgeführt werden.
b) paintComponent() wird -laut Ausgabe - nur zweimal ausgeführt
(wahrscheinlich einmal von setVisible(true)).
An was liegt das?

---------------------------------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainTest10 {
private static void createAndShowGUI(){
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){
System.out.println("i in
createAndShowGUI()="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){
}
}

}


public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}
}


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;
}
}
}
---------------------------------------------------------------

mfg
Ernst
Paul Ebermann
2007-05-24 23:09:51 UTC
Permalink
Post by Ernst Baumann
Post by Marcus Woletz
Das Problem bei paintComponent() ist folgendes: Diese Methode wird
grundsätzlich aus dem Event Dispatch Thread (EDT) aufgerufen bzw.
läuft in diesem. Nun läuft allerdings der komplette Nachrichtenverkehr
in Swing/AWT auch in diesem besagten EDT. Hältst Du Dich also länger in
Deiner paintComponent()-Methode auf, können während dieser Zeit keine
weiteren Ereignisse oder Zeichenoperationen mehr ausgeführt werden. Für
den Anwender scheint Dein Programm zu "hängen".
1)
Aha, paintComponent und die Ereignisverarbeitung laufen innerhalb
eines Threads ab und teilen sich somit die CPU-Zeit für diesen Thread.
Das was der eine zuviel braucht, fehlt dem anderen. Ist das richtig?
Hmm, das Problem ist nicht die CPU-Zeit (ein sleep()
belastet die CPU ja nur unwesentlich), sondern
dass diese Aktionen eben nur _nacheinander_ durchgeführt
werden können.
Post by Ernst Baumann
Post by Marcus Woletz
Deshalb die _Regel_,
möglichst keine rechenzeitintensiven Operationen im EDT (also auch in
der paintComponent()-Methode) durchzuführen. Schleifen etc. sind kein
Problem, so lange Du nicht irgend welche Millionen Schleifendurchläufe
mit entsprechender Laufzeit hast.
2)
Wo - wenn nicht im EDT - soll man dann rechenintensive Operationen
ausführen?
In einem neuen Thread, den du etwa aus deiner
Ereignis-Verarbeitungs-Methode (oder aus main(),
falls es schon beim Start des Programmes losgeht)
startest.


Paul
--
Nun ludigxas: : ()
Ernst Baumann
2007-05-25 19:24:35 UTC
Permalink
Post by Paul Ebermann
Post by Ernst Baumann
Aha, paintComponent und die Ereignisverarbeitung laufen innerhalb
eines Threads ab und teilen sich somit die CPU-Zeit für diesen Thread.
Das was der eine zuviel braucht, fehlt dem anderen. Ist das richtig?
Hmm, das Problem ist nicht die CPU-Zeit (ein sleep()
belastet die CPU ja nur unwesentlich), sondern
dass diese Aktionen eben nur _nacheinander_ durchgeführt
werden können.
Das ist mir nicht ganz klar:
paintComponent() und die Ereignisverarbeitung läuft im Event Dispatch
Thread (EDT).
Heißt das, dass die Ereignisverarbeitung _und_ die Anweisungen in
paintComponent() nicht gleichzeitig (parallel) ablaufen können,
sondern hintereinander. D.h., wenn auf ein Ereignis gelauscht wird,
kann nicht gleichzeitig (im Hintergrund) paintComponent() ablaufen.
Ist das richtig?
Post by Paul Ebermann
Post by Ernst Baumann
2)
Wo - wenn nicht im EDT - soll man dann rechenintensive Operationen
ausführen?
In einem neuen Thread, den du etwa aus deiner
Ereignis-Verarbeitungs-Methode (oder aus main(),
falls es schon beim Start des Programmes losgeht)
startest.
Die Funktion main gehört nicht zum EDT.
Deshalb habe ich diese rechenintensive Operation in main eingebaut
(siehe unten).
Die Kreise werden verzögert gezeichnet.
Ist dieser Versuch von mir also auch möglich, oder _muss_ es ein
eigener Thread sein?

----------------------------------------
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}

----------------------------------------

1) Was ich dann nicht verstehe ist die folgende Ausgabe des Programms:
i in main=0
i in paintComponent=0 <--
i in paintComponent=0 <--
i in main=20
i in paintComponent=20
i in main=40
i in paintComponent=40
i in main=60
i in paintComponent=60
i in main=80
i in paintComponent=80
i in main=100
i in paintComponent=100
i in main=120
i in paintComponent=120
i in main=140
i in paintComponent=140
i in main=160
i in paintComponent=160
i in main=180
i in paintComponent=180 <--
i in paintComponent=180 <--

Wenn alles schön hintereinander kommen würde,
müssten sich doch die Ausgaben in main und paintComponent abwechseln.
Warum tun sie dies nicht?


2)
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Es wird also nicht der ganze offscreen Bereich auf den Bildschirm
kopiert (also kein Double-Buffering).
Oder wie muss ich mir das erklären?

mfg
Ernst
Michael Paap
2007-05-25 19:42:11 UTC
Permalink
Post by Ernst Baumann
paintComponent() und die Ereignisverarbeitung läuft im Event Dispatch
Thread (EDT).
Heißt das, dass die Ereignisverarbeitung _und_ die Anweisungen in
paintComponent() nicht gleichzeitig (parallel) ablaufen können,
sondern hintereinander. D.h., wenn auf ein Ereignis gelauscht wird,
kann nicht gleichzeitig (im Hintergrund) paintComponent() ablaufen.
Ist das richtig?
Ja, selbstverständlich. Ein Thread für sich genommen arbeitet nun mal
rein sequentiell.

Gruß,
Michael
Bernd Eckenfels
2007-05-25 19:45:59 UTC
Permalink
Post by Ernst Baumann
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Genauer: du zeichnest nur einen Kreis. Der Effekt dass du mehrere siehst
liegt daran, dass du davon ausgehst dass das Bild erhalten bleibt, dem ist
aber nicht so: paint component muss die komplette Komponente zeichnen, wenn
sie das nicht tut (z.b. den hintergrund nicht loescht) dann kommt es zu
solchen Effekten dass kreise stehenbleiben.

Das kann man zur Optimierung nutzen (wenn man weiss es aendert sich nicht
viel), aber schon bei einem "expose" muss man alles neu zeichnen.

Wenn du einen Canvas nimmst, dann löscht der vor jedem Paint den Hintergrund.

Was du eigentlich willst ist in eine Graphic Kontext zeichnen und diesem bei
jedem paint event rausschreiben auf den bildschirm (also inkl. des
hintergrundes).

Gruss
Bernd
Ernst Baumann
2007-05-27 11:30:59 UTC
Permalink
Post by Bernd Eckenfels
Post by Ernst Baumann
Wenn ich jetzt allerdings das Fenster vergrößere (oder verkleinere),
wird nur der zuletzt gezeichnete Kreis dargestellt.
Genauer: du zeichnest nur einen Kreis. Der Effekt dass du mehrere siehst
liegt daran, dass du davon ausgehst dass das Bild erhalten bleibt, dem ist
In "Painting in AWT and Swing"
http://java.sun.com/products/jfc/tsc/articles/painting/
steht bei Paint Processing, dass wenn double Bufferung eingestellt ist
(und das ist es standardmässig bei Swing), dass der offscreen image
auf den Bildschirm kopiert wird.
Das heisst - im Gegensatz zu deiner Meinung - müssten alle Kreise auf
den Bildschirm kommen.
Beim Erstzeichnen ist das der Fall, beim vergrßern/verkleinern
allerdings nicht.
Irrt hier die Doku oder wo habe ich meinen Denkfehler?
Post by Bernd Eckenfels
paint component muss die komplette Komponente zeichnen, wenn
sie das nicht tut (z.b. den hintergrund nicht loescht) dann kommt es zu
solchen Effekten dass kreise stehenbleiben.
Wo steht dies in der Doku?
Post by Bernd Eckenfels
Das kann man zur Optimierung nutzen (wenn man weiss es aendert sich nicht
viel),
also gehst du doch davon aus, dass alle Kreise gezeichnet werden, oder
wo habe ich dich falsch verstanden?
Post by Bernd Eckenfels
aber schon bei einem "expose" muss man alles neu zeichnen.
was ist ein expose?

mfg
Ernst
Jochen Theodorou
2007-05-26 18:56:24 UTC
Permalink
Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Die Funktion main gehört nicht zum EDT.
Deshalb habe ich diese rechenintensive Operation in main eingebaut
(siehe unten).
da muss ich nochmal kurz einhacken. main ist eine Methode, keine
Funktion und eine Methode gehört normalerweise nie zu einem bestimmten
Thread. Aber natürlich werden bestimmte Methoden von bestimmten Threads
aufgerufen und bestimmte Methoden sind dafür vorgesehen von bestimmten
Threads aufgerufen zu werden.

Weil es scheinbar an den Grundlagen mangelt ein paar Erklärungen:

Ein Thread alleine arbeitet sequentiell, der Code wird Stück für Stück
abgearbeitet. Hat man mehr als einen Thread kann man mehrere Codestücke
"gleichzeitig" abarbeiten.

Wird ein Javaprogramm gestartet, so wird auch immer ein Thread erzeugt
und dieser wird oft Main-Thread oder so ähnlich genannt. Dieser Thread
ruft deine main Methode auf. Der sogenante EDT (Event Dispatch Thread)
wird erst gestartet wenn Swing benutzt wird. Welche methoden durch diese
Threads aufgerufen werden bestimmt dein Programm.

Der EDT transportiert Ereignisse, die deine Applikationsoberfläche
betreffen. Wird also dort irgendwo lange gewartet oder ähnliches, dann
"blockiert" deine Oberfläche. Wenn du in solche einer Blockade zum
Beispiel dein Fenster verschiebst, dann verschwindet der Inhalt und
kommt erst wieder wenn neu gezeichnet wurde, also nachdem der EDT raus
aus der Blockade ist. Eine solche Blockade kann durch ein einfache sleep
ausgelöst werden, oder durch rechenintensive Methoden. Entscheidend ist,
dass der EDT für die Verarbeitung der Ereignisse zu lange braucht.
Deshalb soll man da keinen Unfug treiben und längere Arbeiten in andere
Threads verlegen. Das kann der main Thread sein, muss er aber nicht.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ralf Callenberg
2007-05-27 00:39:41 UTC
Permalink
Post by Jochen Theodorou
da muss ich nochmal kurz einhacken. main ist eine Methode, keine
Funktion
Auch auf die Gefahr hin, mich als ignorant zu zeigen: ich bin all die
Jahre davon ausgegangen, dass 'Methode' und 'Funktion' im Wesentlichen
nur verschiedene Worte für das Gleiche sind. Es gibt Leute, die
unterscheiden diese Begriffe je nachdem ob etwas einen Wert zurückgibt
(Funktion) oder nicht. In noch strengerer Auslegung dieser
Unterscheidung darf eine Funktion keine sonstigen Nebeneffekte haben. In
C/C++ möchten manche mit der Verwendung des Wortes 'Methode'
sicherstellen, dass hiermit keine globalen Funktionen gemeint sind. Ich
hatte den Eindruck, dass in Java diese Unterscheidungen jedoch
üblicherweise nicht getroffen werden (bzw. nicht sinnvoll sind, da es
keine globalen Funktionen gibt). Spielen diese Unterscheidungen in
diesem Zusammenhang eine Rolle, oder was übersehe ich hier?

Gruß,
Ralf
Stefan Ram
2007-05-27 01:16:27 UTC
Permalink
Post by Jochen Theodorou
da muss ich nochmal kurz einhacken. main ist eine Methode,
keine Funktion
Spielen diese Unterscheidungen in diesem Zusammenhang eine
Rolle, oder was übersehe ich hier?
Der Text »main« ist ein »(simple) method name«. Er gibt eine
»method« an, wenn aus dem Zusammenhang hervorgeht, welche
Methode gemeint ist. (Es könnte beispielweise »Alpha.main« und
»Beta.main« oder »main(String[])« und »main(int)« geben.)

Ob eine »method« eine »Methode« ist, sagt die
Sprachspezifikation nicht, weil sie kein deutsches Vokabular
festlegt. Daher kommt darin auch das Wort »Funktion« nicht
vor. Das Wort »function« findet sich gelegentlich in der JLS3,
aber nicht als Synonym für »method«.

Siehe:

http://java.sun.com/docs/books/jls/
Ralf Callenberg
2007-05-27 01:54:52 UTC
Permalink
Post by Stefan Ram
Post by Jochen Theodorou
da muss ich nochmal kurz einhacken. main ist eine Methode,
keine Funktion
Spielen diese Unterscheidungen in diesem Zusammenhang eine
Rolle, oder was übersehe ich hier?
Der Text »main« ist ein »(simple) method name«. Er gibt eine
»method« an, wenn aus dem Zusammenhang hervorgeht, welche
Methode gemeint ist. (Es könnte beispielweise »Alpha.main« und
»Beta.main« oder »main(String[])« und »main(int)« geben.)
Gut, und wie bringe ich das jetzt in Zusammenhang mit der von Jochen
getroffenen (oben teilweise bereits zitierten) Aussage "main ist eine
Methode, keine Funktion und eine Methode gehört normalerweise nie zu
einem bestimmten Thread."?
Post by Stefan Ram
Ob eine »method« eine »Methode« ist, sagt die
Sprachspezifikation nicht, weil sie kein deutsches Vokabular
festlegt.
Ja, aber üblicherweise wird 'method' eins zu eins mit 'Methode'
übersetzt und 'function' mit 'Funktion'. Ich bin eigentlich nicht davon
ausgegangen, dass eine Unterscheidung im Deutschen anders aussieht als
im Englischen.
Post by Stefan Ram
Das Wort »function« findet sich gelegentlich in der JLS3,
aber nicht als Synonym für »method«.
Technisch Publikationen versuchen einen einheitlichen Sprachgebrauch -
sofern es synonyme Wörter gibt, entscheidet man sich konsequent für eines.

Tut mir leid, aber auch nach Deinem Hinweis bin ich jetzt keinen Deut
schlauer hinsichtlich meiner Frage.

Gruß,
Ralf
Stefan Ram
2007-05-27 04:08:31 UTC
Permalink
Post by Ralf Callenberg
Gut, und wie bringe ich das jetzt in Zusammenhang mit der von Jochen
getroffenen (oben teilweise bereits zitierten) Aussage "main ist eine
Methode, keine Funktion und eine Methode gehört normalerweise nie zu
einem bestimmten Thread."?
Die JLS kennt eben »methods« als Teil des Sprachmodells, nicht
aber »functions«. Nachdem diese Terminologie akzeptiert ist,
kann man dann über »methods« und »threads« sprachen.
Michael Paap
2007-05-27 06:40:43 UTC
Permalink
Post by Ralf Callenberg
Technisch Publikationen versuchen einen einheitlichen Sprachgebrauch -
sofern es synonyme Wörter gibt, entscheidet man sich konsequent für eines.
Wenn es sich bei der Publikation um eine Spezifikation handelt, spricht
einiges dafür, sich bei fachlichen Gesprächen über den Gegenstand der
Spezifikation eben der spezifizierten Begriffe zu bedienen.

Gruß,
Michael
Marcus Woletz
2007-05-27 07:53:52 UTC
Permalink
Hallo Ralf,

Ralf Callenberg schrieb:
[...]
Post by Ralf Callenberg
Auch auf die Gefahr hin, mich als ignorant zu zeigen: ich bin all die
Jahre davon ausgegangen, dass 'Methode' und 'Funktion' im Wesentlichen
nur verschiedene Worte für das Gleiche sind. Es gibt Leute, die
unterscheiden diese Begriffe je nachdem ob etwas einen Wert zurückgibt
(Funktion) oder nicht. In noch strengerer Auslegung dieser
Unterscheidung darf eine Funktion keine sonstigen Nebeneffekte haben. In
C/C++ möchten manche mit der Verwendung des Wortes 'Methode'
sicherstellen, dass hiermit keine globalen Funktionen gemeint sind. Ich
hatte den Eindruck, dass in Java diese Unterscheidungen jedoch
üblicherweise nicht getroffen werden (bzw. nicht sinnvoll sind, da es
keine globalen Funktionen gibt). Spielen diese Unterscheidungen in
diesem Zusammenhang eine Rolle, oder was übersehe ich hier?
Gruß,
Ralf
Prinzipiell bezeichnet "Funktion" und "Methode" in diesem Zusammenhang
IMO dasselbe Konstrukt. In C++ wird das, was in Java mit "Methode"
bezeichnet wird, als "Elementfunktion" bezeichnet (jedenfalls war das
so, als ich noch groß im C++-Geschäft war ;-)

In Pascal z.B. werden Funktionen, die keiner Klasse angehören und keinen
Rückgabewert besitzen, als Prozeduren bezeichnet. Im OO-Pascal werden
Prozeduren und Funktionen, die zu Klassen gehören, auch als Methoden
bezeichnet.

In rein prozeduralen Umgebungen würde ich eher von Funktion sprechen, in
objektorientierten von "Methoden".

Es gibt öfter Begriffsverwirrungen: z.B. gibt es in C++ Deklarationen
und Definitionen. Eine Deklaration führt einen Bezeichner ein, eine
Definition reserviert Speicherplatz. In Java wird seltsamerweise das,
was in C++ als Definition bezeichnet wird, als Deklaration bezeichnet,
denn Deklarationen gibt es in Java IMHO eigentlich nicht.

Ich hoffe, ich werde jetzt nicht geflamed ;-)

ciao

Marcus
Jochen Theodorou
2007-05-27 14:11:01 UTC
Permalink
Post by Ralf Callenberg
da muss ich nochmal kurz einhacken. main ist eine Methode, keine Funktion
Auch auf die Gefahr hin, mich als ignorant zu zeigen: ich bin all die
Jahre davon ausgegangen, dass 'Methode' und 'Funktion' im Wesentlichen
nur verschiedene Worte für das Gleiche sind. Es gibt Leute, die
unterscheiden diese Begriffe je nachdem ob etwas einen Wert zurückgibt
(Funktion) oder nicht. In noch strengerer Auslegung dieser
Unterscheidung darf eine Funktion keine sonstigen Nebeneffekte haben.
Ich mache es mir da relativ einfach.. es gibt keine Funktionen in Java
;) Ich versuche die der Programmiersprache angepasste Semantik der
Wörter zu verwenden und demnach ist alles in Java eine Methode, auch
wenn es in einer anderen Sprache eine Funktion wäre.

Aber vorallem benutze ich das so, weil ich mit mehr als einer Sprache zu
tun habe.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Ralf Ullrich
2007-05-23 19:59:50 UTC
Permalink
Post by Ernst Baumann
class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int i,j;
private Graphics myg;
private int sx;
private int sy;
public Diagramm(int xpAnz, int ypAnz){
i=0;
this.xpAnz=xpAnz;
this.ypAnz=ypAnz;
}
public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}
}
}
--------------------------------------------------------------------------
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
- keine Logik machen.
Allerdings habe ich über dieses Verbot in der Doku über paint nichts
gelesen.
http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Wo steht dieses Verbot ?
Welchen Sinn hat dieses Verbot ?
Da du das "Verbot" wohl aus dem ableitest, was ich dir damals geschrieben
habe, antowrte ich mal schnell ohne erst die restlichen Antworten zu lesen:

In diesem Fall, die oben nochmal wiederholten Code ist 'i' eine lokale
Variable in paintComponent, und natürlich darfst du darüber innerhalb
paintComponent auch eine Schleife laufen lassen.

In dem früheren Code, aus dem du das "Verbot" fehlinterpretiert hast, war
'i' ein Attribute (=field member) des Objekts, also eine Zustandsvariable
des Objekts. Darüber darfst du in paintComponent keine Schleife laufen
lassen, weil du den _Zustand_ des Objekts innerhalb von paintComponent
(bzw. paint() insgesamt) nicht verändern darfst.
Post by Ernst Baumann
2) Komischer Programmablauf
Nicht bei jedem Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
Weil das was paintComponent macht und das was du auf dem Bildschirm siehst
nicht synchron sein muss. In diesem Fall hat paintComponent wohl erst in
einen Buffer gemalt, der dann via Double-Buffering nachträglich komplett
auf den Schirm gebracht wurde.

Das sleep und die Schleife sollen Programmlogik für eine Animation sein
(nämlich die Kreise sichtbar nacheinander zeichnen). Und sowas hat
tatsächlich in paint*() nichts verloren.

So, jetzt hoffe ich die anderen Antworten haben dir das auch schon so oder
ähnlich gesagt.

cu
kilian heckrodt
2007-05-24 20:21:58 UTC
Permalink
Post by Ernst Baumann
Hallo allerseits,
---------------------------------------------------------------------------------------
package de;
import java.awt.*;
import javax.swing.*;
public class MainVerzoegertZeichnen3{
public static void main(String[] args){
int k;
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);
diagramm.repaint();
}
}
class Diagramm extends JPanel{
private int xpAnz;
private int ypAnz;
private int i,j;
private Graphics myg;
private int sx;
private int sy;
public Diagramm(int xpAnz, int ypAnz){
i=0;
this.xpAnz=xpAnz;
this.ypAnz=ypAnz;
}
public void paintComponent(Graphics g){
int i=0;
int k=0;
g.setColor(Color.red);
for(k=0;k<10;k++){
g.drawOval(i , i, 20, 20);
i=i+20;
try{
Thread.sleep(300);
}
catch (Exception e){
}
System.out.println("i="+i);
}
}
}
--------------------------------------------------------------------------
1) Verbot
Mir wurde schon erklärt, dass dies ein schlechtes Programm ist, weil
- keine Zustände (wie oben das i=i+20) ändern und
- keine Logik machen.
Allerdings habe ich über dieses Verbot in der Doku über paint nichts
gelesen.
http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Wo steht dieses Verbot ?
Welchen Sinn hat dieses Verbot ?
2) Komischer Programmablauf
Nicht bei _jedem_ Durchgang durch die Schleife wird sleep(300), also
300 ms gewartet.
Sondern es wird zuerst auf einmal, also auf einen Schlag insgesamt 10
* 300 ms gewartet und dann auf einmal die 10 Kreise auf dem Bildschirm
ausgegeben.
Warum? Ich kann mir das nicht erklären.
mfg
Ernst
Es gibt kein generelles Verbot fuer Schleifen und Programmlogik
innerhalb von paintComponent(), allerdings gibt es folgende Richtlinie:

Aller Code innerhalb von paintComponet() muss schnell ausführbar
sein/eine gerine Laufzeit haben, d.h. es sollte dort keine
zeitaufwendigen Berechnungen oder gar ein Thread.sleep stehen.

Der Grund dafuer liegt im Design von Swing/AWT, bei dem ein Systemthread
paintComponent Aufrufe sequentiell abarbeitet und dieser Thread "hängt"
dann eben bis ein paintCompoment()-Aufruf beendent ist (und damit hängen
auch andere Teile des System, die von diesem Thread verwaltet werden,
d.h. normalerweise haengt die ganze GUI).

Langwierige oder endlos laufende Grafokpoperation (wie z.B.) bei
Simulationen lässt man am besten in ein BufferedImage zeichnen (und
eventuell auch dirch einen eigenen thread) und in paintComponent() wird
lediglich mit drawImage immer das aktuelle BufferedImage gezeichnet.

Man kann auch ohne BufferedImage ausserhalb von paintComponent direkt
mit dem Graphics-Objekt der Componet zeichnen, allerdings gehen diese
Veränderung bei einen refresh wieder verloren.

Auch zu beachten ausserhalb von paintComponent() oder ähnlichen Methoden
sollte man das graphics-Objekt auch wieder freigeben, also

Graphics g = mycomponent.getGraphics();

.....


g.dispose();
Ernst Baumann
2007-05-25 19:24:36 UTC
Permalink
1)
Post by kilian heckrodt
Langwierige oder endlos laufende Grafokpoperation (wie z.B.) bei
Simulationen lässt man am besten in ein BufferedImage zeichnen (und
eventuell auch dirch einen eigenen thread) und in paintComponent() wird
lediglich mit drawImage immer das aktuelle BufferedImage gezeichnet.
Man kann auch ohne BufferedImage ausserhalb von paintComponent direkt
mit dem Graphics-Objekt der Componet zeichnen, allerdings gehen diese
Veränderung bei einen refresh wieder verloren.
a)
Warum ist das so (was meinst du mit refresh?)
b)
Beim erstmaligen Zeichnen werden alle Kreise gezeichnet.
Wenn allerdings danach das Fenster vergrößert/verkleinert wird, wird
nur noch der letzte Kreis gezeichnet.
Warum ist das so?

2)
Post by kilian heckrodt
Auch zu beachten ausserhalb von paintComponent() oder ähnlichen Methoden
sollte man das graphics-Objekt auch wieder freigeben, also
Graphics g = mycomponent.getGraphics();
.....
g.dispose();
macht dies nicht die javaeigene Müllabfuhr, der Garbagecollector?

mfg
Ernst
Marcus Woletz
2007-05-25 20:20:21 UTC
Permalink
Hallo Ernst,

Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
Post by kilian heckrodt
Man kann auch ohne BufferedImage ausserhalb von paintComponent direkt
mit dem Graphics-Objekt der Componet zeichnen, allerdings gehen diese
Veränderung bei einen refresh wieder verloren.
a)
Warum ist das so (was meinst du mit refresh?)
also nachmal: Du kannst einen eigenen Double Buffer mit
JComponent.createImage() erzeugen. In diesen zeichnest Du nun das Zeug,
was im Fenster erscheinen soll. in paintComponent() kopierst Du dann nur
den Pufferinhalt ins Fenster, falls bestimmte Voraussetzungen erfüllt
sind (Fenstergröße nicht verändert etc.) Sind diese Voraussetzungen
nicht erfüllt, musst Du dann entsprechend einen neuen Puffer erzeugen,
füllen und dann wieder ins Fenster kopieren. So jedenfalls mache ich das
prinzipiell. Ich hoffe, das ist OK so.
Post by Ernst Baumann
b)
Beim erstmaligen Zeichnen werden alle Kreise gezeichnet.
Wenn allerdings danach das Fenster vergrößert/verkleinert wird, wird
nur noch der letzte Kreis gezeichnet.
Warum ist das so?
Welcher von Deinen geposteten Quelltexten zeigt dieses Verhalten?
Post by Ernst Baumann
2)
Post by kilian heckrodt
Auch zu beachten ausserhalb von paintComponent() oder ähnlichen Methoden
sollte man das graphics-Objekt auch wieder freigeben, also
Graphics g = mycomponent.getGraphics();
.....
g.dispose();
macht dies nicht die javaeigene Müllabfuhr, der Garbagecollector?
Liegt Dir die API-Doku vor? Schau mal bitte in die Beschreibung der
Methode Graphics.dispose(). Dort ist alles beschrieben.
Post by Ernst Baumann
mfg
Ernst
ciao

Marcus
Ernst Baumann
2007-05-27 11:30:55 UTC
Permalink
Post by Marcus Woletz
Welcher von Deinen geposteten Quelltexten zeigt dieses Verhalten?
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1{
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){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}

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;
}
}
}


mfg
Ernst
Marcus Woletz
2007-05-27 17:11:09 UTC
Permalink
Hallo Ernst,

OK, also damit wird die Anfrage in meinem anderen Posting nichtig.

Ernst Baumann schrieb:
[...]
Post by Ernst Baumann
f.setVisible(true);
--> Fenster wird sichtbar
Post by Ernst Baumann
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}
--> Du zeichnest *einmal* die komplette Sequenz, die auch angezeigt wird.


[...]
Post by Ernst Baumann
public void paintComponent(Graphics g){
synchronized(this){
g.setColor(Color.red);
g.drawOval(i , i, 20, 20);
}
System.out.println("i in paintComponent="+i);
}
nach jeder Änderung deiner Komponente wird obiges "paintComponent()"
aufgerufen. Und was macht paintComponent()? Zeichnet einen einzige
Kreis, und zwar an der letzten Position, die die Schleife in main()
gesetzt hat. Das hat mit Deiner Schleife in main() aber nichts zu tun.
Aus dieser wurde zwar auch 10x paintComponent() aufgerufen, deshalb
wurde die Sequenz auch einmal, nämlich beim Programmstart, durchlaufen.
Wieso sollte sich die Sequenz mit den sleep()-Aufrufen aus main() wie
von Geisterhand wiederholen? Und Du bekommst nur einen Kreis
dargestellt, da der von Swing verwaltete Zwischenpuffer bei jeder
Änderung des Fensters ungültig wird und von Deiner paintComponent()
wieder gefüllt werden will. Und dort zeichnest Du halt nur einen Kreis.
Der Zwischenpuffer hat vor allem die Aufgabe, aufwendige
Zeichenoperationen zuerst komplett im Zwischenspeicher durchzuführen und
diesen dann "auf einen Schlag" im Fenster darzustellen.

[...]
Post by Ernst Baumann
mfg
Ernst
ciao

Marcus
Ernst Baumann
2007-05-31 13:56:54 UTC
Permalink
Post by Marcus Woletz
--> Fenster wird sichtbar
Post by Ernst Baumann
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}
--> Du zeichnest *einmal* die komplette Sequenz, die auch angezeigt wird.
[...]
Post by Ernst Baumann
public void paintComponent(Graphics g){
synchronized(this){
g.setColor(Color.red);
g.drawOval(i , i, 20, 20);
}
System.out.println("i in paintComponent="+i);
}
nach jeder Änderung deiner Komponente wird obiges "paintComponent()"
aufgerufen. Und was macht paintComponent()? Zeichnet einen einzige
Kreis, und zwar an der letzten Position, die die Schleife in main()
gesetzt hat.
Du hast recht.
Ich hatte einen Denkfehler.

1)
Aber trotzdem habe ich noch ein Problem.
Ganz oben bei
Post by Marcus Woletz
--> Fenster wird sichtbar
wird nach jedem Durchgang durch die Schleife ein neuer Kreis
gezeichnet und die alten Kreise stehen gelassen.
Darf man in Swing davon ausgehen, dass bei jedem Zeichnen, z.B. mit
paintComponent(), der alte Bildschirminhalt _nicht_gelöscht wird,
sondern nur zusätzlich das, was das paintComponent() macht
_zusätzlich_ auf den Bildschirm bringt?

2)
Angenommen nach dem ersten Neuzeichnen der 10 Kreise vergrößern oder
verkleinern der Anwender das Fenster.
Dann zeichnet die JVM einen Kreis (den letzten). Das ist jetzt klar.
Aber warum wird vorher der Bildschirm gelöscht.
Beim Neuzeichnen (der 10 Kreise) wird doch auch nicht nach jedem
Zeichnen eines Kreises vorher der Bildschirm gelöscht (siehe 1)).

mfg
Rrnst
kilian heckrodt
2007-05-31 23:58:03 UTC
Permalink
Post by Ernst Baumann
Post by Marcus Woletz
--> Fenster wird sichtbar
Post by Ernst Baumann
while(k<10){
System.out.println("i in main="+i);
diagramm.setI(i);
diagramm.repaint();
i=i+20;
k=k+1;
try{
Thread.sleep(500);
}
catch(Exception e){}
}
}
}
--> Du zeichnest *einmal* die komplette Sequenz, die auch angezeigt wird.
[...]
Post by Ernst Baumann
public void paintComponent(Graphics g){
synchronized(this){
g.setColor(Color.red);
g.drawOval(i , i, 20, 20);
}
System.out.println("i in paintComponent="+i);
}
nach jeder Änderung deiner Komponente wird obiges "paintComponent()"
aufgerufen. Und was macht paintComponent()? Zeichnet einen einzige
Kreis, und zwar an der letzten Position, die die Schleife in main()
gesetzt hat.
Du hast recht.
Ich hatte einen Denkfehler.
1)
Aber trotzdem habe ich noch ein Problem.
Ganz oben bei
Post by Marcus Woletz
--> Fenster wird sichtbar
wird nach jedem Durchgang durch die Schleife ein neuer Kreis
gezeichnet und die alten Kreise stehen gelassen.
Darf man in Swing davon ausgehen, dass bei jedem Zeichnen, z.B. mit
paintComponent(), der alte Bildschirminhalt _nicht_gelöscht wird,
sondern nur zusätzlich das, was das paintComponent() macht
_zusätzlich_ auf den Bildschirm bringt?
Das haengt vom genauen Kontext ab, eine detaillierte Beschreibung der
Ablaeufe findest du hier:

http://java.sun.com/products/jfc/tsc/articles/painting/
Post by Ernst Baumann
2)
Angenommen nach dem ersten Neuzeichnen der 10 Kreise vergrößern oder
verkleinern der Anwender das Fenster.
Dann zeichnet die JVM einen Kreis (den letzten). Das ist jetzt klar.
Aber warum wird vorher der Bildschirm gelöscht.
Beim Neuzeichnen (der 10 Kreise) wird doch auch nicht nach jedem
Zeichnen eines Kreises vorher der Bildschirm gelöscht (siehe 1)).
siehe oben, je nach der genauen Verwendung bzw. dem Kontext deines AWT
oder Swing wird der Hintergrund eventuell geloescht bevor deine Befehle
in paint oder paintComponent ausgefuehrt werden. Da Zeichnen eines
einzelnen Kreises innnerhalb deiner dieser beiden Methoden loescht
natuerlich keinen Hintergrund.
Den genauen Ablauf findest du im obigen Link.
Uebringens waehrend man bei AWT Objekten meist paint und eventuell
update ueberschreibt, sollte man bei Swing stattdessen das neue
paintComponent ueberschreiben und _nicht_ paint.
Post by Ernst Baumann
mfg
Rrnst
kilian heckrodt
2007-05-26 10:15:10 UTC
Permalink
Post by Ernst Baumann
1)
Post by kilian heckrodt
Langwierige oder endlos laufende Grafokpoperation (wie z.B.) bei
Simulationen lässt man am besten in ein BufferedImage zeichnen (und
eventuell auch dirch einen eigenen thread) und in paintComponent() wird
lediglich mit drawImage immer das aktuelle BufferedImage gezeichnet.
Man kann auch ohne BufferedImage ausserhalb von paintComponent direkt
mit dem Graphics-Objekt der Componet zeichnen, allerdings gehen diese
Veränderung bei einen refresh wieder verloren.
a)
Warum ist das so (was meinst du mit refresh?)
Refresh ist irgendein Ereignis, das das Neuzeichnen der Component
ausloest. Diese koennen vom System (z.B. wenn die Component von
einen Fenster ueberdeckt war) oder auch von Benutzter selbst (z.B.
repaint()) ausgeloest werden.
Sprich wenn du direkt (ohne Buffer) zeichnest und deine Anwendung z.B.
in den Hintergrund legst und dann wieder hervorholst, ist der
gezeichnete Inhalt weg.
Post by Ernst Baumann
b)
Beim erstmaligen Zeichnen werden alle Kreise gezeichnet.
Wenn allerdings danach das Fenster vergrößert/verkleinert wird, wird
nur noch der letzte Kreis gezeichnet.
Warum ist das so?
Auf den ersten Blick sehe ich nicht warum , ich habe dein Programm
allerdings nicht getestet oder naeher untersucht.
Post by Ernst Baumann
2)
Post by kilian heckrodt
Auch zu beachten ausserhalb von paintComponent() oder ähnlichen Methoden
sollte man das graphics-Objekt auch wieder freigeben, also
Graphics g = mycomponent.getGraphics();
.....
g.dispose();
macht dies nicht die javaeigene Müllabfuhr, der Garbagecollector?
Nein der Garbagecollector behandelt nur reine Speicherresourcen der JVM,
d.h. er gibt den Speicher den g in der JVM belegt wieder frei.
Andere Systemresourcen wie z.B. geoeffnete Dateien oder vom
Betriebsystem zur Verfuegung gestellte Zeichenflaechen muss mam selbst
verwalten bzw. wieder freigeben.

g.dispose() gibt nicht den Speicher des Graphic-Objektes in der JVM
frei, sondern nur die Betriebssystemresource (Zeichenflaeche).
Post by Ernst Baumann
mfg
Ernst
Lesen Sie weiter auf narkive:
Loading...