GEF入门系列(五、浅谈布局)

【DeepSeek+Office/WPS】智能办公新时代的全新利器 DeepSeek是一家专注于通用人工智能技术研发的中国科技公司,其核心产品DeepSeek-R1是一款开源的推理大模型,专注于复杂任务的高效处理,支持智能对话、文本生成、语义理解、计算推理、代码生成与补全等多种应用场景。作为国产、免费、开源且强大的AI平台,DeepSeek不仅致力于为终端用户提供高质量的智能服务,还为开发者提供了一整套完善的技术解决方案,帮助他们快速构建和部署自己的智能应用。 阅读详情

虽然很多GEF应用程序里都会用到连接(Connection),但也有一些应用是不需要用连接来表达关系的,我们目前正在做的这个项目就是这样一个例子。在这类应用中,模型对象间的关系主要通过图形的包含来表达,所以大多是一对多关系。

cbm1.gif
图1 不使用连接的GEF应用

先简单描述一下我们这个项目,该项目需要一个图形化的模型编辑器,主要功能是在一个具有三行N列的表格中自由增加/删除节点,节点可在不同单元格间拖动,可以合并相邻节点,表格列可增减、拖动等等。由于SWT/Jface提供的表格很难实现这些功能,所以我们选择了使用GEF开发,目前看来效果还是很不错的(见下图),这里就简单介绍一下实现过程中与图形和布局有关的一些问题。

在动手之前首先还是要考虑模型的构造。由于Draw2D只提供了很有限的Layout,如ToolbarLayout、FlowLayout和XYLayout,并没有一个GridLayout,所以不能把整个表格作为一个EditPart,而应该把每一列看作一个EditPart(因为对列的操作比对行的操作多,所以不把行作为EditPart),这样才能实现列的拖动。另外,从需求中可以看出,每个节点都包含在一个列中,但仔细再研究一下会发现,实际上节点并非直接包含在列中,而是有一个单元格对象作为中间的桥梁,即每个列包含固定的三个单元格,每个单元格可以包含任意个节点。经过以上分析,我们的模型、EditPart和Figure应该已经初步成形了,见下表:

 

模型

EditPart

Figure

画布

Diagram

DiagramPart

FreeformLayer

Column

ColumnPart

ColumnFigure

单元格

Cell

CellPart

CellFigure

节点

Node

NodePart

NodeFigure

表中从上到下是包含关系,也就是一对多关系,下图简单显示了这些关系:

cbm2.gif
图2 图形包含关系图

让我们从画布开始考虑。在画布上,列显示为一个纵向(高大于宽)的矩形,每个列有一个头(Header)用来显示列名,所有列在画布上是横向排列的。因此,画布应该使用ToolbarLayout或FlowLayout中的一种。这两种Layout有很多相似之处,尤其它们都是按指定的方向排列显示图形,不同之处主要在于:当图形太多容纳不下的时候,ToolbarLayout会牺牲一些图形来保持一行(列),而FlowLayout则允许换行(列)显示。

对于我们的画布来说,显然应该使用ToolbarLayout作为布局管理器,因为它的子图形ColumnFigure是不应该出现换行的。以下是定义画布图形的代码:

Figure f = new FreeformLayer();
ToolbarLayout layout
=new ToolbarLayout();
layout.setVertical(
false);
layout.setSpacing(
5);
layout.setStretchMinorAxis(
true);
f.setLayoutManager(layout);
f.setBorder(
new MarginBorder(5));

其中setVertical(false)指定横向排列子图形,setSpacing(5)指定子图形之间保留5象素的距离,setStretchMinorAxis(true) 指定每个子图形的高度都保持一致。

ColumnFigure的情况要稍微复杂一些,因为它要有一个头部区域,而且它的三个子图形(CellFigure)合在一起要能够充满下部区域,并且适应其高度的变化。一开始我用Draw2D提供的Label来实现列头,但有一个不足,那就是你无法设置它的高度,因为Label类覆盖了Figure的getPreferedSize()方法,使得它的高度只与里面的文本有关。解决方法是构造一个HeaderFigure,让它维护一个Label,设置列头高度时实际设置的是HeaderFigure的高度;或者直接让HeaderFiguer继承Label并重新覆盖getPreferedSize()也可以。我在项目里使用的是前者。

第二个问题花了我一些时间才搞定,一开始我是在CellPart的refreshVisuals()方法里手动设置CellFigure的高度为ColumnFigure下部区域高度的三分之一,但这样很勉强,而且还需要额外考虑spacing带来的影响。后来通过自定义Layout的方式比较圆满的解决了这个问题,我让ColumnFigure使用自定义的ColumnLayout,这个Layout继承自ToolbarLayout,但覆盖了layout()方法,内容如下:

class ColumnLayout extends ToolbarLayout {
    public 
void layout(IFigure parent) {
        IFigure nameFigure
=(IFigure)parent.getChildren().get(0);
        IFigure childrenFigure
=(IFigure)parent.getChildren().get(1);
        Rectangle clientArea
=parent.getClientArea();
        nameFigure.setBounds(
new Rectangle(clientArea.x,clientArea.y,clientArea.width,30));
        childrenFigure.setBounds(
new Rectangle(clientArea.x,nameFigure.getBounds().height+clientArea.y,clientArea.width,clientArea.height-nameFigure.getBounds().height));
    }
}

也就是说,在layout里控制列头和下部的高度分别为30和剩下的高度。但这还没有完,为了让单元格正确的定位在表格列中,我们还要指定列下部图形(childrenFigure)的布局管理器,因为实际上单元格都是放在这个图形里的。前面说过,Draw2D并没有提供一个像SWT中FillLayout那样的布局管理器,所以我们要再自定义另一个layout,我暂时给它起名为FillLayout(与SWT的FillLayout同名),还是要覆盖layout方法,如下所示(因为用了transposer所以horizontal和vertical两种情况可以统一处理,这个transposer只在horizontal时才起作用):

public void layout(IFigure parent) {
    List children 
= parent.getChildren();
    
int numChildren = children.size();
    Rectangle clientArea 
= transposer.t(parent.getClientArea());
    
int x = clientArea.x;
    
int y = clientArea.y;
    
for (int i = 0; i < numChildren; i++) {
        IFigure child 
= (IFigure) children.get(i);
        Rectangle newBounds 
= new Rectangle(x, y, clientArea.width, -1);

        
int divided = (clientArea.height - ((numChildren - 1* spacing)) / numChildren;
        
if (i == numChildren - 1)
            divided 
= clientArea.height - ((divided + spacing) * (numChildren - 1));
        newBounds.height 
= divided;
        child.setBounds(transposer.t(newBounds));
        y 
+= newBounds.height + spacing;
    }
}

上面这些语句的作用是将父图形的高(宽)度平均分配给每个子图形,如果是处于最后的一位的子图形,让它占据所有剩下的空间(防止除不尽的情况留下空白)。完成了这个FillLayout,只要让childrenFigure使用它作为布局管理器即可,下面是ColumnFigure的大部分代码,列头图形(HeaderFigure)和列下部图形(ChildrenFigure)作为内部类存在:

private HeaderFigure name = new HeaderFigure();
private ChildrenFigure childrenFigure 
= new ChildrenFigure();
public ColumnFigure() {
    ToolbarLayout layout 
= new ColumnLayout();
    layout.setVertical(
true);
    layout.setStretchMinorAxis(
true);
    setLayoutManager(layout);
    setBorder(
new LineBorder());
    setBackgroundColor(color);
    setOpaque(
true);
    add(name);
    add(childrenFigure);
    setPreferredSize(
100-1);
}
class ChildrenFigure extends Figure {
    public ChildrenFigure() {
        ToolbarLayout layout 
= new FillLayout();
        layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER);
        layout.setStretchMinorAxis(
true);
        layout.setVertical(
true);
        layout.setSpacing(
5);
        setLayoutManager(layout);
    }
}
class HeaderFigure extends Figure {
    private String text;
    private Label label;
    public HeaderFigure() {
        
this.label = new Label();
        
this.add(label);
        setOpaque(
true);
    }
    public String getText() {
        
return this.label.getText();
    }
    public Rectangle getTextBounds() {
        
return this.label.getTextBounds();
    }
    public 
void setText(String text) {
        
this.text = text;
        
this.label.setText(text);
        
this.repaint();
    }
    public 
void setBounds(Rectangle rect) {
        super.setBounds(rect);
        
this.label.setBounds(rect);
    }
}

单元格的布局管理器同样使用FillLayout,因为在需求中,用户向单元格里添加第一个节点时,该节点要充满单元格;当单元格里有两个节点时,每个节点占二分之一的高度;依次类推。下面的表格总结了各个图形使用的布局管理。由表可见,只有包含子图形的那些图形才需要布局管理器,原因很明显:布局管理器关心和管理的是"子"图形,请时刻牢记这一点。

 

布局管理器

直接子图形

画布

ToolbarLayout

ColumnLayout

列头部、列下部

-列头部

-列下部

FillLayout

单元格

单元格

FillLayout

节点

节点

这里需要特别提醒一点:在一个图形使用ToolbarLayout或子类作为布局管理器时,图形对应的EditPart上如果安装了FlowLayoutEditPolicy或子类,你可能会得到一个ClassCastException异常。例如例子中的CellFigure,它对应的EditPart是CellPart,其上安装了CellLayoutEditPolicy是FlowLayoutEditPolicy的一个子类。出现这个异常的原因是在FlowLayoutEditPolicy的isHorizontal()方法中会将图形的layout强制转换为FlowLayout,而我们使用的是ToolbarLayout。我认为这是GEF的一个疏忽,因为作者曾说过FlowLayout可应用于ToolbarLayout。幸好解决方法也不复杂:在你的那个EditPolicy中覆盖isHorizontal()方法,在这个方法里先判断layout是ToolbarLayout还是FlowLayout,再根据结果返回合适的boolean值即可。

最后,关于我们的画布还有一个问题没有解决,我们希望表格列增多到一定程度后,画布可以向右边扩展尺寸,前面说过画布使用的是FreeformLayer作为图形。为了达到目的,还必须在editor里设置rootEditPart为ScalableRootEditPart,要注意不是ScalableFreeformRootEditPart,后者在需要各个方向都能扩展的画布的应用程序中经常被使用。关于各种RootEditPart的用法,在后续帖子里将会介绍到。

以上结合具体实例讲解了如何在GEF中使用ToolbarLayout以及自定义简单的布局管理器。我们构造图形应该遵守一个原则,那就是尽量让布局管理器决定每个子图形的位置和尺寸,这样可以避免很多麻烦。当然也有例外,比如在XYLayout这种只关心子图形位置的布局管理器中,就必须为每个子图形指定尺寸,否则图形将因为尺寸过小而不可见,这也是一个开发人员十分容易疏忽的地方。

论文阅读——Bayesian Knowledge Fusion(贝叶斯知识融合) 该篇论文解决了不确定环境中的信息融合问题。想象一下,有多位专家针对同一情况构建概率模型,我们希望汇总他们提供的信息。直接合并每个信息可能会遇到几个问题。例如,专家们可能不同意某个事件发生的概率,或者他们可能不同意两个事件之间因果关系的方向(例如,一个人认为 A 导致 B,而另一个人认为 B 导致 A)。他们甚至可能不同意概率网络中一组变量之间的整个依赖结构。 阅读详情

相关推荐

gef快速入门教程(详细文档,里面有完整例子)

gef快速入门教程 一份文档,让你能对gmf快速入门,且能做出属于自己的文档

[Eclipse]GEF入门系列浅谈布局

虽然很多GEF应用程序里都会用到连接(Connection),但也有一些应用是不需要用连接来表达关系的,我们目前正在做的这个项目就是这样一个例子。在这类应用中,模型对象间的关系主要通过图形的包含来表达,所以大多是一对多关系。 图1 不使用连接的GEF应用 先简单描述一下我们这个项目,该项目需要一个图形化的模型编辑器,主要功能是在一个具有三行N列的表格中自由增加/删除节点,节点可在不同...

diaozhuo4700的博客 296

Eclipse插件开发:GEF入门系列宝典_gef_

由于工作的需要,最近开始研究GEF(Graphical Editor Framework)这个框架,它可以用来给用户提供图形化编辑模型的功能,从而提升用户体验,典型的应用如图形化的流程设计器、UML类图编辑器等等。

[Eclipse]GEF入门系列(四、其他功能)

最近由于实验室任务繁重,一直没有继续研究GEF,本来已经掌握的一些东西好象又丢掉了不少,真是无奈啊,看来还是要经常碰碰。刚刚接触GEF的朋友大都会有这样的印象:GEF里概念太多,比较绕,一些能直接实现的功能非要拐几个弯到另一个类里做,而且很多类的名字十分相似,加上不知道他们的作用,感觉就好象一团乱麻。我觉得这种情况是由图形用户界面(GUI)的复杂性所决定的,GUI看似简单,实际上包含了相...

diaozhuo4700的博客 302

使用图形编辑框架GEF创建基于Eclipse的应用程序

简介: 了解使用图形编辑框架(Graphical Editing Framework,GEF)创建基于 Eclipse 的应用程序涉及的最初步骤。此外,介绍了目前在 Eclipse 中引导创建图形编辑器过程使用的选项。 本文将介绍使用图形编辑框架(Graphical Editing Framework,GEF)的步骤。我们不是完整地完成每个步骤,而是将使用您的应用程序模型的子集,并先使该

wwj13141988的专栏 2902

Eclipse GEF Classic 项目教程

Eclipse GEF Classic 项目教程 1. 项目的目录结构及介绍 Eclipse GEF Classic 项目的目录结构如下: gef-classic/ ├── CONTRIBUTING.md ├── README.md ├── gef/ │ ├── draw2d/ │ ├── mvc/ │ ├── zest/ │ └── ... ├── .gitignore ├── ...

gitblog_01109的博客 478

[Eclipse]GEF入门系列(十二、自定义Request)

先简单回顾一下Request在GEF里的作用。Request是GEF里一个比较重要的角色,Tool将原始的鼠标事件转换为EditPart可以识别的请求,Request则承载了这些请求信息。举例来说,用户在调色板(Palette)里选择了创建节点工具(CreationTool),然后在画布区域按下鼠标左键,这时产生在画布上的鼠标单击事件将被CreationTool转换为一个CreateRe...

diaozhuo4700的博客 199

GEF:应用示例列表

简单的节点连线(应用实例) 隔间形状(用GMF生成简化的数据库设计器) 不使用连接的GEF应用(浅谈布局) 表格的一个实现 树的一个实现 容器应用(XYLayout和展开/折叠功能)          推荐:你可能需要的在线电子书   欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]...

weixin_34232363的博客 236

【亲测免费】 Eclipse GEF Classic 使用教程

Eclipse GEF Classic 使用教程 项目介绍 Eclipse GEF (Graphical Editing Framework) Classic 是一个开源项目,旨在为开发者提供一个强大的图形编辑框架。GEF Classic 是 Eclipse 生态系统的一部分,支持 MVC 架构,并且与 Draw2d 和 Zest 等组件紧密集成。该项目欢迎社区贡献,并且是成熟稳定的项目之一。 项...

gitblog_00041的博客 690

[Eclipse]GEF入门系列(一、Draw2D)

鸡年第一天,首先向大家拜个年——恭祝新春快乐,万事如意。一年之计在于春,你对新的一年有什么安排呢?好的,下面还是进入正题吧。 关于Java2D相信大家都不会陌生,它是基于AWT/Swing的二维图形处理包, JDK附带的示例程序向我们展示了Java2D十分强大的图形处理能力。在Draw2D出现以前,SWT应用程序在这方面一直处于下风,而Draw2D这个SWT世界里的Java2D改变了这...

diaozhuo4700的博客 474

GEF 入门教程

GEF教程.pdf+git项目源码 希望可以帮助入门学习GEF的人

Runner_up的博客 2215

[Eclipse]GEF入门系列(三、应用实例)

构造一个GEF应用程序通常分为这么几个步骤:设计模型、设计EditPart和Figure、设计EditPolicy和Command,其中 EditPart是最主要的一部分,因为在实现它的时候不可避免的要使用到EditPolicy,而后者又涉及到Command。 现在我们来看个例子,它的功能非常简单,用户可以在画布上增加节点(Node)和节点间的连接,可以直接编辑节点的名称以及改变节点的...

diaozhuo4700的博客 373

探索Eclipse Graphical Editing Framework(GEF):构建图形化应用的新时代

探索Eclipse Graphical Editing Framework(GEF):构建图形化应用的新时代 项目地址:https://gitcode.com/gh_mirrors/ge/gef 1、项目介绍 Eclipse Graphical Editing Framework (GEF) 是一个强大的工具包,专为在Eclipse环境中集成和创建图形化应用程序而设计。它提供了一系列组件,包括[D...

gitblog_00092的博客 889

基于知识库的手写体数字识别matlab仿真+操作视频

1.领域:matlab,基于知识库的手写体数字识别算法2.内容:基于知识库的手写体数字识别matlab仿真+操作视频 3.用处:用于基于知识库的手写体数字识别算法编程学习4.指向人群:本硕博等教研学习使用5.运行注意事项:使用matlab2021a或者更高版本测试,运行里面的Runme_.m文件,不要直接运行子函数文件。运行时注意matlab左侧的当前文件夹窗口必须是当前工程所在路径。具体可观看提供的操作录像视频跟着操作。

上一篇: GEF入门系列(四、其他功能)
下一篇: GEF入门系列(六、添加菜单和工具条)
kernel_engine
博客等级 码龄21年 6粉丝 16原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值