Probleme mit dem JTree

mysticado

Grünschnabel
Hallo Leute,
ich sitze grad an meiner GUI welche mir die Rechner und die drauf laufenden Virtuellen Maschinen anzeigt. Soweit klappt das alles auch sehr gut, nur habe ich folgendes Problem:
Es soll ein Thread geschrieben werden, welcher eventuelle Änderungen des Baums bemerkt (sprich, wenn eine VM auf einen anderen Rechner umzieht) und den Baum dementsprechend updaten. Das will jedoch leider nicht wirklich so wie ich es möchte. Daher die Bitte - könnt ihr mir sagen, wie das am besten gehandhabt werden kann?
Ich habe schon versucht den Baum komplett neu zu erstellen, und dann in der GUI neu zu zeichnen, aber keine der Methoden wollte so wirklich und auch wenn ja, dann sind die Nodes alle collapsed...
 
Also, ich versuche es folgendermaßen:

Java:
// Baum neu zeichnen
		javax.swing.tree.DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
		root.removeAllChildren();
		  
		for (NetworkEntity networkEntity : VMDatabase.networkEntities) {
			DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode(
					networkEntity);
			for (VirtualMachine vm : networkEntity.getVirtualMachines()) {
				if (vm.getHostIP().equals(networkEntity.getIP())) {
					entityNode.add(new DefaultMutableTreeNode(vm));
				}
			}
			root.add(entityNode);
		}
	//	((DefaultTreeModel) tree.getModel()).nodeStructureChanged(root);
	//	((DefaultTreeModel) tree.getModel()).reload();
	//	tree.expandRow(0);
	//	expandAll(tree);

Was ich mache ist im Prinzip, dass ich mir den alten schon bestehenden Baum (der bei der Initiierung der GUI gemalt wurde) erstmal leere, sprich ich entferne alle Knoten ausser dem Root.
Danach lese ich die neuen Werte ein, wobei "networkEntity"(der Rechner) der Wert für die Knoten ist, und "virtualMachine" die Leafs der jeweiligen NetworkEntity sind.
Ist ja eigentlich einleuchtend - jeder Rechner enthält ein paar auf ihm laufende VM's.

Ganz unten könnt ihr jedoch anhand der auskommentierten Lines sehen welche Methoden ich schon probiert habe, um den abgeänderten Baum neu zu zeichnen. Leider hat keines davon geklappt :(

Komischerweise scheint es so, als ob nach dem ersten Update (zB. eine VM migriert von einem Rechner auf den anderen, d.h. in einer neue networkEntity) der Baum einfriert. Wenn ich dann auf einen der oberen zwei Eintrage klicke, werden die zwei bestehenden networkEntities collapsed neu angezeit. Wenn ich diese aufmache, kann ich dann die dazugehörigen VMs auch korrekt zugewiesen sehen. Das Ding ist, das klappt nur einmal! Bei der nächsten Migration passiert das nicht mehr und der Baum bleibt dann die ganze Zeit so wie er ist.
Ausserdem nervt es mich unheimlich, dass der Baum collapsed ist. Er MUSS komplett angezeigt werden******

Was kann ich hier machen? :( Ein Bekannter meinte was von TreeModel und dass ich auf Veränderungen im Baum reagiere, denn dann würde ich das reload() usw. gar nicht brauchen. Jedoch weiss ich leider nicht wie das geht :(
 
1. Anzeigen aller Einträge
Also ich fang mal mit dem Problem an, alle Einträge anzeigen zu lassen. Dazu habe ich hier einen Link gefunden, auf dem ein Code-Beispiel steht, welches dir sicherlich weiterhelfen wird.

2. Reload bzw. update JTree
Da hilft dir vielleicht die Methode weiter. Allerdings würde ich diese Methode aufrufen, bevor du alle Nodes anzeigen lässt über 1. . :)
 
Okay, ich habe es nun so versucht:

Java:
        javax.swing.tree.DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
        root.removeAllChildren();
          tree.treeHasChanged();
        for (NetworkEntity networkEntity : VMDatabase.networkEntities) {
            DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode(
                    networkEntity);
            for (VirtualMachine vm : networkEntity.getVirtualMachines()) {
                if (vm.getHostIP().equals(networkEntity.getIP())) {
                    entityNode.add(new DefaultMutableTreeNode(vm));
                }
            }
            root.add(entityNode);
        }


...private void expandAll(JTree tree, boolean b){...}

doch leider wieder das selbe Ergebnis. Der Tree wird einfach nicht upgedated. Ich versteh das nicht! Dabei sind die internen Daten garantiert korrekt (habe es überprüft). Alleine das Zeichnen des Trees will nicht so wie ich es möchte.
Könnte es daran liegen, dass ich die Methode in der die Tree-Veränderung liegt, bei mir innerhalb der
C:
initComponents()
aufgerufen wird?
Oder könnte es evtl. da dran liegen, dass diese Methode dann noch in einen SwingWorker gepackt ist (wg. Threading-safety)?
 
Dass das nicht gehen kann, ist klar. Wenn du dem JTree sagst, es hat sich etwas verändert, nachdem du alle Elemente gelöscht hast und noch keine hinzugefügt hast, kann er da nicht all zu viel machen, außer die Elemente von der GUI löschen. Das wird aber im Normalfall automatisch mit 'tree.removeAllChildren()' gemacht.

Ich habe das so gemeint:
Java:
    public void deineMethode() {
        
        // ...

        javax.swing.tree.DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
        root.removeAllChildren();
          
        for (NetworkEntity networkEntity : VMDatabase.networkEntities) {
            DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode(
                    networkEntity);
            for (VirtualMachine vm : networkEntity.getVirtualMachines()) {
                if (vm.getHostIP().equals(networkEntity.getIP())) {
                    entityNode.add(new DefaultMutableTreeNode(vm));
                }
            }
            root.add(entityNode);
        }

        tree.treeDidChanged();
        expandAll(tree, true);
    }

    private void expandAll(JTree tree, boolean b) {...}
 
Okay, ich habe nun rausbekommen weswegen erstmal mein Tree überhaupt nicht korrekt gezeichnet wird - wieso es sich aber so verhält, weiss ich nicht. Vielleicht kann mir hier jemand helfen?
Meine Klasse sieht wie folgt aus:

Java:
public class MeineKlasse{

public MeineKlasse(){
initComponents();
}
public static void main(String[] args){
MeineKlasse mk = new MeineKlasse();
mk.setVisible(true);
}
public void initComponents(){
// alle Komponenten werden geladen

 SwingWorker job = new SwingWorker(){

            @Override
            protected Object doInBackground() throws Exception {
             while(true){
                Thread.sleep(30000);
                refreshTree();
                return null;
             }
            }

        };
        job.execute();
        pack();
}

public void refreshTree(){
// ...
        javax.swing.tree.DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
        root.removeAllChildren();
          
        for (NetworkEntity networkEntity : VMDatabase.networkEntities) {
            DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode(
                    networkEntity);
            for (VirtualMachine vm : networkEntity.getVirtualMachines()) {
                if (vm.getHostIP().equals(networkEntity.getIP())) {
                    entityNode.add(new DefaultMutableTreeNode(vm));
                }
            }
            root.add(entityNode);
        }
 
        tree.treeDidChange();
        expandAll(tree, true);
    }
}

}

Komischerweise bricht meine For-Schleife in Zeile 34 schon nach dem ersten Durchlauf ab, obwohl ich 2 networkEntities besitze. Auch sind die Werte alle korrekt eingetragen worden in die Listen, sprich es kann nicht an Dateninkonsistenzen liegen, und trotzdem läuft die Schleife nicht zwei Mal durch. Komischerweise wird auch nichts mehr unterhalb der Zeile 41 ausgeführt?! Woran könnte das liegen?
 
Okay, ich habe nun rausbekommen weswegen erstmal mein Tree überhaupt nicht korrekt gezeichnet wird - wieso es sich aber so verhält, weiss ich nicht. Vielleicht kann mir hier jemand helfen?
Das wird schwer, wenn du uns nicht verrätst, wie du das Problem nun gelöst hast.

Komischerweise bricht meine For-Schleife in Zeile 34 schon nach dem ersten Durchlauf ab, obwohl ich 2 networkEntities besitze. Auch sind die Werte alle korrekt eingetragen worden in die Listen, sprich es kann nicht an Dateninkonsistenzen liegen, und trotzdem läuft die Schleife nicht zwei Mal durch. Komischerweise wird auch nichts mehr unterhalb der Zeile 41 ausgeführt?! Woran könnte das liegen?
Ich würde vermuten, dass in Zeile 42 eine Exception geschmissen wird, die von dir "ignoriert" wird und deshalb die Methode verlassen wird.
Wie verhält sich das Programm denn ab Zeile 41? Wird die Methode verlassen und das Programm läuft dann normal weiter oder bleibt das Programm da hängen?
 
Das wird schwer, wenn du uns nicht verrätst, wie du das Problem nun gelöst hast.
Die Satz war falsch formuliert - es ging eigentlich um den Code, der drunter stand. Also einfach ignorieren :)

Den Fehler mit der Zeile 42 hab ich nun auch rausbekommen - mensch, manchmal vergisst man die simpelsten Dinge (wie z.B. eine abgefangene Exception), also danke für die Erinnerung! Im Großen und Ganzen ging es um eine ArrayIndexOutOfBoundsException in meiner System.out.println, die ich dann prompt entfernt habe und nun sind alle Werte auch korrekt.
Leider habe ich aber immer noch das Problem mit dem Erneuern des Trees! Wie gesagt, die Methode die den alten Tree löscht und neu aufbaut ist in einem SwingWorker, der alle 30Sek. wiederholt wird. Vielleicht zickt der SwingWorker ja irgendwie rum?
Mein neuer Ansatz ging darauf hinaus, dass die refreshTree-Methode in der doInBackground() Methode des SwingWorkers aufgerufen wird, und das eigentliche erneuern des Trees dann erst in der done()-Methode geschieht (hab online irgendwas davon gelesen, dass Threads die auf die GUI zugreifen erst in der process() oder done()-Methode aufgerufen werden sollen). Das heisst, mein Code sieht nun so aus:

Java:
public void initComponents(){
//...
SwingWorker job = new SwingWorker(){

            protected Object doInBackground() throws Exception {
                while(true){
                Thread.sleep(30000);
                refreshTree();
                return null;
                }
            }
            protected void done(){
                tree.treeDidChange();
                expandAll(tree);        
                }
        };
        job.execute();
}
public void refreshTree(){
// ...
        List<NetworkEntity> networkEntities = VMDatabase.networkEntities;
        javax.swing.tree.DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
        root.removeAllChildren();
        for (int h=0; h<networkEntities.size();h++) {
            NetworkEntity networkEntity = networkEntities.get(h);
            DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode(networkEntity);
            for (VirtualMachine vm : networkEntity.getVirtualMachines()) {
                if (vm.getHostIP().equals(networkEntity.getIP())) {
                    entityNode.add(new DefaultMutableTreeNode(vm));
                }
            }
            root.add(entityNode);
        }
}

Wieso mag es jetzt net? :-/
 
Mh... also ich hab gerade mal selber eine Testklasse geschrieben. Da funktioniert es. Hier mal mein Code:
Java:
package de.tutorials;

import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

public class JTreeChangeUp extends JFrame
{
	private final DefaultMutableTreeNode rootNode;
	private final DefaultTreeModel model;
	private final JTree tree;

	/**
	 * 
	 */
	public JTreeChangeUp() {
		super("JTreeChangeUp - Test");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setSize(300, 300);

		rootNode = new DefaultMutableTreeNode("Root");
		model = new DefaultTreeModel(rootNode);
		tree = new JTree(model);
		add(tree);

		new Timer().scheduleAtFixedRate(new TimerTask()
		{
			@Override
			public void run() {
				rootNode.removeAllChildren();

				for(int i = 0; i < 10; i++) {
					DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode(
							String.valueOf(Math.random()*1000+1) // durch deine Werte ersetzen
					);
					
					for(int j = 0; j < 10; j++) {
						entityNode.add(new DefaultMutableTreeNode(
							String.valueOf(Math.random()*1000+1) // durch deine Werte ersetzen
						));
					}
					
					rootNode.add(entityNode);
				}
				
				model.nodeStructureChanged(rootNode);
				// hier noch die expandAll-Methode aufrufen
			}
		}, 3000, 3000);
		
		setVisible(true);
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new JTreeChangeUp();
	}
}

Den einzigsten Unterschied den ich feststellen kann ist, dass ich mit einem Timer arbeite und du mit dem SwingWorker. Vielleicht probierst du es auch mal mit dem Timer.
 
Zurück