PHP Editor Strawberry vous présente les questions et réponses Java sur les problèmes de synchronisation entre View de GraphStream et JTextField dans cet article. Lorsque vous utilisez la bibliothèque GraphStream, vous rencontrez parfois des problèmes de synchronisation des données entre View et JTextField. A travers cet article, vous apprendrez comment résoudre ce problème et rendre vos programmes Java plus fluides et plus efficaces.
J'ai publié mon problème sur https://github.com/graphstream/gs-ui-swing/issues/19#issue-2109865450. Mais comme la dernière réponse sur ce référentiel a été publiée le 10 juin 2021 et que d'autres questions ont été publiées sans aucune réponse, je ne sais pas si quelqu'un y prête encore attention. C'est pourquoi je repose ma question ici.
J'ai créé un jframe composé d'une vue et de deux jtextfields :
La vue montre cinq nœuds : quatre ne sont que des points de repère et ne devraient pas être déplacés par l'utilisateur ("fixed_*"), et un sera déplacé par l'utilisateur ("non fixé"). Deux jtextfields affichent les coordonnées des nœuds "non épinglés". View et jtextfield doivent être synchronisés l'un avec l'autre. En fait, lorsque l'utilisateur déplace le nœud "non épinglé" dans la vue, les deux jtextfields doivent être mis à jour en conséquence :
À l'inverse, lorsque l'utilisateur modifie les coordonnées dans l'un des champs jtext, la vue doit également se mettre à jour en conséquence :
Voici quatre cas de test :
Les cas de test 1, 2 et 3 fonctionnent correctement mais le cas de test 4 ne fonctionne pas. En fait, dans le scénario de test 4, une fois que l'utilisateur déplace le nœud « non épinglé » dans la vue, les modifications des coordonnées dans l'un des jtextfields ne mettent pas à jour la vue.
J'essaie d'analyser la différence entre l'exécution des cas de test 3 et 4. Pour ce faire, j'ai imprimé le nom du thread actuel à différents endroits du code. Je vois que les modifications via jtextfield sont exécutées sur le thread "awt-eventqueue-0" (le thread de répartition des événements de swing, non ?), tandis que les modifications via view sont exécutées sur le thread "thread-0". Dans mon implémentation, "thread-0" est le thread sur lequel j'exécute la boucle de pompe de graphstream, en attendant les événements qui se produisent dans le thread du visualiseur de graphstream et en les copiant dans "thread-0". D'après ma compréhension de la documentation graphstream :
Ai-je bien compris la documentation ?
Dans mon implémentation, j'ai choisi d'accéder au graphique de graphstream depuis un autre thread en dehors du thread swing. Je déduis donc des cas de tests 3 et 4 précédemment exécutés :
J'ai l'impression que je fais la mauvaise chose avec tous ces sujets. Pouvez-vous m'aider?
J'essaie de faire un exemple de travail minimal (mwe) pour reproduire mon problème. Voici le contenu du fichier source Java nodesynctest.java :
package mwe; import java.awt.borderlayout; import java.awt.component; import java.awt.gridlayout; import java.awt.event.actionevent; import java.awt.event.actionlistener; import java.util.map; import javax.swing.borderfactory; import javax.swing.jframe; import javax.swing.jlabel; import javax.swing.jpanel; import javax.swing.jtextfield; import javax.swing.swingconstants; import org.apache.commons.lang3.math.numberutils; import org.graphstream.graph.graph; import org.graphstream.graph.node; import org.graphstream.graph.implementations.multigraph; import org.graphstream.ui.graphicgraph.graphicgraph; import org.graphstream.ui.graphicgraph.graphicnode; import org.graphstream.ui.swing_viewer.swingviewer; import org.graphstream.ui.view.view; import org.graphstream.ui.view.viewer; import org.graphstream.ui.view.viewerlistener; import org.graphstream.ui.view.viewerpipe; class nodesynctest { public static void main(string[] args) { system.out.println("nodesynctest.main : " + thread.currentthread().getname()); javax.swing.swingutilities.invokelater(new runnable() { @override public void run() { system.out.println("swingutilities.invokelater.runnable.run : " + thread.currentthread().getname()); new nodesynctest(); } }); } nodesynctest() { map<string, mynode> mynodes = map.of( "fixed_top_left", new mynode(-2, 2), "fixed_top_right", new mynode(2, 2), "fixed_bottom_left", new mynode(-2, -2), "fixed_bottom_right", new mynode(2, -2), "unfixed", new mynode(0, 0) ); graphstreamcontrol graphstreamcontrol = new graphstreamcontrol(mynodes); jtextfieldscontrol jtextfieldscontrol = new jtextfieldscontrol(mynodes); graphstreamcontrol.jtextfieldscontrol = jtextfieldscontrol; jtextfieldscontrol.graphstreamcontrol = graphstreamcontrol; graphstreamcontrol.fillgraphstreamgraph(); jframe maindialog = new jframe(); maindialog.setdefaultcloseoperation(jframe.exit_on_close); maindialog.setsize(300, 300); maindialog.getcontentpane().add((component) graphstreamcontrol.view, borderlayout.center); maindialog.getcontentpane().add(jtextfieldscontrol.panel, borderlayout.south); maindialog.setlocationrelativeto(null); maindialog.setvisible(true); graphstreamcontrol.startpumploop(); } class graphstreamcontrol { map<string, mynode> mynodes; mynode myunfixednode; graph graphstreamgraph; viewer viewer; view view; viewerpipe viewerpipe; jtextfieldscontrol jtextfieldscontrol; graphstreamcontrol(map<string, mynode> mynodes) { this.mynodes = mynodes; myunfixednode = mynodes.get("unfixed"); system.setproperty("org.graphstream.ui", "swing"); graphstreamgraph = new multigraph(""); viewer = new swingviewer(graphstreamgraph, viewer.threadingmodel.graph_in_another_thread); viewer.disableautolayout(); view = viewer.adddefaultview(false); viewerpipe = viewer.newviewerpipe(); viewerpipe.addsink(graphstreamgraph); viewerpipe.addviewerlistener(new viewerlistener() { @override public void viewclosed(string viewname) {} @override public void buttonpushed(string id) {} @override public void buttonreleased(string id) { system.out.println("viewerlistener.buttonreleased : " + thread.currentthread().getname()); if ("unfixed".equals(id)) { graphicgraph graphicgraph = viewer.getgraphicgraph(); graphicnode unfixedgraphstreamnode = (graphicnode) graphicgraph.getnode("unfixed"); myunfixednode.x = unfixedgraphstreamnode.getx(); myunfixednode.y = unfixedgraphstreamnode.gety(); jtextfieldscontrol.update(); } } @override public void mouseover(string id) {} @override public void mouseleft(string id) {} }); } public void fillgraphstreamgraph() { for (var entry : mynodes.entryset()) { string nodeid = entry.getkey(); mynode mynode = entry.getvalue(); node graphstreamnode = graphstreamgraph.addnode(nodeid); graphstreamnode.setattribute("xy", mynode.x, mynode.y); graphstreamnode.setattribute("ui.label", nodeid); graphstreamnode.setattribute("ui.style", "text-alignment: under;"); } } void startpumploop() { new thread(() -> { system.out.println("graphstreamcontrol.startpumploop : " + thread.currentthread().getname()); while (true) { try { viewerpipe.blockingpump(); } catch (interruptedexception e) { e.printstacktrace(); } } }).start(); } void update() { graphicgraph graphicgraph = viewer.getgraphicgraph(); graphicnode unfixedgraphstreamnode = (graphicnode) graphicgraph.getnode("unfixed"); unfixedgraphstreamnode.setattribute("xy", myunfixednode.x, myunfixednode.y); } } class jtextfieldscontrol { map<string, mynode> mynodes; mynode myunfixednode; jpanel panel; jtextfield xtextfield; jtextfield ytextfield; graphstreamcontrol graphstreamcontrol; jtextfieldscontrol(map<string, mynode> mynodes) { this.mynodes = mynodes; myunfixednode = mynodes.get("unfixed"); panel = new jpanel(new gridlayout(1, 4)); jlabel xlabel = new jlabel("x:", swingconstants.right); xlabel.setborder(borderfactory.createemptyborder(5, 5, 5, 5)); panel.add(xlabel); xtextfield = new jtextfield(3); xtextfield.sethorizontalalignment(swingconstants.right); xtextfield.settext(double.tostring(myunfixednode.x)); xtextfield.getcaret().setdot(0); xtextfield.addactionlistener(new actionlistener() { public void actionperformed(actionevent event) { system.out.println("jtextfieldscontrol - actionperformed on xtextfield : " + thread.currentthread().getname()); string xnodestring = xtextfield.gettext(); double xnodedouble = numberutils.todouble(xnodestring); myunfixednode.x = xnodedouble; graphstreamcontrol.update(); } }); panel.add(xtextfield); jlabel ylabel = new jlabel("y:", swingconstants.right); ylabel.setborder(borderfactory.createemptyborder(5, 5, 5, 5)); panel.add(ylabel); ytextfield = new jtextfield(3); ytextfield.sethorizontalalignment(swingconstants.right); ytextfield.settext(double.tostring(myunfixednode.y)); ytextfield.getcaret().setdot(0); ytextfield.addactionlistener(new actionlistener() { public void actionperformed(actionevent event) { system.out.println("jtextfieldscontrol - actionperformed on ytextfield : " + thread.currentthread().getname()); string ynodestring = ytextfield.gettext(); double ynodedouble = numberutils.todouble(ynodestring); myunfixednode.y = ynodedouble; graphstreamcontrol.update(); } }); panel.add(ytextfield); } void update() { string xnodestring = double.tostring(myunfixednode.x); xtextfield.settext(xnodestring); xtextfield.getcaret().setdot(0); string ynodestring = double.tostring(myunfixednode.y); ytextfield.settext(ynodestring); ytextfield.getcaret().setdot(0); } } class mynode { double x; double y; mynode(double x, double y) { this.x = x; this.y = y; } } }
Voici le fichier pom maven pom.xml utilisé pour construire le pot exécutable avec toutes les dépendances :
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>mwe</groupId> <artifactId>mwe</artifactId> <version>0.0.1-SNAPSHOT</version> <name>MWE</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.14.0</version> </dependency> <dependency> <groupId>org.graphstream</groupId> <artifactId>gs-core</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.graphstream</groupId> <artifactId>gs-ui-swing</artifactId> <version>2.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <!-- Build an executable JAR --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <!-- here we specify that we want to use the main method within the App class --> <mainClass>mwe.NodeSyncTest</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>mwe.NodeSyncTest</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build> </project>
Pour utiliser ces deux fichiers, créez simplement un dossier mwe/, mettez nodesynctest.java dans mwe/src/main/java/mwe/ et pom.xml dans mwe/, puis exécutez dans mwe/
mvn 编译程序集:single
et
java -jar 目标/mwe-0.0.1-snapshot-jar-with-dependency.jar
Voici le dossier complet du projet : mwe.zip
Après quelques débogages, j'ai finalement trouvé le problème. J'ai juste besoin de changer la ligne :
unfixedgraphstreamnode.setattribute("xy", myunfixednode.x, myunfixednode.y);
Par deux lignes :
unfixedgraphstreamnode.setattribute("x", myunfixednode.x); unfixedgraphstreamnode.setattribute("y", myunfixednode.y);
Faire fonctionner correctement.
Pourquoi
unfixedgraphstreamnode.setattribute("xy", myunfixednode.x, myunfixednode.y);
Ne pas travailler reste un mystère pour moi. En fait, la documentation de https://www.php.cn/link/c305a250710e95cf6bad18c18a1c02f4 et https://www.php.cn/link/7c097a5ed40a8d91afd49026dd3b1062 indique que nous pouvons utiliser xy
属性来设置节点的坐标。但也鼓励使用属性 xyz
. J'ai donc essayé de changer le code en :
unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0);
并且它有效!我将在项目的 github 存储库上发布问题。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!