[SWT]动态生成WizardPage

Eclipse的Wizard是由一系列WizardPage组成的。缺省情况下,WizardDialog在初始化的时候就会调用每个WizardPage的createControl()方法来布局这些WizardPage,这是为了方便决定WizardDialog窗口的大小。但如果某个WizardPage里的控件是需要动态生成的,例如,用户在Page1里输入一个整数n,Page2里要根据这个整数生成n个文本框,由于Page2的createControl()只有一次被调用的机会,并且在得到n之前就被WizardDialog调用过了,集中布局的方式就为生成Page2的界面带来了困难。

和问题的描述比起来,解决的方法简单很多,只要覆盖Wizard的createPageControls()方法让它什么都不要做就可以了:

@Override
public void createPageControls(Composite pageContainer) {
    //super.createPageControls(pageContainer);
}

理论上讲,这样做带来的问题将是WizardDialog的大小不一定能容纳所有的控件,但在实际应用中我还没遇到,只要动态生成的控件不要太多,或者使用滚动的方式容纳即可。

参考资料

http://dev.eclipse.org/newslists/news.eclipse.tools/msg02641.html

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/06/07/775314.html

脱离Eclipse环境使用EMF

一旦熟练使用EMF后,有新项目我总是习惯于先使用工具构造出数据模型,然后让EMF帮我生成java代码。当模型需要修改时,也是用工具修改模型本身,然后让EMF把改动更新到java代码,从而保证模型与代码的同步。最近的一个基于struts的Web项目里我试验了脱离Eclipse运行环境使用EMF,发现比想象中的要更容易,以下是一些经验总结。

  1. EMF可以脱离Eclipse环境使用,所以不论你要做的项目是纯swt应用程序、swing应用程序、基于web的应用程序甚至没有GUI的应用程序,这些非Eclipse插件的项目也都可以利用EMF生成的模型代码。不过EMF生成的.edit和.editor部分就难以利用了,因为.edit主要为jface的各种viewer提供支持,.editor则是基于Eclipse的一个编辑器。

  2. 要脱离Eclipse独立使用EMF生成的代码,需要在项目里包含EMF的运行库,可以在EMF下载页面找到这个名为Standalone的下载项,下载以后要让里面的.jar文件都包含在项目classpath里。

  3. 除添加这些库文件以外,还有少量初始化工作需要在你的应用程序启动时执行。在EMF-FAQ页面专门介绍了初始化的方法,基本上要做的就是在程序启动时执行下面两条命令:

XXXPackage xxxPackage = XXXPackage.eINSTANCE;
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xxx", new XMIResourceFactoryImpl());

Update(2009-2-19): 如果模型文件包含了除emf以外的名称空间声明(例如要打开一个.emx文件),则需要对它们进行注册,下面的代码注册了gmf1.0的名称空间:

((Delegator)EPackage.Registry.INSTANCE).put("http://www.eclipse.org/gmf/runtime/1.0.0/notation", NotationPackage.eINSTANCE);
  1. 用EMF生成模型代码的步骤和平时基本没有区别,仍然是".ecore->.genmodel->java代码"。一般来说,直接在这个EMF项目里以EMF生成的java代码为基础继续开发就可以了,如果你用其他IDE则可以把这些java代码复制过去使用,只是注意一下每次修改ecore后要重复这个生成和复制代码的过程。

  2. 关于多个模型和跨模型引用的处理。EMF的一个好处是可以把整个项目的模型保存为多个文件,并且允许文件之间的相互依赖。例如一个在线商店系统里,在设计的时候可以把模型分为用户信息、商品信息和订单信息三大部分,显然订单信息依赖用户和商品信息,所以对用户和商品的管理可以独立进行开发,一方面提高了模块化程度,也增加了重用的可能性,例如在另外一个项目里可以只做很小的修改甚至不做修改的重用用户管理部分或是商品管理部分。

EMF是通过ResourceSet处理多个模型之间的引用的,以上面的在线商店为例,当从一个文件(一般是xmi格式)里载入一个订单实例时,EMF并不会去实际读取存储用户或商品信息的文件,而是使用代理(Proxy)的方式生成一个替代品,当程序里需要得到订单对应的用户的详细信息时(例如执行了theOrder.getCustomer().getAddress()方法),才会实际读取用户信息文件,并根据订单的用户id找到对应的用户信息。

因此,当你的项目里有多个模型并且存在相互引用的这种情况时,建议维护一个全局的ResourceSet实例,可以在程序启动时创建:

ResourceSet resourceSet = new ResourceSetImpl();

对于普通应用程序,可以用单例模式(Singleton)维护ResourceSet实例;若在web应用程序里,也可以利用application维护它。在程序里,每载入一个模型都要把它加到这个ResourceSet实例上。具体方法如下:

//get global ResourceSet instance
ResourceSet resourceSet = ;
//file to load
String filename = ;//e.g. "c:\\work\\test.xxx"
URI fileURI = URI.createFileURI(filename);
//create resource
XMIResource resource = (XMIResource)resourceSet.createResource(fileURI);
//load from file
resource.load(null);
//add resource to global ResourceSet instance
resourceSet.getResources().add(resource);
//get your model
XXXModel xxxModel = (XXXModel) resource.getContents().get(0); 
  1. 其他常用方法,以下仍以在线商店里的模型(见此帖)为例:

创建一个新产品(Product),通过ShopFacotory的createProduct()方法而不要直接new ProductImpl():

Product p = ShopFactory.eINSTANCE.createProduct();

Product p = (Product)ShopFactory.eINSTANCE.create(ShopPackage.Literals.Product);

把新创建的产品添加到商店(Shop):

Shop model = …;
Product p = …;
model.getProducts().add(p);

通过EMF反射机制得到Product类型的所有属性(非引用类型),任何一个EObject都可以通过这种方式得到其所有属性:

Product p = …;
EClass eclass = p.eClass(); 
for (Iterator iterator = eclass.getEAllAttributes().iterator(); iterator.hasNext();) {
  EAttribute attr = (EAttribute)iterator.next(); //属性,如Product#name
  Object value = p.eGet(attr); //属性值,如“walkman”
  … //do whatever with the value
}

通过EMF反射机制得到Category类型的所有引用,任何一个EObject都可以通过这种方式得到其所有引用:

Category category = …;
EClass eclass = category.eClass();
for (Iterator iterator = eclass.getEAllReferences().iterator(); iterator.hasNext();) {
  EReference ref = (EReference)iterator.next(); //引用,如Category#products
  Object value = category.eGet(ref); //引用值,如EList<Product>(一对多的情况)
  … //do whatever with the value
}

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/05/07/737869.html

GEF常见问题7:计算字符串在画布上占据的空间

要准确的计算文字在画布上占据的空间,可以利用org.eclipse.swt.graphics.GC的stringExtent()方法实现,见下面的代码:

GC gc = new GC(Display.getDefault());
gc.setFont(yourFont);//这一步不可缺少,因为有些字体里各字符的宽度是不同的
Point size = gc.stringExtent(text);//得到文字占据的尺寸
label.setPreferredSize(size.x + 16, size.y + 10);//让标签的尺寸比文字稍大
gc.dispose();

运行时的效果:

file

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/04/14/712794.html

用JFreeChart画仪表盘

JFreeChart画仪表盘(Speedo Meter Chart)的例子:

DefaultValueDataset data = new DefaultValueDataset(32.0);
MeterPlot plot = new MeterPlot(data);
plot.setDialShape(DialShape.CHORD);
plot.setDialBackgroundPaint(Color.WHITE);
plot.setRange(new Range(0, 120));
plot.setDialOutlinePaint(Color.GRAY);
plot.setNeedlePaint(Color.BLACK);
plot.setTickLabelsVisible(true);
plot.setTickLabelPaint(Color.BLACK);
plot.setTickPaint(Color.GRAY);
plot.setTickLabelFormat(NumberFormat.getNumberInstance());
plot.setTickSize(10);
plot.setValuePaint(Color.BLACK);
plot.addInterval(new MeterInterval("Low", new Range(0, 70), null, null,new Color(128, 255, 128,90) ));
plot.addInterval(new MeterInterval("Normal", new Range(70, 100), null, null, new Color(255, 255, 128,90)));
plot.addInterval(new MeterInterval("High", new Range(100, 120), null, null, new Color(255, 128, 128,90)));

//创建chart,最后一个参数决定是否显示图例
final JFreeChart chart = new JFreeChart("Meter Chart", JFreeChart.DEFAULT_TITLE_FONT, plot, false);

//放到SWT的Composite里,以前介绍过这个方法
Composite drawarea = new Composite(tabFolder, SWT.EMBEDDED);
drawarea.setLayout(new FillLayout());
Frame canvasFrame = SWT_AWT.new_Frame(drawarea);
java.awt.Canvas canvas = new java.awt.Canvas() {
    public void paint(Graphics g) {
        super.paint(g);
        if (chart != null)
            chart.draw((Graphics2D) g, getBounds());
    }
};
TabItem tab = new TabItem(tabFolder, SWT.NONE);
tab.setControl(drawarea);
tab.setText("Meter");
canvasFrame.add(canvas);

运行效果:

file

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/03/13/673303.html

为GMF应用程序设置背景图片

要为GMF应用程序的画布设置背景图片,可以覆盖XXXDiagramEditor的configureGraphicalViewer()方法,加入如下代码即可。不过加入背景图片后,网格线无法显示,有可能是图层顺序的问题。

/**
 * @generated NOT
 */
protected void configureGraphicalViewer() {
    super.configureGraphicalViewer();

    //////Background Layer//////////////////
    Layer backgroundLayer = new Layer() {
        @Override
        protected void paintFigure(Graphics graphics) {
            super.paintFigure(graphics);
            graphics.drawImage(NetworkDiagramEditorPlugin.getInstance().getBundledImage("images/worldmap_no_text.gif"), 0,
                    0);
        }
    };
    backgroundLayer.setSize(4990, 2484);
    ////////////////////////////////////////

    DiagramRootEditPart root = (DiagramRootEditPart) getDiagramGraphicalViewer().getRootEditPart();
    LayeredPane printableLayers = (LayeredPane) root.getLayer(LayerConstants.PRINTABLE_LAYERS);
    FreeformLayer extLabelsLayer = new FreeformLayer();
    extLabelsLayer.setLayoutManager(new DelegatingLayout());
    printableLayers.addLayerAfter(extLabelsLayer, NetworkEditPartFactory.EXTERNAL_NODE_LABELS_LAYER,
            LayerConstants.PRIMARY_LAYER);

    //////Insert Background Layer///////////
    printableLayers.addLayerBefore(backgroundLayer, NetworkEditPartFactory.EXTERNAL_NODE_LABELS_LAYER,
            LayerConstants.PRIMARY_LAYER);
    ////////////////////////////////////////

    LayeredPane scalableLayers = (LayeredPane) root.getLayer(LayerConstants.SCALABLE_LAYERS);
    FreeformLayer scaledFeedbackLayer = new FreeformLayer();
    scaledFeedbackLayer.setEnabled(false);
    scalableLayers.addLayerAfter(scaledFeedbackLayer, LayerConstants.SCALED_FEEDBACK_LAYER,
            DiagramRootEditPart.DECORATION_UNPRINTABLE_LAYER);
}

参考资料

为图形编辑器设置背景图片

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/03/13/673273.html

Graphical Modeling Framework简介

本文已发表在2006年12月《程序员》杂志,请勿转载。

本文假设读者有Eclipse平台应用程序开发经验,了解Eclipse平台的插件机制,使用EMF和GEF开发过应用程序。在本文中,“Eclipse应用程序”等价于“Eclipse插件”。

Eclipse是一个开源的平台,从一开始就被设计为允许各种功能以插件(Plug-in)的形式自由组装的结构,它凝聚了无数天才的设计,目前仅Eclipse.org下的子项目就有数十种,全世界范围内的各种插件则数以千计。随着Eclipse开源社区影响力的不断增强,国内外有越来越多的实际应用构造在Eclipse平台上,同时有更多组织和个人加入到为Eclipse贡献力量的行列中,这样的良性循环形成了以Eclipse平台为中心的巨大生态系统。

在众多插件中,一些插件存在的目标是为了简化开发人员开发其他Eclipse插件的过程,接下来要介绍的GMF就是这样一个插件。

一、GMF与EMF、GEF

开发过Eclipse插件的朋友们一定知道EMF和GEF这两个插件项目。EMF (Eclipse Modeling Framework)可以帮助我们以模型驱动的方式开发Eclipse应用程序,具体来说,它定义了一套名为Ecore的元模型(类似UML元模型,包括包、类型、属性和方法等元素),用Ecore定义的模型可以被EMF转换为运行在Eclipse平台上的Java代码,这些代码实现了一套消息通知机制,是构造一个可靠应用不可缺少的基础。

绝大多数应用程序都有一个业务模型,不论以什么方式描述,这个模型都是整个应用程序的基础。利用EMF强大而完善的建模能力,我们不仅可以更直观方便的构造业务模型,更重要的是,得益于EMF的代码生成能力,在整个应用程序的开发周期里,这个模型都能够保持最新的版本,开发人员在任何时候都能很快的了解整个业务。

虽然对开发人员来说,业务逻辑是应用程序的关键,但直接和用户打交道的还是程序的UI部分。我们经常说一幅图胜过千言万语,这句话在应用程序的UI设计里也同样适用。试想如果我们熟悉的UML类图是用文本来描述的,我们将多花费多少时间在理清类与类之间的关系上呢。GEF的出现,为我们在Eclipse平台下开发图形化的应用程序铺平了道路。

GEF(Graphical Editing Framework)框架具有标准的MVC(Model-View-Control)结构。在模型部分,GEF没有对开发人员进行任何限制,只要模型发生改变时能够通知到控制器即可。GEF里的控制器被称为EditPart,它们是应用程序的核心部分,负责将模型的改变反映到视图,并将用户在视图上所做的操作应用到模型。在视图方面,GEF使用Draw2D,一个轻量级的图形系统。有了GEF的帮助,要在Eclipse里开发出类似Visio的图形化应用程序变得简单多了。

随着Eclipse平台被更多开发人员接受,结合EMF和GEF逐渐成为了构造GEF应用程序的主要模式,也就是在EMF生成的模型基础上开发GEF应用程序,这样可以在模型部分节约不少的工作量。但也有一些问题,例如两个框架各实现了一套命令机制来操作模型等等,它们又在一定程度上削弱了结合这两种技术带来的改善程度。

GMF(Graphical Modeling Framework)让我们可以用更简单的方法实现以前要同时用EMF和GEF开发的应用程序,而结合二者所带来的的各种问题则不需要关心。同时,借助Eclipse的插件机制,GMF还提供了十分丰富的扩展性,便于开发适合特定需求的应用程序。

file

图表 1 GMF的运行机制

我们不妨拿房子做比喻来说明他们在构造Eclipse应用程序中的地位。一个完整的图形化应用程序就像一个漂亮舒适的家,用EMF构造了业务模型后就相当于买到了毛坯房,这时的房子甚至连门都不全,看起来就像EMF为我们生成的缺省编辑器,十分的简陋。所以接下来的工作就是装修,也就是让应用程序的界面更加漂亮。装修的风格因人而异,这项花费有时甚至比房子本身的价值更高,在装修过程中还有可能对房子本身做出一些修改,例如拆掉一面墙或是做一个楼梯等等,可以把GEF的部分比做对客厅的装修,因为客人来参观时对整个房子的第一印象往往来自客厅,所以在客厅上多花些精力十分有必要,GEF帮我们实现的图形化编辑器就是很大的亮点。然而,不可否认房子的主人每天最多时间是在卧室度过的,所以卧室的布置也是决定房子是否舒适的重要因素,可以把应用程序里各种视图、向导、偏好、菜单和工具条等等比作对卧室的装饰。

至于GMF呢,用它构造图形化应用程序,就像买了一套“精装修”的现房,按开发商的承诺您是可以立即入住的,不仅墙壁喷涂了,地板铺好了,就连整套家具和家电也预备齐全。然而开发商给所有人的装修都是差不多的,顶多提供几套可选方案,不可能完全满足你的全部个性化需求。同样,用GMF生成的程序虽然比EMF生成的好看很多,但还有不少工作是需要自己动手的。

下面,为了能让大家对GMF能为我们做什么有一个直观的印象,我们简单演示一下如何用GMF开发一个流程编辑器。

二、用GMF实现图形化流程编辑器

GMF是以EMF为建模基础的,理所当然业务模型也是一个ecore模型。开始一个GMF项目的第一步就是新建一个空白的EMF项目,在项目里定义我们的业务模型,这个ecore模型里,我们定义一个流程(Process)需要的各种元素。为了节约篇幅,这里的流程模型元素十分简单,只包括开始节点(Beginning)、结束节点(Ending)、流程(Process)、活动(Activity)、条件(Condition)和关系连接(Relationship)这几种类型。其中,Process是根节点,即一个流程文件里只包含一个Process元素。Process下包含多个Activity、多个Condition和多个Relationship,但只包含一个Beginning和一个Ending。

每个流程元素都具有一个字符串类型的名字属性,我们为Activity添加一个优先级(priority)属性用来表示活动的紧急程度,这个属性是枚举类型,具有High、Normal和Low这三个值,在ecore里定义枚举类型是十分方便的;再为Condition添加一个字符串类型的表达式(expression)属性。

file

图表 2 流程编辑器的ecore模型

现在从ecore模型生成genmodel模型(菜单File -> New -> EMF Model),并生成全部代码(在genmodel模型根节点的右键菜单里选Generate All命令),这个genmodel模型将被后面需要定义的一些模型引用,模型和.edit部分的代码是GMF图形项目编译的前提条件,所以这一步是必要的。

到现在为止的这些步骤和构造普通的EMF应用程序并没有什么不同,不要着急,下面要做的事情就进入GMF的领域了。

现在我们来定义图形模型(Graph Model)、工具模型(Tooling Model)和映射模型(Mapping Model)这三个模型,然后利用它们生成名为GMF生成模型的模型(GMFGen Model,相当于EMF里的Gen Model),最后由生成代码。为了把将要建立的几个GMF模型与已经定义好的EMF模型分开,我们可以新建一个Eclipse项目存放它们,以便在需要时更快的找到想要的模型。

首先来定义图形模型,顾名思义,在这个模型里我们要定义出流程编辑器里每种元素的外观,例如,用圆圈表示开始和结束节点,用方块表示Activity,用菱形表示Condition等等。GMF提供了名为“GMFGraph Simple Model”的向导可以从刚才定义好的ecore文件得到一个缺省的图形模型,我们只要在向导里指定ecore文件所在的位置即可。

接下来定义工具模型,这个模型指定流程编辑器的调色板(Palette)里都可以有哪些工具,你还可以在这个模型里对这些工具进行分组。同样,使用“GMFTool Simple Model”向导可以很快的得到这个模型。

然后是映射模型,这个模型的作用是把ecore模型、图形模型和工具模型联系起来,这样每个业务元素就可以对应到一个外观和一个工具,并且ecore模型里各元素之间的包含、引用关系也可以通过这个模型映射到图元的包含或连线方式。GMF提供的“Guide GMFMap Creation”向导可以帮助我们建立这个模型的一部分内容,其余部分还是需要在编辑器里手动添加或修改。尽管现在这几个模型的编辑界面还显得十分简陋,但在下一个版本(2.0)的GMF里将会提供更多新的GUI以帮助编辑这几个模型,例如提供可视化的模型编辑器。

有了映射模型,我们可以像在EMF里从ecore模型生成genmodel模型那样生成一个gmfgen模型了。生成步骤很简单,只要在Package Explorer里的gmfmap文件上点右键,选择“Create generator model”命令即可。生成以后,我们还可以对gmfgen模型进行微调,因为其中有些信息是在前三个模型里都没有包含的,大部分时候我们只需要根据需要修改其中的少数几个就够了。常用的一个是可以修改Gen Compartment的List layout属性来控制子图形是否可以在父图形里随意拖动。

最后,在gmfgen文件上点右键,选择“Generate diagram code”命令,GMF就会为我们生成完整的图形编辑器代码了。这些代码会存放在名为your.package.diagram的新工程里,这个工程依赖EMF帮你生成的两个工程,所以前面要先生成EMF的工程,否则这个工程无法正确编译。怎么样,这个界面和EMF生成的编辑器比起来够不够“精装修”的标准:

file

图表 3-1 GMF生成的流程编辑器

file

图表 3-2 EMF缺省编辑器

当然,这个编辑器的外观可能还不能让你满意,比如除了连接线外所有的图元都是用矩形表示的,未免过于单调,而且每个图元上只显示了有限的文字信息。下面我们介绍一下如何通过修改GMF的几个模型来定制流程编辑器。

首先,要改变各个图形元素的缺省外观,在缺省情况下,GMF建模向导里都将节点元素映射为矩形(Rectangle),连接元素映射为折线(Polyline),当然这些都可以在gmfgraph里修改,例如我们可以让开始和结束节点显示为圆点,让条件节点显示为菱形,将活动节点填充为蓝色,同时可以为连接线的目标端增加箭头图形来表示连接的方向,这样一来,熟悉UML的用户就更容易理解流程图的含义了。

从上面不难看出,gmfgraph模型的作用是定义图形表现的方式,GMF映射模型负责关联业务模型、gmfgraph模型和gmftool模型。任何在gmfgraph里定义的元素如果没有映射到业务模型也是没有意义的,相对来讲gmfmap模型是这三个模型中最复杂的一个,因为几乎每个元素都至少有三个属性需要定义。通过修改gmfmap模型,我们可以规定图形编辑器里,各图形元素的包含关系。在流程编辑器里,可以通过添加一个标签映射(Label Mapping)元素让活动节点里显示优先级属性。

file

图表 4 定制后的流程编辑器界面

需要注意的是,在每次修改任意一个模型文件后,都要重新生成gmfgen文件,这样重新生成的代码才能反映最近的更新。

三、GMF Runtime介绍

前面已经说过,GMF的Runtime部分是GMF的核心,不依赖GMF帮助生成代码,你也完全可以利用Runtime提供的功能写出漂亮的图形编辑器应用程序,但前提是你对GMF Runtime有充分的了解。

关于GMF Runtime提供了哪些实用功能,在Eclipse.org上有一篇文章“Introducing the GMF Runtime”已经介绍得很详细了,其中主要包括可展开折叠的compartment,Direct-Editing时的快速协助(Quick Assistant)、Pop-up工具条、连接手柄、通用工具项(如备注、文本、各种形状等等)、通用菜单命令和工具条(如颜色、字体、选择、排列、缩放,等等)、动态展示的缩放和自动布局功能、通用属性页、打印和打印预览支持、多种图片格式输出、系统剪贴板支持,等等。

GMF的Runtime相当于对GEF进行了一层包装,如果不想利用GMF生成代码的能力,只使用GMF Runtime也可以开发基于GMF的应用程序。与GEF相比,GMF Runtime充分利用了Eclipse的扩展功能,它提供了十分丰富的扩展点,不仅开发起来很方便(因为各个功能都通过定义的扩展点划分清楚了),同时还便于以后对应用程序的扩展。GMF提供的Logic例子就没有使用代码生成,而是直接写成的,你对GMF Runtime的使用方法如果不清楚,这个例子是很好的参考。

相比起EMF来,GMF项目成立的时间要晚很多,如果想让现有的GEF应用程序使用GMF Runtime带来的新功能,最简单的方法就是在原先代码的基础之上进行修改,而不是从头开始以生成代码的方式进行开发。

和EMF类似,GMF也有代码生成的功能。除了上面提到的情况以外,最好利用GMF的代码生成功能先得到应用程序的框架,再在此基础之上进行开发。

在GEF或EMF+GEF构造的应用程序里,一个让人头疼的问题就是在业务模型里不得不包含一些与图形有关的信息,例如每个元素在编辑器里的位置、大小、颜色、字体等等,这可能让本来清晰的业务模型变得难以理解。GMF通过引入记号模型(Notation Model)解决了这个问题。在GMF里,EditPart不再与业务模型直接相连,而是连接到记号模型(GMF的EditPart#getModel()方法得到的是记号模型),由记号模型维护到业务模型的引用。这个记号模型是GMF预先定义好的,它把图形抽象为画布、节点和连接三大类元素,开发人员只要知道GMF的这个设计就可以了,而不需要在业务模型里做任何特殊操作。GMF在持久化一个业务模型实例的时候(即用户保存图形编辑器时),缺省会得到两个文件,一个是业务模型的持久化信息,另一个是记号模型的持久化信息,后者依赖前者的内容。当然,用户也可以在gmfgen里设定参数让GMF只生成一个文件包含这两者的全部信息,但就失去了业务与图形分离的好处。

前面说过,GMF应用程序里的很多功能都是通过扩展点实现的,GMF里这些扩展点被称为服务(Services),用户通过在插件里实现这些服务可以改变图形编辑器的行为。这些服务包括控制控制调色板(Palette)上有哪些工具,业务模型与EditPart的对应关系,EditPart上的EditPolicy等等。实现GMF服务的方法和实现其他Eclipse平台扩展点的方法是一样的,用户要实现的接口名称一般以Provider结尾,例如EditPartProvider、ViewProvider和PaletteProvider,等等。

四、总结

GMF不仅在名称上集合了EMF和GEF这两个框架,它同时拥有EMF的代码生成能力和GEF的图形编辑能力。以往对EMF程序的定制主要通过修改代码实现,在GMF里,我们更多通过实现各种扩展点来改变应用程序的外观和行为,当然,必要的时候修改代码也是不可避免的。

另外,GMF还利用了EMFT项目里的不少成果,例如EMFT Transaction、EMFT Workspace和EMFT OCL等等,EMFT项目是对EMF项目的有益补充,它们让GMF应用程序更加健壮,更好的和Eclipse平台集成以及更好的用户体验,但也在一定程度上增加了GMF本身的入门门槛。

本文发出时,GMF 1.0版本已经相对比较稳定,而GMF 2.0也有了Milestone2版本。如果是做实际项目推荐选择1.0版本,因为虽然这两个版本的模型略有区别,但2.0正式版按计划要在2007年四月前后才能发出,这中间API很可能还会有多次修改。GMF 2.0的建模用户界面更友好一些,至于Runtime增加的新功能我们拭目以待。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/01/16/621545.html

将SWT包装成Eclipse Plugin需要修改的地方

把一个SWT程序包装到Eclipse里作为Plugin运行很容易,但有以下几点需要注意:

1、Eclipse的PDE Tools->Convert Projects to Plug-in Projects可生成Plugin工程需要的文件,注意原来在classpath里对swt.jar的引用应改为manifest.mf文件里对 org.eclipse.ui的依赖。

2、相对路径文件的使用,在SWT里用ClassLoader.getResourceAsStream()等方法引用的文件放在Plugin里会找不到,应改为

FileLocator.toFileURL(Platform.getBundle("plugin.id").getEntry("/images")).getFile();

或与其等效的方式。

3、对本地方法需要的静态链接库dll文件的引用,如果SWT程序是在启动参数里指定的,在Plugin里需要修改,可在系统环境变量里加到Path里。

4、SWT程序的入口类一般会被Editor或View等Plugin元素代替,要注意原来在入口类里初始化的变量也要改到Plugin里,特别是静态变量,正确初始化以防止NullPointerException。

5、必要的话,利用JFace等Plugin特性改写原来的SWT对话框、树、表格等元素,可使UI更具Plugin风格。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/01/11/617449.html

[Eclipse]在程序里隐藏但利用Resource Navigator

有些时候需要在应用程序里隐藏Resource Navigator,用程序控制而非双击鼠标打开编辑器。一个方法是让程序在目标workspace里创建一个project和需要的文件,用户不知道它们的存在:

IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject project = root.getProject(".my");
if (!project.exists()) {
    try {
        project.create(null);
    } catch (CoreException e1) {
        e1.printStackTrace();
    }
}
if(!project.isOpen()){
    try {
        project.open(null);
    } catch (CoreException e1) {
        e1.printStackTrace();
    }
}
IFile file = project.getFile("default.my");
if (!file.exists()) {
    InputStream is = this.getClass().getResourceAsStream("/data/blank.my");
    try {
        file.create(is, false, null);
    } catch (CoreException e1) {
        e1.printStackTrace();
    }
}
FileEditorInput input = new FileEditorInput(file);
openEditor(input, MyEditor.EDITOR_ID);//IWorkbenchPage#openEditor()

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2007/01/11/617260.html

改变GMF应用程序画布的布局

在GMF帮我们生成的应用程序里,画布(Canvas)的布局缺省是使用XYLayout的,这种布局适合UML类图、流程图等应用,但并不适合序列图或表格这类应用。而且目前GMF的.gmfgraph模型里没有提供为整个Canvas设置布局的地方,因此只能通过修改代码的方式实现。因为GMF Runtime是基于GEF的,即GMF生成的代码本质上也是一个GEF应用程序,所以根据以往编写GEF应用程序的经验,不难找到需要修改的地方。

首先要修改作为画布的那个EditPart(下称yourCanvasEditPart),这个类是从gmf的DiagramEditPart继承过来的,看一下后者的createDefaultEditPolicies()方法就会发现它的LAYOUT_ROLE的EditPolicy是gmf的XYLayoutEditPolicy:

installEditPolicy(EditPolicy.LAYOUT_ROLE, new XYLayoutEditPolicy());

GEF里同一个EditPart上要覆盖一个已有EditPolicy的方法是使用同样的Role安装新的EditPolicy,所以我们应该在yourCanvasEditPart的createDefaultEditPolicies()方法里使用LAYOUT_ROLE安装自己的EditPolicy,这里为方便使用了GEF提供的FlowLayoutEditPolicy,你若有特殊需要可实现自己的EditPolicy:

installEditPolicy(EditPolicy.LAYOUT_ROLE, new FlowLayoutEditPolicy() {

    @Override
    protected Command getCreateCommand(CreateRequest request) {
        return null;
    }

    @Override
    protected Command createMoveChildCommand(EditPart child,
            EditPart after) {
        return null;
    }

    @Override
    protected Command createAddCommand(EditPart child, EditPart after) {
        return null;
    }

    @Override
    protected boolean isHorizontal() {
        return true;
    }

});

我们知道,GEF里每个EditPart上作为LAYOUT_ROLE安装的EditPolicy应该和此EditPart对应的Figure使用的LayoutManager匹配,例如XYLayoutEditPolicy对应XYLayout,而FlowLayoutEditPolicy匹配的LayoutManager就是FlowLayout或ToolbarLayout,这两个LayoutManager最大的区别是前者允许子图形换行(列)排列,而后者只能有一行(列)。要注意一点,FlowLayoutEditPolicy与ToolbarLayout一起使用存在一点问题,原因是在FlowLayoutEditPolicy的isHorizontal()方法里认为传入的layoutmanager参数是FlowLayout,因此我们在上面代码里覆盖了isHorizontal()方法,至于返回true或false根据你的需求决定。

现在,为了匹配修改后使用的FlowLayoutEditPolicy,我们要覆盖yourCanvasEditPart的createFigure()方法,在父类DiagramEditPart里这个方法返回的Figure使用的是FreeFormLayout,我们现在需要的是ToolbarLayout,所以:

protected IFigure createFigure() {
    Figure f = new FreeformLayer();
    ToolbarLayout layout = new ToolbarLayout();
    layout.setVertical(false);
    layout.setSpacing(10);
    layout.setStretchMinorAxis(true);
    f.setLayoutManager(layout);
    f.setBorder(new MarginBorder(15));
    return f;
}

经过这样两处修改后,你的编辑器应该已经像下图所示这样了(可能还有些细节代码需要处理一下):

file

GMF在GEF基础上做了很多包装,例如添加了SemanticEditPolicy、CanonicalEditPolicy等等新的EditPolicy,以及相关的很多Command和EditPart,它们为GMF应用程序提供了新的功能,虽然掌握GMF Runtime的API需要花些时间,但这允许我们在不进行大规模编写代码的情况下获得符合需求的图形化编辑器,建议先提前了解一下。

Update(2007/07/17): 在GMF 2.0里,可以通过设置xxx.gmfgen文件里Figure Viewmap的"Layout Type"属性为"TOOLBAR_LAYOUT"达到同样目的。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/12/25/603074.html

[Eclipse]RCP程序的启动进度条

在RCP应用的.product文件里指定Add a progress bar和进度条的尺寸以后,进度条很可能并未如你设想中出现在Splash图片上,解决的方法如下:

1、在.product文件同一目录里创建customization.ini文件,包含这样的文本内容:

org.eclipse.ui/SHOW_PROGRESS_ON_STARTUP=true

2、在plugin.xml里的org.eclipse.core.runtime.products扩展点下的对应product下添加属性preferenceCustomization,值为customization.ini;这个扩展点一般是在编辑.product文件后导出RCP时自动生成的。

3、为让所有的修改生效,可能需要以-clean方式启动你的RCP程序。

4、进度条的位置和大小也可直接在plugin.xml里指定,同样是对应product下的属性,分别为startupProgressRect和startupMessageRect,属性值例如10,290,200,16,前两个数字是位置,后两个是尺寸。

file

BTW,在3.3版本的Eclipse里可能会免去在customization.ini文件里做设置的步骤。

补充(2009.8.20):控制perspective切换按钮位置用org.eclipse.ui/DOCK_PERSPECTIVE_BAR = topRight

参考资料

http://www.eclipsezone.com/eclipse/forums/m92055175.html
http://eclipsezone.com/eclipse/forums/m92053781.html

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/10/31/545974.html