-
Notifications
You must be signed in to change notification settings - Fork 14
Description
Hello,
I created a JFrame composed of one View and two JTextField:
The View displays five nodes: four are just landmarks and are not expected to be moved by the user ("fixed_*"), and one is there to be moved by the user ("unfixed"). The two JTextField show the coordinates of the "unfixed" node. Both the View and the JTextField have to be synchronized with each other. Indeed, when the user moves the "unfixed" node in the View, then the two JTextField have to be updated accordingly :
Reciprocally, when the user modifies the coordinate in one of the JTextField, then the View has to be updated accordingly too:
Here is the code:
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 javax.swing.SwingWorker;
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 NodeSyncTest2 {
public static void main(String[] args) {
new NodeSyncTest2();
}
NodeSyncTest2() {
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(1, 2)
);
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_GUI_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) {
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("x", myNode.x); // WORKS!
// graphStreamNode.setAttribute("y", myNode.y); // WORKS!
// graphStreamNode.setAttribute("xyz", myNode.x, myNode.y, 0.0); // WORKS!
graphStreamNode.setAttribute("xy", myNode.x, myNode.y); // WORKS!
graphStreamNode.setAttribute("ui.label", nodeId);
graphStreamNode.setAttribute("ui.style", "text-alignment: under;");
}
}
void startPumpLoop() {
SwingWorker<Void, Void> swingWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
while (true) {
try {
viewerPipe.blockingPump();
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
}
};
swingWorker.execute();
}
void update() {
Node unfixedGraphStreamNode = graphStreamGraph.getNode("unfixed");
// unfixedGraphStreamNode.setAttribute("x", myUnfixedNode.x); // WORKS!
// unfixedGraphStreamNode.setAttribute("y", myUnfixedNode.y); // WORKS!
// unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0); // WORKS!
unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y); // DOES NOT WORK!
}
}
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) {
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) {
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;
}
}
}
and here is the one of the Maven POM file pom.xml to build an executable JAR including all dependencies:
<?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.NodeSyncTest2</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>mwe.NodeSyncTest2</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
(to use these two files, just create a folder mwe/, put NodeSyncTest2.java into mwe/src/main/java/mwe/ and pom.xml into mwe/, and, in mwe/, run
mvn compile assembly:single
and
java -jar target/mwe-0.0.1-SNAPSHOT-jar-with-dependencies.jar
;
here is the full project folder: MWE.zip)
My issue: in the method GraphStreamControl.update(), the use of
unfixedGraphStreamNode.setAttribute("x", myUnfixedNode.x);
unfixedGraphStreamNode.setAttribute("y", myUnfixedNode.y);
or of
unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0);
updates the graph correctly. On the contrary, the use of
unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y);
doesn't update the graph. Why ?
Also, in the method GraphStreamControl.fillGraphStreamGraph(), each of
unfixedGraphStreamNode.setAttribute("x", myUnfixedNode.x);
unfixedGraphStreamNode.setAttribute("y", myUnfixedNode.y);
,
unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0);
and
unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y);
perfectly works. Why?