org.graphstream.ui.fx_viewer.FxViewer Maven / Gradle / Ivy
Show all versions of gs-ui-javafx Show documentation
/*
* This file is part of GraphStream .
*
* GraphStream is a library whose purpose is to handle static or dynamic
* graph, create them from scratch, file or any source and display them.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL or under the terms of the GNU LGPL as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
/**
* @author Antoine Dutot
* @author Guilhelm Savin
* @author Hicham Brahimi
*/
package org.graphstream.ui.fx_viewer;
import org.graphstream.graph.Graph;
import org.graphstream.stream.ProxyPipe;
import org.graphstream.stream.Source;
import org.graphstream.stream.thread.ThreadProxyPipe;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.javafx.FxGraphRenderer;
import org.graphstream.ui.view.GraphRenderer;
import org.graphstream.ui.view.View;
import org.graphstream.ui.view.Viewer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.util.Duration;
/**
* Set of views on a graphic graph.
*
*
* The viewer class is in charge of maintaining :
*
* - A "graphic graph" (a special graph that internally stores the graph under
* the form of style sets of "graphic" elements, suitable to draw the graph, but
* not to adapted to used it as a general graph),
* - The eventual proxy pipe from which the events come from (but graph events
* can come from any kind of source),
* - A default view, and eventually more views on the graphic graph.
* - A flag that allows to repaint the view only if the graphic graph changed.
*
-
*
*
*
*
* The graphic graph can be created by the viewer or given at construction (to
* share it with another viewer).
*
*
*
* Once created, the viewer runs in a loop inside the UI thread. You
* cannot call methods on it directly if you are not in this thread. The
* only operation that you can use in other threads is the constructor, the
* {@link #addView(View)}, {@link #removeView(String)} and the {@link #close()}
* methods. Other methods are not protected from concurrent accesses.
*
*
*
* Some constructors allow a {@link ProxyPipe} as argument. If given, the
* graphic graph is made listener of this pipe and the pipe is "pumped" during
* the view loop. This allows to run algorithms on a graph in the main thread
* (or any other thread) while letting the viewer run in the ui thread.
*
*
*
* Be very careful: due to the nature of graph events in GraphStream, the viewer
* is not aware of events that occured on the graph before its creation.
* There is a special mechanism that replay the graph if you use a proxy pipe or
* if you pass the graph directly. However, when you create the viewer by
* yourself and only pass a {@link Source}, the viewer will not display
* the events that occured on the source before it is connected to it.
*
*/
public class FxViewer extends Viewer {
// Attributes
/**
* Timer in the JavaFX thread.
*/
protected Timeline timeline ;
/**
* Delay in milliseconds between frames.
*/
protected int delay = 40;
/**
* Name of the default view.
*/
public static String DEFAULT_VIEW_ID = "defaultView";
public String getDefaultID() {
return DEFAULT_VIEW_ID ;
}
// Construction
/**
* The graph or source of graph events is in another thread or on another
* machine, but the pipe already exists. The graphic graph displayed by this
* viewer is created.
*
* @param source
* The source of graph events.
*/
public FxViewer(ProxyPipe source) {
graphInAnotherThread = true;
init(new GraphicGraph(newGGId()), source, (Source) null);
}
/**
* We draw a pre-existing graphic graph. The graphic graph is maintained by
* its creator.
*
* @param graph
* THe graph to draw.
*/
public FxViewer(GraphicGraph graph) {
graphInAnotherThread = false;
init(graph, (ProxyPipe) null, (Source) null);
}
/**
* New viewer on an existing graph. The viewer always run in the UI
* thread, therefore, you must specify how it will take graph events from
* the graph you give. If the graph you give will be accessed only from the
* UI thread use ThreadingModel.GRAPH_IN_GUI_THREAD. If the graph you use
* is accessed in another thread use ThreadingModel.GRAPH_IN_ANOTHER_THREAD.
* This last scheme is more powerful since it allows to run algorithms on
* the graph in parallel with the viewer.
*
* @param graph
* The graph to render.
* @param threadingModel
* The threading model.
*/
public FxViewer(Graph graph, ThreadingModel threadingModel) {
switch (threadingModel) {
case GRAPH_IN_GUI_THREAD:
graphInAnotherThread = false;
init(new GraphicGraph(newGGId()), (ProxyPipe) null, graph);
enableXYZfeedback(true);
break;
case GRAPH_IN_ANOTHER_THREAD:
graphInAnotherThread = true;
ThreadProxyPipe tpp = new ThreadProxyPipe();
tpp.init(graph, true);
init(new GraphicGraph(newGGId()), tpp, (Source) null);
enableXYZfeedback(false);
break;
case GRAPH_ON_NETWORK:
throw new RuntimeException("TO DO, sorry !:-)");
}
}
/**
* Initialise the viewer.
*
* @param graph
* The graphic graph.
* @param ppipe
* The source of events from another thread or machine (null if
* source != null).
* @param source
* The source of events from this thread (null if ppipe != null).
*/
public void init(GraphicGraph graph, ProxyPipe ppipe, Source source) {
super.graph = graph;
super.pumpPipe = ppipe;
super.sourceInSameThread = source;
this.timeline = new Timeline(new KeyFrame(Duration.millis(delay), actionEvent -> actionPerformed()));
assert ((ppipe != null && source == null) || (ppipe == null && source != null));
if (pumpPipe != null)
pumpPipe.addSink(graph);
if (sourceInSameThread != null) {
if (source instanceof Graph)
replayGraph((Graph) source);
sourceInSameThread.addSink(graph);
}
//timer.setCoalesce(true);
//timer.setRepeats(true);
//timer.start();
timeline.setCycleCount(Timeline.INDEFINITE);
String debugModule = System.getProperty("org.graphstream.debug");
new Thread(() -> {
if (debugModule != null && !debugModule.isEmpty()) {
// if debug is enable, add a delay to allow time for the interface to be created (some compilation require that)
System.out.println("-- Debug Mode");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
timeline.play();
}).start();
}
/**
* Close definitively this viewer and all its views.
*/
public void close() {
synchronized (views) {
disableAutoLayout();
for (View view : views.values())
view.close(graph);
timeline.stop();
//timer.removeActionListener(this);
if (pumpPipe != null)
pumpPipe.removeSink(graph);
if (sourceInSameThread != null)
sourceInSameThread.removeSink(graph);
graph = null;
pumpPipe = null;
sourceInSameThread = null;
timeline = null;
}
}
@Override
public GraphRenderer, ?> newDefaultGraphRenderer() {
// TODO Auto-generated method stub
return new FxGraphRenderer();
}
/**
* Build the default graph view and insert it. The view identifier is
* {@link #DEFAULT_VIEW_ID}. You can request the view to be open in its own
* frame.
*
* @param renderer
* @param openInAFrame
* It true, the view is placed in a frame, else the view is only
* created and you must embed it yourself in your application.
*/
public View addDefaultView(boolean openInAFrame, GraphRenderer, ?> renderer) {
synchronized (views) {
View view = renderer.createDefaultView(this, getDefaultID());
addView(view);
if (openInAFrame)
view.openInAFrame(true);
return view;
}
}
/**
* Called on a regular basis by the timer. Checks if some events occurred
* from the graph pipe or from the layout pipe, and if the graph changed,
* triggers a repaint. Never call this method, it is called by a UI Timer
* automatically.
*/
public void actionPerformed() {
synchronized (views) {
// long t1=System.currentTimeMillis();
// long gsize1=graph.getNodeCount();
if (pumpPipe != null)
pumpPipe.pump();
// long gsize2=graph.getNodeCount();
// long t2=System.currentTimeMillis();
if (layoutPipeIn != null)
layoutPipeIn.pump();
// long t3=System.currentTimeMillis();
// Prevent the timer from using a empty graph to display
if(graph != null){
boolean changed = graph.graphChangedFlag();
if (changed) {
computeGraphMetrics();
// long t4=System.currentTimeMillis();
for (View view : views.values())
view.display(graph, changed);
}
// long t5=System.currentTimeMillis();
graph.resetGraphChangedFlag();
}
// System.err.printf("display pump=%f layoutPump=%f metrics=%f
// display=%f (size delta=%d size1=%d size2=%d)%n",
// (t2-t1)/1000.0, (t3-t2)/1000.0, (t4-t3)/1000.0, (t5-t4)/1000.0,
// (gsize2-gsize1), gsize1, gsize2);
}
}
}