views:

2057

answers:

4

Does anyone have experience with the prefuse graph toolkit? Is it possible to change an already displayed graph, ie. add/remove nodes and/or edges, and have the display correctly adapt?

For instance, prefuse comes with an example that visualizes a network of friends:

http://prefuse.org/doc/manual/introduction/example/Example.java

What I would like to do is something along the lines of this:

// -- 7. add new nodes on the fly -------------------------------------
new Timer(2000, new ActionListener() {
    private Node oldNode = graph.nodes().next(); // init with random node

    public void actionPerformed(ActionEvent e) {
        // insert new node //
        Node newNode = graph.addNode();

        // insert new edge //
        graph.addEdge(oldNode, newNode);

        // remember node for next call //
        oldNode = newNode;
    }
}).start();

But it doesn't seem to work. Any hints?

A: 

You need to tell the control container ('d', in example.java) do get redrawn. Calling invalidate() should be enough (not sure, though).

Anyway, this might help you.

aldrinleal
Thanks a lot of the hint - tried it, doesn't work. I think the nodes are not implemented as AWT or Swing components, prefuse uses its own mechanism for layouting. That's probably why it doesn't care for calls to invalidate().
No, it is still Swing/AWT inside. Check the javadocs wrt damage management. Also, repaint and revalidade might work as well. If in doubt, read the Java Tutorial.
aldrinleal
Could you please have a look at one more prefuse question at http://stackoverflow.com/questions/2273068/how-to-display-3-nodes-with-3-diff-shapes-in-prefuse-library-for-java
Yatendra Goel
+6  A: 

You should be aware the several layers of prefuse:

  • Data
  • Visualization
  • Display

To be short, the three layers can be linked this way:

Graph graph = new Graph(eg. yourXML_file);
Visualization viz = new Visualization();
viz.add(GRAPH, graph);
Display disp = new Display();
disp.setVisualization(viz);

Display is a graphic component that you add to a panel as usual.

Here you only modify the data layer.

Node newNode = graph.addNode();
graph.addEdge(oldNode, newNode);

You need now to update the visual layer:

viz.run("repaint");

The repaint action has to be defined.

ActionList repaint = new ActionList();
repaint.add(new RepaintAction());
viz.putAction("repaint", repaint);

I really advise you to read the prefuse doc. And you can find a lot a resources on the official forum

At least, I can say you that prefuse is for the moment not really efficient for live graph update.

But it should not be enough, as you modified the graph structure, you have to regenerate it in the visualization (ie. recalculate the node placements etc...). There are two actions already defined in your sample code. Run them at the end of your actionPerformed.

viz.run("color");
viz.run("layout");

This method is not very efficient, because it adds a lot of computation each time you add a node, but there are not any others for the moment with prefuse.

Jérôme
Thanks a lot for the help. I have read the doc and I was aware of the three different layers, however I had not tried the repaint action. Unfortunately though, it doesn't work either. I've added the action to viz and call viz.run("repaint") at the end of actionPerformed (s. above) but to no effect.
Could you please have a look at one more prefuse question at http://stackoverflow.com/questions/2273068/how-to-display-3-nodes-with-3-diff-shapes-in-prefuse-library-for-java
Yatendra Goel
+1  A: 

Okay, after digging a bit through the prefuse sources, I now have a better understanding of how things work under the hood. I found out that actually the new nodes I create with the code above are not only added correctly to the graph, the visualization also takes note of it!

So, unlike Jerome suggests, it is not necessary to call vis.run("layout") explicitly.

The reason I thought the nodes were not added correctly was the fact that they are drawn with white background-, border- and text color - on white background that is. Not astonishing that they are a bit difficult to spot.

To fix that one has to call the color action after a new node is inserted, like this:

// insert new edge //
graph.addEdge(oldNode, newNode);
vis.run("color"); // <- this is new

(Note that this action is defined further up in the code of Example.jar under //-- 4.)

One last thing I am unsure about now is whether calling this action will make prefuse go over all graph nodes again and set their color - for very large graphs that would be undesired, of course.

Could you please have a look at one more prefuse question at http://stackoverflow.com/questions/2273068/how-to-display-3-nodes-with-3-diff-shapes-in-prefuse-library-for-java
Yatendra Goel
+4  A: 

As pointed out in my other post, the reason new nodes and edges are not visible in the original example is that the colors etc. for the nodes are not set correctly. One way to fix this is to explicitly call vis.run("color"); whenever a node or edge was added.

Alternatively, we can ensure that the color action is always running, by initializing the ActionList to which we add it (called "color" in the original example) slightly differently:

instead of

ActionList color = new ActionList();

we could write

ActionList color = new ActionList(Activity.INFINITY);

This keeps the action list running indefinitely, so that new nodes/edges will automatically be initialized for their visual appearance.

However, it is unclear to me whether this would actually be the preferred method - for things like a dynamic layout action (e.g. ForceDirectedLayout), such a declaration makes perfect sense, but for colors it seems to me that a constantly running coloring action is mostly overhead.

So, perhaps the previously posted solution of just running the "color" action explicitly (but only once) whenever the graph gets extended, might be the better choice...

Could you please have a look at one more prefuse question at http://stackoverflow.com/questions/2273068/how-to-display-3-nodes-with-3-diff-shapes-in-prefuse-library-for-java
Yatendra Goel