Animation wird nicht geupdatet

Palme4001

Grünschnabel
Hallo allerseits,

Ich arbeite momentan an meiner Facharbeit im Informatik LK ueber Wegfindungs Algorihmen, dazu habe ich eine kleine Anwendung geschrieben.

Die drei Klassen "Karte", "Fenster" und "AlgorithmusAStern" sind fuer meine Frage mehr oder weniger von Bedeutng.

In der Klasse Karte wird ein Graph "erzeugt", aufdem der weg von a nach b gefunden werden soll.
Man kann sich vielleicht schon denken, dass in der 3. Klasse der Algorithmus mit seiner Hauptschleife steht. Momentan wird der Weg,
nachdem man auf einen Button gedrueckt hat direkt blau im Frame angezeit, da dies nur wenige Millisekunden dauert.
Jetzt moechte ich aber in der Schleife des Algorithmus eine kleine SleepTime von z.B. 100ms einrichten, und danach mit
"karte.fenster.repaint();" die neu eingefaerbten Bloecke im Frame updaten, um dem Vorgehen des Algorithmus bei der Arbeit zuzuschauen. Das hat alles bevor ich das Frame von dem Algorithmus separiert habe, und diese in einer grossen Klasse standen auch noch super geklappt. Jetzt ist es so, dass nach dem Start eine Weile lang keine Veraenderung im Frame zu sehen ist, und erst wenn der Weg gefunden wurde, er voll und ganz angezeigt wird.

Ich wurde mich ueber eine Antwort freuen :)

Hier noch die zwei Klassen:

Code:
public class AlgorithmusAStern {
	
	private Karte karte;
	private Knoten aktuellerknoten;

	protected ArrayList <Knoten> openList;
	protected ArrayList <Knoten> closedList;
	
	protected int schaetzungsMethode = 0;
	
	protected AlgorithmusAStern(Karte k) {
		
		this.karte = k;
		
	}
	
	
	private void entfernungSchaezten( int zielX , int zielY ) {}
	
	private int getManhattanSchaetzung(int x1, int y1, int x2, int y2) {}
	
	private int getEuclideanSchaetzung(int x1, int y1, int x2, int y2) {}
	
	private Knoten getKleinsten(ArrayList<Knoten> liste) {}
	
	protected void findeKuerzestenWeg( int startx , int starty , int zielx , int ziely ) {
		
		// ================ Start der Hauptschleife ========================== //
		while(...) {
			
                         ...			

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			karte.fenster.repaint();
		}
		
		// =========================== Ende der Hauptschleife ========================= //
		
	}
	
	private void rekonstruiereWeg(Knoten knoten) {}
}




Code:
public class Karte {
	
	protected Graph graph = new Graph();
	protected Fenster fenster;
	protected KarteSpeichern speicherer;
	
	AlgorithmusAStern aStern;
	
        ...

	protected Karte() {
		
		fenster = new Fenster(this);
		speicherer = new KarteSpeichern(this);
		aStern = new AlgorithmusAStern(this);
		
		neueKarteErzeugen();
		
	}

         protected void aSternLoesung() {
		
		aStern.findeKuerzestenWeg(startx, starty, zielx, ziely);
		
	}
	
	protected void neueKarteErzeugen() {}
	
	protected void zufaelligeKarte() {}
	
	protected void reset() {}
	
	protected void wandSetzen( int X , int Y ) {}
	
	protected void wandEntfernen( int X , int Y ) {}
	
	protected void startSetzen( int X , int Y ) {}
	
	protected void zielSetzen( int X , int Y ) {}

	protected int getXIndex( int knotenIndex ) {}
	
	protected int getYIndex( int knotenIndex ) {}
	
	public int getLetztenIndex() {}
	
	protected void groesseAendern(int groesse) {}
	
	protected void feldFaerben(Knoten knoten, String farbe) {}
		
	}
 

Harrier

Erfahrenes Mitglied
Verwende repaintImmediately() statt repaint().

repaint() führt nicht zwingend zu einem sofortigen neu zeichnen, sondern lagert deine Anfrage ggf. erstmal zwischen. In deinem Fall schickst du immer wieder eine Anfrage, dass du Fenster repainten willst, was aber erst ausgeführt wird, wenn deine Schleife durchlaufen ist.

Das erhöht in manchen Fällen die Performance, weil nicht unnötig oft neu gezeichnet werden muss, hat in deinem Fall aber eben den Effekt, dass erstmal gar nichts passiert bis alles vorbei ist.
 

Palme4001

Grünschnabel
Verwende repaintImmediately() statt repaint().

Hallo, erstmal vielen Dank fuer deine schnelle Antwort!
Allerdings klappt es immer noch nicht, vielleicht mache ich ja auch etwas grundlegend falsch.

Also die vermeitlichen Bloecke, welche gefaerbt werden sind alles kleine JLabel von der Groesse 40x40 px. Diese faerbe ich dann mit " fenster.block[x][y].setIcon(fenster.blau); ".
Wenn ich nun aber in der Schleife schreibe:
" karte.fenster.block[x][y].paintImmediately(karte.fenster.getBounds()); " habe ich genau das selbe Problem wieder.

Habe ich da was falsch verstanden, und wenn wie kann ich es vielleicht besser machen ?
 

Harrier

Erfahrenes Mitglied
Hm. Also damit ich das richtig verstehe:
Fenster ist irgendeine Unterklasse von JComponent? Und darin platzierst du JLabels, deren Position du im Verlauf der Applikation veränderst.

In diesem Fall solltest du, nachdem du die neuen Positionen gesetzt hast, fenster.validate() aufrufen.
 

Palme4001

Grünschnabel
darin platzierst du JLabels, deren Position du im Verlauf der Applikation veränderst.

In diesem Fall solltest du, nachdem du die neuen Positionen gesetzt hast, fenster.validate() aufrufen.

Die Klasse Fenster sieht im groben so aus:


Code:
public class Fenster extends JFrame{
	
	protected final JLabel[][] block;
	
	protected ImageIcon boden_ImageIcon;
	protected ImageIcon stein_ImageIcon;
	protected ImageIcon rot_ImageIcon;
	protected ImageIcon gruen_ImageIcon;
	protected ImageIcon blau_ImageIcon;
	protected ImageIcon start_ImageIcon;
	protected ImageIcon ziel_ImageIcon;
	protected ImageIcon busch_ImageIcon;
	
	...
	protected Fenster(Karte karte) {
		
		super("Pathfinding Algorithmen");

		
		
		this.karte = karte;
		this.lader = new KarteLaden();
		this.speicher = new KarteSpeichern(karte);

		****** this.block = new JLabel[80][80]; 

		this.grosseBilderVerwenden();
		
		
	
	private void initialisiereBloecke() {
		
		for(int i = 0; i < 80; i++) {
			for(int j = 0; j < 80; j++) {
				block[i][j] = new JLabel();
				add(block[i][j]);
			}
		}
		
	}
	
	protected void grosseBilderVerwenden() {
		
		boden_ImageIcon =  new ImageIcon(getClass().getResource("Boden20x20.png"));
		stein_ImageIcon = new ImageIcon(getClass().getResource("Stein20x20.png"));
		rot_ImageIcon = new ImageIcon(getClass().getResource("inClosedList20x20.png"));
		gruen_ImageIcon = new ImageIcon(getClass().getResource("inOpenList20x20.png"));
		blau_ImageIcon = new ImageIcon(getClass().getResource("Weg20x20.png"));
		start_ImageIcon = new ImageIcon(getClass().getResource("Start20x20.png"));
		ziel_ImageIcon = new ImageIcon(getClass().getResource("Ziel20x20.png"));
		busch_ImageIcon = new ImageIcon(getClass().getResource("Busch20x20.png"));
		
	}
	
}

Wenn nun eine neue Karte (Graph) in der Klasse Karte erzeugt wird, dann wird diese ueber ein Feld von 40 x 40 JLabeln dargestellt. Die Positionen dieser sind fest, nur ihr Bild wird z.B mit
"block[0][0].setIcon(stein_ImageIcon); " veraendert.
Die Positionen moechte ich also garnicht veraendern, und mit fenster.validate() klappts dann auch nicht.

Vielleicht ist die Methode, 1600 JLabel fuer eine 40 x 40 Karte zu erzeugen ja auch nicht die beste ?
Wie wurdest du das denn am besten loesen ?
 

Palme4001

Grünschnabel

Hallo,
ich habe das Problem jetzt soweit verstanden, weiß aber nicht genau, wie ich das jetzt in meine Programm umsetzen soll. ich habe nun in der Klasse mit dem Algorithmus einen neuen Thread erzeugt, und ihn gestartet, sobald ein Objekt dieser Klasse erstellt wird. Die Schleife des Algorithmusses steht nun in der muthoden "run()" des Threads.
Nach jedem Schleifendurchlauf rufe ich nun: "this.t.sleep(100);" und anschließend: "fenster.repaint();" auf.

Jedoch scheint der EDT immernoch blockiert zu sein.

Code:

Code:
public class AlgorithmusAStern{
	
	Thread t;

	protected AlgorithmusAStern(Karte k) {
		
		this.karte = k;

                t = new Thread();
		t.start();
	}

       public void run() {	

		while( aktuellerknoten != karte.zielKnoten ) {

                        [...]
			
			try {
				this.t.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			karte.fenster.repaint();
		}
	[...]
}
 

Harrier

Erfahrenes Mitglied
Wird deine run()-Methode überhaupt aufgerufen? So wie dein Bsp. da oben aussieht, sollte das gar nicht funktionieren.

Deine Klasse müsste das Runnable-Interface implementieren und dann sollte es heißen:
t = new Thread(this);