sharky的点滴积累

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  56 随笔 :: 104 文章 :: 10 评论 :: 0 Trackbacks

两天前GEF发布了3.1M7版本,但使用下来发现和M6没有什么区别,是不是主要为了和Eclipse版本相配套?希望3.1正式版早日发布,应该会新增不少内容。上一篇帖子介绍了如何实现表格功能,在开发过程中,另一个经常用到的功能就是树,虽然SWT提供了标准的树控件,但使用它完成如组织结构图这样的应用还是不够直观和方便。在目前版本(3.1M7)的GEF中虽然没有直接支持树的实现,但Draw2D提供的例子程序里却有我们可以利用的代码(org.eclipse.draw2d.examples.tree.TreeExample,运行界面见下图),通过它可以节约不少工作量。

treeexample.gif
图1 Draw2D例子中的TreeExample

记得数年前曾用Swing做过一个组织结构图的编辑工具,当时的实现方式是让画布使用XYLayout,在适当的时候计算和刷新每个树节点的位置,算法的思想则是深度优先搜索,非树叶节点的位置由其子节点的数目和位置决定。我想这应该是比较直观的方法吧,但是这次看了Draw2D例子里的实现觉得也很有道理,以前没想到过。在这个例子里树节点图形称为TreeBranch,它包含一个PageNode(表现为带有折角的矩形)和一个透明容器contentsPane,(一个Layer,用来放置子节点)。在一般情况下,TreeBranch本身使用名为NormalLayout的布局管理器将PageNode放在子节点的正上方,而contentsPane则使用名为TreeLayout的布局管理器计算每个子节点应在的位置。所以我们看到的整个树实际上是由很多层子树叠加而成的,任何一个非叶节点对应的图形的尺寸都等于以它为根节点的子树所占区域的大小。

从这个例子里我们还看到,用户可以选择使用横向或纵向组织树(见图2),可以压缩各节点之间的空隙,每个节点可以横向或纵向排列子节点,还可以展开或收起子节点,等等,这为我们实现一个方便好用的树编辑器提供了良好的基础(视图部分的工作大大简化了)。

treevertical.gif
图2 纵向组织的树

这里要插一句,Draw2D例子中提供的这些类的具体内容我没有仔细研究,相当于把它们当作Draw2D API的一部分来用了(包括TreeRoot、TreeBranch、TreeLayout、BranchLayout、NormalLayout、HangingLayout、PageNode等几个类,把代码拷到你的项目中即可使用),因为按照GEF 3.1的计划表,它们很有可能以某种形式出现在正式版的GEF 3.1里。下面介绍一下我是如何把它们转换为GEF应用程序的视图部分从而实现树编辑器的。

首先从模型部分开始。因为树是由一个个节点构成的,所以模型中最主要的就是节点类(我称为TreeNode),它和例子里的TreeBranch图形相对应,它应该至少包含nodes(子节点列表)和text(显示文本)这两个属性;例子里有一个TreeRoot是TreeBranch的子类,用来表示根节点,在TreeRoot里多了一些属性,如horizontal、majorSpacing等等用来控制整个树的外观,所以模型里也应该有一个继承TreeNode的子类,而实际上这个类就应该是编辑器的contents,它对应的图形TreeRoot也就是一般GEF应用程序里的画布,这个地方要想想清楚。同时,虽然看起来节点间有线连接,但这里我们并不需要Connection对象,这些线是由布局管理器绘制的,毕竟我们并不需要手动改变线的走向。所以,模型部分就是这么简单,当然别忘了要实现通知机制,下面看看都有哪些EditPart。

与模型相对应,我们有TreeNodePart和TreeRootPart,后者和前者之间也是继承关系。在getContentPane()方法里,要返回TreeBranch图形所包含的contentsPane部分;在getModelChildren()方法里,要返回TreeNode的nodes属性;在createFigure()方法里,TreeNodePart应返回TreeBranch实例,而TreeRootPart要覆盖这个方法,返回TreeRoot实例;另外要注意在refreshVisuals()方法里,要把模型的当前属性正确反映到图形中,例如TreeNode里有反映节点当前是否展开的布尔变量expanded,则refreshVisuals()方法里一定要把这个属性的当前值赋给图形才可以。以下是TreeNodePart的部分代码:

public IFigure getContentPane() {
    
return ((TreeBranch) getFigure()).getContentsPane();
}


protected List getModelChildren() {
    
return ((TreeNode) getModel()).getNodes();
}


protected IFigure createFigure() {
    
return new TreeBranch();
}


protected void createEditPolicies() {
    installEditPolicy(EditPolicy.COMPONENT_ROLE, 
new TreeNodeEditPolicy());
    installEditPolicy(EditPolicy.LAYOUT_ROLE, 
new TreeNodeLayoutEditPolicy());
    installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, 
new ContainerHighlightEditPolicy());
}

上面代码中用到了几个EditPolicy,这里说一下它们各自的用途。实际上,从Role中已经可以看出来,TreeNodeEditPolicy是用来负责节点的删除,没有什么特别;TreeNodeLayoutEditPolicy则复杂一些,我把它实现为ConstrainedLayoutEditPolicy的一个子类,并实现createAddCommand()和getCreateCommand()方法,分别返回改变节点的父节点和创建新节点的命令,另外我让createChildEditPolicy()方法返回NonResizableEditPolicy的实例,并覆盖其createSelectionHandles()方法如下,以便在用户选中一个节点时用一个控制点表示选中状态,不用缺省边框的原因是,边框会将整个子树包住,不够美观,并且在多选的时候界面比较混乱。

protected List createSelectionHandles() {
    List list
=new ArrayList();
    list.add(
new ResizeHandle((GraphicalEditPart)getHost(), PositionConstants.NORTH));
    
return list;
}

选中节点的效果如下图,我根据需要改变了树节点的显示(修改PageNode类):

treeselection.gif
图3 同时选中三个节点(Node2、Node3和Node8)

最后一个ContainerHighlightEditPolicy的唯一作用是当用户拖动节点到另一个节点区域中时,加亮显示后者,方便用户做出是否应该放开鼠标的选择。它是GraphicalEditPolicy的子类,部分代码如下,如果你看过Logic例子的话,应该不难发现这个类就是我从那里拿过来然后修改一下得到的。

protected void showHighlight() {
    ((TreeBranch) getContainerFigure()).setSelected(
true);
}


public void eraseTargetFeedback(Request request) {
    ((TreeBranch) getContainerFigure()).setSelected(
false);
}

好了,现在树编辑器应该已经能够工作了。为了让用户使用更方便,你可以实现展开/收起子节点、横向/纵向排列子节点等等功能,在视图部分Draw2D的例子代码已经内置了这些功能,你要做的就是给模型增加适当的属性。我这里的一个截图如下所示,其中Node1是收起状态,Node6纵向排列子节点(以节省横向空间)。

treeeditor.gif
图4 树编辑器的运行界面

这个编辑器我花一天时间就完成了,但如果不是利用Draw2D的例子,相信至少要四至六天,而且缺陷会比较多,功能上也不会这么完善。我感觉在GEF中遇到没有实现过的功能前最好先找一找有没有可以利用的资源,比如GEF提供的几个例子就很好,当然首先要理解它们才谈得上利用。

posted on 2005-05-27 00:09 八进制 阅读(2028) 评论(20)  编辑 收藏 收藏至365Key 所属分类: Eclipse

评论

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-06-21 20:00 freegoldlu
请问 这里例子的代码 能mail给我吗freegoldlu@sohu.com
不方便的话 能不能把draw2d的例子代码发一份 非常感谢 :)
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-06-22 20:30 八进制
例子代码确实不方便外传,否则我都会提供下载链接的,见谅。
draw2d例子已发邮箱,请查收。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-06-23 14:42 freegoldlu
非常感谢你的答复,请教一个技术问题 按照你的文档
整个这个画布的figure应该是roottree,我的画布是merlin 自动generate 的 EDiagramEditPart, 我在里面
protected IFigure createFigure() {
//FigureCanvas treeroot=new FigureCanvas(this.getViewer().getControl().getShell());
//treeroot.setBounds(0,0,200,200);
//treeroot.setBackground(ColorConstants.white);
TreeRoot root=new TreeRoot(createPageNode("Graph Root"));
root.setBounds(new Rectangle(100,100,100,100));
//treeroot.setContents(root);
return root;
}
总是 出异常,请问 画布 的figure应该怎么设置 谢谢
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-06-23 18:23 八进制
不好说,最好把异常信息贴一下,并且说明在哪句出的异常。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-05 13:59 vicstart
能否给个draw2d的例子代码?
vicstart@gmail.com
谢谢
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-05 17:30 dorothy
Could you send me the draw2d example too as fast as possible?
Thank you so much! ^_^

Regards,
dorothyhelene@hotmail.fr
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-05 21:49 八进制
vicstart&dorothy:Draw2D的例子可以从eclipse的cvs里获得。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-06 17:08 dorothy
Thank you for the information, in fact, I've found the example in eclipse cvs, I just wonder that we will have to download all the files in the directories of cvsviewer one by one by hand? or there is a better and simpler way to download the whole directory in the very same time? you see, how precious our time is! Maybe ask from you directly is a better choice, that's I did yesterday. :)

Thanks a lot for answering to the silly question.

have a good day.
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-06 17:15 dorothy
sorry I forgot, good evening. :)
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-06 21:48 八进制
dorothy:不客气。可以下载整个目录,不知道你用的是什么工具,我是用eclipse自带的cvs客户端,选中要下载的目录再check out即可。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-07 15:22 dorothy
呵呵,终于装了中文输入法了,还是这样讲话比较亲切。:)

我刚用了一个月零几天的eclipse,还不知道用cvs呢. :P

多谢八进制了,以后还请多多指教!^_^
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-07-13 20:14 Bankey
也发个例子给我好吗
fengbinhua@163.com
谢谢

  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-03 11:53 fkpwolf
看了你的很多文章,问个问题下,如何把GEF的图存为XML文件格式?我都没有找到相关方面的文档。
另:不知楼主用过jgraph没有,能不能比较下,哈哈
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-03 21:33 八进制
是指把GEF的模型保存为XML格式吧,用dom自己实现就可以了,或者如果你用EMF做模型,它提供了缺省的XML方式。还有很多工具可以实现。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-04 11:14 fkpwolf
怎么把图的所有信息存在XML文件中呢?这样GEF就可以打开一个XML文件格式的图。GEF带的shape example做法是序列化为二进制了。EMF好像很复杂,有方便的工具吗?
多谢
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-04 21:23 八进制
保存的工作不是gef负责,在editor的dosave里由你自己实现,序列化是一种方式,想存为xml用dom或其他工具重写dosave方法。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-08 09:15 fkpwolf
多谢提醒,现在利用eclopse.org上面的EMF + GEF已经可以存为XML了。但是当我后来在MODEL中加了一个类Action,并在Shape中引用它(多个),编辑完Shape后,保存,报异常:
org.eclipse.emf.ecore.xmi.DanglingHREFException: The object 'org.eclipse.gef.examples.shapes.emf.model.impl.ActionImpl@1a503f (description: wokao)' is not contained in a resource. at org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl.handleDanglingHREF(XMLHelperImpl.java:644)
对EMF不熟悉,不知道如何处理。
多谢
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-08 20:37 八进制
在Shape中是怎样引用Action类的?
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-09 08:51 fkpwolf
我是在“Sample Ecore Model Editor”中打开modle.ecore文件(原来例子的),在其中添加一个EClass:Action,然后在Shape中引用它(跟引用Connection一样),然后产生出类出来,没有作其他处理。
  

# re: [Eclipse]GEF入门系列(十一、树的一个实现) 2005-08-12 09:00 fkpwolf
弄了半天是引用的containment熟悉应该设置为true,55555
posted on 2005-08-26 17:16 sharky的点滴积累 阅读(756) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: