1) Du brauchst kein neues Model erzeugen. Du musst nur die Daten innerhalb des Models (also dem root) verändern
2) Bei allen Änderungen am Model musst du entsprechende fire-Methoden aufrufen damit der JTree das auch mitkriegt und sich neuzeichnet. Das hatten wir hier schon.
3) Alle Änderungen auf dem Tree müssen vom EDT ausgeführt werden, das sollte dir mittlerweile auch klar sein
edit: Ich würde dir übrigens empfehlen den Thread in einem schnelleren Intervall laufen zu lassen, allerdings nicht jedes mal das gesamte Model auseinander nehmen und neubauen, sondern nur wenn es tatsächlich eine Änderung gab. Den Check auf die Änderung musst du lediglich möglichst performant machen, dann kannst du den Thread das auch alle 3-5 Sekunden durchlaufen lassen.
Na gut, jetzt habe ichs neu versucht einfach die VMs an die dazugehörigen NetworkEntities zu hängen, nachdem eine Migration festgestellt wurde. Hier der Code - sieht ein bisschen komplex aus, doch müsste seine Sache tun:
Java:
HashMap<String,String> host_vms =newHashMap<String,String>();// Beispiel: [192.168.0.16, 192.168.0.2] (=VM,Host)hostsUndVMsEinlesen();for(int u=0; u<VMDatabase.networkEntities.size();u++){//gehe alle NE's durchfor(int v=0;v<VMDatabase.networkEntities.get(u).virtualMachines.size();v++){//gehe die dazugehörigen VMs durchfor(String key2 : host_vms.keySet()){String value = host_vms.get(key2);if(VMDatabase.networkEntities.get(u).virtualMachines.get(v).getIP().equals(key2)){if(!VMDatabase.networkEntities.get(u).virtualMachines.get(v).getHostIP().equals(value)){//falls die IP des Hosts der VM nicht mehr stimmt...VirtualMachine vm =VMDatabase.networkEntities.get(u).virtualMachines.get(v);for(int w=0; w<VMDatabase.networkEntities.size();w++){// hole die entsprechende NE und füge die VM nun hinzu. Von der alten NE wird die VM entfernt.if(VMDatabase.networkEntities.get(w).getIP().equals(value)){VMDatabase.networkEntities.get(w).addVM(vm);VMDatabase.networkEntities.get(v).removeVM(vm);
vm.setHostIP(value);}}}}}}}
javax.swing.tree.DefaultMutableTreeNode root =new javax.swing.tree.DefaultMutableTreeNode("root");for(NetworkEntity networkEntity :VMDatabase.networkEntities){DefaultMutableTreeNode entityNode =newDefaultMutableTreeNode(
networkEntity);for(VirtualMachine vm : networkEntity.getVirtualMachines()){if(vm.getHostIP().equals(networkEntity.getIP())){
entityNode.add(newDefaultMutableTreeNode(vm));}}
root.add(entityNode);}
tree.validate();
Das müsste nun deinen Vorschlag implementieren und auch das TreeModel wird nicht neu erstellt. Ich weiss jedoch nicht, ob es denn so nun funktioniert. Leider habe ich aber keine andere Möglichkeit auf den alten Tree zuzugreifen, ausser eben so. Und trotzdem wird mein Tree wieder nicht geupdated. Was könnte jetzt der Grund sein?
Edit: Ja, ich weiss, dass alle GUI-Änderungen über den EDT aufgerufen werden müssen, da es ja sonst zu Inkonsistenzen kommt (wie es mir am Anfang dieses Threads beigebracht wurde ). Ich habe diese ganze Methode auch über einen SwingWorker aufgerufen, um diese ganze Thread.sleep Geschichte richtig ausführen zu können, ohne dass meine ganze GUI einfriert.
Edit2: Okay, ich habe grad festgestellt, dass ichs doch nciht so machen kann wie oben beschrieben, da ich auch irgendwie die Möglichkeit mit berücksichtigen muss, dass evtl. neue VM's dazukommen. Ich habe nun doch den Code wieder auf die alte Version gebracht (wo alles komplett eingelesen wird), was aber nix ausmacht, da eh nie mehr als 10 VM's gleichzeitig laufen werden und somit auch nicht wirklich ein Bottleneck darstellt.
Trotzdem krieg ich aber so komische Ergebnisse raus wie zB.:
Refresh list: 16:35:40
Refresh list: 16:35:40
Show number of children: 0
Show number of children: 0
Show number of children: 0
Show number of children: 0
Show number of children (NetworkEntity 0): 2
Show number of children (NetworkEntity 1): 1
Show number of children: 0
Show number of children: 0
Show number of children: 2
Show number of children: 1
Show number of children: 0
Show number of children: 0
publicvoidrefreshTree()throwsIOException,InterruptedException{while(true){Thread.sleep(30000);System.out.println("Refresh list: "+now());// Alte VMs in Liste speichernList<VirtualMachine> virtualMachines =newArrayList<VirtualMachine>();for(int i=0; i<VMDatabase.networkEntities.size();i++){for(int j=0;j<VMDatabase.networkEntities.get(i).virtualMachines.size();j++){
virtualMachines.add(VMDatabase.networkEntities.get(i).virtualMachines.get(j));}VMDatabase.networkEntities.get(i).virtualMachines.clear();}// VMs neu einlesenVMDatabase.networkEntities.clear();Vector<String> one_hosts =newVector<String>();HashMap<String,String> host_vms =newHashMap<String,String>();
one_hosts.add(temp);// Speichere alle Hosts
host_vms.put(ip,temp[6]);// Speichere alle VMs + Hosts auf dem sie sind}}// Hier alle Rechner und VMs in die neue Database einlesenboolean lock =false;for(int i =0; i < one_hosts.size(); i++){NetworkEntity nw =newNetworkEntity(one_hosts.get(i));for(String key2 : host_vms.keySet()){String value = host_vms.get(key2);if(value.equals(one_hosts.get(i))){for(int k=0;k<virtualMachines.size();k++){if(key2.equals(virtualMachines.get(k).getIP())){// Falls Eintrag schon in alter Liste existiert...VirtualMachine vm = virtualMachines.get(k);
vm.setHostIP(value);
nw.addVM(vm);
lock =true;}}if(lock==false){VirtualMachine vm =newVirtualMachine(key2,one_hosts.get(i),0L,0,0,false,false,0,"");
nw.addVM(vm);}
lock =false;}}VMDatabase.networkEntities.add(nw);}// Baum neu zeichnen
javax.swing.tree.DefaultMutableTreeNode root =new javax.swing.tree.DefaultMutableTreeNode("root");for(NetworkEntity networkEntity :VMDatabase.networkEntities){DefaultMutableTreeNode entityNode =newDefaultMutableTreeNode(
networkEntity);for(VirtualMachine vm : networkEntity.getVirtualMachines()){if(vm.getHostIP().equals(networkEntity.getIP())){
entityNode.add(newDefaultMutableTreeNode(vm));}}
root.add(entityNode);System.out.println("Kinder: "+entityNode.getChildCount());}
tree.validate();expandAll(tree);}}
Du solltest dir schön langsam mal die Basics aneignen. Das hier hat null komma nix mit Trees oder Threads zu tun, du hast einfach nicht den blassesten Schimmer wie Java funktioniert..
Also "nicht den blassesten Schimmer" ist vielleicht doch ein bisschen zu hart, oder? Natürlich habe ich verstanden, was du vor 3 Kommentaren gemeint hast mit "innerhalb des Modells ändern", aber da ich wie gesagt eine Möglichkeit berücksichtigen muss welche es ermöglicht auch neue Knoten hinzuzufügen muss ich ja erstmal komplett die VMs einlesen und dann den Baum theoretisch neu erzeugen (geht jedenfalls schneller).
Das Problem, das ich nun habe ist, wie kann ich denn den alten Tree verändern? Ich habe nur das eine JTree tree-Objekt, aber ich ging davon aus, dass ich es eben komplett neu zeichnen muss - daher auch der neue root. Wie würdest du das machen?
Und ja, ich gebe es ja zu, dass ich nicht wirklich der beste Programmierer der Welt bin, aber zu meiner eigenen Verteidigung muss ich auch sagen, dass das das erste Mal ist, dass ich was mit Swing mache. Trotzdem bin ich aber offen für alles. Ich möchte natürlich gerne dazulernen. Kannst du mir was empfehlen (ausser die API) wo man wirklich Schritt für Schritt alles beigebracht bekommt? Java ist ne Insel?
Wie ich schon sagte hat das grad nix mit Swing o.ä. zu tun. Du erzeugst irgendein Objekt, speicherst es dir lokal innerhalb einer Methode ab und wunderst dich dass es nicht auf magischem Wege mit deiner Table verknüpft wird? Dir musst doch klar sein dass es überhaupt keine Verbindung zwischen deinem Tree und dieser lokalen Root-Node gibt. Das sind absolute Basics. Ich weiß das "keinen blassen Schimmer" vllt hart klingt, aber du musst auch akzeptieren wenn das so ist. Ist ja keine Beleidigung, immerhin wird keiner geboren und kann Java Aber ändert trotzdem nix daran dass du hier ganz, ganz fundamentale Dinge noch nicht verstanden hast, und dir weiterhin sehr schwer tun wirst, egal bei was, wenn du dir keine solide Grundlage schaffst.
Das Problem, das ich nun habe ist, wie kann ich denn den alten Tree verändern?
So, wie du's bisher auch getan hast?! Du hast dein TreeModel, welches deine Daten auf den JTree mappt. In diesem TreeModel kannst du doch alles löschen, einfügen, ändern etc wie du lustig bist. Ganz einfach die Referenz auf den root-Knoten dieses Models verwenden in deiner Methode, und nicht irgendeinen neuen Knoten machen der damit gar nix am Hut hat.
Kannst du mir was empfehlen (ausser die API) wo man wirklich Schritt für Schritt alles beigebracht bekommt? Java ist ne Insel?
ich versuchs grad per:
[c]javax.swing.tree.DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();[/c]
Edit: Hab grad gesehen, dass ich oben den Code nicht richtig kopiert habe - das war eine temporäre Version. Daher auch der Fehler, den du als "absolute Basics" bezeichnet hast Puh, und dabei dachte ich ich wär schon grenzdebil
Okay, letzter Versuch. Ich habe es nun geschafft den bestehenden Tree korrekt upzudaten, bzw. die alten Knoten erstmal rauszulöschen und dafür dann neue zu setzen.
Das Problem, das ich nun habe ist, dass der Tree jedoch nicht alle 60 Sekunden visuell upgedated wird, sprich, wenn ich an den Knoten nicht rumspiele wird die Ausgangssituation angezeigt. Erst wenn ich ganz oben auf die erste oder zweite Stelle klicke (da ich ja zwei aufklappbare Knoten = NetworkEntities habe), wird der Tree auch sichtbar upgedated.
Ich habe schon diverse Ansätze probiert:
1. ((DefaultTreeModel)tree.getModel()).reload();
2. tree.invalidate(); tree.validate();
3. repaint der kompletten JScrollPane, usw.
doch leider hat nichts geholfen.
Ausserdem stört mich, dass die ersten zwei Knoten auch collapsed sind und nicht wie von mir gewollt, gleich offen angezeigt werden.
Ich habe folgenden Code eingebaut, welchen ich nach dem "reload()" aufrufe, um eigentlich alle Knoten offen anzuzeigen:
aber irgendwie wird das nicht übernommen. Ich habe auch schon das Forum durchsucht, aber dort fand ich nur Methoden die ich oben eben schon erwähnt habe und die bei mir nicht wirklich klappten.
Wo ist jetzt der Denkfehler?
2) Bei allen Änderungen am Model musst du entsprechende fire-Methoden aufrufen damit der JTree das auch mitkriegt und sich neuzeichnet. Das hatten wir hier schon.
3) Alle Änderungen auf dem Tree müssen vom EDT ausgeführt werden, das sollte dir mittlerweile auch klar sein
Diese beiden Dinge beachtet? Deswegen meinte ich auch du kannst gleich über dein TreeModel gehen, denn dort hast du Zugriff auf die fire-Methoden. Dann brauchst du kein repaint/reload/invalidate/revalidate/schiessmichtot
Edit: Ja, ich weiss, dass alle GUI-Änderungen über den EDT aufgerufen werden müssen, da es ja sonst zu Inkonsistenzen kommt (wie es mir am Anfang dieses Threads beigebracht wurde ). Ich habe diese ganze Methode (refreshTree()) auch über einen SwingWorker aufgerufen, um diese ganze Thread.sleep Geschichte richtig ausführen zu können, ohne dass meine ganze GUI einfriert.
Das mit dem EDT weiss ich also.
Das mit dem Model habe ich aber (noch) nicht so implementiert wie du es vorschlägst. Ich muss nämlich zugeben, dass ich wiedermals ein bisschen auf dem Schlauch stehe. Ich implementiere also einen TreeModelListener und hänge es ans Modell. Aber was muss ich denn nun in die treeStructureChanged-Methode schreiben, da ich sie nun brauche oder? Und wie kann ich dann den fire-Event abfangen über das DefaultTreeModel? Pls help
Hier zumindest mal ein Anfang:
Java:
TreeModelListener tml =newTreeModelListener(){@OverridepublicvoidtreeNodesChanged(TreeModelEvent e){}@OverridepublicvoidtreeNodesInserted(TreeModelEvent e){}@OverridepublicvoidtreeNodesRemoved(TreeModelEvent e){}@OverridepublicvoidtreeStructureChanged(TreeModelEvent e){// Was muss hier rein? repaint()?}};
model.addTreeModelListener(tml);
Wie du jetzt auf einmal auf einen TreeModelListener kommst weiß ich nicht. Es geht hier um's TreeModel. Von einem Listener hab ich doch gar nix gesagt?! Du musst mal etwas besser aufpassen, iIch sag dir etwas und du scheinst es gar nicht zu lesen. zB grad im vorigen Beitrag hab ich gesagt:
Dann brauchst du kein repaint/reload/invalidate/revalidate/schiessmichtot
Und als direkte Antwort darauf kommt die Frage "Muss hier repaint() rein?"
Und ich habe dir auch schon ausführlich erklärt wie das mit dem TreeModel und den fire-Methoden funktioniert. Den Link zur API-Doc hatte ich dir auch schon gegeben. Eigentlich solltest du das ja auch schon in deinem Code drin haben, immerhin wird der Tree bei Programmstart ja scheinbar auch korrekt angezeigt. Beim Update des Trees musst du genau das selbe machen. Lies dir nochmal alle Beiträge hier durch, all deine Fragen hast du schon mal gestellt und ich hab dir auf alle schon eine Antwort gegeben...
Sorry aber ich weiß auch nicht mehr was ich dir noch schreiben soll...
Das passiert wenn ich versuche selber über die API drauf zu kommen
So wie ich das Beispiel aus der API (How to Use Trees (The Java™ Tutorials > Creating a GUI With JFC/Swing > Using Swing Components)) verstehe muss nämlich so ein Listener erzeugt werden, welcher aufpasst ob die Knoten in irgend einer Art und Weise abgeändert werden. Falls ja, muss ja was gemacht werden - deswegen fragte ich oben auch in der treeStructureChanged-Methode was da nun hingehört.
Ich wollte es dir eigentlich mit mir einfacher machen - nehm es mir nicht übel
Ich weiss, dass wir schon mal das alles durchgekaut hatten, hier ist auch der Code über den wir den Tree erzeugt hatten:
Genau den selben Code habe ich auch für meine refresh-Methode benutzt, weswegen ich ja auch wieder die richtigen Werte im tree habe. Ich brauche eben nur noch Hilfe bei dieser "fire-Geschichte", da ich nicht weiss wo die hinkommt. Offensichtlich war es ja per Listener falsch....
Und wenn ichs per Cast versuche: [c]TreeModel model = (DefaultTreeModel) tree.getModel();[/c] kriege ich keine [c]model.fireMethode()[/c]
Deswegen verstehe ich leider nicht wirklich auf was du hinaus willst?
Hi,
es tut mir echt Leid dich zu nerven, doch ich muss dich einfach noch diese Kleinigkeit fragen, da du einfach mehr Ahnung hast als ich und wahrscheinlich auch sofort drauf kommen wirst, worum es sich bei meinem Problem handelt.
Kurz zur Erinnerung - ich habe in meine initComponents()-Methode der GUI einen SwingWorker eingebaut, welcher alle 30 Sekunden nach Änderungen in der Tree-Struktur schaut. Soweit funktioniert auch alles - die Daten werden korrekt upgedated (auch in der VMDatabase Klasse), nur will sich mein Tree nicht refreshen in der GUI.
Ich weiss, dass:
1. dieser sich wiederholende Thread in einen SwingWorker gepackt werden muss, da Swing nicht threadsicher ist.
2. Methoden die von innerhalb des SwingWorkers auf die GUI zugreifen, in der process() oder done() Methode aufgerufen werden müssen - alles andere kommt in die doInBackground()
3. ich über das TreeModel die Knoten einfach updaten kann und java übernimmt das refreshen (normalerweise) dann automatisch.
Aber! Das tut es bei mir leider immer noch nicht, obwohl ich verschiedenste Methoden ausprobiert hatte. Zuerst hatte ich es so gemacht, wie du es mir von ein paar Beiträgen erklärt hattest (wo ich meinen Tree das erste Mal erstellen wollte, beim Aufbau der GUI), sprich ich habe einfach nur diese alte Methode kopiert und sie für den SwingWorker etwas abgeändert - hat leider nichts gebracht. Nun versuche ich es über das Modell, aber trotzdem will es nicht so wirklich. Das erste Mal klappt es normalerweise immer, aber es will sich einfach nicht alle 30 Sekunden refreshen. Kann es sein, dass meine done()-Methode nur einmal aufgerufen wird, obwohl die doInBackground()-Methode endlos läuft (siehe Code unten)?
Ausserdem bekam ich immer wieder eine NullPointerException bei jedem Refresh, doch die fange ich nun ab in der Hoffnung, dass sie den Programmablauf nicht stört. Jedoch könnte auch sie vielleicht der Grund sein, dass der Refresh nicht funktioniert? Die NPE tauchte wie gesagt jedes Mal beim Refresh des Baums auf, nachdem mein TreeSelectionListener versucht, den letzten ausgewählten Pfad zu finden - du erinnerst dich:
Java:
DefaultMutableTreeNode node =(DefaultMutableTreeNode) tree
.getLastSelectedPathComponent();Object data = node.getUserObject();VirtualMachine vm =null;if(data instanceofNetworkEntity){}else{
vm =(VirtualMachine) data;
ueberschrift.setText(" VM: "+ vm.getIP());}
Es könnte also mehrere Gründe haben und ich bin einfach der Hoffnung, du wirst es mit deiner reichen Erfahrung sofort rausfinden können
Hier noch der Code für den SwingWorker: