八进制

少年壮志无烟抽

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  233 随笔 :: 0 文章 :: 3036 评论 :: 11 引用

利用自动布局功能,我们可以把本来不包含图形信息的文件以图形化的方式展示出来,典型的例子比如将一组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()));
    }
}

posted on 2006-07-02 17:56 八进制 阅读(3866) 评论(27)  编辑 收藏 所属分类: EclipseGEF

评论

#1楼  2006-07-03 15:44 谢谢 [未注册用户]
八进制你好,我一直看你的文章在学习插件开发方面的知识,现在公司让我开发一个插件项目,是有关项目自动生成以及数据库设计的,项目自动生成已经完成,而数据库数据生成,我想用到wtp,dtp中的数据库schema树形结构,但是我找了很久也没有找到具体的实现代码,哪些图标倒是都在org.eclipse.datatools.connectivity.sqm.core.internal.ui.util.resources.ImagePath;中定义了,但是项目中我导入了DTP的大概20多M代码还是没有找到,能加你的MSN聊聊吗?
  回复  引用    

哈哈,八进制,好久不见!在有向图布局这一块貌似我实现得更丰富一些啊。其实在GEF3.2---包含Draw2D的API里对于有向图有一个属性是direction的,通过设置direction可以改变布局是横向派列还是纵向排列。我还实现了一个按钮给用户,可以改变横向还是纵向排列。

除此之外,还可以加入动画的功能,具体参考flow等例子看看怎么实现的.


第三点,有向图布局也只是一个视图的布局方式而已,没有改变模型数据。而 且在这种情况下,是不能拖动结点的.我曾经还实现过通过这个算法的原理去修改模型。就在 applyGraphResult的函数里执行一个改变位置的Command而已,不过这种方法也失去了动画功能。

再说第四点,多说一点,如果想做两个布局的话,(我以前实现的,XYLayout和DirectedGraphLayout),也可以加一个按钮改变布局,不过,对于不同的布局也要相应的改变为不同的editpolicy.
  回复  引用    

#3楼  2006-08-08 16:37 migrant [未注册用户]
楼上这么强的东西就贴出来大家学习学习啊
  回复  引用    

#4楼  2007-04-30 20:15 豌豆脆 [未注册用户]
请教一个问题,如何在GEF框架中创建容器节点呢?即能够包含其他节点的节点。
  回复  引用    

#5楼  2007-07-18 10:07 jiezi [未注册用户]
如何在自动布局后用户仍可以拖动里面node的图元?是用CompoundCommand么?大大有相关的sample么?
  回复  引用    

#6楼  2007-07-18 10:23 jiezi [未注册用户]
这种方法改变了原来的layout,有没有办法触发这个action只是给出一个比较规整的布局,但之后用户仍可以在这个自动布局基础上去做一些手动的修改。比如原来是xylayout的。
  回复  引用    

#7楼  2007-07-18 10:52 hehui0912 [未注册用户]
@豌豆脆
和我联系 qq 81553652
  回复  引用    

#8楼 [楼主] 2007-07-18 22:07 八进制      
@jiezi
可以先应用这个layout然后马上换回xylayout
  回复  引用  查看    

#9楼  2007-07-19 02:01 jiezi [未注册用户]
谢谢八进制大大这么晚了还回帖,我先试试,貌似换layout都有点麻烦.因为上面的GefPractice好像没有改model层的数据,只是figure的布局
  回复  引用    

#10楼  2007-07-19 08:40 yy [未注册用户]
请教一个问题:
在diagram中添加contributeNodesToGraph, 但是如果有很多节点类型,每种都写一个函数比较麻烦,是否有方法得到所有node?
谢谢
  回复  引用    

#11楼  2007-07-19 10:24 jiezi [未注册用户]
通过上面的例子的action点击一次,实现从freeformLayout到GraphLayoutManager再到freeformLayout可行么,- -!!!
  回复  引用    

#12楼 [楼主] 2007-07-19 22:35 八进制      
我觉得model不需要改变,只用改figure部分。
  回复  引用  查看    

#13楼 [楼主] 2007-07-19 22:38 八进制      
@yy
GraphicalEditPart#getChildren()方法返回的就是全部子editpart,不管是什么类型节点。
  回复  引用  查看    

#14楼  2007-07-24 17:23 jiezi [未注册用户]
八进制大大,你上面提到

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

但如果最外层Diagram用的是toollayout,画布是一系列横格里,横格里再包含Node,我试了下,用CompoundDirectedGraphLayout不好使了,该怎么办啊?
  回复  引用    

#15楼 [楼主] 2007-07-24 23:09 八进制      
在横格里使用CompoundDirectedGraphLayout应该可以对该格里的Node自动布局
  回复  引用  查看    

#16楼  2007-07-26 09:08 jiezi [未注册用户]
把ToolbarLayout布局的Figure设为subgraph?但是CompoundDirectedGraphLayout().visit(CompoundDirectedGraph)好像不认ToolbarLayout的啊,里面Node的坐标很混乱
  回复  引用    

#17楼 [楼主] 2007-07-27 11:17 八进制      
我的意思是只对横格里的节点进行layout,不对画布自动布局。
  回复  引用  查看    

#18楼  2007-07-27 12:58 jiezi [未注册用户]
。。恩,就是不同横格之间里节点的连线不能规整了
这样是否就用DirectedGraph就行了,相当于没有subgraph那层了?
  回复  引用    

#19楼 [楼主] 2007-07-27 17:21 八进制      
没错。
  回复  引用  查看    

#20楼  2007-07-30 09:31 jiezi [未注册用户]
谢谢八进制大大,看来CompoundDirectedGraphLayout也不是万能的啊,里面init()方法逻辑写死了,如果直接写一套太麻烦,而且CompoundDirectedGraphLayout居然是final的
  回复  引用    

#21楼  2007-07-30 21:05 qiuju [未注册用户]
您好,请教一下有没有关于DirectGraphLayout算法说明的文档?谢谢
  回复  引用    

#22楼 [楼主] 2007-07-30 21:32 八进制      
印象里没见过,你不妨去gef新闻组问问看。
  回复  引用  查看    

#23楼  2007-07-31 10:03 qiuju [未注册用户]
DirectGraphLayout能适用于有环的有向图吗?谢谢!
  回复  引用    

#24楼 [楼主] 2007-07-31 22:45 八进制      
我试过,能布局带环图,因为它有一个break cycle的子过程。
  回复  引用  查看    

#25楼  2007-10-23 08:59 jiezi [未注册用户]
请问下,为什么我用这种自动布局,复杂的图会出现一条直的迁移线上,自动被加上十来个转折点,可能因为太多,拖动那些转折点就报数组越界异常了
  回复  引用    

#26楼  2008-03-03 16:49 DennyJohn [未注册用户]
正需要这个。。。再把动画功能加进去,就完美了。哈哈
  回复  引用    

#27楼  2008-04-06 16:10 schem啊 [未注册用户]
GEF真他妈的强!!!
  回复  引用    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-04-09 17:59 编辑过


相关链接:

历史上的今天:
2005-07-02 通过OCP考试