I created a graph using JUNG, and I want to add a combobox that give the user the possibility to change the used layout (Circle, KK, FR, etc.)
But I couldn't do that.
that's how I visualize my graph:
// The Layout<V, E> is parameterized by the vertex and edge types
this.layout = new CircleLayout<Ressource,Float>(this.graph);
layout.setSize(new Dimension(500, 500)); // sets the initial size of the
// layout space
// The BasicVisualizationServer<V,E> is parameterized by the vertex and
// edge types
this.vv = new BasicVisualizationServer<Ressource, Float>(layout);
vv.setPreferredSize(new Dimension(550, 550)); // Sets the viewing area
// size
// Adjust the edges thikness
Transformer<Float, Stroke> edgeStroke = new Transformer<Float, Stroke>() {
#Override
public Stroke transform(Float arg0) {
return new BasicStroke(arg0);
}
};
vv.getRenderContext().setEdgeStrokeTransformer(edgeStroke);
// Show vertex and edge labels
vv.getRenderContext().setVertexLabelTransformer(
new Transformer<Ressource, String>() {
public String transform(Ressource r) {
return (r.nom);
}
});
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());
I tried to create a whole new BasicVisualizationServer object having each time a different Layout, but it didn't work, It sticks with the first layout (Circle in my case).
What is the best way to change layout ?
Thanks guys !
The source file for this demo (in your distribution) demonstrates how to do it: http://jung.sourceforge.net/doc/api/edu/uci/ics/jung/samples/ShowLayouts.html
Related
I'm trying to visualize a tree graph with the JUNG library.
The code I use is:
JPanel base = new JPanel();
Graph<String, String> grafo = OntologyGraph.getGraph(ontology);
Layout<String, String> layout = new TreeLayout<String, String>((Forest<String, String>) grafo);
VisualizationViewer<String, String> vv = new VisualizationViewer<String, String>(layout);
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<String>());
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<String>());
vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
final DefaultModalGraphMouse<String, Number> graphMouse3 = new DefaultModalGraphMouse<>();
vv.setGraphMouse(graphMouse3);
graphMouse3.setMode(DefaultModalGraphMouse.Mode.PICKING);
base.add(vv);
return base;
It displays this
Now I want to change the circled verteces with a labelled JButton and to enlarge the space between them, but I can't find a tutorial on the web to achive this.
Changing the spacing between the nodes is easy enough; just use the TreeLayout constructor that accepts the distx and disty parameters.
Providing a JButton for each node is not something that JUNG natively supports, although you could do some hacking to enable it.
What problem are you trying to solve by using JButtons for nodes?
Here is some example code that you can use to open a JFrame with information about the node that was clicked (when you are in picking mode). If you want to have it respond to node clicks even when you are in the transforming mode, you'd have to change the graphmouseplugins a little to not remove the PickingGraphMousePlugin when in transforming mode.
vv.getRenderContext().getPickedVertexState().addItemListener(new ItemListener(){
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
JFrame frame = new JFrame("Vertex "+e.getItem()+" picked");
frame.getContentPane().add(new JButton("hello from "+e.getItem()));
frame.setSize(new Dimension(200,100));
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
});
If you want the vertices to look more like rectangular buttons, the VertexLableAsShapeDemo may help. That demo uses JLabels to draw the vertices
I'm working on my personal family tree in Java/Eclipse, and happily bumped into prefuse as for graphic representation.
So far the result looks adaquate in regard to my database feed, but I'm still missing key points to make it easier to browse.
Point 1: verteces represent either a person or a union, and my graph is directed from older to younger members. This is reflected by the arrows on the edges. Yet I'd love to group the arrows in 1 direction only (I'm trying to group generations together if you like), but I can't start to find how to do that. For information, I'm using the NodeLinkTreeLayout as of now.
Point 2: aside from the graph itself, my app main window contains a second JPanel where I would like to modify / insert members. So I want to add an action to each node to call the procedures in the second JPanel. My research on how to access a java class from a node are inconclusive so far, it seems that all the examples from the starter prefuse pack are only based on graph interaction.
There it is. You might already have understood that I'm very new to prefuse and not a pro in Java. So any comment / directions / advice would really be appreciated. I will add a screecap and my graph code so you can see what could be done better.
Thank you for your time, and looking forward to reading your insights.
yorran
public class ShowGraph extends Display {
public static final String EDGES = "graph.edges";
public ShowGraph() {
super(new Visualization());
Graph mG = FamGraph.getGraph();
m_vis.addGraph("graph", mG);
m_vis.setInteractive("graphe.edges", null, false);
m_vis.setValue("graph.nodes", null, VisualItem.SHAPE, new Integer(Constants.SHAPE_ELLIPSE));
EdgeRenderer edgeR = new EdgeRenderer(Constants.EDGE_TYPE_CURVE, Constants.EDGE_ARROW_FORWARD);
LabelRenderer nodeR = new LabelRenderer("name");
nodeR.setRoundedCorner(8, 8);
nodeR.setHorizontalAlignment(Constants.LEFT);
DefaultRendererFactory drf = new DefaultRendererFactory();
drf.setDefaultRenderer(nodeR);
drf.setDefaultEdgeRenderer(edgeR);
m_vis.setRendererFactory(drf);
int[] palette = new int[] {
ColorLib.rgb(255, 180, 180), ColorLib.rgb(190, 190, 255)
};
DataColorAction nFill = new DataColorAction("graph.nodes", "label", Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
ColorAction edges = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.gray(230));
ColorAction arrow = new ColorAction("graph.edges", VisualItem.FILLCOLOR, ColorLib.gray(230));
ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));
ActionList color = new ActionList();
color.add(nFill);
color.add(edges);
color.add(arrow);
color.add(text);
ActionList layout = new ActionList(Activity.INFINITY);
//layout.add(new ForceDirectedLayout("graph", true));
layout.add(new NodeLinkTreeLayout("graph"));
layout.add(new RepaintAction());
m_vis.putAction("color", color);
m_vis.putAction("layout", layout);
setSize(1200, 900); //size controlled by parent jpanel - Comment out after tests
pan(360, 250);
setHighQuality(true);
addControlListener(new DragControl());
addControlListener(new PanControl());
addControlListener(new ZoomControl());
addControlListener(new ZoomToFitControl());
m_vis.run("color");
m_vis.run("layout");
}
public static void main(String[] args) {
Fulltree.fireUp();
ShowGraph mG = new ShowGraph();
JFrame frame = new JFrame("My family chart");
JPanel thePanel = new JPanel();
frame.getContentPane().add(thePanel);
thePanel.add(mG);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
So after a lot of reseach, I'm answering to my own questions in case someone meets the same issues :
as for point 1 : ForceDirectedGraph is a lot better than NodeLinkTreeLayout, especially when your graph starts counting many members. Family branches make a lot more sense than viewing generations aligned.
as for point 2 : node related actions are the way to go, through a ControlListener:
addControlListener(new ControlAdapter() {
public void itemClicked(VisualItem item, MouseEvent e) {
// anything you need here
// even filter right and left click for a sub menu
}
});
One more thing : if you add actions to your graph (search, predicates...), make sure to stop them if you need to rebuild your graph at some point. If you don't, your actions will generate errors you will spend hours (if not days) to debug.
I'm using JGraphX to draw a graph,using mxCircleLayout as basic representation, but I want to change some behavior. For example I want the EDGESTYLE_TOPTOBOTTOM, so before build mxGraphComponent I define a new edge style:
'
JGraphXAdapter graphAdapter = new JGraphXAdapter(this.stradario.getStradario());
graphAdapter.getModel().beginUpdate();
try {
Map<String, Object> edgeStyle = new HashMap<String, Object>();
edgeStyle = graphAdapter.getStylesheet().getDefaultEdgeStyle();
edgeStyle.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM);
mxStylesheet stylesheet = new mxStylesheet();
stylesheet.setDefaultEdgeStyle(edgeStyle);
graphAdapter.setStylesheet(stylesheet);
} finally {
graphAdapter.getModel().endUpdate();
}
mxCircleLayout layout = new mxCircleLayout(graphAdapter);
layout.execute(graphAdapter.getDefaultParent());
mxGraphComponent graphComponent = new mxGraphComponent(graphAdapter);
graphComponent.getViewport().setBackground(Color.white);
'
The graph was drawn as a circle layout, but edges are not in TOPTOBOTTOM style.
First draw
Then, if I draw a new edge, or I change an existing one, the edge is drawn with TOPTOBOTTON style.
Modified edge take the TOPTOBOTTOMSTYLE
I don't understand why the initial drawn is without TOPTOBOTTOM style and the modified edge was done with the new style.
The layout disables edge styles by default. Set the disableEdgeStyle member to false.
mxCircleLayout layout = new mxCircleLayout(graphAdapter);
layout.setDisableEdgeStyle(false);
layout.execute(graphAdapter.getDefaultParent());
I draw a simple graph (using Java swing interface).
The graph is OK, but the default / automatic vertices size is small for my purpose. How to set the vertex size (rectangle or ellipse)?
I can change some graph behavior using put() ( like put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE)) but not the size of vertices.
The graph is OK, but the default / automatic vertices size is small for my purpose. How to set the vertex size (rectangle or ellipse)?
May I have misunderstood many about jgraphx?
The documentation is very difficult to understand, very cryptic (at least for me), can I have suggestions for books or links for better understand jgraphx for Java.
One more detail: the graph isn't built element by element, but it come from a graph built with jgraphT using jGraphXAdapter
JGraphXAdapter<Incrocio, Strada> graphAdapter =
new JGraphXAdapter<Incrocio, Strada>(listenableGraph);
Thank to Andrew for her patience.
English is not my language, and sometime I make a mistakes even in italian (my native language). Add that I'm new in Java. New even in this forum (an related rules, and this may not be the right place for my replay). I've not read your message previous with the due attention.
I put the code, whith a partial solution, I hope it's more clear.
package it.rex.view;
public class StradarioViewBis2 extends JFrame {
private JPanel contentPane;
// listenableGraph is a complete graph done with JgraphT
public StradarioViewBis2(ListenableGraph<Incrocio, Strada> listenableGraph) {
JGraphXAdapter<Incrocio, Strada> graphAdapter =
new JGraphXAdapter<Incrocio, Strada>(listenableGraph);
//graphAdapter.getStylesheet().getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, "1"); //remove label from edge
graphAdapter.getModel().beginUpdate();
try {
graphAdapter.clearSelection();
graphAdapter.selectAll();
Object[] cells = graphAdapter.getSelectionCells(); //here you have all cells
// Iterate into graph to change cells
for (Object c : cells) {
mxCell cell = (mxCell) c; //cast
mxGeometry geometry = cell.getGeometry();
if (cell.isVertex()) { //isVertex
// Here I can change vertex dimensions
geometry.setWidth(40);
geometry.setHeight(40);
}else{ //is not a vertex, so u can get source and target
// cell.setStyle("orthogonalEdgeStyle");
// cell.getChildAt(x); //Returns the child at the specified index. (target)
}
}
}
finally
{
graphAdapter.getModel().endUpdate();
}
mxIGraphLayout layout = new mxCircleLayout(graphAdapter); // questo sistema le posizione degli elementi
layout.execute(graphAdapter.getDefaultParent());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 550, 450);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
mxGraphComponent graphComponent = new mxGraphComponent(graphAdapter); // is JScrollPane extension
graphComponent.setPageVisible(true);
graphComponent.setBounds(30, 30, 300, 300);
contentPane.add(graphComponent);
//graphComponent.setEnabled(false);
graphComponent.getViewport().setBackground(Color.white);
;
}
}
After some tries, I've found that the vertex's size is back to initial dimension when I change the cell's label, not when I move the focus away from cell.
I think I need to change the default behavior of the graph, but i'm not yet discovered how.
This is the result, after editing label on top vetex:
enter image description here
I want to use Prefuse to visualise a graph. I followed their tutorial and tried their sample application. Its sourcecode can be found here
However, even if I simply copy the full code, the resulting graph does not look as displayed in the tutorial. It is only half visible, stuck in JPanel's upper left corner. Some parts of it are missing becuase they would have to be displayed outside the panel.
I tried with some graphs of my own, but I keep running into the same phenomenon.
I suppose this is not expected behaviour, but I have no idea where to hunt for the problem. I don't know if this is a problem with Swing(x) or prefuse or ... ?
Update:
This is the revised code. I did not change much from the example, only added what trashgod suggested.
package visualise;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.data.io.DataIOException;
import prefuse.data.io.GraphMLReader;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
public class PrefuseExample {
public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// -- 1. load the data ------------------------------------------------
// load the socialnet.xml file. it is assumed that the file can be
// found at the root of the java classpath
Graph graph = null;
try {
graph = new GraphMLReader().readGraph("../../resources/visualisation/prefuse/Prefuse-master/data/socialnet.xml");
} catch ( DataIOException e ) {
e.printStackTrace();
System.err.println("Error loading graph. Exiting...");
System.exit(1);
}
// -- 2. the visualization --------------------------------------------
// add the graph to the visualization as the data group "graph"
// nodes and edges are accessible as "graph.nodes" and "graph.edges"
Visualization vis = new Visualization();
vis.add("graph", graph);
vis.setInteractive("graph.edges", null, false);
// -- 3. the renderers and renderer factory ---------------------------
// draw the "name" label for NodeItems
LabelRenderer r = new LabelRenderer("name");
r.setRoundedCorner(8, 8); // round the corners
// create a new default renderer factory
// return our name label renderer as the default for all non-EdgeItems
// includes straight line edges for EdgeItems by default
vis.setRendererFactory(new DefaultRendererFactory(r));
// -- 4. the processing actions ---------------------------------------
// create our nominal color palette
// pink for females, baby blue for males
int[] palette = new int[] {
ColorLib.rgb(255,180,180), ColorLib.rgb(190,190,255)
};
// map nominal data values to colors using our provided palette
DataColorAction fill = new DataColorAction("graph.nodes", "gender",
Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
// use black for node text
ColorAction text = new ColorAction("graph.nodes",
VisualItem.TEXTCOLOR, ColorLib.gray(0));
// use light grey for edges
ColorAction edges = new ColorAction("graph.edges",
VisualItem.STROKECOLOR, ColorLib.gray(200));
// create an action list containing all color assignments
ActionList color = new ActionList();
color.add(fill);
color.add(text);
color.add(edges);
// create an action list with an animated layout
ActionList layout = new ActionList(Activity.INFINITY);
layout.add(new ForceDirectedLayout("graph"));
layout.add(new RepaintAction());
// add the actions to the visualization
vis.putAction("color", color);
vis.putAction("layout", layout);
// -- 5. the display and interactive controls -------------------------
Display d = new Display(vis);
d.setSize(720, 500); // set display size
// drag individual items around
d.addControlListener(new DragControl());
// pan with left-click drag on background
d.addControlListener(new PanControl());
// zoom with right-click drag
d.addControlListener(new ZoomControl());
// -- 6. launch the visualization -------------------------------------
// create a new window to hold the visualization
JFrame frame = new JFrame("prefuse example");
// ensure application exits when window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(d);
frame.pack(); // layout components in window
frame.setVisible(true); // show the window
// assign the colors
vis.run("color");
// start up the animated layout
vis.run("layout");
}
});
}
}
I'm new to Prefuse, but a number of common errors can contribute to the observed problem. Looking at the example,
As discussed here, don't use setSize() on the Display when you really mean to override getPreferredSize().
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
The initial clustering is an artifact of the graph's origin falling on the top-left corner of the Display component at the point (0, 0). Having chosen a preferred size, one can pan() to the center.
private static final int W = 640;
private static final int H = 480;
…
Display d = new Display(vis) {
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
};
d.pan(W / 2, H / 2);