[IBM DW] Zest(Eclipse Visualization Toolkit)开发入门

红薯 发布于 2010/11/14 15:12
阅读 1K+
收藏 3

简介: 本文介绍了如何使用 Eclipse Visualization Toolkit(http://www.eclipse.org/gef/zest/)这个优秀精巧的 Java 图形工具库来进行图形建模工作流的开发应用。通过程序示例由浅入深,让读者在短时间内掌握 Zest 图形库的基本概念和实用开发技巧,学习本文,相信读者可以使用 Eclipse Visualization Toolkit 图形库为 Eclipse 或单独的程序开发出更加精彩和 UML 建模,工作流程图形化表现相关的应用示例。

文章摘要

本文介绍了如何使用 Eclipse Visualization Toolkit(http://www.eclipse.org/gef/zest/)这个短小精悍的 Java 图形工具库来进行简单的工作流程图形化的应用开发。通过程序示例由浅入深,让读者在短时间内掌握 Zest 图形库的基本概念和实用开发技巧,学习本文,相信读者可以使用 Eclipse Visualization Toolkit 图形库为 Eclipse 或单独的程序开发出更加精彩和 UML 建模 ( 类图,对象关系图等 ),工作流程图形化表现相关的应用范例。

使用 Zest 开发应用程序,建议读者首先了解基本的 SWT(http://www.eclipse.org/swt/) 开发知识,在 DeveloperWorks 的网站上有许多和 SWT/JFace 相关的文章和教程,感兴趣的读者可以查找阅读。Zest的 开发 SDK 可以从网站 (http://www.eclipse.org/gef/zest/) 下载。Zest SDK 需要依赖于 Draw2D,Zest 的网站上有整合所有内容的完整开发包供下载。Draw2D 主要作用是在 SWT 的画布 (Canvas) 上提供了一种轻量级呈现和布局管理功能。在开发环境中设置好所依赖的程序库 (org.eclipse.draw2d_Version.jar,org.eclipse.zest.core_Version.jar,org.eclipse.zest.layouts_Version.jar 和 swt.jar),就可以进行 Zest 程序的代码编写和编译开发了。

Zest 项目概述

Zest 库简介和优势

Zest(The Eclipse Visualization Toolkit) 是在 Eclipse 平台基础上开发的一套可视化图形构件集合,方便开发和 UML 相关的图形应用程序,但范围不限于 UML 相关的应用,也可以用来开发工作流程图形化建模,树状结构图等。本文的示例代码都是以开发简单工作流程图形建模为例子。

Zest 库是从 SWT 和 Draw2D 扩展开发而来,可以无缝的集成到 Eclipse 的应用当中。因为 Zest 是基于 SWT(JFace) 的,所以 Zest 遵循 Eclipse 平台视图 (View) 的相关标准和规范,可以很容易在开发 Eclipse 的各种视图应用当中被集成和扩展。

虽然 Eclipse 的图形编辑框架 (GEF, http://www.eclipse.org/gef/) 也能够开发出丰富的图形应用,但是基于 GEF 的应用程序无法脱离 Eclipse 平台而单独运行;而基于 Zest 的应用没有这个限制,可以作为独立的应用程序在存在,从而脱离庞大的 Eclipse 平台,让应用程序更加小巧和灵活。

Zest 的组件类型

Zest 库提供了如下几种最基本的组件。

  • 图形节点 (GraphNode):最基本的包含某些特性的节点图形,例如颜色,大小,位置和标签等。
  • 图形关联 (GraphConnections):存储关联两个节点之间关联关系的图形对象,也包含连线的一些属性信息,例如:连线的颜色,线条宽度等。
  • 图形容器 (GraphContainer):图形容器和图形节点类似,包含图形节点的所有属性,但图形容器支持折叠和展开的行为特性。
  • 图形 (Graph):一个容器,用来容纳图形节点,图形容器以及图形关联这些对象。
  • 样式常量 (ZestStyles):Zest 库默认设置的一些系统常量,例如线形等 ( 实线,虚线 ...)

Zest 的布局

Zest 库也提供了布局管理器,通过布局管理器来决定图形当中的节点,关联等这些图形对象如何在屏幕上显示分布。


表 1. Zest 布局管理器

布局管理器名称 描述
CompositeLayoutAlgorithm 组合其他布局方法来设置图形显示
DirectedGraphLayoutAlgorithm 以全部重叠的方式来设置图形显示
GridLayoutAlgorithm 以网格的布局方式来设置图形显示
HorizontalLayoutAlgorithm 以水平方式来设置图形显示
HorizontalShift 以重叠的方式依次向右水平来设置图形显示
HorizontalTreeLayoutAlgorithm 以水平树状方式来设置图形显示
RadialLayoutAlgorithm 以放射状的布局来设置图形显示
SpringLayoutAlgorithm 以相同关联长度,图形节点重叠最少来设置图形显示
TreeLayoutAlgorithm 以垂直树状方式来设置图形显示
VerticalLayoutAlgorithm 以垂直方式来设置图形的显示

用户也可以开发自定义的布局管理器。

Zest 基础组件开发

学习了 Zest 库当中的一些基本概念,就容易理解创建各种图形对象的类和方法,将这些图形对象通过合适的逻辑关系建立联系,就可以开发出一个简单的图形显示程序。

图形节点 (GraphNode) 的创建和属性

通常使用如下的构造函数来创建一个图形节点对象,需要传入 3 个参数。

GraphNode(IContainer graphModel, int style, String text)

各个参数的含义分别是:

设置图像节点对象所在的图形 (Graph) 对象,就是设置在哪个对象上面显示。

设置图形节点的风格,就是节点要显示成为什么样子。

设置图形节点上的标签,就是节点上要显示什么文字内容。

图形节点对象当中包含一系列的 setXX() 方法,通过这些方法,可以设置节点的大写,位置,颜色,字体等等。


图 1. 设置图形节点对象属性的方法
图 1. 设置图形节点对象属性的方法

节点关联 (GraphConnection) 的创建和设置

创建节点关联对象只有一个构造函数,需要传入 4 个参数。

GraphConnection(Graph graphModel, int style, GraphNode source, GraphNode destination)

各个参数的含义分别是:

设置节点关联对象所在的图形 (Graph) 对象,就是设置在哪个对象上面显示。

设置节点关联的风格,就是节点关联要显示成为什么样子。

设置关联的源节点对象,从哪个节点开始。

设置关联的目标节点对象,到哪个节点结束。

节点关联对象的属性设置方法如下所示:


图 2. 设置节点关联对象属性的方法
图 2. 设置节点关联对象属性的方法

第一个 Zest 程序

知道了如何创建节点对象和节点关系对象,就可以轻松创建出第一个基于 Zest 的程序。一个最简单的工作流程图形化建模程序,包含一个开始 (Start) 节点,一个结束 (End) 节点和两者之间的一条连线。


清单 1. 第一个 Zest 程序代码

				
import org.eclipse.zest.core.widgets.Graph;
import org.eclipse.zest.core.widgets.GraphConnection;
import org.eclipse.zest.core.widgets.GraphNode;
import org.eclipse.zest.layouts.LayoutStyles;
import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class FirstZest {

public static void main(String[] args) {
// SWT
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("First Zest Program Demo");
shell.setLayout(new FillLayout());
shell.setSize(300, 300);

// 创建 Graph
Graph graph = new Graph(shell, SWT.NONE);
// 创建一个图形节点
GraphNode startNode = new GraphNode(graph, SWT.NONE, "Start");
// 创建另外一个图形节点
GraphNode endNode = new GraphNode(graph, SWT.NONE, "End");
// 创建节点关联
new GraphConnection(graph, SWT.NONE, startNode, endNode);
// 设置布局管理器
graph.setLayoutAlgorithm(new SpringLayoutAlgorithm(
LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);

// 显示 SWT
shell.open();
while (!shell.isDisposed()) {
while (!display.readAndDispatch()) {
display.sleep();
}
}
}
}



图 3. 第一个 Zest 程序
图 3. 第一个 Zest 程序

通过简单的几行代码,就创建了 2 个图形节点,并在它们之间建立了关联。你会发现,任何一个图形节点可以随意的通过鼠标拖动,而且连线也会随着节点的移动而移动。把很多复杂的功能的实现进行了封装和屏蔽。代码简单,但是功能强大。

也很容易的通过设置图形节点和节点关联的风格,来改变显示的样子。


清单 2. 设置图形节点和节点关联显示风格示例代码

				
Graph graph = new Graph(shell, SWT.NONE);
// 设置连线风格
graph.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED);
// 设置图像
Image startIcon = new Image(display,"StartIcon.png");
// 创建图形节点
GraphNode startNode = new GraphNode(graph, SWT.NONE, "Start", startIcon);

 

显示的效果如下图所示:


图 4. 显示风格示例
图 4. 显示风格示例

为了美化这个最简单的工作流程图形化建模程序,给连线增加了箭头,给图形节点增加了图标。

Zest 事件驱动开发

Zest 库当中组件的行为也是通过事件的方式进行驱动的。用户可以通过键盘,鼠标来操作 Zest 的图形组件,通过触发相应的事件来完成用户期望的业务行为。Zest 库的事件模型和编程方式和 SWT 是完全相同的。

元素的事件驱动

对于图形节点 (GraphNode) 和图形关联 (GraphConnection) 对象可以通过调用下面的方法

addListener (int eventType, Listener listener)

为对象注册相应的事件,当这个事件发生的时候,就会触发相应的操作。具体的用法和接口参数可以参考 SWT 的文档。

对于图形 (Graph) 对象 Zest 提供了更多和更便利的事件使用注册方法。


图 5. Graph 对象提供的事件注册方法
图 5. Graph 对象提供的事件注册方法

下面通过代码来演示一下,在图形当中,当某个对象被选中的事件发生的时候,如何来触发特定的逻辑。

事件注册和触发示例

当某个对象被选中的时候,通过调用 addSelectionListener() 方法来注册事件。


清单 3. Graph 对象注册事件代码示例

				
// 创建 Graph
Graph graph = new Graph(shell, SWT.NONE);
// 注册对象选择侦听事件
graph.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
List selection = ((Graph) e.widget).getSelection();
// 确认只选择了一个对象
if (selection.size() == 1) {
Object o = selection.get(0);
// 图形节点对象
if (o instanceof GraphNode) {
// 改变边线宽度
((GraphNode) o).setBorderWidth(3);
// 图形关联对象
} else if (o instanceof GraphConnection) {
// 改变连线宽度
((GraphConnection) o).setLineWidth(3);
}
}
}
});

 

工作流程图形化建模程序的运行效果进行美化,当图形节点或者图形关联被选中的时候,边线会被自动加粗。


图 6. Graph 注册事件运行结果
图 6. Graph 注册事件运行结果

Zest 布局管理开发

Zest 库已经提供了多种图形显示的布局管理方式,需要用户根据图形应用的具体的情况来选择合适的布局管理算法。如果在程序当中没有设置任何布局管理,那么所有的图形节点和图形关联对象都将重合在一起。

常用布局管理的示例

下面分别给出了 Zest 当中常用布局管理的显示效果。

清单 4. 常用布局管理代码示例

 Graph graph = new Graph(shell, SWT.NONE); 
graph.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED);
for (int i = 0; i < 10; i++) {
GraphNode node1 = new GraphNode(graph, ZestStyles.NODES_FISHEYE,
"Begin");
GraphNode node2 = new GraphNode(graph, ZestStyles.NODES_FISHEYE,
"Middle");
GraphNode node3 = new GraphNode(graph, ZestStyles.NODES_FISHEYE,
"Finish");
new GraphConnection(graph, SWT.NONE, node1, node2);
new GraphConnection(graph, SWT.NONE, node2, node3);
}
graph.setLayoutAlgorithm(new GridLayoutAlgorithm(
LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
/*
graph.setLayoutAlgorithm(new SpringLayoutAlgorithm(
LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
graph.setLayoutAlgorithm(new RadialLayoutAlgorithm(
LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
graph.setLayoutAlgorithm(new TreeLayoutAlgorithm(
LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
graph.setLayoutAlgorithm(new DirectedGraphLayoutAlgorithm(
LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
*/

 

布局显示的效果依次为 Gridlayout, SpringLayout, Radialayout, TreeLayout 和 DirectedFraphLayout。


图 7. 不同布局管理器的显示效果对比
图 7. 不同布局管理器的显示效果对比

如何开发自定义的布局管理

除了系统内置的布局管理器,Zest 也支持自定义的布局管理器。要实现自定义的布局管理器需要继承一个抽象类 AbstractLayoutAlgorithm。这个抽象类当中,要实现 7 个抽象方法,它们的名称和作用分别是:

void setLayoutArea()

设置布局的区域

boolean isValidConfiguration()

判断对于当前的布局配置是否正确

void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight)

把给定的对象进行布局,需要布局的对象会按照设定的算法被重新排列。这是最核心的一个方法。各个参数的含义分别是:

InternalNode[] entitiesToLayout:需要重新布局的对象数组

InternalRelationship[] relationshipsToConsider:重新布局对象之间关系的数组

double boundsX:布局区域可以放置对象的横 (X) 坐标开始位置

double boundsY:布局区域可以放置对象的纵 (Y) 坐标开始位置

double boundsWidth:布局区域的宽度

double boundsHeight:布局区域的高度

void preLayoutAlgorithm()

新布局算法之前调用的方法

postLayoutAlgorithm()

新布局算法之后调用的方法

getTotalNumberOfLayoutSteps()

获取布局当中总的步骤

int getCurrentLayoutStep()

获取当前的布局步骤

下面演示一个自定义的布局算法,让所有图形节点按照水平方式以相同间隔依次排列。


清单 4. 自定义布局算法代码示例

				
Graph graph = new Graph(shell, SWT.NONE);
graph.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED);
GraphNode node1 = new GraphNode(graph, SWT.NONE, "Node 1");
GraphNode node2 = new GraphNode(graph, SWT.NONE, "Node 2");
GraphNode node3 = new GraphNode(graph, SWT.NONE, "Node 3");
new GraphConnection(graph, SWT.NONE, node1, node2);
new GraphConnection(graph, SWT.NONE, node2, node3);
graph.setLayoutAlgorithm(new AbstractLayoutAlgorithm(SWT.NONE) {
int totalNodes;
protected void applyLayoutInternal(InternalNode[] entitiesToLayout,
InternalRelationship[] relationshipsToConsider,
double boundsX, double boundsY, double boundsWidth,
double boundsHeight) {
// 需要布局的对象数量
totalNodes = entitiesToLayout.length;
// 设置固定间隔距离
double spcaing = 100;
// 开始位置坐标
int startX = 10;
// 触发布局进程
fireProgressStarted(totalNodes);
// 循环所有对象,按照设定的布局算法来排列对象
for (int curNode = 0; curNode < entitiesToLayout.length; curNode++) {
LayoutEntity layoutEntity = entitiesToLayout[curNode]
.getLayoutEntity();
// 设置一个对象显示的位置
layoutEntity.setLocationInLayout(startX, layoutEntity
.getYInLayout()+10);
// 保持相同的间隔
startX += spcaing;
// 让对象在新位置上显示
fireProgressEvent(curNode, totalNodes);
}
// 结束布局进程
fireProgressEnded(totalNodes);
}

protected int getCurrentLayoutStep() {
return 0;
}

protected int getTotalNumberOfLayoutSteps() {
return totalNodes;
}

protected boolean isValidConfiguration(boolean asynchronous,
boolean continuous) {
return true;
}

protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout,
InternalRelationship[] relationshipsToConsider) {
}

protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout,
InternalRelationship[] relationshipsToConsider, double x,
double y, double width, double height) {
}
public void setLayoutArea(double x, double y, double width,
double height) {
}
}, true);

Zest 开发小技巧

下面是一些 Zest 开发当中小技巧的代码示例。

如何更改图形节点的默认形状

在 Zest 当中,图形节点的默认形状是矩形的,其实可以根据需求,来更改节点的默认形状,可变成为圆形,方形或者菱形等等。下面的示例是将图形节点的形状显示为椭圆。

需要创建用户自定义的图形节点,一般来说需要继承 CGraphNode 这个类


清单 5. 自定义图形节点 EllipseGraphNode 代码示例

				
import org.eclipse.draw2d.IFigure;
import org.eclipse.zest.core.widgets.CGraphNode;
import org.eclipse.zest.core.widgets.IContainer;

public class EllipseGraphNode extends CGraphNode {
public EllipseGraphNode(IContainer graphModel, int style, IFigure figure) {
super(graphModel, style, figure);
}
}

 

同时也需要继承 Figure 这个类


清单 6. 自定义图形节点 EllipseFigure 的代码示例

				
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.ToolbarLayout;
import org.eclipse.draw2d.geometry.Rectangle;

public class EllipseFigure extends Figure {


public EllipseFigure(String name) {
// 定义显示名称
Label label = new Label(name);
// 定义图形节点的显示布局
ToolbarLayout layout = new ToolbarLayout();
setLayoutManager(layout);
setBorder(new LineBorder(ColorConstants.white, 1));
setOpaque(true);

add(label);
}
// 重写这个方法,实现自己需要显示的图形,更多的信息可以参考 Zest 的源代码
@Override
public void paint(Graphics graphics) {
super.paint(graphics);
// 获取默认的矩形信息
Rectangle rectangle = getBounds().getCopy();
graphics.setLineWidth(2);
// 画椭圆
graphics.drawOval(rectangle);
}
}

 

同时简要修改一下主程序,在程序当中添加一个新的方法,用来创建椭圆图形对象 .


清单 7. 创建 EllipseFigure 对象的代码示例

				
// 创建 EllipseFigure 对象的方法
private static IFigure createEllipseFigure(String name) {
EllipseFigure circleFigure = new EllipseFigure(name);
circleFigure.setSize(-1, -1);
return circleFigure;
}

GraphNode endNode = new GraphNode(graph, SWT.NONE, "End", stopIcon);
// 创建椭圆图形节点对象
EllipseGraphNode ellipseNode = new EllipseGraphNode(graph, SWT.NONE,
createEllipseFigure("ellipse"));

// 创建节点关联
new GraphConnection(graph, SWT.NONE, startNode, ellipseNode);
new GraphConnection(graph, SWT.NONE, ellipseNode, endNode);

 

运行程序得到如下的显示效果。在开始节点和结束节点之间增加了一个椭圆形的节点。


图 8. 椭圆图形节点显示效果
图 8. 椭圆图形节点显示效果

如何禁止图形节点的移动事件

Zest 图形 (Graph) 当中默认所有的组件对象都是可以被用户拖动。某些情况下,希望把某个元素固定不动,不可以被用户随意改变位置,实现的方法可以参考如下的代码:


清单 8. 禁止图形节点 (GraphNode) 被移动的代码示例

				
// 创建 Graph
Graph graph = new Graph(shell, SWT.NONE);
// 设置事件分发
graph.getLightweightSystem().setEventDispatcher(
new SWTEventDispatcher() {

@Override
public void dispatchMouseMoved(MouseEvent me) {
List selection = ((Graph) me.widget).getSelection();
for (Iterator iterator = selection.iterator(); iterator.hasNext();) {
Object object = (Object) iterator.next();
if (object instanceof GraphNode) {
String nodeName = ((GraphNode) object).getText();
if ("Start".equalsIgnoreCase(nodeName)) {
// 如果是 Start 图形节点,就无法被移动
} else {
// 其他图形节点,不受影响
super.dispatchMouseMoved(me);
}
}
}
}
});

 

这样显示名字为 Start 的图形节点就无法被用户移动,而其他工作节点不受任何影响。

结束语

关于 Zest 的开发入门介绍到此就结束了,希望本篇文章能够对您的了解 Zest 库的使用和编程开发有所帮助和启发。

加载中
返回顶部
顶部