Heim  >  Artikel  >  Synchronisierungsprobleme zwischen GraphStreams View und JTextField

Synchronisierungsprobleme zwischen GraphStreams View und JTextField

WBOY
WBOYnach vorne
2024-02-22 12:30:171055Durchsuche

PHP Editor Strawberry stellt Ihnen in diesem Artikel die Java-Fragen und Antworten zu Synchronisierungsproblemen zwischen GraphStream's View und JTextField vor. Bei der Verwendung der GraphStream-Bibliothek treten manchmal Probleme bei der Datensynchronisierung zwischen View und JTextField auf. In diesem Artikel erfahren Sie, wie Sie dieses Problem lösen und Ihre Java-Programme reibungsloser und effizienter gestalten können.

Inhalt der Ausgabe

Ich habe meine Ausgabe unter https://github.com/graphstream/gs-ui-swing/issues/19#issue-2109865450 gepostet. Aber da die letzte Antwort in diesem Repository am 10. Juni 2021 veröffentlicht wurde und andere Fragen ohne Antworten gepostet wurden, bin ich mir nicht sicher, ob den Fragen dort noch jemand Aufmerksamkeit schenkt. Deshalb stelle ich meine Frage hier noch einmal.

Ich habe einen JFrame erstellt, der aus einer Ansicht und zwei JTextfeldern besteht:

Die Ansicht zeigt fünf Knoten: Vier sind nur Orientierungspunkte und werden voraussichtlich nicht vom Benutzer verschoben („fixed_*“), und einer wird vom Benutzer verschoben („unfixed“). Zwei Jtextfelder zeigen die Koordinaten von „nicht fixierten“ Knoten an. Sowohl view als auch jtextfield müssen miteinander synchron sein. Tatsächlich müssen beide Jtextfelder entsprechend aktualisiert werden, wenn der Benutzer den „nicht angehefteten“ Knoten in der Ansicht verschiebt:

Umgekehrt muss die Ansicht auch entsprechend aktualisiert werden, wenn der Benutzer die Koordinaten in einem der Jtextfelder ändert:

Hier sind vier Testfälle:

  • Testfall 1: Der Benutzer führt die App aus und verschiebt einen „nicht angehefteten“ Knoten in der Ansicht.
  • Testfall 2: Der Benutzer führt die Anwendung aus und ändert die Koordinaten in einem der Jtextfelder.
  • Testfall 3: Der Benutzer führt die Anwendung aus, ändert die Koordinaten in einem der Jtextfelder und verschiebt dann den „nicht fixierten“ Knoten in der Ansicht.
  • Testfall 4: Der Benutzer führt die Anwendung aus, verschiebt einen „nicht fixierten“ Knoten in der Ansicht und ändert dann die Koordinaten in einem der Jtextfelder.

Testfälle 1, 2 und 3 funktionieren einwandfrei, aber Testfall 4 funktioniert nicht. Tatsächlich wird in Testfall 4, sobald der Benutzer den „nicht angehefteten“ Knoten in der Ansicht verschiebt, Änderungen an den Koordinaten in einem der Jtextfelder die Ansicht nicht aktualisieren.

Ich versuche den Unterschied zwischen der Ausführung der Testfälle 3 und 4 zu analysieren. Dazu habe ich an verschiedenen Stellen im Code den Namen des aktuellen Threads gedruckt. Ich sehe, dass Änderungen über jtextfield auf dem Thread „awt-eventqueue-0“ (dem Event-Dispatching-Thread von Swing, nicht wahr?) ausgeführt werden, während Änderungen über view auf dem Thread „thread-0“ ausgeführt werden. In meiner Implementierung ist „thread-0“ der Thread, auf dem ich die Pump-Schleife von Graphstream ausführe, auf Ereignisse warte, die im Viewer-Thread von graphstream auftreten, und sie zurück in „thread-0“ kopiere. Aus meinem Verständnis der Graphstream-Dokumentation:

  • Der Viewer von Graphstream läuft immer im Event-Dispatch-Thread (EDT) von Swing,
  • Auf das Diagramm des Graphstreams, das mit dem Viewer des Graphstreams verknüpft ist, kann über das EDT oder einen anderen Thread zugegriffen werden, abhängig vom verwendeten Threadingmodell des Graphstreams,
  • Der Zugriff auf Diagramme aus einem anderen Thread ist leistungsfähiger als edt, da es die Ausführung von Algorithmen auf dem Diagramm parallel zum Viewer von Graphstream ermöglicht.

Habe ich die Dokumentation vollständig verstanden?

In meiner Implementierung habe ich mich dafür entschieden, von einem anderen Thread außerhalb des Swing-Threads auf den Graphen von Graphstream zuzugreifen. Ich schließe also aus den zuvor durchgeführten Testfällen 3 und 4:

  • Das Aktualisieren der Graphstream-Ansicht von edt verhindert nicht zukünftige Aktualisierungen der Graphstream-Ansicht von „Thread-0“ (Testfall 3),
  • Aber Aktualisierungen der Graphstream-Ansicht in „thread-0“ verhindern zukünftige Aktualisierungen der Graphstream-Ansicht in edt (Testfall 4).

Ich habe den Eindruck, dass ich mit all diesen Threads das Falsche mache. Kannst du mir helfen?

Ich versuche, ein minimales Arbeitsbeispiel (mwe) zu erstellen, um mein Problem zu reproduzieren. Das Folgende ist der Inhalt der Java-Quelldatei 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;
        }
    }
    
}

Dies ist die Maven-POM-Datei pom.xml, die zum Erstellen der ausführbaren JAR-Datei mit allen Abhängigkeiten verwendet wird:

<?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>

Um diese beiden Dateien zu verwenden, erstellen Sie einfach einen Ordner mwe/, fügen Sie nodesynctest.java in mwe/src/main/java/mwe/ und pom.xml in mwe/ ein und führen Sie es dann in mwe/ aus

mvn 编译程序集:single

und

java -jar 目标/mwe-0.0.1-snapshot-jar-with-dependency.jar

Das ist der komplette Projektordner: mwe.zip

Solution

Nach einigem Debuggen habe ich endlich das Problem gefunden. Ich muss nur die Zeile ändern :

unfixedgraphstreamnode.setattribute("xy", myunfixednode.x, myunfixednode.y);

Durch zwei Zeilen:

unfixedgraphstreamnode.setattribute("x", myunfixednode.x);
unfixedgraphstreamnode.setattribute("y", myunfixednode.y);

Stellen Sie sicher, dass alles ordnungsgemäß funktioniert.

Warum

unfixedgraphstreamnode.setattribute("xy", myunfixednode.x, myunfixednode.y);

Es ist mir immer noch ein Rätsel, dass es nicht funktioniert. Tatsächlich heißt es in der Dokumentation für https://www.php.cn/link/c305a250710e95cf6bad18c18a1c02f4 und https://www.php.cn/link/7c097a5ed40a8d91afd49026dd3b1062, dass wir xy 属性来设置节点的坐标。但也鼓励使用属性 xyz verwenden können. Also habe ich versucht, den Code zu ändern:

unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0);

并且它有效!我将在项目的 github 存储库上发布问题。

Das obige ist der detaillierte Inhalt vonSynchronisierungsprobleme zwischen GraphStreams View und JTextField. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen