Question

I am working to a program that reads some xml files and creates graph diagram based on information in those xml files.

I have difficulties in making the graph diagram. I want that my graph to look: http://pic.dhe.ibm.com/infocenter/elixent/v3r5/topic/com.ibm.ilog.elixir.doc/Content/Visualization/Documentation/Flex/Diagram4Flex/_media/TreeLayoutExample_default.png

So, I want it to have a treelayout, names of vertexes to be inside some colored rectangles(pref have different colors for different kind of items) and I also want to have some writing on the edges between two vertexes. On top of that, I would very much like that the vertexes to be some Objects that I created in my project, so when I click on a vertex to get the instance of the Object that was placed on the Vertex.

So far, I tried two frameworks to have my graph drawn easily and without working too much on the drawing algorithm:

At first I tried jgraph frameowork:

    package mainPack;

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Rectangle;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;

    import javax.swing.JApplet;
    import javax.swing.JFrame;

    import org.jgraph.JGraph;
    import org.jgraph.graph.DefaultGraphCell;
    import org.jgraph.graph.GraphConstants;
    import org.jgrapht.ListenableGraph;
    import org.jgrapht.ext.JGraphModelAdapter;
    import org.jgrapht.graph.ListenableDirectedGraph;
    import org.jgrapht.graph.DefaultEdge;
    import org.jgrapht.graph.ListenableDirectedWeightedGraph;

    /**
     * A demo applet that shows how to use JGraph to visualize JGraphT graphs.
     *
     * @author Barak Naveh
     *
     * @since Aug 3, 2003
     */
    public class Test extends JApplet {
  private static final Color     DEFAULT_BG_COLOR = Color.decode( "#FAFBFF" );
            private static final Dimension DEFAULT_SIZE = new Dimension( 530, 320 );

    // 
    private JGraphModelAdapter m_jgAdapter;

    /**
     * @see java.applet.Applet#init().
     */
    public void init(  ) {
        // create a JGraphT graph
        ListenableGraph g = new ListenableDirectedGraph( DefaultEdge.class );

        // create a visualization using JGraph, via an adapter
        m_jgAdapter = new JGraphModelAdapter( g );

        JGraph jgraph = new JGraph( m_jgAdapter );

        adjustDisplaySettings( jgraph );
        getContentPane(  ).add( jgraph );
        resize( DEFAULT_SIZE );

        // add some sample data (graph manipulated via JGraphT)
        g.addVertex( "v1" );
        g.addVertex( "v2" );
        g.addVertex( "v3" );
        g.addVertex( "v4" );

        g.addEdge( "v1", "v2", "1" );
        g.addEdge( "v2", "v3", "2" );
        g.addEdge( "v3", "v1", "3" );
        g.addEdge( "v4", "v3", "4" );

        // position vertices nicely within JGraph component
        positionVertexAt( "v1", 130, 40 );
        positionVertexAt( "v2", 60, 200 );
        positionVertexAt( "v3", 310, 230 );
        positionVertexAt( "v4", 380, 70 );

        // that's all there is to it!...
    }


    private void adjustDisplaySettings( JGraph jg ) {
        jg.setPreferredSize( DEFAULT_SIZE );

        Color  c        = DEFAULT_BG_COLOR;
        String colorStr = null;

        try {
            colorStr = getParameter( "bgcolor" );
        }
         catch( Exception e ) {}

        if( colorStr != null ) {
            c = Color.decode( colorStr );
        }

        jg.setBackground( c );
    }


    private void positionVertexAt( Object vertex, int x, int y ) {
        DefaultGraphCell cell = m_jgAdapter.getVertexCell( vertex );

        Map             attr = cell.getAttributes();
   //      Rectangle       b    =  new Rectangle((int)(Math.random()*1000),(int)(Math.random()*500),100,30);

        Rectangle       b    =  new Rectangle(20,50 ,100,30);



       GraphConstants.setBounds( attr, b );

         Map cellAttr = new HashMap(  );
         cellAttr.put( cell, attr );

         m_jgAdapter.edit( cellAttr, null, null, null );
    }

}

This framework works well on making the graph, with vertex name inside Rectangles and names on the edges and can get the String that is inside the vertex when click on it using MouseListener.

But I could not find a way to make it have a TreeLayout and add vertexes as some Objects that I may get returned when click on the Vertex . I tried to make use of the generic classes that are in this example, but got only Exceptions when tried to run it. I searched in the Internet but could not find a way to apply a treelayout on this Graph

I have also tried Java JUNG Framework which has a lot more options for drawing the graph. I found this example:

    @SuppressWarnings("serial")
    public class TreeLayoutDemo extends JApplet {

/**
 * the graph
 */
Forest<String,Integer> graph;

Factory<DirectedGraph<String,Integer>> graphFactory = 
    new Factory<DirectedGraph<String,Integer>>() {

        public DirectedGraph<String, Integer> create() {
            return new DirectedSparseMultigraph<String,Integer>();
        }
    };

Factory<Tree<String,Integer>> treeFactory =
    new Factory<Tree<String,Integer>> () {

    public Tree<String, Integer> create() {
        return new DelegateTree<String,Integer>(graphFactory);
    }
};

Factory<Integer> edgeFactory = new Factory<Integer>() {
    int i=0;
    public Integer create() {
        return i++;
    }};

Factory<String> vertexFactory = new Factory<String>() {
    int i=0;
    public String create() {
        return "V"+i++;
    }};

/**
 * the visual component and renderer for the graph
 */
VisualizationViewer<String,Integer> vv;

VisualizationServer.Paintable rings;

String root;

TreeLayout<String,Integer> treeLayout;

RadialTreeLayout<String,Integer> radialLayout;

public TreeLayoutDemo() {

    // create a simple graph for the demo
    graph = new DelegateForest<String,Integer>();





    createTree();

    treeLayout = new TreeLayout<String,Integer>(graph);
    radialLayout = new RadialTreeLayout<String,Integer>(graph);
    radialLayout.setSize(new Dimension(600,600));
    vv =  new VisualizationViewer<String,Integer>(treeLayout, new Dimension(600,600));
    vv.setBackground(Color.white);
   vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
    vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
   // vv.getRenderContext().setVertexShapeTransformer(arg0);
    // add a listener for ToolTips
    vv.setVertexToolTipTransformer(new ToStringLabeller());
    vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
    rings = new Rings();

    Container content = getContentPane();
    final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
    content.add(panel);

    final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();

    vv.setGraphMouse(graphMouse);

    JComboBox modeBox = graphMouse.getModeComboBox();
    modeBox.addItemListener(graphMouse.getModeListener());
    graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);

    final ScalingControl scaler = new CrossoverScalingControl();

    JButton plus = new JButton("+");
    plus.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            scaler.scale(vv, 1.1f, vv.getCenter());
        }
    });
    JButton minus = new JButton("-");
    minus.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            scaler.scale(vv, 1/1.1f, vv.getCenter());
        }
    });

    JToggleButton radial = new JToggleButton("Radial");
    radial.addItemListener(new ItemListener() {

        public void itemStateChanged(ItemEvent e) {
            if(e.getStateChange() == ItemEvent.SELECTED) {

                LayoutTransition<String,Integer> lt =
                    new LayoutTransition<String,Integer>(vv, treeLayout, radialLayout);
                Animator animator = new Animator(lt);
                animator.start();
                vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
                vv.addPreRenderPaintable(rings);
            } else {
                LayoutTransition<String,Integer> lt =
                    new LayoutTransition<String,Integer>(vv, radialLayout, treeLayout);
                Animator animator = new Animator(lt);
                animator.start();
                vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
                vv.removePreRenderPaintable(rings);
            }
            vv.repaint();
        }});

    JPanel scaleGrid = new JPanel(new GridLayout(1,0));
    scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));

    JPanel controls = new JPanel();
    scaleGrid.add(plus);
    scaleGrid.add(minus);
    controls.add(radial);
    controls.add(scaleGrid);
    controls.add(modeBox);

    content.add(controls, BorderLayout.SOUTH);
}

class Rings implements VisualizationServer.Paintable {

    Collection<Double> depths;

    public Rings() {
        depths = getDepths();
    }

    private Collection<Double> getDepths() {
        Set<Double> depths = new HashSet<Double>();
        Map<String,PolarPoint> polarLocations = radialLayout.getPolarLocations();
        for(String v : graph.getVertices()) {
            PolarPoint pp = polarLocations.get(v);
            depths.add(pp.getRadius());
        }
        return depths;
    }

    public void paint(Graphics g) {
        g.setColor(Color.lightGray);

        Graphics2D g2d = (Graphics2D)g;
        Point2D center = radialLayout.getCenter();

        Rectangle2D rectangle = new Rectangle2D.Double();   // (center.getX()-10, center.getY()-20, 20, 40);

        Ellipse2D ellipse = new Ellipse2D.Double();
        for(double d : depths) {


            ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, 
                    center.getX()+d, center.getY()+d);

            rectangle.setFrameFromDiagonal(center.getX()-d, center.getY()-d, 
                    center.getX()+d, center.getY()+d);

            Shape shape = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(rectangle);
            g2d.draw(shape);
        }
    }

    public boolean useTransform() {
        return true;
    }
}

/**
 * 
 */
private void createTree() {
    graph.addVertex("V0");
    graph.addEdge(edgeFactory.create(), "V0", "V1"); 
    graph.addEdge(edgeFactory.create(), "V0", "V2");
    graph.addEdge(edgeFactory.create(), "V1", "V4");
    graph.addEdge(edgeFactory.create(), "V2", "V3");
    graph.addEdge(edgeFactory.create(), "V2", "V5");
    graph.addEdge(edgeFactory.create(), "V4", "V6");
    graph.addEdge(edgeFactory.create(), "V4", "V7");
    graph.addEdge(edgeFactory.create(), "V3", "V8");
    graph.addEdge(edgeFactory.create(), "V6", "V9");
    graph.addEdge(edgeFactory.create(), "V4", "V10");

    graph.addVertex("A0");
    graph.addEdge(edgeFactory.create(), "A0", "A1");
    graph.addEdge(edgeFactory.create(), "A0", "A2");
    graph.addEdge(edgeFactory.create(), "A0", "A3");

    graph.addVertex("B0");
    graph.addEdge(edgeFactory.create(), "B0", "B1");
    graph.addEdge(edgeFactory.create(), "B0", "B2");
    graph.addEdge(edgeFactory.create(), "B1", "B4");
    graph.addEdge(edgeFactory.create(), "B2", "B3");
    graph.addEdge(edgeFactory.create(), "B2", "B5");
    graph.addEdge(edgeFactory.create(), "B4", "B6");
    graph.addEdge(edgeFactory.create(), "B4", "B7");
    graph.addEdge(edgeFactory.create(), "B3", "B8");
    graph.addEdge(edgeFactory.create(), "B6", "B9");




}


/**
 * a driver for this demo
 */
public static void main(String[] args) {
    JFrame frame = new JFrame();
    Container content = frame.getContentPane();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    content.add(new TreeLayoutDemo());
    frame.pack();
    frame.setVisible(true);
}

}

Using this framework I could add the Vertexes as some Objects that I created and on the vertex name is displayed what that object returns in the toString() method. It also has the TreeLayout algorithm, that I wanted so much, implemented but I cannot manage to change the look of the vertexes. I want them to look like the vertexes that are drawn by JGraph framework, but with JUNG I only get some circles and name of vertex is outside the vertex shape. I have discovered an example that changes the shape of vertex, but the writing is still outside.

So, I would like very much some suggestions about how can I draw some graph that is in between what I get with these two frameworks: Have the name of vertex inside the shape, have the Treelayout and add vertexes as some Object that I can get returned when I click on the vertex.

The answer from Marco13 was very useful. I have figured to make the Rectangles have a size to fit he String by setting String.length * 10 for the width of the Rectangle.

I think this may be useful for someone else trying to make a graph like this: Instead of that appears in most generic classes in this example can be used whatever Object is needed, String is for Vertex and Integer is for Edge type. Inside the Transformer functions you get the Object that is assigned for the Vertex or Edge. Like this, I could set different shapes, colors and fonts for objects with some specific properties.

I work to figure how to make a MouseListener that returns the Edge Object when I click on it but I hope that will be easy.

Était-ce utile?

La solution

You can configure the VisualizationViewer in the second example like this:

    vv.getRenderContext().setVertexShapeTransformer(new Transformer<String, Shape>()
    {
        @Override
        public Shape transform(String vertex)
        {
            return new Rectangle2D.Double(-10,-10,40,20);
        }
    });

    BasicVertexLabelRenderer<String, Integer> vertexLabelRenderer = 
        new BasicVertexLabelRenderer<String, Integer>();
    vertexLabelRenderer.setPosition( Renderer.VertexLabel.Position.CNTR);
    vv.getRenderer().setVertexLabelRenderer(vertexLabelRenderer);

This will render the vertices as rectangles, and put the label into the center. Extensions (like adapting the size of the rectangle for the contents) may be a bit fiddly, but should be doable.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top