Swing mal wieder EDT -> nichts passiert

Status
Nicht offen für weitere Antworten.

hdi

Top Contributor
Hey, folgendes Szenario:

Ich habe einen JDialog, der als Komponente ein JPanel hält. Ausserdem hat er einen MouseListener. In der mouseExited() wird eine Methode rollOut() aufgerufen:

Java:
private void rollOut() {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				removeAll();
				add(new JLabel("Good bye!"));
				validate();
			}
		});
}

Allerdings passiert das nicht. Wenn ich per sysout prüfe, sehe ich, dass der Code in der run-Methode nicht ausgeführt wird. (die rollOut() selbst wird natürlich schon betreten). Der EDT hat sonst eig. grad nix zu tun. Ich kann mir das Verhalten nicht erklären, wieso hat der EDT grad keine Lust? Ich habs auch per invokeAndWait versucht, da ich mir dachte naja vllt macht er es erst sehr viel später. Aber ne, da krieg ich nur ne Exception.

...ich hab spontan noch eine kleine Verständnisfrage: Wieso kann ich diese Methoden ohne das Dialog-Objekt aufrufen? ist zB removeAll() nicht this.removeAll() ? Ich dachte wenn man kein Objekt dazu schreibt ist es immer this. Aber this müsste doch hier das Runnable sein? Vllt hab ich grad n Blackout aber das verstehe ich nicht. Hätte jetzt eig. einen Compile-Error erwartet, weil ein Runnable nicht so Methoden wie removeAll() hat.

Könnt ihr mir bitte helfen? Danke
 
Zuletzt bearbeitet:
Hey, folgendes Szenario:

Ich habe einen JDialog, der als Komponente ein JPanel hält. Ausserdem hat er einen MouseListener. In der mouseExited() wird eine Methode rollOut() aufgerufen:

Java:
private void rollOut() {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				removeAll();
				add(new JLabel("Good bye!"));
				validate();
			}
		});
}

Allerdings passiert das nicht. Wenn ich per sysout prüfe, sehe ich, dass der Code in der run-Methode nicht ausgeführt wird. (die rollOut() selbst wird natürlich schon betreten). Der EDT hat sonst eig. grad nix zu tun. Ich kann mir das Verhalten nicht erklären, wieso hat der EDT grad keine Lust? Ich habs auch per invokeAndWait versucht, da ich mir dachte naja vllt macht er es erst sehr viel später. Aber ne, da krieg ich nur ne Exception.
Welcher von den ganzen EDT's denn? Weißt du, wohin du diese Runnables mit diesem "EventQueue" schickst? Ich weiß es jedenfalls nicht. :bahnhof: Was dieses EventQueue ist weiß ich auch nicht. Ich würde es jedenfalls zunächst mal mit SwingUtilities.invokeLater() versuchen 😉

...ich hab spontan noch eine kleine Verständnisfrage: Wieso kann ich diese Methoden ohne das Dialog-Objekt aufrufen? ist zB removeAll() nicht this.removeAll() ? Ich dachte wenn man kein Objekt dazu schreibt ist es immer this. Aber this müsste doch hier das Runnable sein? Vllt hab ich grad n Blackout aber das verstehe ich nicht. Hätte jetzt eig. einen Compile-Error erwartet, weil ein Runnable nicht so Methoden wie removeAll() hat.
Bei den nicht-statischen inneren Klassen (..und den ganzen unterarten davon, anonyme innere, private usw.) kannst du direkt auf methoden der umgebenden Klasse(n) zugreifen [in diesem Fall wird Runnable vom Listener, und dieser wiederum von JDialog umgeben, also landen diese Funktionsaufrufe letztendlich beim JDialog, wenn nichts dazwischenkommt, was genauso heißt]
Wenn du es genau spezifizieren willst, an wen von den ganzen geschachtelten Klassen sich der methodenaufruf wendet, dann kannst du den klassennamen vor dem this mit angeben:
Java:
class X{
   public void method(){};
   class Y{
      void method(){};
      public void doSomeStuff(){
          X.this.method(); //ruft method bei der umgebenden klasse auf. Überflüssig, wenn name eindeutig
      }
   }
}
alles klar? 😉
 
Welcher von den ganzen EDT's denn? Weißt du, wohin du diese Runnables mit diesem "EventQueue" schickst? Ich weiß es jedenfalls nicht. :bahnhof: Was dieses EventQueue ist weiß ich auch nicht. Ich würde es jedenfalls zunächst mal mit SwingUtilities.invokeLater() versuchen 😉
Es kann zwar mehr als ein EDT geben (wenn modale Dialoge im Spiel sind), aber es gibt eigentlich nur eine EventQueue. Man kann mit invokeLater nicht die falsche Queue treffen.
SwingUtilities.invokeLater leitet direkt an EventQueue.invokeLater weiter.
 
Ja genau hab's grad probiert, also macht kein Unterschied wenn ich SwingUtilities benutze. Danke für die Erklärung der inneren Klassen btw 😉
 
Ich bin nun ein Stückchen weitergekommen mit meinem Problem. Und zwar wird ja die mouseExited() auf dem EDT ausgeführt, ich meine immerhin ist ja genau der EDT es, der dieses Event feuert. D.h. wenn ich nun einfach darin die rollout() aufrufe, blockiert es sich.
Daher habe ich den rollOut()-Aufruf in einen eigenen Thread gelegt. Ich dachte, damit müsste das Problem ja eig. erledigt sein. Es wird ein neuer Thread gestartet und dann returned der EDT auch schon wieder aus der mouseExited(), womit er wieder "frei" ist und nix zu tun hat:

Java:
			public void mouseExited(MouseEvent e) {
				super.mouseExited(e);
				new Thread() {
					@Override
					public void run() {
						rollOut();
					}
				}.start();
			}

Es gibt leider weiterhin ein Problem, diesmal blockiert sich zwar nichts mehr, aber meine Events werden nicht so abgearbeitet, wie ich es wollte. Hier nochmal die rollOut() Methode:

Java:
	private void rollOut() {
		// Replace main content with a good bye message
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				removeAll();
				add(new JLabel("Good bye!"));
				validate();
			}
		});
		rollEffectTo(0); 
	}

die "rollEffecTo()" Methode legt wiederum in einer Schleife Events in den EDT per invokeLater. (Resize vom Dialog). Das funktioniert auch super, allerdings wird erst nach dem kompletten Durchlauf der rollEffectTo() Methode die run() Methode gestartet, die ich ja in der obigen Methode vor dem Aufruf von rollEffectTo() aufrufe.

...dann ist das Fenster aber schon geschlossen (damit meine ich nur: höhe == 0). Natürlich bringt es mir nun nix mehr, da irgendwelchen Content auszutauschen. Ich verstehe nicht, warum das Event für das Austauschen erst nach den Events, die rollEffectTo() auf den EDT legt, ausgeführt wird. Der EDT müsste doch eig. zu dem Zeitpunkt, wo ich das removeAll() usw drauf lege, nix zu tun haben. (Ich mach sonst nix, und es passiert auch nix grafisches oder sonst was).

Und dann dachte ich mir: Okay, machste invokeAndWait, damit die rollOut() sicher erst danach ausgeführt wird. Und hier check ich nun gar nix mehr, denn es ändert nichts daran. Es kommt kein Fehler und nix, aber auch dann wird der Code erst nach allen Aufrufen von rollEffectTo() aufgerufen.

Jetzt bin ich echt ratlos :bahnhof: Obwohl ich per SwingUtilites.invokeAndWait etwas drauf lege, werden andere Events, die genauso drauf gelegt werden, vorher abgearbeitet. Ich dachte der returned da gar nicht erst, bis das gemacht wurde?

Also ich seh das ganze so:
1) Der EDT ruft mouseExited() auf, erstellt einen neuen Thread, startet ihn und verlässt die Methode.
2) Damit ist der EDT nun wieder frei (er hat nix zu tun)
3) Gleichzeitig wird per invokeLater ein Request für den EDT gelegt, dass er doch bitte Inhalt austauschen soll. Das passiert von dem Thread aus, der in der mouseExited gestartet wurde.
4) Noch immer der gleiche Thread ruft nun die rollEffectTo() Methode auf.
5) In dieser Methode wird ein paar mal etwas per invokeLater auf den EDT gelegt.
6) Der Thread verlässt die rollEffectTo-Methode, womit seine run-Methode endet und er sich beendet

Stimmt das denn nicht so? Ich verstehe das Verhalten des EDT einfach nicht. Erst recht nicht das mit invokeAndWait. Wie kann er andere Events abarbeiten, wenn ich ihm davor was per invokeAndWait gebe. Der is doch doof 😛ueh:

edit: Nochmal damit ihr das 100% nachvollziehen könnt auch die rollEffectTo Methode:

Java:
	private void rollEffectTo(int targetHeight) {
		// determine whether to expand or collapse
		int sign = (targetHeight > getHeight() ? 1 : -1);
		// do roll effect
		int height = getHeight();
		while (height != targetHeight) {
			height += 5 * sign;
			final int heightNow = height;
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					setSize(Cfg.MAIN_WIDTH, heightNow);
				}
			});
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// Many Runnable objects have been created. A GC may be good
		System.gc();
	}
 
Zuletzt bearbeitet:
Hm - compilierbarer Code hilft halt immer. Ansonsten müßtest du nochmal genau sagen, wie der Ablauf sein sollte: Man bewegt die Maus raus, dann soll die Component animiert verkleinert werden, und am Ende soll "good bye" dastehen?

BTW: System.gc() weg - das sollte man nicht aufrufen (müssen). Der GC weiß schon, was er wann zu tun hat.
 
Hm - compilierbarer Code hilft halt immer
Naja, ich hab ja die 3 Metohden vollständig gepostet, mehr läuft da nicht ab. Die Methoden liegen in einem JDialog und der hat n JPanel drauf, das war's. Ich dachte man hat das verstanden. Es soll so ablaufen, wie im Code zu sehen:
Erst soll der Inhalt ausgetauscht werden, dann der rollout-effekt. Aber es passiert andersrum, was natürlich nix bringt. Und es passiert auch andersrum mit invokeAndWait, was ich extrem erstaunlich finde nachdem mir hier neulich jmd gesagt hat dass diese Methode den Thread blockiert bis die EDT das abgearbeitet hat. Was sie nicht tut, hab das per sysout getestet.
Ka...was soll ich noch posten? Das ist wie gesagt der ganze Code, liegt nur in einem JDialog, und sonst passiert in meinem Programm nichts

edit: Aber ich bastel gleich was

BTW: System.gc() weg
Das ist schon okay denke ich?! Habs extra getestet als ich es eingebaut hab. Ich weiss dass es keine Garantie zur Auführung ist, und auch keine dass die Objekte weggeworfen werden, aber er tut es halt doch eig. immer. So ähnlich wie es bei repaint() usw ja auch ist. Sysout hat mir gezeigt: Er räumt immer einiges an Speicher frei (und verbraucht innerhalb des loops auch einiges wegen den Objekten).
Ist ja nicht so dass ich das nach jeder Methode aufrufe, aber wenn in einer Schleife zig neue Instanzen erstellt und gleich unerreichabr gemacht werden, finde ich es okay das mal aufzurufen. Schaden tut es soweit ich weiss auf jeden Fall nicht.
Wobei ich das wieder aus dem Study Guide hab, der ja wie ich inzwischen weiss gerne mal ein paar Dinge verheimlicht. Also wenn du ein gutes Argument gegen die Nutzung kennst -> gerne
 
Zuletzt bearbeitet:
So hier ein ausführbares Bsp. Die Methode für den Panel-Exchange wird doch öfters ausgeführt (auch am Anfang) aber die Effekte kommen nie zum tragen.

Einfach warten bis sich das Fenster aufgebaut hat, dann mit der Maus rein und raus, und zusehen 😉

Java:
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class EDTTest extends JDialog {
	public static void main(String[] x) {
		EDTTest t = new EDTTest();
		t.setVisible(true);
		t.rollIn();
	}

	public EDTTest() {
		JPanel p = new JPanel();
		p.setBackground(Color.green);
		this.add(p);
		
		setSize(200,0);

		this.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseExited(MouseEvent arg0) {
				super.mouseExited(arg0);
				new Thread() {
					@Override
					public void run() {
						rollOut();
						System.exit(0);
					}
				}.start();
			}
		});
	}

	public void rollIn() {
		rollEffectTo(400);
	}

	private void rollOut() {
		// exchange content
		
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					System.out.println("Executing run() method for panel exchange.");
					removeAll();
					JPanel p2 = new JPanel();
					p2.setBackground(Color.red);
					add(p2);
					validate();
					pack();
					System.out.println("panel exchange method done.");
				}
			});

		// THEN roll out
		rollEffectTo(0);
	}

	private void rollEffectTo(int targetHeight) {
		System.out.println("Entering rollout effect.");
		// determine whether to expand or collapse
		int sign = (targetHeight > getHeight() ? 1 : -1);
		// do roll effect
		int height = getHeight();
		while (height != targetHeight) {
			height += 1 * sign;
			final int heightNow = height;
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					setSize(200, heightNow);
				}
			});
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// Many Runnable objects have been created. A GC may be good
		System.gc();
		System.out.println("rollout effect done.");
	}
}
 
Okay, wenn man beim start gleich mit der maus drüberfährt, scheint es gleichzeitig länger & kürzer werden zu wollen, und ruckelt wie bekloppt 😉
Aber das ist nicht ganz das problem? Was sollte denn da zu sehen sein?
 
Bei mouseExited() sollte erst das Panel gegen ein rotes ausgetauscht werden, dann sollte es zuklappen. Der Code zum Panel-Austausch wird wie gesagt, wie man an den sysouts sieht, meistens auch vor dem Rest aufsgeführt. Aber nix passiert.
Mit invokeAndWait auch nicht. Das is das Problem 🙂

edit: Mit "rein und raus" meinte ich nicht dauernd rein und raus. Ich meinte, falls die Maus nicht drin ist -> rein, dann raus. Es geht einfach nur darum die mouseExited einmal aufzurufen und zuzusehen was passiert. Eig. sollte das Panel rot werden, das Fenster sich zusammenklappen, und das Programm sich beenden.

(ich frag mich ob dieser Post auftaucht wenn man nach "sick insertions" googlet 😉)
 
Zuletzt bearbeitet:
Da scheinen manche Sachen nicht automatisch ans ContentPane zu delegieren...? Müßte man mal genauer schauen... ansonsten vielleicht so
Code:
// Von http://www.java-forum.org/awt-swing-swt/81756-mal-wieder-event-dispatcher-thread-nichts-passiert.html#post508157

import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class EDTTest extends JDialog {
    public static void main(String[] x) {
        EDTTest t = new EDTTest();
        t.setVisible(true);
        t.rollIn();
    }

    public EDTTest() {
        JPanel p = new JPanel();
        p.setBackground(Color.green);
        this.getContentPane().add(p);

        setSize(200,0);

        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent arg0) {
                super.mouseExited(arg0);
                rollOut();
            }
        });
    }

    public void rollIn() {
        rollEffectTo(400);
    }

    private void rollOut() {
        // exchange content

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Executing run() method for panel exchange.");
                    getContentPane().removeAll();
                    JPanel p2 = new JPanel();
                    p2.setBackground(Color.red);
                    getContentPane().add(p2);
                    validate();
                    System.out.println("panel exchange method done.");

                    new Thread() {
                        @Override
                        public void run() {
                            // THEN roll out
                            rollEffectTo(0);
                            System.exit(0);
                        }
                    }.start();

                }
            });

    }

    private void rollEffectTo(int targetHeight) {
        System.out.println("Entering rollout effect.");
        // determine whether to expand or collapse
        int sign = (targetHeight > getHeight() ? 1 : -1);
        // do roll effect
        int height = getHeight();
        while (height != targetHeight) {
            height += 1 * sign;
            final int heightNow = height;
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    setSize(200, heightNow);
                }
            });
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // Many Runnable objects have been created. A GC may be good
        System.gc();
        System.out.println("rollout effect done.");
    }
}
 
Ahhhh :idea: removeAll() delegiert nicht, ja. Schon dreist dass sie add delegiert haben aber remove nicht. Naja ,tausend Dank! Jetzt funzt alles wie gewollt 🙂
 
Ahhhh :idea: removeAll() delegiert nicht, ja. Schon dreist dass sie add delegiert haben aber remove nicht.
getContentPane() bringt's nach wie vor :autsch: Schon dämlich sowas...
Jetzt funzt alles wie gewollt 🙂
dann fahr mal während des roll-effekt ein paar mal mit der maus rüber, vielleicht fällt noch was auf...

Hier ist meine Vorschlag nochmal, kA ob das besser oder schlechter ist, es tut's jedenfalls:
Java:
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException;
 
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
 
public class EDTTest extends JDialog {
    public static void main(String[] x) {
        final EDTTest t = new EDTTest();
        t.setVisible(true);
        
        SwingUtilities.invokeLater(new Runnable(){
        	@Override
        	public void run(){
        		new Thread(){
                	@Override
                	public void run(){
                		t.showRollEffect(400,600,400);
                	}
                }.start();
        	}
        });
    }

    public EDTTest() {
        JPanel p = new JPanel();
        p.setBackground(Color.GREEN);
        this.getContentPane().add(p);
        
        setSize(0,0);
 
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent arg0) {
                super.mouseExited(arg0);
                EDTTest.this.getContentPane().removeAll();
                JPanel p2 = new JPanel();
                p2.setBackground(Color.RED);
                EDTTest.this.getContentPane().add(p2);
                EDTTest.this.validate();
                
                //this thread shows the roll effect and terminates the execution of application 
                new Thread(){
                	@Override
                	public void run(){
                		EDTTest.this.showRollEffect(0,0,400);
						System.exit(0);
                	}
                }.start();
            }
        });
    }
    
    private void showRollEffect(final int width,final int height,final int millis){
		int dt=5; //wait 5 milliseconds between each animation step
		int numberOfSteps=millis/dt;
		//animation works with floating numbers, for the case that it's very slow
		double xStep=(width-getWidth())/(double)numberOfSteps;
		double yStep=(height-getHeight())/(double)numberOfSteps;
		double x=getWidth(),y=getHeight();
		
		//animate
		for(int step=0; step<numberOfSteps; step++){
			setSize((int)x,(int)y);
			validate();
			x+=xStep;
			y+=yStep;
			try{
				Thread.sleep(dt);
			}catch(InterruptedException e){
				break;
			}
		}
		
		//ensure that final size is exact
		setSize(width,height);
    }
}
 
Zuletzt bearbeitet:
Jo, deine Methode is bisschen schöner aufgezogen, nu das

setSize(width,height);

kann eine NullPointer Exception bringen, wenn man es schliesst. Hatte ich grad "Component is disposed". Aber egal, passt schon 😉 Danke nochmal!
 
Sorry, doch noch zwei Fragen:

1) Und zwar läuft die mouseExited nich eh schon im EDT? Wieso legst du den Code darin in den EDT?

2) Bei mir ist es so, wenn ich ganz schnell aus dem Dialog geh und wieder rein, reagiert der Listener gar nicht. Ich kann die Maus, kA sagen wir mal so ne Achtel Sekunde ausserhalb haben und wieder reinziehen und nix passiert. Bei dir krieg ich das nich hin, da geht das sofort.
Aber wie kann es sein dass deine Listener-Methode besser reagiert als meine ?! Ich meine ich hab ne sysout als erste Zeile reingemacht, und das wird nicht ausgegeben. Also die Methode wird nicht aufgerufen ? Wie gehtn das, was is da los?
 
kann eine NullPointer Exception bringen, wenn man es schliesst. Hatte ich grad "Component is disposed". Aber egal, passt schon 😉 Danke nochmal!
Da sollte man aber allgemein aufpassen, wann man was welche listener tun lässt... Ich würde das überhaupt alles etwas globaler angehen, eine schöne separate klasse anlegen, die solche animationen verwaltet und aufpasst, dass da nichts durcheinanderkommt. Aber wie genau weiß ich im moment auch nicht, hab mir noch nie großartig viele gedanken dazu gemacht...

1) Und zwar läuft die mouseExited nich eh schon im EDT? Wieso legst du den Code darin in den EDT?
Hast recht, ist sinnfrei, mach's aus dem code oben gleich weg...

2) Bei mir ist es so, wenn ich ganz schnell aus dem Dialog geh und wieder rein, reagiert der Listener gar nicht. Ich kann die Maus, kA sagen wir mal so ne Achtel Sekunde ausserhalb haben und wieder reinziehen und nix passiert. Bei dir krieg ich das nich hin, da geht das sofort.
Aber wie kann es sein dass deine Listener-Methode besser reagiert als meine ?! Ich meine ich hab ne sysout als erste Zeile reingemacht, und das wird nicht ausgegeben. Also die Methode wird nicht aufgerufen ? Wie gehtn das, was is da los?
Da bin ich echt überfragt. Hab im moment keine Theorien... :bahnhof:
 
Ich würde das überhaupt alles etwas globaler angehen
Hm, viel mehr als ne Wrapper-Methode für invokeLater kann man da denk ich nich rausreissen. Vllt eine Methode die überprüft ob der aufgerufene Thread der EDT ist. Wenn ja, wird eine Exception geworfen damit man sich klar ist: Hier stimmt was nicht, da man scheinbar einen Prozess auf den EDT legen will, man aber schon im EDT ist.
Ich glaub SWT hat sowas, hab ich mal gehört, und dass das ja schon recht gut ist.
Mal sehen, im Moment rätsel ich noch an meinem "lahmen" Listener rum, der es nich schnallt wenn meine Mouse ausm Fenster ist. Kann das iwie was mit DPI einstellungen zu tun haben? Refresh-Rate? Ich hab ne Gamer Mouse. Aber... eig. sobald sich der Zeiger 1px bewegt müsste das ja jeder Listener mitkriegen.
 
und als Nachtrag: Dieses... unschöne Verhalten, wenn man während der Animation rein- und raus geht, könnte man natürlich mit einem Flag verhindern. Oder ... man macht bei mouseExited das "Good bye!"-Label und verkleinert den Dialog, und bei einem nachträglichen mouseEntered schreibt man "Öh- is' noch irgendwas?" und vergrößert ihn wieder 😀
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben