zoukankan      html  css  js  c++  java
  • GEF常见问题5:自动布局

    利用自动布局功能,我们可以把本来不包含图形信息的文件以图形化的方式展示出来,典型的例子比如将一组Java接口反向工程为类图,那么图中每个图元的坐标应该必须都是自动生成的。GEF里提供了DirectedGraphLayout类用来实现自动布局功能,下面介绍一下怎样在程序里使用它。

    DirectedGraphLayout提供的visit()方法接受一个org.eclipse.draw2d.graph.DirectedGraph实例,它遍历这个有向图的所有节点和边,并按照它自己的算法计算出每个节点布局后的新位置。所以在使用它布局画布上的图元分为两个步骤:1、构造有向图,2、将布局信息应用到图元。

    还是以gefpractice为基础,我们在主工具条上增加了一个自动布局按钮,当用户按下它时自动布局编辑器里的图形,再次按下时恢复以前的布局。为了完成步骤1,我们要在DiagramPart里添加以下两个方法:

    /**
     * 将图元(NodePart)转换为节点(Node)到有向图
     * 
    @param graph
     * 
    @param map
     
    */
    public void contributeNodesToGraph(DirectedGraph graph, Map map) {
        
    for (int i = 0; i < getChildren().size(); i++) {
            NodePart node 
    = (NodePart)getChildren().get(i);
            org.eclipse.draw2d.graph.Node n 
    = new org.eclipse.draw2d.graph.Node(node);
            n.width 
    = node.getFigure().getPreferredSize().width;
            n.height 
    = node.getFigure().getPreferredSize().height;
            n.setPadding(
    new Insets(10,8,10,12));
            map.put(node, n);
            graph.nodes.add(n);
        }
    }

    /**
     * 将连接(ConnectionPart)转换为边(Edge)添加到有向图
     * 
    @param graph
     * 
    @param map
     
    */
    public void contributeEdgesToGraph(DirectedGraph graph, Map map) {
        
    for (int i = 0; i < getChildren().size(); i++) {
            NodePart node 
    = (NodePart)children.get(i);
            List outgoing 
    = node.getSourceConnections();
            
    for (int j = 0; j < outgoing.size(); j++) {
                ConnectionPart conn 
    = (ConnectionPart)outgoing.get(j);
                Node source 
    = (Node)map.get(conn.getSource());
                Node target 
    = (Node)map.get(conn.getTarget());
                Edge e 
    = new Edge(conn, source, target);
                e.weight 
    = 2;
                graph.edges.add(e);
                map.put(conn, e);
            }
        }
    }

    要实现步骤2,在DiagramPart里添加下面这个方法:

    /**
     * 利用布局后的有向图里节点的位置信息重新定位画布上的图元
     * 
    @param graph
     * 
    @param map
     
    */
    protected void applyGraphResults(DirectedGraph graph, Map map) {
        
    for (int i = 0; i < getChildren().size(); i++) {
            NodePart node 
    = (NodePart)getChildren().get(i);
            Node n 
    = (Node)map.get(node);
            node.getFigure().setBounds(
    new Rectangle(n.x, n.y, n.width, n.height));
        }
    }

    为了以最少的代码说明问题,上面的方法里只是简单的移动了图形,而没有改变模型里Node的属性值,在大多情况下这里使用一个CompoundCommand对模型进行修改更为合适,这样用户不仅可以撤消(Undo)这个自动布局操作,还可以在重新打开文件时看到关闭前的样子。注意,DirectedGraphLayout是不保证每次布局都得到完全相同的结果的。

    因为Draw2D里是用LayoutManager管理布局的,而DirectedGraphLayout只是对布局算法的一个包装,所以我们还要创建一个布局类。GraphLayoutManager调用我们在上面已经添加的那几个方法生成有向图(partsToNodes变量维护了编辑器图元到有向图图元的映射),利用DirectedGraphLayout对这个有向图布局,再把结果应用到编辑器里图元。如下所示:

    class GraphLayoutManager extends AbstractLayout {

        
    private DiagramPart diagram;

        GraphLayoutManager(DiagramPart diagram) {
            
    this.diagram = diagram;
        }

        
    protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
            container.validate();
            List children 
    = container.getChildren();
            Rectangle result 
    = new Rectangle().setLocation(container.getClientArea().getLocation());
            
    for (int i = 0; i < children.size(); i++)
                result.union(((IFigure) children.get(i)).getBounds());
            result.resize(container.getInsets().getWidth(), container.getInsets().getHeight());
            
    return result.getSize();
        }

        
    public void layout(IFigure container) {
            DirectedGraph graph 
    = new DirectedGraph();
            Map partsToNodes 
    = new HashMap();
            diagram.contributeNodesToGraph(graph, partsToNodes);
            diagram.contributeEdgesToGraph(graph, partsToNodes);
            
    new DirectedGraphLayout().visit(graph);
            diagram.applyGraphResults(graph, partsToNodes);
        }

    }

    当用户按下自动布局按钮时,只要设置DiagramPart对应的图形的布局管理器为GraphLayoutManager就可以实现自动布局了,布局的结果如图所示。


    自动布局的结果

    点此下载工程,此工程修改自GEF应用实例中的GefPractice,目标文件的扩展名改为.gefpracticeal。最后有几点需要说明:

    1、DirectedGraphLayout只能对连通的有向图进行布局,否则会产生异常“graph is not fully connected”,参考Eclipse.org文章Building a Database Schema Diagram Editor中使用的NodeJoiningDirectedGraphLayout可以解决这个问题;

    2、如果要对具有嵌套关系的有向图自动布局,应使用SubGraph和CompoundDirectedGraphLayout;

    3、这个版本的gefpractice在自动布局后,没有对连接线进行处理,所以可能会出现连接线穿过图元的情况,这个问题和上一个问题都可以参考GEF提供的flow例子解决。

    Update(2007/4/9):如果diagram是放在ScrollPane里的,则要修改一下applyGraphResults()方法,增加container作为参数以使整个diagram能正确滚动。

    protected void applyGraphResults(DirectedGraph graph, Map map, IFigure container) {
        
    for (Iterator iterator = this.nodeParts.iterator(); iterator.hasNext();) {
            NodePart element 
    = (NodePart) iterator.next();
            Node n 
    = (Node) map.get(element);
            Rectangle containerBounds
    =container.getBounds();
            Rectangle elementBounds
    =new Rectangle(n.x, n.y, n.width, n.height);
            element.getFigure().setBounds(elementBounds.translate(containerBounds.getLocation()));
        }
    }

    如果觉得本文内容有帮助:试试我开发的Android应用 应用计时折扣君推荐+1(需登录)

  • 相关阅读:
    hdu1879 继续畅通工程
    hdu1875 畅通工程再续 最小生成树并查集解决---kruskal
    hdu1863 畅通工程2 还是用并查集思想解决最小生成树问题
    hud2544dijkstra+堆优化
    PHP holiday1
    记忆化搜索hdu1078 dfs
    hdu 1548 楼梯 bfs或最短路 dijkstra
    隐藏原生html5 video controls
    工具网站gallery
    判断节点包含
  • 原文地址:https://www.cnblogs.com/bjzhanghao/p/440896.html
Copyright © 2011-2022 走看看