Eclipse RCP开发桌面程序

本文将带领读者从基础知识开始,逐步深入Eclipse RCP开发,包括创建RCP程序、添加菜单和工具栏、构建视图与透视图等内容,最终展示如何创建功能丰富、界面美观的桌面应用程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所谓RCP,就是Rich Client Platform的缩写,即富客户平台,是Eclipse进化的产物(自3.0版以后出现),是Eclipse组织向用户提供的强大的开放性开发平台,能 够使用户方便地创建自己的基于Eclipse的应用程序,并且这些应用程序能够得到Eclipse的底层支持。更重要的是,我们可以利用Java创建象 Eclipse这么漂亮的桌面程序。

  我相信,在未来的几年里,RCP一定会变得非常流行。使用RCP,我们可以开发界面象 Eclipse这样漂亮的桌面程序,比如医院管理系统啊、CAD软件等等。遗憾的是,目前在国内基本上找不到关于RCP的中文资料,我们只能通过自己的探 索来为我们的程序添加我们想要的功能。

  下面让我们一步一步来建立一个Eclipse RCP程序,下面的内容可以说在Google上一搜一大把,有些人会觉得乏味,但是没关系,这只是一个快速的起步。

  选择“新建--项目”,选择“插件项目”:
rcp00.JPG

rcp01.JPG

点下一步,输入项目名称,选择Eclipse版本,我这里选择的是3.2:
rcp02.JPG

  点下一步,插件标识和插件名称可以更改,其他的内容都可以保持默认,一定要记得选中富客户机应用程序支持:
rcp03.JPG

  点下一步,选中一个模板,这里选一个最简单的,到时候看源代码的时候便于理解:
rcp04.JPG

  点下一步,改一下应用程序标题:
rcp05.JPG

  点完成,我们可以在项目上面点右键,选择按Eclipse程序运行,就可以看到效果了:
rcp16.JPG

rcp17.JPG

  在这个程序中,窗口上显示的是一个透视图,透视图中含有一个编辑器区域,以后,我们可以逐步为这个程序添加菜单、工具条和为这个透视图添加视图、编辑器等等。

  现在,这个程序只能在Eclipse环境下运行,而RCP的目标是创建可以独立运行的应用程序,我们的事情还没完呢。下一步,在项目上点右键,创建产品配置文件:
rcp06.JPG

  输入产品配置文件名:

rcp07.JPG

  生成的产品配置文件在编辑器中打开,应该是这个样子的:
rcp09.JPG

  刚开始,上面的几个文本框都是空的,点新建按钮之后,弹出如下的对话框,输入产品名称后,点完成就行了。

rcp08.JPG

  点击配置文件中的“启动程序”,我们可以试着启动我们的RCP程序。结果呢,会出错。原因很简单,因为我们没有为我们的程序选中它依赖的插件。

  选中配置文件的“配置”选项卡,添加以下几个依赖项,记住,一定要把我们自己,也就是com.blogjava.youxia.rcp_start加进依赖项,否则会出错。最开始的时候,就是这么一点小问题,让我浪费了几天时间。
rcp10.JPG

  再点击添加必须的插件,自动添加其它的依赖项。

  再下一步,设置项目的构建路径,如下图:
rcp11.JPG

  下一步,导出我们的程序:
rcp12.JPG

rcp13.JPG

  点下一步,输入我们程序导出的目录,如下图:
rcp14.JPG

  点完成按钮之后,我们的程序就导出到我们的指定的目录中了,打开这个目录,可以看到一个类似eclipse的程序图标,双击运行,效果如下图:rcp15.JPG

  最后,需要说明两点:第一,如果希望生成的程序有自己的图标,可以在产品配置文件中的最后两个配置文件中设置;第二,生成的程序应该是没有菜单栏的,因为我的Eclipse安装了MyEclipse,所以导出的程序就多了两个菜单。

  好了,快速起步就到这里了,以后再仔细研究生成的代码和为我们的程序添加功能。
=========================================================================
使用Eclipse RCP进行桌面程序开发(一):快速起步中,我们通过Eclipse的插件开发向导,逐步建立了一个RCP应用程序,但是,这个程序没有任何功能,难以激起我们学习的兴趣。在这一节,我们将一起探索怎样在程序中添加菜单和工具条。先看一下成果:

图一、图二:带有菜单和工具条的RCP程序
rcp18.JPG

rcp19.JPG

图三:工具栏上的按钮的提示文本
rcp20.JPG

图四:点击菜单项或者工具栏按钮后,弹出一个简单的对话框。
rcp21.JPG

这里需要说明一点,为什么要在讲菜单和工具栏的时候一起讲对话框,这是因为对话框是我们所能想到的最简单最直接的用户交互方式,在对话框上可以添加 各种各样的控件来实现复杂的功能,为了让我们点击菜单项的时候能够看到效果,这里就用了一个简单的对话框。当然,当我们以后接触到视图、编辑器和透视图这 样的概念之后,我们能使用的用户交互方式就不仅仅只是对话框了。

打开我们上一节使用向导建立的工程,可以发现工程下面自动生成了如下文件:
Application.java
ApplicationWorkbenchAdvisor.java
ApplicationWorkbenchWindowAdvisor.java
ApplicationActionBarAdvisor.java
Perspective.java
plugin.xml
这里的Application.java是我们整个程序的入口点,我们的程序运行的时候,会先执行Application的run方法,run方法的代码如下:

1 public Objectrun(Objectargs) throws Exception {
2Displaydisplay=PlatformUI.createDisplay();
3try{
4intreturnCode=PlatformUI.createAndRunWorkbench(display,newApplicationWorkbenchAdvisor());
5if(returnCode==PlatformUI.RETURN_RESTART){
6returnIPlatformRunnable.EXIT_RESTART;
7}

8returnIPlatformRunnable.EXIT_OK;
9}
finally{
10display.dispose();
11}

12}

在第4行我们可以看出,该入口函数将创建用户界面的工作交给了ApplicationWorkbenchAdvisor类。接着,我们打开ApplicationWorkbenchAdvisor.java,代码如下:

1 public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
2
3privatestaticfinalStringPERSPECTIVE_ID="cn.blogjava.youxia.rcp_start.perspective";
4
5publicWorkbenchWindowAdvisorcreateWorkbenchWindowAdvisor(IWorkbenchWindowConfigurerconfigurer){
6returnnewApplicationWorkbenchWindowAdvisor(configurer);
7}

8
9publicStringgetInitialWindowPerspectiveId(){
10returnPERSPECTIVE_ID;
11}

12}

可以看出,这个类的工作就是为我们的程序指定默认的透视图,然后把创建窗口的工作交给了 ApplicationWorkbenchWindowAdvisor类。接着,我们打开 ApplicationWorkbenchWindowAdvisor.java文件,看到代码如下:

1 public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
2
3publicApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurerconfigurer){
4super(configurer);
5}

6
7publicActionBarAdvisorcreateActionBarAdvisor(IActionBarConfigurerconfigurer){
8returnnewApplicationActionBarAdvisor(configurer);
9}

10
11publicvoidpreWindowOpen(){
12IWorkbenchWindowConfigurerconfigurer=getWindowConfigurer();
13configurer.setInitialSize(newPoint(600,450));
14configurer.setShowCoolBar(true);
15configurer.setShowStatusLine(false);
16configurer.setTitle("第一个RCP程序");
17
18}

19
20}

这个类的功能很强大,我们可以重载它的preWindowCreate、postWindowCreate、preWindowOpen、 postWindowOpen等方法,以便修改我们窗口的外观。在这里可以看出,我们重载了preWindowOpen方法来设置窗口的大小和让工具栏可 见。很显然,这个类的另外一个功能,就是把创建菜单和工具栏的任务交给了ApplicationActionBarAdvisor类。

到这里,谜底已经揭晓,要创建我们自己的菜单和工具条,就一定是在ApplicationActionBarAdvisor.java中做文章了。不错,打开这个文件,我们可以看到这个类有两个重要的方法:
protected void makeActions(IWorkbenchWindow window);
protected void fillMenuBar(IMenuManager menuBar);
我 们可以在makeActions方法中创建我们的Action,什么是Action呢?Action是jface中的一个概念,在jface中通过 org.eclipse.jface.action中的Action和ActionContributionItem类实现了视图和处理代码的分离,这样 无论何时用户触发了一个控件的事件,都会激活一个相应的Action类实例来进行时间处理。毫无疑问,我们的菜单项是一个Action类的子类了。

下面请看ApplicationActionBarAdvisor.java的源代码:

1 package cn.blogjava.youxia.rcp_start;
2
3 import org.eclipse.jface.action.IMenuManager;
4 import org.eclipse.jface.action.MenuManager;
5 import org.eclipse.ui.IWorkbenchWindow;
6 import org.eclipse.ui.application.ActionBarAdvisor;
7 import org.eclipse.ui.application.IActionBarConfigurer;
8 import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
9 import cn.blogjava.youxia.actions.Action1;
10
11 public class ApplicationActionBarAdvisor extends ActionBarAdvisor {
12
13privateIWorkbenchActionaction1;
14
15publicApplicationActionBarAdvisor(IActionBarConfigurerconfigurer){
16super(configurer);
17}

18
19protectedvoidmakeActions(IWorkbenchWindowwindow){
20action1=newAction1(window);
21action1.setText("第一个菜单项");
22action1.setId("cn.blogjava.youxia.actions.action1");
23register(action1);
24}

25
26protectedvoidfillMenuBar(IMenuManagermenuBar){
27MenuManagernewMenu=newMenuManager("第一个菜单","cn.blogjava.youxia.firstmenu");
28menuBar.add(newMenu);
29newMenu.add(action1);
30}

31
32}

可以看出,我们通过创建cn.blogjava.youxia.actions.Action1类的实例来创建一个菜单项,然后把它加入到菜单 newMenu中,然后再把newMenu加入menuBar中,整个过程很容易理解。那么register(action1)是做什么的呢?这是为了把 我们的Action的实例注册到工作台中,这样当我们的工作台销毁的时候,我们的Action也可以被销毁。

下面请看Action1类的源代码:

1 package cn.blogjava.youxia.actions;
2
3 import org.eclipse.jface.action.Action;
4 import org.eclipse.ui.IWorkbenchWindow;
5 import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
6 import cn.blogjava.youxia.rcp_start.FirstDialog;
7
8
9 public class Action1 extends Action implements IWorkbenchAction {
10
11privateIWorkbenchWindowworkbenchWindow;
12
13publicAction1(IWorkbenchWindowwindow){
14if(window==null){
15thrownewIllegalArgumentException();
16}

17
18this.workbenchWindow=window;
19}

20
21publicvoidrun(){
22//makesureactionisnotdisposed
23if(workbenchWindow!=null){
24//在这里添加功能
25FirstDialogdg=newFirstDialog(workbenchWindow.getShell());
26dg.open();
27
28}

29}

30
31publicvoiddispose(){
32workbenchWindow=null;
33
34}

35
36}

在构造函数中保存我们工作台窗口的引用,在run方法中执行功能,是不是很简单?在这里,我们用到了一个对话框类 cn.blogjava.youxia.rcp_start.FirstDialog,这个类从 org.eclipse.swt.widgets.Dialog类继承,熟悉swt的朋友一定不会陌生。我建议大家可以使用Designer插件,这个插 件对swt/jface提供非常好的可视化支持,在这个对话框中,我们只简单的添加了两个按钮。

FirstDialog.java源文件如下:

1 package cn.blogjava.youxia.rcp_start;
2
3 import org.eclipse.swt.SWT;
4 import org.eclipse.swt.events.SelectionAdapter;
5 import org.eclipse.swt.events.SelectionEvent;
6 import org.eclipse.swt.widgets.Button;
7 import org.eclipse.swt.widgets.Dialog;
8 import org.eclipse.swt.widgets.Display;
9 import org.eclipse.swt.widgets.Shell;
10
11
12 public class FirstDialog extends Dialog {
13
14protectedShellshell;
15
16privateintresult;
17
18publicFirstDialog(Shellparent,intstyle){
19super(parent,style);
20}

21
22publicFirstDialog(Shellparent){
23this(parent,SWT.NONE);
24}

25
26publicintopen(){
27createContents();
28shell.open();
29shell.layout();
30Displaydisplay=getParent().getDisplay();
31while(!shell.isDisposed()){
32if(!display.readAndDispatch())
33display.sleep();
34}

35returnresult;
36}

37
38protectedvoidcreateContents(){
39shell=newShell(getParent(),SWT.DIALOG_TRIM|SWT.APPLICATION_MODAL);
40shell.setSize(150,70);
41shell.setText("第一个对话框");
42
43finalButtonokButton=newButton(shell,SWT.NONE);
44okButton.addSelectionListener(newSelectionAdapter(){
45publicvoidwidgetSelected(SelectionEvente){
46result=1;
47shell.dispose();
48}

49}
);
50okButton.setText("OK");
51okButton.setBounds(10,10,48,22);
52
53finalButtoncancelButton=newButton(shell,SWT.NONE);
54cancelButton.addSelectionListener(newSelectionAdapter(){
55publicvoidwidgetSelected(SelectionEvente){
56result=2;
57shell.dispose();
58}

59}
);
60cancelButton.setText("Cancel");
61cancelButton.setBounds(89,10,48,22);
62}

63
64}

65


上面所讲的,只是添加菜单和工具栏的第一种方法,这种方法把构建菜单的工作以静态代码的方式加入到了ApplicationActionBarAdvisor类中,如果需要修改用户界面,则需要修改代码并重新编译。

添加菜单项的第二种方法就要简单得多,而且修改起来也方便,还可以对菜单项实现更加灵活的控制,但是,需要对Eclipse的插件基础有比较好的了解。那这第二种方法就是通过扩展actionSets扩展点来添加菜单。

对扩展点的扩展,可以通过编辑plugin.xml文件了实现,比如我们添加的第二个菜单项,其配置文件如下:

1 < extension
2 id ="cn.blogjava.youxia.actionset"
3 name ="我的菜单扩展"
4 point ="org.eclipse.ui.actionSets" >
5 < actionSet
6 description ="第一个扩展"
7 id ="RCP_Start.actionSet1"
8 label ="RCP_Start.actionSet1"
9 visible ="true" >
10 < action
11 class ="cn.blogjava.youxia.actions.Action2"
12 icon ="icons/alt_window_16.gif"
13 id ="RCP_Start.action2"
14 label ="第二个菜单项"
15 menubarPath ="cn.blogjava.youxia.firstmenu/additions"
16 style ="push"
17 toolbarPath ="additions"
18 tooltip ="第二个菜单项的按钮" />
19 </ actionSet >
20 </ extension >

其实Eclipse为我们提供了很好的可视化plugin.xml的编辑器,如下图,我们可以对菜单的外观进行和行为进行灵活的控制:

rcp22.JPG

从配置文件中我们可以看到,我们为这第二个菜单项指定的Action是cn.blogjava.youxia.actions.Action2类, 这个类我们必须实现org.eclipse.ui.IWorkbenchWindowActionDelegate接口,这个接口中比 org.eclipse.jface.actions.Action中多定义了一个方法public void selectionChanged(IAction action, ISelection selection),这个方法是必须的,以便工作台窗口在用户选定哪一项资源的时候通知我们的Action类的实例。其代码如下:

1 package cn.blogjava.youxia.actions;
2
3 import org.eclipse.jface.action.IAction;
4 import org.eclipse.jface.viewers.ISelection;
5 import org.eclipse.ui.IWorkbenchWindow;
6 import org.eclipse.ui.IWorkbenchWindowActionDelegate;
7 import cn.blogjava.youxia.rcp_start.FirstDialog;
8
9 public class Action2 implements IWorkbenchWindowActionDelegate {
10
11privateIWorkbenchWindowwindow;
12
13publicvoiddispose(){
14//TODO
15
16}

17
18publicvoidinit(IWorkbenchWindowwindow){
19//TODO
20this.window=window;
21
22}

23
24publicvoidrun(IActionaction){
25//TODO
26FirstDialogdg=newFirstDialog(window.getShell());
27dg.open();
28
29}

30
31publicvoidselectionChanged(IActionaction,ISelectionselection){
32//TODO
33
34}

35
36}


总结:通过向工作台中添加菜单和工具栏,并使用对话框作为与用户交互的基础,我们已经基本上可以构建功能比较复杂的程序了。但这仅仅只是RCP编程的开端。下一节,我们将一起探索Eclipse的透视图和视图。
===============================================================
Eclipse RCP开发中,和用户进行交互最多的界面,应该是视图了,而透视图就是将已有的视图、菜单、工具栏、编辑器等等进行组合和布局。看完这一节,我们就可以建立如下图这样的程序界面了。

rcp25.JPG

首 先我们来介绍一下视图,建立一个视图其实非常简单,只要从org.eclipse.ui.part.ViewPart继承一个类,然后在 plugin.xml中进行视图的配置。其中,向视图中添加控件的操作,我们即可以手工编写,也可以使用Designer插件,我这里推荐大家使用 Designer插件,该插件对RCP提供功能非常强大的支持,如果使用Designer插件开发视图,则plugin.xml文件也不需要我们手动修改 了。

比如我们上图中的第一个视图,就是从ViewPart继承一个类,然后在上面加入了几个swt的控件,做得非常得简单,而它的配置文件如下:

1 < extension
2 point ="org.eclipse.ui.views" >
3 < view
4 class ="cn.blogjava.youxia.views.FirstView"
5 id ="cn.blogjava.youxia.views.FirstView"
6 name ="第一个View" />
7 </ extension >


可以看到,实现这个视图的class为cn.blogjava.youxia.views.FirstView,那么我们看看FirstView.java吧:

1 package cn.blogjava.youxia.views;
2
3 import org.eclipse.jface.action.IMenuManager;
4 import org.eclipse.jface.action.IToolBarManager;
5 import org.eclipse.jface.viewers.TableViewer;
6 import org.eclipse.swt.SWT;
7 import org.eclipse.swt.widgets.Composite;
8 import org.eclipse.swt.widgets.Label;
9 import org.eclipse.swt.widgets.Table;
10 import org.eclipse.swt.widgets.Text;
11 import org.eclipse.ui.part.ViewPart;
12
13 public class FirstView extends ViewPart {
14
15privateTabletable;
16privateTexttext_1;
17privateTexttext;
18publicstaticfinalStringID="cn.blogjava.youxia.views.FirstView";//$NON-NLS-1$
19
20/**
21*Createcontentsoftheviewpart
22*@paramparent
23*/

24@Override
25publicvoidcreatePartControl(Compositeparent){
26Compositecontainer=newComposite(parent,SWT.NONE);
27
28finalLabellabel=newLabel(container,SWT.NONE);
29label.setText("姓名:");
30label.setBounds(56,41,36,12);
31
32text=newText(container,SWT.BORDER);
33text.setBounds(98,38,80,15);
34
35finalLabellabel_1=newLabel(container,SWT.NONE);
36label_1.setText("性别:");
37label_1.setBounds(212,41,30,12);
38
39text_1=newText(container,SWT.BORDER);
40text_1.setBounds(252,38,80,15);
41
42finalTableViewertableViewer=newTableViewer(container,SWT.BORDER);
43//tableViewer.setInput(newObject());
44table=tableViewer.getTable();
45table.setBounds(56,75,374,143);
46table.setItemCount(10);
47table.setLinesVisible(true);
48//
49createActions();
50initializeToolBar();
51initializeMenu();
52}

53
54/**
55*Createtheactions
56*/

57privatevoidcreateActions(){
58//Createtheactions
59}

60
61/**
62*Initializethetoolbar
63*/

64privatevoidinitializeToolBar(){
65IToolBarManagertoolbarManager=getViewSite().getActionBars()
66.getToolBarManager();
67}

68
69/**
70*Initializethemenu
71*/

72privatevoidinitializeMenu(){
73IMenuManagermenuManager=getViewSite().getActionBars()
74.getMenuManager();
75}

76
77@Override
78publicvoidsetFocus(){
79//Setthefocus
80}

81
82}


其中,添加控件的代码由Disgner插件自动生成。这个时候,如果我们运行程序的话,我们的视图还不会被显示出来。为了让我们的视图可以显示,我们还需要修改Perspective.java文件,代码如下:

1 packagecn.blogjava.youxia.rcp_start ;
2
3 importorg.eclipse.ui.IPageLayout ;
4 importorg.eclipse.ui.IPerspectiveFactory ;
5
6 publicclassPerspectiveimplementsIPerspectiveFactory{
7
8 publicvoidcreateInitialLayout(IPageLayoutlayout){
9 StringeditorArea = layout.getEditorArea() ;
10 layout.addView( " cn.blogjava.youxia.views.FirstView " , IPageLayout.RIGHT , 0 .2f , editorArea) ;
11 }
12 }

运行程序,得到如下效果:

rcp23.JPG

我们可以发现,上面这个视图的标签不是我们通常看到的波浪形,我们可以通过配置文件的方式来更改产品的样式。
首先,在plugin.xml中对org.eclipse.core.runtime.products扩展点的属性进行更改,如下:

1 < extension
2 id = " product "
3 point = " org.eclipse.core.runtime.products " >
4 < product
5 application = " cn.blogjava.youxia.rcp_start.application "
6 name = " 第一个RCP程序 " >
7 < property
8 name = " preferenceCustomization "
9 value = " plugin_customization.ini " />
10 </ product >
11 </ extension >


可见,我们为我们的产品添加了一个prefereneCustomization属性,该属性的值为plugin_customization.ini文件,在该文件中,我们可以配置我们的样式。在这里,它的内容如下:

1 org.eclipse.ui/SHOW_TRADITIONAL_STYLE_TABS = false
2 org.eclipse.ui/DOCK_PERSPECTIVE_BAR = topRight


事实上,在这个文件中可以定义的参数有上百个,大家可以查看Eclipse的文档。
这个时候,效果应该是这样的了:
rcp24.JPG

好 了,我们现在对以上的代码做一个总结。我不是写教科书,在Blog中也没有写得那么详细的条件。我们这里主要关注在哪个地方对代码进行扩展,可以达到我们 想要的效果。比如,我们要创建视图,就是需要扩展org.eclipse.ui.part.ViewPart类,然后向其中添加控件,再然后配置 plugin.xml文件,最后修改透视图的代码,以便它能够显示出来。

在ViewPart类中,我们添加控件的操作主要是在public void createPartControl(Composite parent)这个方法中进行,而方法最后会调用以下三个方法:
createActions();
initializeToolBar();
initializeMenu();
从这三个方法的方法名我们不难看出,它们的功能是创建视图特有的菜单栏和工具栏的,结合上一小节的内容,我们应该很快就可以探索到怎么给视图添加漂亮的工具栏了,这里我不再罗嗦。

再 来看Perspective.java,不难发现,所有的透视图类都需要实现IPerspectiveFactory接口,而该接口的 createInitialLayout方法,就是描述工作台窗口中编辑器和视图的布局。默认情况下,透视图中只包含一个编辑器区域,就是我们第一节中看 到的那个效果。在createInitialLayou中,我们可以通过以下几个方法向透视图中添加视图、编辑器和菜单:
addView —— 添加视图
addActionSet —— 添加菜单和工具栏
createFolder —— 创建一个IForderLayou,可以让多个视图重叠在同一个位置

写 到这里,肯定有人会问,如果我要创建一个象Eclipse中的资源视图这样的视图,该怎么做呢?这我们就要感谢 org.eclipse.jface.viewers包了,Viewer,这里翻译为查看器,它和视图是不一样的。JFace查看器是Jface对SWT 部件的封装,它简化了我们对小部件的操作。在使用查看器的时候,它的数据使用单独的模型对象来保存,使用查看器的setInput方法可以为查看器设置模 型,此外,在使用查看器的时候,需要为它提供ContentProvider(内容提供器)和LabelProvider(标签提供器)。

JFace查看器主要分为以下几类:
1. ListViewer: 对应于SWT的列表控件,目的是将列表中的元素映射至SWT列表控件
2. TreeViewer: 对应于SWT的树控件,提供树的展开和折叠等基本操作
3. TableViewer: 对应于SWT的表控件,映射表中的元素
4. TextViewer: 对应于SWT的StyledText控件,创建编辑器的时候,使用这个查看器是最合适不过了。

好了,介绍性的文字就写到这里,我想大家一定已经知道了探索的方向。下面,我们看一个简单的示例,就是这篇文章开头给出的效果图。它是我模仿医院管理系统做的一个简单例子,左边的视图就是使用了一个ListView查看器。这里给出它的关键代码:

1 public void createPartControl(Compositeparent) {
2
3
4viewer=newListViewer(parent,SWT.BORDER);
5viewer.setContentProvider(newPersonContentProvider());
6viewer.setLabelProvider(newPersonLabelProvider());
7viewer.setInput(newPersonModel());
8
9createActions();
10initializeToolBar();
11initializeMenu();
12}


可以看到,这里需要设置内容提供器和标签提供器和模型。下面,我们先创建一个病人类Person.java:

1 package cn.blogjava.youxia.views;
2
3 public class Person {
4
5privateStringname;
6privateStringsex;
7publicStringgetName(){
8returnname;
9}

10publicvoidsetName(Stringname){
11this.name=name;
12}

13publicStringgetSex(){
14returnsex;
15}

16publicvoidsetSex(Stringsex){
17this.sex=sex;
18}

19
20}


下面,创建模型类PersonModel.java,在构造函数中我们向List中填入了几个初始化数据:

1 package cn.blogjava.youxia.views;
2 import java.util.ArrayList;
3
4 public class PersonModel {
5
6privateArrayList<Person>list=newArrayList<Person>();
7
8publicinterfaceListener{
9publicvoidadd(Personp);
10publicvoidremove(Personp);
11}

12
13privateListenerlistener;
14
15publicPersonModel(){
16//向list里面填入几个初始化数据
17Personp1=newPerson();
18p1.setName("病人1");
19p1.setSex("");
20list.add(p1);
21
22Personp2=newPerson();
23p2.setName("病人2");
24p2.setSex("");
25list.add(p2);
26
27}

28
29publicvoidsetListener(Listenerlistener){
30this.listener=listener;
31}

32
33publicvoidadd(Personp){
34list.add(p);
35if(listener!=null){
36listener.add(p);
37}

38}

39
40publicvoidremove(Personp){
41list.remove(p);
42if(listener!=null){
43listener.remove(p);
44}

45}

46
47publicArrayListelements(){
48returnlist;
49}

50}


在这里,我们还定义了一个Listener接口,为什么要有这么一个接口呢?就是为了让我们模型中的数据被改变时,查看器能够相应更改。下 面,我们实现内容提供器,该内容提供器实现了PersonModel中定义的Listener接口,如下 PersonContentProvider.java:

1 package cn.blogjava.youxia.views;
2
3 import org.eclipse.jface.viewers.IStructuredContentProvider;
4 import org.eclipse.jface.viewers.Viewer;
5 import org.eclipse.jface.viewers.ListViewer;
6
7 import cn.blogjava.youxia.views.PersonModel.Listener;
8
9 public class PersonContentProvider implements IStructuredContentProvider,
10 Listener {
11
12PersonModelinput;
13ListViewerviewer;
14
15publicObject[]getElements(ObjectinputElement){
16//TODO自动生成方法存根
17returninput.elements().toArray();
18}

19
20publicvoiddispose(){
21//TODO自动生成方法存根
22if(input!=null){
23input.setListener(null);
24}

25input=null;
26
27}

28
29publicvoidinputChanged(Viewerviewer,ObjectoldInput,ObjectnewInput){
30//TODO自动生成方法存根
31viewer=(ListViewer)viewer;
32input=(PersonModel)newInput;
33input.setListener(this);
34
35}

36
37publicvoidadd(Personp){
38//TODO自动生成方法存根
39viewer.add(p);
40}

41
42publicvoidremove(Personp){
43//TODO自动生成方法存根
44viewer.remove(p);
45}

46
47}


我们知道,列表中的元素都是Person类的对象,怎么让他们显示出来呢,需要实现标签提供器,在标签提供器中,我们可以设置对象显示的图标和文字,如下PersonLabelProvider.java:

1 package cn.blogjava.youxia.views;
2
3 import org.eclipse.jface.viewers.ILabelProvider;
4 import org.eclipse.jface.viewers.ILabelProviderListener;
5 import org.eclipse.swt.graphics.Image;
6
7 public class PersonLabelProvider implements ILabelProvider {
8
9publicImagegetImage(Objectelement){
10returnnull;
11}

12
13publicStringgetText(Objectelement){
14//TODO自动生成方法存根
15return((Person)element).getName();
16}

17
18publicvoidaddListener(ILabelProviderListenerlistener){
19//TODO自动生成方法存根
20
21}

22
23publicvoiddispose(){
24//TODO自动生成方法存根
25
26}

27
28publicbooleanisLabelProperty(Objectelement,Stringproperty){
29//TODO自动生成方法存根
30returnfalse;
31}

32
33publicvoidremoveListener(ILabelProviderListenerlistener){
34//TODO自动生成方法存根
35
36}

37
38}


运行程序,就得到了文章开头的效果,但是不能在右边的视图中显示病人的详细信息。

如果要做到视图的交互,需要添加事件的监听器。使用Java 进行GUI开发的人应该都不会陌生,而我在RCP上,也处于探索阶段,更深一步的内容,让我们自己慢慢研究吧。
================================================================================
没有嵌入Active X控件的视图:
rcp29.JPG

嵌入浏览器控件,并显示www.blogjava.net的主页:
rcp30.JPG

在 Windows系统下,OLE和Active X控件是两个非常吸引人的技术,它们的基础都是COM。OLE的体验,就是平时我们可以把Excel表格嵌入Word文档,或者把PDF嵌入浏览器显示一 样,而Active X控件更是无处不在,做VB开发和网页开发的人都应该很熟悉。使用Windows系统中丰富的Active X控件资源,我们可以实现功能非常强大的程序。

在Windows平台下,SWT图形工具包提供了对OLE的支持,Active X控件和OLE文档都可以被很方便地嵌入SWT窗口部件或者JFace部件,在这里,我只讨论将Active X控件插入视图。

在 一个视图中包含一个Active X控件需要两个对象的支持,即一个OleFrame和一个OleClientSite对象。如果需要创建一个OLE应用,需要先后创建他们。创建 OleFrame对象比较简单,OleFrame类定义在org.eclipse.swt.ole.win32中,创建OleFrame对象只需要简单的 new就可以,如下:

1 OleFrameframe = new OleFrame(parent,SWT.NONE);

在这个构造函数中,第一个参数指的是该OleFrame的母窗口部件,即Active X控件将要被嵌入的窗口部件。

在 OleFrame的基础上就可以创建OleClientSite对象,创建该对象需要知道控件的programID,这个ID的信息存放在windows 的注册表中。在我们这篇文章的例子中,我们使用的是一个浏览器控件,那么我们怎么知道浏览器控件的ProgID呢?我使用的是Visual Studio 2003自带的OleView工具,如下图:
rcp26.JPG

可以看到,Microsoft Web 浏览器的ProgID为Shell.Explorer.2,我们可以这样创建OleClientSite对象:

1 OleClientSiteclient = new OleClientSite(frame,SWT.NONE, " Shell.Explorer.2 " );


创建对象后,还需要激活,才能够在RCP程序中对这些OLE对象进行操作。如下:

client.doVerb(OLE.OLEIVERB_SHOW);


然后,我们需要操作这个Active X控件,调用它的方法,或者设置它的属性。比如在此例中,我们需要调用浏览器控件的navigate方法,以便我们的浏览器控件显示www.blogjava.net的主页。对Active X控件的操作通过OleAutomation对象来实现,创建OleAutomation对象的方法如下:

OleAutomationautomation = new OleAutomation(client);

再通过automation.invoke()来调用Active X控件的方法,其中invoke方法有几种重载形式,有只带一个int参数的,也有带int和Variant[]两个参数的,其中的int参数表示要调用 的Active X控件的方法的ID,Variant[]参数就是要传递给Active X控件的方法的参数。

这里我们要说一说Variant类,这个类提供了多个构造函数,可以方便的将int,float,long,double,string等等基本数据类型封装为Variant,比如我们要传递给浏览器控件的navigate方法的地址参数:

Varianturl = new Variant( " http://www.blogjava.net " );


那么我们怎么才能得到Active X控件的方法的ID,还有它需要哪些参数呢?还是要借助前面提到的OleView.exe工具,如下图:
rcp27.JPG

rcp28.JPG

可以看到,Navigate方法的id为0x00000068,转化为十进制就是104,而它需要的参数第一个是一个字符串,其它的都是可选的,因此,我们可以这样调用它的方法:

Varianturl = new Variant( " http://www.blogjava.net/ " );
automation.invoke(
104 , new Variant[] {url} );


下面,贴出本文例子中的视图的代码和菜单Action的代码,在写这篇文章之前,我一直在探索怎样从菜单控制视图,后来发现是这样:
window.getActivePage.getViewReferences();
虽然我不知道Eclipse中Page的概念究竟是什么,但是只要能找到我要操作的视图就可以了。视图的代码如下:

OleView.java

1 package cn.blogjava.youxia.views;
2
3 import org.eclipse.jface.action.IMenuManager;
4 import org.eclipse.jface.action.IToolBarManager;
5 import org.eclipse.swt.SWT;
6 import org.eclipse.swt.widgets.Composite;
7 import org.eclipse.ui.part.ViewPart;
8 import org.eclipse.swt.ole.win32.OleFrame;
9
10 public class OleView extends ViewPart {
11publicOleFrameframe;
12
13publicstaticfinalStringID="cn.blogjava.youxia.views.OleView";//$NON-NLS-1$
14
15/**
16*Createcontentsoftheviewpart
17*@paramparent
18*/

19@Override
20publicvoidcreatePartControl(Compositeparent){
21frame=newOleFrame(parent,SWT.NONE);
22
23//
24createActions();
25initializeToolBar();
26initializeMenu();
27}

28
29/**
30*Createtheactions
31*/

32privatevoidcreateActions(){
33//Createtheactions
34}

35
36/**
37*Initializethetoolbar
38*/

39privatevoidinitializeToolBar(){
40IToolBarManagertoolbarManager=getViewSite().getActionBars()
41.getToolBarManager();
42}

43
44/**
45*Initializethemenu
46*/

47privatevoidinitializeMenu(){
48IMenuManagermenuManager=getViewSite().getActionBars()
49.getMenuManager();
50}

51
52@Override
53publicvoidsetFocus(){
54//Setthefocus
55}

56
57}

58


在这个视图中,我创建了OleFrame对象,并让它是public的,至于OleClientSite和OleAutomation对象,我们在点击菜单项后创建。菜单动作的代码如下:

OpenFileAction.java

1 package cn.blogjava.youxia.actions;
2
3
4 import org.eclipse.jface.action.IAction;
5 import org.eclipse.jface.viewers.ISelection;
6 import org.eclipse.swt.SWT;
7 import org.eclipse.swt.ole.win32.OLE;
8 import org.eclipse.swt.ole.win32.OleClientSite;
9 import org.eclipse.ui.IWorkbenchWindow;
10 import org.eclipse.ui.IWorkbenchWindowActionDelegate;
11 import org.eclipse.ui. * ;
12 import cn.blogjava.youxia.views. * ;
13 import org.eclipse.swt.ole.win32.OleAutomation;
14 import org.eclipse.swt.ole.win32.Variant;
15
16 public class OpenFileAction implements IWorkbenchWindowActionDelegate {
17
18IWorkbenchWindowwindow;
19
20publicvoiddispose(){
21//TODO自动生成方法存根
22
23}

24
25publicvoidinit(IWorkbenchWindowwindow){
26//TODO自动生成方法存根
27this.window=window;
28
29}

30
31publicvoidrun(IActionaction){
32//TODO自动生成方法存根
33
34IViewReference[]vfs=window.getActivePage().getViewReferences();
35IViewPartvw;
36for(inti=0;i<vfs.length;i++){
37vw=vfs[i].getView(false);
38if(vw.getTitle().equals("使用ActiveX控件")){
39OleClientSiteclient=newOleClientSite(((OleView)vw).frame,SWT.NONE,"Shell.Explorer.2");
40client.doVerb(OLE.OLEIVERB_SHOW);
41OleAutomationoa=newOleAutomation(client);
42Variantstr=newVariant("http://www.blogjava.net/");
43oa.invoke(104,newVariant[]{str});
44
45}

46}

47
48}

49
50publicvoidselectionChanged(IActionaction,ISelectionselection){
51//TODO自动生成方法存根
52
53}

54
55}

56


根据前面几节将的内容配置plugin.xml和修改Perspective.java的代码,就可以看到文章开头的效果了。
================================================================================

看完这篇文章,可以实现如下界面:
rcp32.JPG

rcp33.JPG


当 我第一次看到RCP的时候,我就梦想着有一天能够用它开发界面华丽的2D和3D程序,经历过前面的探索,今天终于可以揭开2D绘图的神秘面纱。在包资源管 理器的插件依赖项中,我们一眼就可以看到org.eclipse.swt.graphics包,毫无疑问,和2D绘图有关的类就在这个包中。还有一个 org.eclipse.swt.opengl包也很引人注目,但是里面却只有GLCanvas类和GLData类,怎么也找不到传说中的GL类和GLU 类,也许下一篇文章我会写出关于3D的内容,但也许这个计划会夭折。

我刚开始发现org.eclipse.swt.graphics包的时候,要使用包里面的类却不是那么容易。比如,从名称上可以看出Image类是 处理图像的,但是它的构造函数无一例外都需要一个Device参数,于是,我迷惑了,Device,我该如何取得?再比如,GC类里面含有各种绘图的方 法,但是GC的构造函数需要Drawable参数,那Drawable我又该如何获得呢?

于是,我在网上搜索关于SWT 2D方面的内容,终于,让我看到了别人这样构造Image和GC:
Image img = new Image(display,"pic.gif");
GC gc = new GC(Image);
你能看出什么?为什么display是Device的子类?为什么Image是Drawabe的子类?最简单的办法,使用Eclipse的类层次结构视图查看:

rcp31.JPG

高,实在是高,在这里我不得不佩服SWT的设计者,在一开始,他们就把所有的控件都设计为可绘制的,而且使用Device来抽象绘图的设备。从图中 可以看出,所有的控件都实现Drawable接口,Image也实现Drawable接口,而Device的子类Display和Printer刚好分别 代表了屏幕和打印机。所有的谜团都在这里解决了,我们可以使用任何控件作为GC构造函数的参数来构造GC,然后绘图,而所有需要Device参数的地方, 我们可以根据我们需要输出的设备是显示器还是打印机而分别选择Display或Printer。

在org.eclipse.swt.widgets包中,有一个Canvas类,不难看出,如果我们要绘图,这个控件是最佳选择了。在下面的代码中,我们可以通过选择不同的菜单,分别绘制椭圆,矩形,填充渐变色的矩形和一个图像,运行效果就是文章开头的图片。

视图CanvasView.java

1 package cn.blogjava.youxia.views;
2
3 import org.eclipse.swt.widgets.Composite;
4 import org.eclipse.ui.part.ViewPart;
5 import org.eclipse.swt.widgets.Canvas;
6 import org.eclipse.swt.SWT;
7 import org.eclipse.swt.events. * ;
8 import org.eclipse.swt.graphics.Image;
9 import org.eclipse.ui.PlatformUI;
10
11 public class CanvasView extends ViewPart {
12
13publicCanvascanvas;
14@Override
15publicvoidcreatePartControl(Compositeparent){
16//TODO自动生成方法存根
17canvas=newCanvas(parent,SWT.NONE);
18}

19
20@Override
21publicvoidsetFocus(){
22//TODO自动生成方法存根
23
24}

25
26}

27

菜单项绘制椭圆DrawOvalAction.java的关键部分:

1 public void run(IActionaction) {
2//TODO自动生成方法存根
3IViewReference[]vfs=window.getActivePage().getViewReferences();
4IViewPartvw;
5for(inti=0;i<vfs.length;i++){
6vw=vfs[i].getView(false);
7if(vw.getTitle().equals("画图板")){
8GCgc=newGC(((CanvasView)vw).canvas);
9gc.drawOval(80,50,100,100);
10gc.dispose();
11}

12}

13}

菜单项绘制矩形DrawRectAction.java的关键部分:

1 public void run(IActionaction) {
2//TODO自动生成方法存根
3IViewReference[]vfs=window.getActivePage().getViewReferences();
4IViewPartvw;
5for(inti=0;i<vfs.length;i++){
6vw=vfs[i].getView(false);
7if(vw.getTitle().equals("画图板")){
8GCgc=newGC(((CanvasView)vw).canvas);
9gc.drawRectangle(280,50,100,100);
10gc.dispose();
11}

12}

13
14}

菜单项绘制渐变矩形DrawGradientAction.java的关键部分:

1 public void run(IActionaction) {
2//TODO自动生成方法存根
3IViewReference[]vfs=window.getActivePage().getViewReferences();
4IViewPartvw;
5for(inti=0;i<vfs.length;i++){
6vw=vfs[i].getView(false);
7if(vw.getTitle().equals("画图板")){
8GCgc=newGC(((CanvasView)vw).canvas);
9gc.setBackground(window.getShell().getDisplay().getSystemColor(SWT.COLOR_BLUE));
10gc.fillGradientRectangle(80,200,100,100,false);
11
12gc.dispose();
13}

14}

15
16}

菜单项绘制图像DrawImageAction.java的关键部分:

1 public void run(IActionaction) {
2//TODO自动生成方法存根
3IViewReference[]vfs=window.getActivePage().getViewReferences();
4IViewPartvw;
5for(inti=0;i<vfs.length;i++){
6vw=vfs[i].getView(false);
7if(vw.getTitle().equals("画图板")){
8GCgc=newGC(((CanvasView)vw).canvas);
9Imageimg=newImage(window.getShell().getDisplay(),"E://img.gif");
10gc.drawImage(img,280,200);
11gc.dispose();
12}

13}

14
15}


上面的方法虽然实现了绘图,但是还有一个问题,就是一旦我们的窗口最小化或者被别的窗口遮挡后,图形就会消失。原因其实很简单,一旦我们的 窗口最小化或者被别的窗口遮挡后,控件就需要重画,所以我们画的图形就不见了,如果要让控件重画的时候也能绘制图形,就应该使用 canvas.addPaintListener()为控件添加Paint事件的监听器。示例代码见下。

1 package cn.blogjava.youxia.views;
2
3 import org.eclipse.swt.widgets.Composite;
4 import org.eclipse.ui.part.ViewPart;
5 import org.eclipse.swt.widgets.Canvas;
6 import org.eclipse.swt.SWT;
7 import org.eclipse.swt.events. * ;
8 import org.eclipse.swt.graphics.Image;
9 import org.eclipse.ui.PlatformUI;
10
11 public class CanvasView extends ViewPart {
12
13publicCanvascanvas;
14@Override
15publicvoidcreatePartControl(Compositeparent){
16//TODO自动生成方法存根
17canvas=newCanvas(parent,SWT.NONE);
18canvas.addPaintListener(newPaintListener(){
19publicvoidpaintControl(PaintEvente){
20//画椭圆
21e.gc.drawOval(80,50,100,100);
22//画矩形
23e.gc.drawRectangle(280,50,100,100);
24//画渐变矩形
25e.gc.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_BLUE));
26e.gc.fillGradientRectangle(80,200,100,100,false);
27//画图形
28Imageimg=newImage(PlatformUI.getWorkbench().getDisplay(),"E://img.gif");
29e.gc.drawImage(img,280,200);
30
31}

32}
);
33}

34
35@Override
36publicvoidsetFocus(){
37//TODO自动生成方法存根
38
39}

40
41}

42

GC类的绘图方法很多,而且可以设置不同的前景色,背景色,画笔,画刷等等,还可以裁减图形,这些就靠我们慢慢探索了。
===========================================================================================
看完这一篇,我们应该可以使用OpenGL绘制如下图的场景了。该场景是一个旋转的三菱锥矩阵,下面是旋转到不同方位的截图:
rcp37.JPG

rcp38.JPG

rcp36.JPG

我整整花了一个星期的时间来研究SWT中的OpenGL,遇到的第一个困难是找不到传说中的GL类和GLU类,最后,通过搜索引擎终于找到了,原来使用Eclipse进行OpenGL开发,还需要另外下载OpenGL插件,如下图:
rcp34.JPG

这里有OpenGL的类库,还有一个示例,把类库下载下来,解压,放到Eclipse的Plugin目录下,然后在我们的项目中添加依赖项,就可以看到我们需要使用的类了,如下图:
rcp35.JPG

我们需要对OpenGL编程的一些基本概念有点了解,在OpenGL中,3D场景不是直接绘制到操作系统的窗口上的,而是有一个称为着色描述表(Rendering Context)的东西,我们这里简称它为context,OpenGL的绘图命令都是在当前context上进行绘制,然后再把它渲染到操作系统的设备描述表(Device Context)上,这里,我们可以简单的理解成把它渲染到窗口控件上(其实也可以渲染到全屏幕)。

在Windows中使用OpenGL编程比较麻烦,因为我们需要设置一个叫做象素格式的东西,大家只要看看下面的这段C代码,就知道我为什么说它麻烦了:

static PIXELFORMATDESCRIPTORpfd=     //pfd告诉窗口我们所希望的东东
     {
         sizeof(PIXELFORMATDESCRIPTOR),   
//上诉格式描述符的大小
         1,                //版本号
         PFD_DRAW_TO_WINDOW|        //格式必须支持窗口
         PFD_SUPPORT_OPENGL|        //格式必须支持OpenGL
         PFD_DOUBLEBUFFER,         //必须支持双缓冲
         PFD_TYPE_RGBA,           //申请RGBA格式
         bits,               //选定色彩深度
         0,0,0,0,0,0,         //忽略的色彩位
         0,                 //无Alpha缓存
         0,                 //忽略ShiftBit
         0,                 //无聚集缓存
         0,0,0,0,            //忽略聚集位
         16,                //16位Z-缓存(深度缓存)
         0,                 //无模板缓存
         0,                 //无辅助缓存
         PFD_MAIN_PLANE,          //主绘图层
         0,                 //保留
         0,0,0              //忽略层遮罩
    }
;
if ( ! (PixelFormat = ChoosePixelFormat(hDC, & pfd)))    // Windows找到相应的象素格式了吗?
     {
        KillGLWindow();               
//重置显示区
        MessageBox(NULL,"Can'tFindASuitablePixelFormat.",
           
"ERROR",MB_OK|MB_ICONEXCLAMATION);
        
returnFALSE;                //返回FALSE
    }

if ( ! SetPixelFormat(hDC,PixelFormat, & pfd))      // 能够设置象素格式么?
     {
        KillGLWindow();              
//重置显示区
        MessageBox(NULL,"Can'tSetThePixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
        
returnFALSE;               //返回FALSE
    }

if ( ! (hRC = wglCreateContext(hDC)))          // 能否取得着色描述表?
     {
        KillGLWindow();              
//重置显示区
        MessageBox(NULL,"Can'tCreateAGLRenderingContext.",
           
"ERROR",MB_OK|MB_ICONEXCLAMATION);
        
returnFALSE;               //返回FALSE
    }


在SWT中,我们开发OpenGL应用就要简单得多,这全部要归功于org.eclipse.swt.opengl包下面的 GLCanvas类和GLData类,使用GLCanvas类可以直接创建一个用于OpenGL渲染的控件,至于设置象素格式这样复杂的问题,它已经帮我 们解决了,不信你看GLCanvas类的构造函数的实现。

GLCanvas类中的几个方法代表了我一开始提到的OpenGL的几个基本概 念,setCurrent()方法就是为了把该控件的context设置为OpenGL的当前着色描述表,然后使用GL和GLU类中的方法在当前 context上进行绘图,绘制完图形以后,再使用GLCanvas类的swapBuffers()方法交换缓冲区,也就是把context中的3D场景 渲染到控件上。

写到这里,大家肯定认为一切问题都应该迎刃而解了,然而,我却碰到了另外一个困难,这个困难就是SWT的OpenGL表现怪异,怎么个怪异呢?请看下面视图类的代码:

public void createPartControl(Compositeparent) {
//TODO自动生成方法存根
GLDatadata=newGLData();
data.depthSize
=1;
data.doubleBuffer
=true;
GLCanvascanvas
=newGLCanvas(parent,SWT.NO_BACKGROUND,data);
//设置该canvas的context为OpenGL的当前context
if(!canvas.isCurrent()){
canvas.setCurrent();
}

//这里可以进行OpenGL绘图

//交换缓存,将图形渲染到控件上
canvas.swapBuffers();
}


按道理,我们应该可以得到一个经典的3D的黑色场景,但是,我得到的却是这样的效果:
rcp39.JPG

相 当的郁闷啊,就是这个问题困扰了我至少一个星期。我把官方网站上的示例看了有看,就是找不到问题的关键所在。直到最后,我用了另外一个线程,每100ms 都调用canvas.swapBuffers()把场景渲染一遍问题才解决。由此可见,之所以回出现上面的问题,主要是因为我们渲染的场景很快会被操作系 统的其他绘图操作所覆盖,只有不断的渲染我们才能看到连续的3D图形。

我是这样实现让3D场景连续渲染的:

public void createPartControl(Compositeparent) {
//TODO自动生成方法存根
GLDatadata=newGLData();
data.depthSize
=1;
data.doubleBuffer
=true;
GLCanvascanvas
=newGLCanvas(parent,SWT.NO_BACKGROUND,data);
//将绘图代码转移到定时器中
Refresherrf=newRefresher(canvas);
rf.run();
}


Refresher类的代码如下:

class Refresher implements Runnable {
publicstaticfinalintDELAY=100;

privateGLCanvascanvas;

publicRefresher(GLCanvascanvas){
this.canvas=canvas;
}


publicvoidrun(){
if(this.canvas!=null&&!this.canvas.isDisposed()){
if(!canvas.isCurrent()){
canvas.setCurrent();
}

//这里添加OpenGL绘图代码
canvas.swapBuffers();
this.canvas.getDisplay().timerExec(DELAY,this);
}

}


}


问题解决,得到的效果图如下:
rcp40.JPG

OK,下面的任务就是完完全全的使用OpenGL的绘图功能了,不管你的OpenGL教材使用的是什么操作系统什么编程语言,你都能很简单的把它的概念拿到这里来使用。

使用OpenGL的第一件事,就是要设置投影矩阵、透视图和观察者矩阵,如果你不知道为什么要这么做,请查看OpenGL的基础教材,在这里,照搬就行了。为了让我们的控件在每次改变大小的时候都能够做这些设置,我们使用事件监听器,如下:

public void createPartControl(Compositeparent) {
//TODO自动生成方法存根
GLDatadata=newGLData();
data.depthSize
=1;
data.doubleBuffer
=true;
canvas
=newGLCanvas(parent,SWT.NO_BACKGROUND,data);
canvas.addControlListener(
newControlAdapter(){
publicvoidcontrolResized(ControlEvente){
Rectanglerect
=canvas.getClientArea();
GL.glViewport(
0,0,rect.width,rect.height);

//选择投影矩阵
GL.glMatrixMode(GL.GL_PROJECTION);
//重置投影矩阵
GL.glLoadIdentity();
//设置窗口比例和透视图
GLU.gluPerspective(45.0f,(float)rect.width/(float)rect.height,0.1f,100.0f);
//选择模型观察矩阵
GL.glMatrixMode(GL.GL_MODELVIEW);
//重置模型观察矩阵
GL.glLoadIdentity();

//黑色背景
GL.glClearColor(0.0f,0.0f,0.0f,0.0f);
//设置深度缓存
GL.glClearDepth(1.0f);
//启动深度测试
GL.glEnable(GL.GL_DEPTH_TEST);
//选择深度测试类型
GL.glDepthFunc(GL.GL_LESS);
//启用阴影平滑
GL.glShadeModel(GL.GL_SMOOTH);
//精细修正透视图
GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,GL.GL_NICEST);
//清除屏幕和深度缓存
GL.glClear(GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT);
//重置当前的模型观察矩阵
GL.glLoadIdentity();
}

}
);
canvas.addDisposeListener(
newDisposeListener(){
publicvoidwidgetDisposed(DisposeEvente){
dispose();
}

}
);



调用glLoadIdentity()之后,实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。OpenGL屏幕中心的坐标值是XY轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。

glTranslatef(x, y, z)是将当前点沿着XYZ轴移动,当我们绘图的时候,不是相对于屏幕中间,而是相对于当前点。

glBegin(GL.GL_TRIANGLES)的意思是开始绘制三角形,glEnd()告诉OpenGL三角形已经创建好了。通常当我们需要画3个顶点时,可以使用GL_TRIANGLES。在绝大多数的显卡上,绘制三角形是相当快速的。如果要画四个顶点,使用GL_QUADS的话会更方便。但据我所知,绝大多数的显卡都使用三角形来为对象着色。最后,如果想要画更多的顶点时,可以使用GL_POLYGON

glVertex(x,y,z)用来设置顶点,如果绘制三角形,这些顶点需要三个一组,如果绘制四边形,则是四个为一组。如果我们要为顶点着色,就需要glColor3f(r,g,b)方法,记住,每次设置以后,这个颜色就是当前颜色,直到再次调用该方法重新设置为止。

最后需要介绍的是glRotatef(Angle,Xvector,Yvector,Zvector)方法,该方法负责让对象围绕指定的轴旋转,Angle参数指转动的角度,注意是浮点数哦。

下面是我的视图类的全部代码,我把3D绘图的任务全部放到了另外一个线程中,并且定义了一个递归方法public void drawPyramid(float x, float y, float z, int n)用来绘制三菱锥矩阵。如下:

packagecn.blogjava.youxia.views;

importorg.eclipse.opengl.GL;
importorg.eclipse.opengl.GLU;
importorg.eclipse.swt.events.ControlAdapter;
importorg.eclipse.swt.events.ControlEvent;
importorg.eclipse.swt.events.DisposeEvent;
importorg.eclipse.swt.events.DisposeListener;
importorg.eclipse.swt.graphics.Rectangle;
importorg.eclipse.swt.opengl.GLData;
importorg.eclipse.swt.widgets.Composite;
importorg.eclipse.ui.part.ViewPart;
importorg.eclipse.swt.opengl.GLCanvas;
importorg.eclipse.swt.SWT;

publicclassOpenGLViewextendsViewPart{

GLCanvascanvas;
@Override
publicvoidcreatePartControl(Compositeparent){
//TODO自动生成方法存根
GLDatadata=newGLData();
data.depthSize
=1;
data.doubleBuffer
=true;
canvas
=newGLCanvas(parent,SWT.NO_BACKGROUND,data);
canvas.addControlListener(
newControlAdapter(){
publicvoidcontrolResized(ControlEvente){
Rectanglerect
=canvas.getClientArea();
GL.glViewport(
0,0,rect.width,rect.height);

//选择投影矩阵
GL.glMatrixMode(GL.GL_PROJECTION);
//重置投影矩阵
GL.glLoadIdentity();
//设置窗口比例和透视图
GLU.gluPerspective(45.0f,(float)rect.width/(float)rect.height,0.1f,100.0f);
//选择模型观察矩阵
GL.glMatrixMode(GL.GL_MODELVIEW);
//重置模型观察矩阵
GL.glLoadIdentity();

//黑色背景
GL.glClearColor(0.0f,0.0f,0.0f,0.0f);
//设置深度缓存
GL.glClearDepth(1.0f);
//启动深度测试
GL.glEnable(GL.GL_DEPTH_TEST);
//选择深度测试类型
GL.glDepthFunc(GL.GL_LESS);
//启用阴影平滑
GL.glShadeModel(GL.GL_SMOOTH);
//精细修正透视图
GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,GL.GL_NICEST);
//清除屏幕和深度缓存
GL.glClear(GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT);
//重置当前的模型观察矩阵
GL.glLoadIdentity();
}

}
);
canvas.addDisposeListener(
newDisposeListener(){
publicvoidwidgetDisposed(DisposeEvente){
dispose();
}

}
);
/*

*/



Refresherrf
=newRefresher(canvas);
rf.run();
}


@Override
publicvoidsetFocus(){
//TODO自动生成方法存根

}


}


classRefresherimplementsRunnable{
publicstaticfinalintDELAY=100;

privateGLCanvascanvas;
privatefloatrotate=0.0f;

publicRefresher(GLCanvascanvas){
this.canvas=canvas;
}


publicvoidrun(){
if(this.canvas!=null&&!this.canvas.isDisposed()){
if(!canvas.isCurrent()){
canvas.setCurrent();
}

//这里添加OpenGL绘图代码
GL.glLoadIdentity();
GL.glClear(GL.GL_COLOR_BUFFER_BIT
|GL.GL_DEPTH_BUFFER_BIT);
GL.glTranslatef(
0,4.5f,-11);
//围绕y轴转起来
rotate+=0.5;
GL.glRotatef(rotate,
0,1.0f,0);
//调用递归函数,绘制三菱锥矩阵
drawPyramid(0,0,0,4);
canvas.swapBuffers();
this.canvas.getDisplay().timerExec(DELAY,this);
}

}


publicvoiddrawPyramid(floatx,floaty,floatz,intn){
if(n==0)return;
//画一个三菱锥
GL.glBegin(GL.GL_TRIANGLES);
//画背面
GL.glColor3f(1.0f,0.0f,0.0f);
GL.glVertex3f(x,y,z);
GL.glColor3f(
0.0f,1.0f,0.0f);
GL.glVertex3f(x
+1.0f,y-1.63f,z-0.57f);
GL.glColor3f(
0.0f,0.0f,1.0f);
GL.glVertex3f(x
-1.0f,y-1.63f,z-0.57f);
//画底面
GL.glColor3f(1.0f,0.0f,0.0f);
GL.glVertex3f(x,y
-1.63f,z+1.15f);
GL.glColor3f(
0.0f,1.0f,0.0f);
GL.glVertex3f(x
-1.0f,y-1.63f,z-0.57f);
GL.glColor3f(
0.0f,0.0f,1.0f);
GL.glVertex3f(x
+1.0f,y-1.63f,z-0.57f);
//画左侧面
GL.glColor3f(1.0f,0.0f,0.0f);
GL.glVertex3f(x,y,z);
GL.glColor3f(
0.0f,1.0f,0.0f);
GL.glVertex3f(x
-1.0f,y-1.63f,z-0.57f);
GL.glColor3f(
0.0f,0.0f,1.0f);
GL.glVertex3f(x,y
-1.63f,z+1.15f);
//画右侧面
GL.glColor3f(1.0f,0.0f,0.0f);
GL.glVertex3f(x,y,z);
GL.glColor3f(
0.0f,1.0f,0.0f);
GL.glVertex3f(x,y
-1.63f,z+1.15f);
GL.glColor3f(
0.0f,0.0f,1.0f);
GL.glVertex3f(x
+1.0f,y-1.63f,z-0.57f);
GL.glEnd();
//递归调用,画多个三菱锥
drawPyramid(x,y-1.63f,z+1.15f,n-1);
drawPyramid(x
-1.0f,y-1.63f,z-0.57f,n-1);
drawPyramid(x
+1.0f,y-1.63f,z-0.57f,n-1);
}

}
目 录 1.富客户端平台 1 1.1.概述 1 1.2.Eclipse RCP 建设风格——插件,扩展和扩展点 1 2.创建你的第一个RCP程序 1 2.1.创建一个RCP程序 2 2.2.启动你的RCP程序 5 2.3.应用程序VS 产品 6 3.应用程序里的插件ID 7 4.Actions的用法(菜单和工具栏) 7 4.1.概述 7 4.2.通过编码添加 8 4.3.由“扩展”方式向程序添加菜单和工具栏 11 5.添加组合键 19 5.1.概述 19 5.2.声明actions的组合键 19 6.系统托盘 24 7.外观 30 7.1.向程序中添加视图模板 31 7.2.向应用程序添加VIEW 36 7.3.向VIEW里添加action 40 8.和编辑器一起工作 51 8.1.概述 51 8.2.创建工程 51 8.3.创建并准备domain 模型 51 8.4.在视口中使用domain模型 57 8.5.加入编辑器 58 8.6.调用编辑器 66 8.7.向编辑器提供内容 70 9.对话框 71 9.1概述 71 9.2.预定义的对话框 71 9.2.1.概述 71 9.2.2.创建工程 71 9.2.3.声明action 71 9.2.4.调用对话框 71 9.3.用户自定义对话框 74 9.3.1.概述 74 9.3.2.创建工程 74 9.3.3.声明action 74 9.3.4.声明action 74 9.3.5.创建对话框 75 10.向导(wizard) 77 10.1.概述 77 10.2.例子 77 11.首选项 85 11.1 首选项 85 11.2.使用首选项 85 11.3.首选项页 91 12.添加状态条 97 12.1.安装状态条 97 12.2.共享状态条 98 13.透视 103 13.1.向你的程序中添加透视图 103 13.2.使透视图可选。 107 13.2.1.使透视图可由一个coolbar可选 107 13.2.2.使透视图可通过菜单选择。 109 14.进度报告 112 15.将外部类包含进你的程序 118 15.1.概述 118 15.2.向构建路径中添加jar 118 15.3.使jar在你的运行路径里有效 119 16.提示和策略 120 16.1.控制台日志 120 16.2.保存用户的布局 121 16.3.获得display 122 16.4.使用eclipse的“保存”action 123 16.5.装载模型 127 16.6.向你的程序添加错误日志视口 127 17.制造一个产品 129 17.1.概述 129 17.2.创建一个工程 130 17.3.测试你的产品 134 18.商标 134 18.1.欢迎页面 134 18.2.商标 135 18.3.风格化launcher 136 19.发布你的产品 137 20.发布引入外部jar的产品 141 20.1.整合外部jar和第三方库 141 Tip 142 21.1.创建一个新工程 144 21.2.创建一个产品 144 21.3.添加依赖性 145 21.4.向程序中添加action 146 21.5.创建一个帮助插件工程////原著写的不好 149 22.附录:配置文件 152 22.1.概述 152 22.2..project 153 22.3.Manifest.MF 154 23.附录:使用接口技术 154 23.2.Jface 154
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值