# JLabel will nicht "fliegen"



## buecherschrank (26. Okt 2007)

Hallo,
ich bin Neuling in Swingprogrammierung mit NetBeans55 und Java 1.6 und will ein kleines Spiel machen. Ein JLabel habe ich testweise, das bei Buttondruck problemlos seine randomisierte Location ändern kann.
Will ich dies jetzt in einer Schleife einfach mal scheinbar "fliegen" lassen (auf dem Frame bzw. Panel) passiert nichts - es bleibt stehen.
(Es liegt auf einem JPanel, das in einem Frame plaziert ist.)
Was ist der Fehler?

Codeauszug: (Hier hatte ich vorher die randomisierten Stellen, die funktionierten)


```
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
// TODO add your handling code here:
            for (int i=1; i<100; i++)   {
                jLabel3.setLocation(100, 100+i);          
            }
 }
```


Main-Class wie gewöhnlich durch NetBeans vorcodiert:

```
public static void main(String args[]) {

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new mainframe().setVisible(true);
            }   
        });       
        System.out.println("123");
    }
```

Wie kann generell etwas wie ein JLabel am laufen halten und dabei reagieren, wenn z.B. eine Texteingabe ausgeführt wird?
Sollte jemand eine Quelle kennen, wo solch etwas gezeigt wird, wäre ich dankbar für die Adresse.  In den ganzen Sun-Tutorials wird darauf nicht wirklich eingegangen oder habe es übersehen...

Danke für Hilfe,
bs


----------



## Gast2 (26. Okt 2007)

mit einem thread...
und wenn du ein JPnael verwendest dann sollest du auch ein JFrame verwenden und kein Frame


----------



## buecherschrank (26. Okt 2007)

Meine Klasse ist auf einem JFrame aufgebaut; der ist nur mainframe getauft.
public class mainframe extends javax.swing.JFrame {
...
}


----------



## buecherschrank (26. Okt 2007)

Ich habe nun eine Klasse für testweise einen Thread - wie komme ich an die Komponenten wie das JLabel aus der anderen Klasse?
Soll ich die Klasse mainframe auch in die andere Klasse einbinden? Die würden sich gegenseitig einnehmen.


----------



## DocRandom (26. Okt 2007)

Hallo buecherschrank!

Ich habe Dein Beispiel kurz nachvollzogen und bin zu folgendem Ergebnis gekommen:

Einmal die Thread-Klasse:

```
/*
 * Worker.java
 *
 * Created on 26. Oktober 2007, 21:26
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package buecherschrank;

import java.util.Random;
import javax.swing.JLabel;

/**
 *
 * @author aco
 */
public class Worker extends Thread{
    
    private int steps;
    private JLabel lable;
    private Random rd = new Random();
    
    /** Creates a new instance of Worker */
    public Worker(int steps, JLabel lable) {
        this.steps = steps;
        this.lable = lable;
    }
    public void run() {
        int i = 0;
        while (i < steps) {
            lable.setLocation(100, i++);
            try {
                sleep((rd.nextInt(5)+1)*100);
            }catch(InterruptedException iex) {
                // was auch imme Du machen willst beim Interrupt
            }
        }
    }    
}
```
Du übergibst der Klasse die Anzahl der Durchläufe(steps) und Dein Lable(JLable)
Das wars auch dann schon.
Im Code-Teil des Formulares mußt Du nur noch:
	
	
	
	





```
private void jButton1MouseClicked(java.awt.event.MouseEvent evt) {                                      
        Worker wk = new Worker(100,jLabel1);
        wk.start();
    }
```
einfügen!

das wars dann!

lg
DocRandom


----------



## Wildcard (26. Okt 2007)

nicht ganz. Das muss mit dem EDT synchronisiert werden, sprich SwingUtilities#invokeLater  :wink:


----------



## DocRandom (26. Okt 2007)

..also bei mir läufts!
Wobei Du könntes schon recht haben, ich mache ja sonst nix mit Swing, bzw GUI 

lg
DocRandom


----------



## Wildcard (26. Okt 2007)

Es läuft immer so lange bis es durch den QA Test ist und beim Kunden läuft  :wink: 
Spaß beiseite, natürlich läuft das in 99,9% aller Fälle, aber so ist das nunmal mit Threading Issues.


----------



## DocRandom (26. Okt 2007)

..und wie syncronisiere ich das?
Jetzt hast mich neugierig gemacht 

lg
DocRandom


----------



## Wildcard (26. Okt 2007)

I müsste dann als Member deklariert werden...

```
while (i < steps) {
			SwingUtilities.invokeLater(new Runnable() {
				
				@Override
				public void run() {
					lable.setLocation(100, i++);
			
				}
			
			});
	        
	        try {
	            sleep((rd.nextInt(5)+1)*100);
	        }catch(InterruptedException iex) {
	            // was auch imme Du machen willst beim Interrupt
	        }
	    }
```


----------



## DocRandom (26. Okt 2007)

thx
wieder was dazugelernt!

lg
DocRandom


----------



## Gast2 (27. Okt 2007)

kurze frage für was ist das Synchronisieren notwendig bzw. warum wird es gebraucht??


----------



## André Uhres (27. Okt 2007)

Threads in Swing

*Die drei Threadarten*
In Swing Anwendungen unterscheidet man drei Threadarten:
_Initial Thread_, wo der den Anwendungscode beim Starten läuft.

_Event Dispatch Thread_, wo der Event-handling Code ausgeführt wird. 
Der meiste Code, der mit dem Swing Framework arbeitet, 
muss ebenfalls auf diesem Thread laufen.

_Worker Threads_, auch Background Threads genannt, 
wo zeitaufwendige Hintergrundprozesse ausgeführt werden.

*Der Event Dispatch Thread (EDT)*
Im Event Dispatch Thread (kurz EDT) sollten immer nur sehr kurze Prozesse laufen,
sonst wird die GUI blockiert. Alles, was die GUI verändert gehört auf diesen Thread.
Mit invokeLater (oder invokeAndWait) kannst du einen Prozess auf den EDT schicken.

*Der Programmstart *
Beim Programmstart sollte die GUI immer auf dem EDT gestartet werden (invokeLater).
Da die Swing Events von Natur aus auf dem EDT laufen, läuft danach alles automatisch auf dem EDT,
ausser wenn die Anwendung einen speziellen Worker Thread startet.
Beispiel für den sicheren Start einer Swing Anwendung:

```
public static void main(final String args[]) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            new NewJFrame().setVisible(true);
        }
    });
}
```

*Einige "thread-safe" Methoden*
Einige Swing Methoden sind thread-safe und können von jedem Thread aufgerufen werden.
In der API Dokumentation steht dann: _This method is thread safe, although most Swing methods are not._
Beispiele: _JComponent#repaint(), JComponent#revalidate(), JTextComponent#setText(), ..._

*Mehr Infos*
Siehe Concurrency in Swing 
[/list:dd2bc9444a]


----------



## buecherschrank (27. Okt 2007)

Hallo zusammen,
erstmal großen Dank an DocRandom. Ich muss gestehen, ich brauchte einige zeit, um das zu kapieren (ich hatte vorher mit Threads nichts am Hut) - aber jetzt klappts.
Auch die sleep Funktion ist eine große Hilfe.
mit Threads ging bereits ich in eine ganz andere Richtung... alles gelöscht.. war nutzlos. 
Auch Wildcards Synchronisation klappt.
Gruß,
bs


----------



## Gast2 (27. Okt 2007)

@ Andres Uhres ok alles klar , dass ist gut zu wissen...
Ein Beispiel bzw Frage dazu. z.B. ich will jetzt (aus welchem Grund auch immer ) ein JLabel die ganze zeit anders platzieren lassen ohne Ende... dann schicke ich dass ja nicht auf den EDT sonst ist ja die restliche GUI geblockt oder????? Oder z.B. wenn ich eine Bildabfolge ablaufen lassen will in einem JPanel , dachte ich bis jetzt immer dass ich einen ganz normalen Thread verwende ohne invokeLater wie es beim 1.Beispiel von docRandom gemacht wurde ...


----------



## DocRandom (27. Okt 2007)

Hi SirWayne!

..also wie ich das verstanden habe, teilst Du nur dem EDT mit, das er auch Deine Thread-Zeitscheibe mitverwalten soll, damit es zu keiner Blochade der GUI kommt.

Wenn nicht, bitte um Berichtigung @André Uhres 

lg
DocRandom


----------



## Wildcard (27. Okt 2007)

Nein, das ist falsch. Der Thread hat in diesem Fall primär jedoch einen Zweck: zu schlafen.
Alles was direkt oder indirekt die GUI verändert (mit ganz wenigen threadsicheren Ausnahmen) muss aus dem EDT erfolgen. Daher wird die eigentliche Aufgabe, das setzen der Position, über die SwingUtilites in die Queue des EDT geschoben und von diesem verarbeitet.
Wird die GUI von einem anderen Thread beeinflußt ist das Verhalten undefiniert.


----------



## Gast2 (27. Okt 2007)

mhm o.k !!! Aber =) wie gesagt, wenn ich jetzt viele Bilder hintereinander auf einem Panel ablaufen(zeichnen) lassen will und dieser Ablauf sich endlos wiederholen soll bis ein Panelwechsel satt findet. Nehmen wir einmal mal wir wollen das Panel durch einen Buttondruck wechseln.Wenn ich dann das schlafen legen dieses Thread in den EDT schiebe, wird dann die restliche GUI blockiert,w eil der EDT ja die ganze zeit meinen thread verwalten muss und damit beschäftigt ist meine Bilder zu zeichnen.... oder kann der EDT  in meinem beispiel immer noch auf den Buttondruck reagieren???

Hoffe meine Frage ist verständlicher =)

EDIT: Mit Punktation =)


----------



## Wildcard (27. Okt 2007)

Ähhh. Satzzeichen!?  :bahnhof:


----------



## Gast2 (27. Okt 2007)

erledigt =)


----------



## Wildcard (27. Okt 2007)

Der EDT darf wie gesagt *nicht* schlafen gelegt werden.


----------



## Gast2 (27. Okt 2007)

ja des denk ich mir aber irgendwie ich versteh das nicht...
Also es ist mir soweit bewusst, dass der EDT alles was mit der GUI zu tun hat machen soll...
ABER was ist wenn der Code so aussehen würde


```
while (true)

i=i+10;
       SwingUtilities.invokeLater(new Runnable() { 
             
            @Override 
            public void run() { 
               lable.setLocation(100, i); 
          
            } 
          
         });

...
```

also es ist ja jetzt eine endlosschleife ... meine frage : Ist die GUI jetzt blockiert z.B. Buttons ????


----------



## Wildcard (27. Okt 2007)

In diesem Fall eben das setLocation vom EDT ausgeführt wird.


----------



## DocRandom (27. Okt 2007)

Wildcard hat gesagt.:
			
		

> Der EDT darf wie gesagt *nicht* schlafen gelegt werden.


Ich lege ja meinen Thread zum Schlafen und nicht den EDT!
Oder steh ich aufm Schlauch?

lg
DocRandom


----------



## Wildcard (27. Okt 2007)

DocRandom hat gesagt.:
			
		

> Ich lege ja meinen Thread zum Schlafen und nicht den EDT!


Genau darum geht's.


----------



## DocRandom (27. Okt 2007)

..hm heißt das: 
wenn ich bei der GUI-Programmierung innerhalb eines Events einen oder mehrere Threads aufrufe, das ich dann Gefahr laufe den EDT zu blockieren, wenn ich kein
	
	
	
	





```
SwingUtilities.invokeLater(new Runnable()
```
 einbaue?
Hab ich das jetzt richtig verstanden?

Edit: natürlich nur Threads die mit der GUI zu tun haben, oder betrifft das alle Threads?
lg
DocRandom


----------



## Gast2 (27. Okt 2007)

ah ok ich glaub jetzt hab ich es des invokelater() zeichnet nur das label an eine andere position und durch das invokelater wird es im EDt thread abgeabreitet und nicht im normalen thread...


----------



## DocRandom (27. Okt 2007)

..ahh, thx SirWayne jetzt hab ichs auch kapiert *fg*
Sollte mich doch mal mit der GUI etwas näher auseinandersetzen!

lg
DocRandom


----------



## Gast2 (27. Okt 2007)

ja ich bin mir gar nicht sicher ob meine Aussage stimmt =)!!!! @wildcard hab ich es richtig verstanden oder net???


----------



## Wildcard (27. Okt 2007)

Passt schon so. Nur das setLocation noch nichts zeichnet, sondern nur die Position verändert.
Das eigentliche Zeichnen übernimmt der RepaintManager seperat, aber das hat mit der Sache schon nichts mehr zu tun.


----------



## Gast2 (27. Okt 2007)

o.k alles klar danke!!!


----------



## Gast2 (28. Okt 2007)

Eine frage zu der Sache hätte ich noch!!! Wenn ich in meiner Anwendung per buttondruck eine neues JTabbedPane anlege und auf den container adde, muss ich dann in meiner actionPerformed methode auch die SwingUtilities.invokelater() aufrufen ????


```
public void actionPerformed(ActionEvent ae) 
	{
		if(ae.getSource().equals(button))
		{
			vewaltung=new JTabbedPane();
			SwingUtilities.invokeLater(new Runnable(){
	            public void run(){
	            	vewaltung.addTab("Panel1",panel1);
                                ....
	            	add(vewaltung);
	            	container.repaint();
	            }
	        });

            }
```


----------



## Wildcard (28. Okt 2007)

EDT = *Event Dispatcher* Thread
Ein ActionEvent ist augenscheinlich ein *Event*
Ergo: Der Code wird bereits vom EDT ausgeführt.


----------



## Gast2 (29. Okt 2007)

ok klingt logisch ...
und was wäre wenn man den Code im Konstruktor ausführt??? mit oder ohne invokeLater()???



```
public class TestFrame extends JFrame
{

JPanel panel1;
JTabbedPane verwaltung;
...

    public TestFrame()
    {
    panel1=new JPanel();

    ...
      vewaltung=new JTabbedPane(); 
         SwingUtilities.invokeLater(new Runnable(){ 
               public void run(){ 
                  vewaltung.addTab("Panel1",panel1); 
                                .... 
                  add(vewaltung); 
            
               } 
           }); 
    }
}
```


----------

