`
liugang594
  • 浏览: 987542 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

GEF理解系列九

    博客分类:
  • GEF
阅读更多
到目前为止,其实我们已经有了一个虽然简单,但还算完整的应用。不过总是有点单调。
这一节我们给我们的Editor加一点其他的东西。严格来说已经不属性GEF的范围了。

一、增加属性页的支持

      在Eclipse里要支持属性页,你选择的对象必须是实现了ISelectionProvider接口。幸好,在GEF里,所有的选择都返回的是一个EditPart,而这些EditPart已经默认实现了这个接口,所以我们就省了很多事了。
      要想在属性页里显示内容,除了对象实现ISelectionProvider接口以外,我们还需要实现一个IPropertySource接口,用来提供属性页要显示的内容。更具体的可以看另一篇文章。这里我就直接进入主题了。
      如果你运行方式是RCP,那么要使得属性页可见,你需要在Perspective类的createInitialLayout(IPageLayout layout)方法里追加一句:
layout.addView("org.eclipse.ui.views.PropertySheet", IPageLayout.BOTTOM, 0.7f, layout.getEditorArea());
     
我们的Editor只是一个HelloWorld模型,所以没得选只能是它了,而且好像我们也只有一个值text好编辑,当然了,你也可以编辑大小,这里为了显得内容多一些,我把宽高和X、Y都加上了。
按照我们上面的说法:每次选中的都是HelloWorldEditPart。所以我们可以设置HelloWorldEditPart实现IPropertySource接口。然后实现它的五个方法如下(具体的就不介绍了):
  public Object getEditableValue() {
  return null;
 }
 public IPropertyDescriptor[] getPropertyDescriptors() {
  IPropertyDescriptor[] descriptors = new IPropertyDescriptor[5];
  descriptors[0] = new TextPropertyDescriptor(HelloWorldModel.P_TEXT,
    "name");
  descriptors[1] = new TextPropertyDescriptor("x", "x");
  descriptors[2] = new TextPropertyDescriptor("y", "y");
  descriptors[3] = new TextPropertyDescriptor("width", "width");
  descriptors[4] = new TextPropertyDescriptor("height", "height");
  return descriptors;
 }
 public Object getPropertyValue(Object id) {
  HelloWorldModel model = (HelloWorldModel) getModel();
  Rectangle constraints = model.getConstraints();
  if (id.equals(HelloWorldModel.P_TEXT)) {
   return model.getText();
  } else if (id.equals("x")) {
   return constraints.x+"";
  } else if (id.equals("y")) {
   return constraints.y+"";
  } else if (id.equals("width")) {
   return constraints.width+"";
  } else if (id.equals("height")) {
   return constraints.height+"";
  }
  return null;
 }
 public boolean isPropertySet(Object id) {
  return true;
 }
 public void resetPropertyValue(Object id) {
  HelloWorldModel model = (HelloWorldModel) getModel();
  if (id.equals(HelloWorldModel.P_TEXT)) {
   (model).setText("<unname>");
  } else {
   model.setConstraints(new Rectangle(0, 0, 100, 20));
  }
 }
 public void setPropertyValue(Object id, Object value) {
  HelloWorldModel model = (HelloWorldModel) getModel();
  Rectangle constraints = model.getConstraints();
  if (id.equals(HelloWorldModel.P_TEXT)) {
   ((HelloWorldModel) getModel()).setText(value.toString());
  } else {
   int parseInt = Integer.parseInt(value.toString());
   if (id.equals("x")) {
    constraints = new Rectangle(parseInt, constraints.y,
      constraints.width, constraints.height);
   } else if (id.equals("y")) {
    constraints = new Rectangle(constraints.x, parseInt,
      constraints.width, constraints.height);
   } else if (id.equals("width")) {
    constraints = new Rectangle(constraints.x, constraints.y,
      parseInt, constraints.height);
   } else if (id.equals("height")) {
    constraints = new Rectangle(constraints.x, constraints.y,
      constraints.width, parseInt);
   }
   model.setConstraints(constraints);
  }
 }
 
运行一下,这时属性页上已经可以显示我们选中的HelloWorldEditPart的信息了。通过属性,我们可以修改text值,还可以进行精确的定位:

propertyPage

图一

 

二、增加大纲视图

这里我们再来做一下增加大纲视图。如果你运行方式是RCP,那么要使得大纲页可见,你需要在Perspective类的createInitialLayout(IPageLayout layout)方法里追加一句:
layout.addView("org.eclipse.ui.views.ContentOutline", IPageLayout.RIGHT, 0.7f, layout.getEditorArea());
Outline视图和Property视图有点不一样的是:一般来说Property视图都是针对一个选中的对象。而Outline视图通常是针对当前激活的编辑器的整体。
要想让编辑器支持Outline视图,我们需要让重写一个方法:getAdapter(Class)。返回一个对应需要的类。例如这里我们的Outline视图要想被支持,我们需要如下完成:
 @Override
 public Object getAdapter(Class type) {
  if(type.equals(IContentOutlinePage.class)){
   return new DiagramEditorOutlinePage(getGraphicalViewer());
  }
  return super.getAdapter(type);
 }
 
这里的DiagramEditorOutlinePage是我们自定义一个类,它需要继承ContentOutlinePage。你可以有两个选择,一个就是普通的Eclipse提供的ContentOutlinePage类;另一个就是GEF提供的ContentOutlinePage。一般来说都需要继承GEF提供的那个。不过这里因为我只是想显示一个导航图。所以我还是用那个普通的。
我们给DiagramEditorOutlinePage传入了一个参数:GraphicalViewer。这样我们在我们的OutlinePage里就可以得到Editor的信息了。
最后就是完成我们DiagramEditorOutlinePage里的createControl方法。具体的就不说了。我的实现如下:
public class DiagramEditorOutlinePage extends ContentOutlinePage {
 private GraphicalViewer viewer;
 private Composite control;
 public DiagramEditorOutlinePage(GraphicalViewer graphicalViewer) {
  this.viewer = graphicalViewer;
 }
 @Override
 public void createControl(Composite parent) {
  control = new Composite(parent, SWT.NONE);
  control.setLayout(new GridLayout(1, false));
  control.setLayoutData(new GridData(GridData.FILL_BOTH));
  Canvas canvas = new Canvas(control, SWT.NONE);
  canvas.setLayoutData(new GridData(GridData.FILL_BOTH));
  LightweightSystem liSystem = new LightweightSystem(canvas);
  ScalableFreeformRootEditPart rootEditPart = ((ScalableFreeformRootEditPart) viewer
    .getRootEditPart());
  ScrollableThumbnail thumbnail = new ScrollableThumbnail(
    (Viewport) rootEditPart.getFigure());
  thumbnail.setSource(rootEditPart
    .getLayer(LayerConstants.SCALABLE_LAYERS));
  liSystem.setContents(thumbnail);
 }
 @Override
 public Control getControl() {
  return control;
 }
 @Override
 public void setFocus() {
  control.setFocus();
 }
}
 
其中Thumbnail的写法可以参考别人的实现;GMF中,默认Editor的实现就完成了这个功能。在GMF中,你可以去以下位置找到缺省的实现:
org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor的方法:initializeOverview()。实现如下:
        protected void initializeOverview() {
            LightweightSystem lws = new LightweightSystem(overview);
            RootEditPart rep = getGraphicalViewer().getRootEditPart();
            DiagramRootEditPart root = (DiagramRootEditPart) rep;
            thumbnail = new ScrollableThumbnail((Viewport) root.getFigure());
            // thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
            thumbnail.setSource(root.getLayer(LayerConstants.SCALABLE_LAYERS));
            lws.setContents(thumbnail);
            disposeListener = new DisposeListener() {
                public void widgetDisposed(DisposeEvent e) {
                    if (thumbnail != null) {
                        thumbnail.deactivate();
                        thumbnail = null;
                    }
                }
            };
            getEditor().addDisposeListener(disposeListener);
            this.overviewInitialized = true;
        }
 
具体的就不过多探讨了。最后我们的实现图如下:

outlinePage

图二

 三、dirty标记的增加

一般来说,在Eclipse里,当Editor的内容发生改变时,会有个dirty标记,例如:dirty。但是目前我们的Editor上有编辑时并不会出现dirty标记。所以我们也应该给我们的Editor提供一个。

其实目前也不是对编辑完全无知。当我们做了一些修改,然后关闭时,会得到一个提示:

needsSave

说明我们的编辑器已经知道了有改动。之所没有显示dirty标记,是因为我们没有通知它。在Eclipse里,要想显示dirty标记是需要发出通知的。下面我们就来完成我们的dirty标记。

我们先理解几点:

1.我们都知道(假设)Eclipse里的Editor是通过方法:isDirty()的返回值来判断是否为脏。

2.Eclipse里的Editor并不是一直轮询isDirty()方法的,它是需要一个事件触发的。

3.我们都知道,所有的GEF里的操作都是通过command来完成的,而所有的command都是放在一个command堆栈中的。这个堆栈可以由editor直接得到。

有这三条,我觉得就够了:isDirty()方法的返回值就由命令栈是否为空决定了。所以就只剩下一个问题了:怎么发出通知机制?简单的想应该是在每个command执行和撤消时都需要发出这个通知。那如果真要这么实现的话,我想大家都会觉得很不好了。幸好,在GEF的Editor里就有一个方法可以完成这件事:commandStackChanged(EventObject event)。 在这个方法在每次命令栈有变化的时候都被会调用。

不知道大家有没有注意到:虽然我们的脏标记没有出来。但是我们的Actions是否可用的状态总是正确。说明这个action的状态总是被更新了。其实看看源码就知道是怎么回事了。commandStackChanged(EventObject event)的默认实现如下:

public void commandStackChanged(EventObject event) {
 updateActions(stackActions);
}
 

所以action的状态总是会被更新的。

刚才上面我也说到了,如果我们要自己完成发出事件机制,那么我们需要在每个command有执行或撤消的地方都发出一个事件。事实上GEF本身也是这样做的,我们看一下commandStackChanged(EventObject event)方法被调用的地方就知道了:

callStack

图三

那现在知道怎么做了没?好了,不说了,直接给出代码:

 @Override
 public boolean isDirty() {
  return getCommandStack().isDirty();
 }
 @Override
 public void commandStackChanged(EventObject event) {
  super.commandStackChanged(event);
  firePropertyChange(PROP_DIRTY);
 }
 

最后再编辑的时候:

isDirty

图四

分享到:
评论
7 楼 carrett 2008-11-25  



谢谢你,刘刚,找到错误了,是因为在非UI线程里访问了UI造成的!
呵呵!
6 楼 liugang594 2008-11-05  
在你的Editpart的propertyChange方法里,有没有做refresh()?
5 楼 carrett 2008-11-05  
刘刚,你好,谢谢你的回复,我后来也用了一个UI线程,但还是更新不了颜色,如果你方便的话,能加我的qq吗?68647819,呵呵!
public class ThalesCtrl extends Thread
{
    private final int n_sleep = 3000;
    private int n = 0;
    private Job job;
    public ThalesCtrl()
    {
        job = new Job("My First Job")
{
protected IStatus run(IProgressMonitor moinitor)
{
System.out.println("Hello World (from a background job).");
HelloModel hello1 =  (HelloModel) Session.htHelloModel.get("1");
if (n < 1000)
{
    n = n + 1;
    if (hello1 != null)
    {
if ((n % 3) == 0)
{
System.out.println("red");
hello1.setColor(hello1.P_COLOR_RED);
}
else
{
System.out.println("blue");
hello1.setColor(hello1.P_COLOR_BLUE);
}
    }
    else
    {
System.out.println("hello1 is null");
    }
}
else
{
    n = 0;
}
return Status.OK_STATUS;
         }
};
this.start();
    }

    public void run()
    {
while (true)
{
try
{
Thread.sleep(n_sleep);
job.schedule(2000);
}
catch (Exception e)
{
e.printStackTrace();
}
}
    }
}

HelloModel 文件:
public class HelloModel extends AbstractModel
{
public static final String P_COLOR = "color";
public static final String P_COLOR_RED = "red";
public static final String P_COLOR_BLUE = "blue";
public static final String P_COLOR_ORANGE = "orange";
public static final String P_COLOR_GRAY = "gray";
public void setColor(String color)
{
this.color = color;
System.out.println("set Color!");
firePropertyChange(P_COLOR, null, color);
}
。。。。。
}

AbstractModel文件:
public class AbstractModel implements IPropertySource
{
private PropertyChangeSupport listeners = new PropertyChangeSupport(this);

public void addPropertyChangeListener(PropertyChangeListener listener)
{
listeners.addPropertyChangeListener(listener);
}

public void firePropertyChange(String propName,Object oldValue,Object newValue)
{
listeners.firePropertyChange(propName,oldValue,newValue);
}

public void removePropertyChangeListener(PropertyChangeListener listener)
{
listeners.removePropertyChangeListener(listener);
}

public Object getEditableValue()
{
return this;//返回模型自身作为可编辑的属性值
}

public IPropertyDescriptor[] getPropertyDescriptors()
{ //如果在抽象模型中返回null会出现异常,因此这里返回一个长度为0的数组
return new IPropertyDescriptor[0];
}

public Object getPropertyValue(Object id)
{
return null;
}

public boolean isPropertySet(Object id)
{
return false;
}

public void resetPropertyValue(Object id)
{

}

public void setPropertyValue(Object id,Object value)
{

}
}
4 楼 liugang594 2008-10-29  
carrett 写道

public class HelloEditorPart extends EditPartWithListener implements NodeEditPart,Runnable//AbstractGraphicalEditPart { private CustomDirectEditManager directManager = null; private Thread timer = null; private boolean isNext = true; protected IFigure createFigure() { timer = new Thread(this); timer.start(); HelloModel model = (HelloModel)getModel(); System.out.println("id : " + model.getId()); Label label = new Label(); label.setText(model.getText()); label.setBorder(new CompoundBorder(new LineBorder(),new MarginBorder(3))); //背景色 label.setBackgroundColor(ColorConstants.green); //背景色不透明 label.setOpaque(true); return label; } public void setColor(Color color) { if(color != null &amp;&amp; getFigure()!= null) ((Label)getFigure()).setBackgroundColor(color); } protected void createEditPolicies() { ... } protected void refreshVisuals() { ... } public void performRequest(Request req) { ... } private void performDirectEdit() { ... } public void run() { while(true) { try { if(isNext) { setColor(ColorConstants.green); isNext = false; } else { setColor(ColorConstants.red); isNext = true; } Thread.sleep(1000); } catch(Exception e) { e.printStackTrace(); } } } } 然后报错:java.lang.NullPointerException at org.eclipse.draw2d.DeferredUpdateManager.sendUpdateRequest(DeferredUpdateManager.java:225) at org.eclipse.draw2d.DeferredUpdateManager.queueWork(DeferredUpdateManager.java:215) at org.eclipse.draw2d.DeferredUpdateManager.addDirtyRegion(DeferredUpdateManager.java:111) at org.eclipse.draw2d.Figure.repaint(Figure.java:1319) at org.eclipse.draw2d.Figure.repaint(Figure.java:1311) at org.eclipse.draw2d.Figure.repaint(Figure.java:1326) at org.eclipse.draw2d.Figure.setBackgroundColor(Figure.java:1357) at gef.tutorial.step.parts.HelloEditorPart.setColor(HelloEditorPart.java:77) at gef.tutorial.step.parts.HelloEditorPart.run(HelloEditorPart.java:224) at java.lang.Thread.run(Thread.java:595) 希望指教!

应该是因为你在非UI线程中访问了UI。看出错地方的源码:
protected void sendUpdateRequest() {
    Display.getCurrent().asyncExec(new UpdateRequest());
}
虽然不知道你的代码实际运行情况,但我猜想这个空指的是:Display.getCurrent()返回值为空。所以,如果要想能正确运行,你需要使用UI线程。
3 楼 carrett 2008-10-29  
public class HelloEditorPart extends EditPartWithListener implements NodeEditPart,Runnable//AbstractGraphicalEditPart
{
private CustomDirectEditManager directManager = null;
private Thread timer = null;
private boolean isNext = true;

protected IFigure createFigure()
{
timer = new Thread(this);
timer.start();
HelloModel model = (HelloModel)getModel();
System.out.println("id : " + model.getId());
Label label = new Label();
label.setText(model.getText());
label.setBorder(new CompoundBorder(new LineBorder(),new MarginBorder(3)));
//背景色
label.setBackgroundColor(ColorConstants.green);
//背景色不透明
label.setOpaque(true);
return label;
}



public void setColor(Color color)
{
if(color != null && getFigure()!= null)
((Label)getFigure()).setBackgroundColor(color);
}

protected void createEditPolicies()
{
...
}

protected void refreshVisuals()
{
...
}

public void performRequest(Request req)
{ ...
}

private void performDirectEdit()
{
...
}

public void run()
{
while(true)
{
try
{
if(isNext)
{
setColor(ColorConstants.green);
isNext = false;
}
else
{
setColor(ColorConstants.red);
isNext = true;
}
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
然后报错:java.lang.NullPointerException
at org.eclipse.draw2d.DeferredUpdateManager.sendUpdateRequest(DeferredUpdateManager.java:225)
at org.eclipse.draw2d.DeferredUpdateManager.queueWork(DeferredUpdateManager.java:215)
at org.eclipse.draw2d.DeferredUpdateManager.addDirtyRegion(DeferredUpdateManager.java:111)
at org.eclipse.draw2d.Figure.repaint(Figure.java:1319)
at org.eclipse.draw2d.Figure.repaint(Figure.java:1311)
at org.eclipse.draw2d.Figure.repaint(Figure.java:1326)
at org.eclipse.draw2d.Figure.setBackgroundColor(Figure.java:1357)
at gef.tutorial.step.parts.HelloEditorPart.setColor(HelloEditorPart.java:77)
at gef.tutorial.step.parts.HelloEditorPart.run(HelloEditorPart.java:224)
at java.lang.Thread.run(Thread.java:595)

希望指教!
2 楼 liugang594 2008-10-23  
我想你这个处理应该应该经由command来实现。
1 楼 pengjinwen1983 2008-10-22  
  我在节点的模型中加了一个ArrayList,在editor中添加了
对ArrayList里的内容进行编辑的窗口。对内容进行编辑后editor
不会显示dirty(),应该怎样处理???
在 线  等!!!!

相关推荐

    GEF理解系列三

    在GEF(Graphical Editing Framework)中,理解其工作流程对于开发图形编辑工具至关重要。GEF主要用于构建基于SWT和JFace的图形用户界面,它提供了丰富的功能来处理图形元素的绘制、操作和交互。本篇文章将深入探讨...

    GEF理解系列二

    【GEF理解系列二】是深入学习GEF框架的第二部分,主要讲解如何配置和初始化Editor,以及如何创建多层结构以支持添加多个图形。GEF(Graphical Editing Framework)是一个用于构建图形编辑器的开源框架,常用于开发...

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

    在"GEF入门系列宝典"中,我们将深入探讨如何使用GEF进行Eclipse插件开发,以创建自定义的图形编辑器。以下是一些关键知识点: 1. **Eclipse插件体系结构**: - 插件是Eclipse的核心组成部分,它们通过OSGi模块系统...

    GEF入门必读 GEF入门系列 GEF-whole-upload

    本资料集旨在为初学者提供一个全面的入门指南,帮助理解GEF的基础概念和实现方法。 GEF的核心功能是提供了一套强大的组件和API,用于创建可交互的、图形式的用户界面。通过使用GEF,开发者可以轻松地创建出具有拖放...

    GEF入门系列 pdf 格式资料

    **GEF入门系列 PDF 格式资料** GEF(Graphical Editing Framework)是Eclipse开源组织推出的一个图形编辑框架,主要用于构建可视化的图形编辑...在阅读过程中,建议结合实际编程实践,以便更好地理解和掌握GEF的精髓。

    GEF入门系列

    - 示例代码:Eclipse项目中包含多个GEF示例,是学习和理解GEF的好资料。 - 在线教程:互联网上有许多博客和教程,针对不同水平的学习者提供了丰富的教学内容。 通过这个GEF入门系列,你可以逐步掌握如何利用GEF...

    EMF_GEF_入门系列

    本入门系列旨在帮助初学者快速理解并掌握这两个强大的工具。 EMF是Eclipse项目的一部分,它提供了一种模型驱动的开发方式。EMF允许开发者创建、存储和操作数据模型,这些模型可以用来表示复杂的数据结构或业务逻辑...

    Eclipse插件开发--gef开发指南与入门系列宝典

    "GEF入门系列宝典.doc"可能更侧重于实践,提供逐步的教程和实例,帮助初学者快速上手。它可能包含一些实际的代码示例,教你如何构建一个简单的图形编辑器,例如绘制流程图或网络拓扑图。 通过学习这两份资料,你将...

    GEF-ALL-3.7+GEF-ALL-3.8+GEF_Draw2d学习资料

    2. "GEF入门系列(一、Draw2D).doc" 可能是一个逐步的教学系列,从基础的Draw2D开始,逐步引入GEF的概念和应用。 3. "如何在Eclipse3.3.1.1中安装VE,emf,GEF插件.doc" 提供了在特定版本的Eclipse中安装相关插件的步骤...

    GEF教程

    为了帮助读者更好地理解GEF框架的实际应用,作者提供了由简至繁的一系列示例项目。这些项目按序排列,从最简单的pt1开始,逐步添加新功能,最终形成一个具备DirectEdit、Palette等功能的图形编辑器。这种逐步递进的...

    GEF入门学习例子

    这个入门学习例子旨在帮助初学者理解GEF的基本用法和核心概念,通过实例展示如何利用GEF实现工具栏、菜单栏、属性栏以及图形的交互功能,如移动、删除、撤销、连线等,并涵盖了大纲视图和鹰眼功能的实现。...

    简单gef框架

    4. **流程设计基础**:在gef框架下进行流程设计,首先要明确流程的目标和步骤,这可能涉及到对程序逻辑的理解和拆解,将复杂的任务分解为一系列可执行的子任务。这通常需要扎实的编程基础和问题解决能力。 5. **gef...

    gef-step3.zip_3gef_GEF eclipse_GEF-step2_gef_gef-st

    **3gef**可能是这个系列教程的一个特定版本或者阶段,强调了对GEF的三次迭代改进或深化理解。这可能涉及到更高级的功能,如图形的动态更新、自定义手势识别或者与其他Eclipse插件的集成。 **gef_eclipse**标签表明...

    eclipse_gef_example.rar

    这个"eclipse_gef_example.rar"压缩包提供了一系列的例子,帮助开发者更好地理解和掌握GEF框架。以下是对每个部分的详细解释: 1. **org.eclipse.gef.examples.text** 这个部分主要展示了如何在GEF环境中处理文本...

    gef工程代码

    总结来说,gef工程代码是一个强大的学习资源,对于想要提升调试技能和深入理解代码运行机制的开发者来说,是不可多得的宝藏。通过深入研究和实践,你将能够掌握gef的精髓,从而在日常开发和调试工作中更加游刃有余。

    GEF 入门教程 中文版

    ### GEF(Graphical Editing Framework)入门教程知识点详解 #### 一、简介 GEF(Graphical Editing Framework),即图形编辑框架,是一种基于...希望您能够通过实践加深对GEF的理解,并成功地应用于自己的项目中。

    GEF入门实例代码2《Eclipse插件开发》中实例

    GEF包含了一系列的类和接口,如Command、Model、View、EditPart、Figure等,它们共同构成了图形编辑的基础架构。Command代表可撤销/重做的操作,Model表示数据模型,View负责显示图形,EditPart作为模型和视图之间...

    GEF.rarGEF.rar

    10. **示例和教程**:GEF通常会提供一系列的示例项目和教程,帮助开发者快速上手,理解和使用框架。 总的来说,"GEF.rar"压缩包可能是为开发者提供的一个完整的开发环境,包括了源代码、必要的库文件以及示例,使得...

Global site tag (gtag.js) - Google Analytics