Eclipse RCP 开发:从登录对话框说开去

在今天的这篇文章里,我要和大家探讨的是登录对话框,不要小看这个对话框,虽然SWT和JFace里面提供了很好用的对话框基类,但是如果你不理解SWT中GUI线程和非GUI线程的概念,那么你依然难以达到你想要的效果。具体什么情况呢?这要从我最近接的一个项目说起。

有人委托我做一个药店管理 系统 ,这种系统属于进销存管理系统的范畴,理论上讲没有什么难度,可供选择的开发 工具有很多,最主流的当然要数VisualC++和VB、Delphi,如果偷一点懒,选择Office中的Access开发也很简单,但是为了挑战自己,我决定选择Eclipse RCP来写这个程序,Eclipse RCP开发的程序界面很漂亮,但是EclipseRCP很复杂,稍有不慎就会陷入Bug的泥沼,严重延误工期。这不,一开始就碰到了对话框的难题。

我的本意是在打开工作台窗口前,先打开一个用户登录的对话框,如果用户登录成功,则关闭对话框,打开工作台窗口。使用Eclipse的向导创建了项 目之后,很快我就决定在Application类中实现该功能。我的代码如下,只列出Application类中的start方法:

  1. public   Object start(IApplicationContext context) {
  2.         Display display  =  PlatformUI.createDisplay();
  3.         
  4.          // 下面是我的代码
  5.         LoginDialog loginDialog  =   new  LoginDialog();
  6.          // 我的代码结束
  7.         
  8.          try  {
  9.              int  returnCode  =  PlatformUI.createAndRunWorkbench(display,  new  ApplicationWorkbenchAdvisor());
  10.              if  (returnCode  ==  PlatformUI.RETURN_RESTART) {
  11.                  return  IApplication.EXIT_RESTART;
  12.             }
  13.              return  IApplication.EXIT_OK;
  14.         }  finally  {
  15.             display.dispose();
  16.         }
  17.     }
复制代码



我的本意是只弹出登录对话框,登录对话框关闭后才出现工作台窗口,可是事实证明,即使登录等话框不关闭,也不会影响工作台窗口的创建,效果如下图:
Eclipse RCP 开发:从登录对话框说开去_第1张图片

即使我自己加入阻塞代码也不行,我先加入的代码如下:

  1. public  Object start(IApplicationContext context) {
  2.         Display display  =  PlatformUI.createDisplay();
  3.         
  4.          // 下面是我的代码
  5.         LoginDialog loginDialog  =   new  LoginDialog();
  6.          while ( ! loginDialog.getSShell().isDisposed()){
  7.              try {
  8.                 Thread.sleep( 100 );
  9.             } catch (Exception e){
  10.                
  11.             }
  12.         }
  13.          // 我的代码结束
  14.         
  15.          try  {
  16.              int  returnCode  =  PlatformUI.createAndRunWorkbench(display,  new  ApplicationWorkbenchAdvisor());
  17.              if  (returnCode  ==  PlatformUI.RETURN_RESTART) {
  18.                  return  IApplication.EXIT_RESTART;
  19.             }
  20.              return  IApplication.EXIT_OK;
  21.         }  finally  {
  22.             display.dispose();
  23.         }
  24.     }
复制代码



这个时候虽然可以阻止工作台窗口的产生,但是程序会失去响应,Splash Screen也不消失,关闭程序时会出错,如下图:


这个问题难就难在我们既需要阻止工作台窗口的创建,又要不让程序失去响应。下面我们就来讨论其中隐藏的秘密。

在SWT程序中,一般都会存在两种线程,一种是GUI线程,一种是非GUI线程,GUI线程就是用来生成窗口和控件的,生成窗口和控件之后一般都有一个事 件循环,用来处理GUI中产生的事件。在我上面的例子中,我自己加入的阻塞代码不仅阻塞了工作台窗口的创建,同时也阻塞了事件循环,使得窗口事件得不到处 理,所以出现了应用 程序没有响应。

那有了上面的知识,就可以解决我们的这个问题了,我们不能用暴力的方法(即Thread的sleep方法)来阻塞GUI线程,但是我们可以在GUI线程里 面构建一个事件循环,只要这个事件循环不退出,那后面的工作台窗口当然就不会创建了。构建事件循环,需要用到Display类的一些方法。

所以,正确的代码如下:
               

  1. public   Object start(IApplicationContext context) {
  2.         Display display  =  PlatformUI.createDisplay();
  3.         
  4.          // 下面是我的代码
  5.         LoginDialog loginDialog  =   new  LoginDialog();
  6.          while ( ! loginDialog.getSShell().isDisposed()){
  7.              if  ( ! display.readAndDispatch ())
  8.                 display.sleep ();
  9.         }
  10.          // 我的代码结束
  11.         
  12.          try  {
  13.              int  returnCode  =  PlatformUI.createAndRunWorkbench(display,  new  ApplicationWorkbenchAdvisor());
  14.              if  (returnCode  ==  PlatformUI.RETURN_RESTART) {
  15.                  return  IApplication.EXIT_RESTART;
  16.             }
  17.              return  IApplication.EXIT_OK;
  18.         }  finally  {
  19.             display.dispose();
  20.         }
  21.     }
复制代码



这个时候,我们可以想到模式 对话框,我想模式对话框的实现原理也是一样的,就是在Dialog类中自己加入了一个事件循环,只有当对话框关闭的时候,对话框的事件循环才退出,这样工作台窗口才能继续出处理事件。

另外需要说明的是,在Eclispe中实现对话框有几种方法,可以继承自SWT中的Dialog类,也可以继承自JFace里的Dialog类,当 然,也可以什么类都不继承而自己创建一个简单的Shell。在上面的例子中,我的LoginDialog类没有继承自 org.eclipse.jface.dialogs.Dialog,就是使用的一个普通Shell,其代码如下:

  1. package   opengsp;

  2. import  org.eclipse.swt.layout.GridLayout;
  3. import  org.eclipse.swt.graphics.Point;
  4. import  org.eclipse.swt.widgets.Shell;
  5. import  org.eclipse.swt.widgets.Label;
  6. import  org.eclipse.swt.SWT;
  7. import  org.eclipse.swt.widgets.Text;
  8. import  org.eclipse.swt.widgets.Button;
  9. import  org.eclipse.swt.layout.GridData;

  10. public   class  LoginDialog {

  11.      private  Shell sShell  =   null ;  
  12.      private  Label lbUserName  =   null ;
  13.      private  Text txtUserName  =   null ;
  14.      private  Label lbPassword  =   null ;
  15.      private  Text txtPassword  =   null ;
  16.      private  Button btnOK  =   null ;
  17.      private  Button btnCancel  =   null ;

  18.      public  LoginDialog() {
  19.          //  TODO Auto-generated constructor stub
  20.         createSShell();
  21.         sShell.open();
  22.     }

  23.      public  Shell getSShell() {
  24.          return  sShell;
  25.     }

  26.      /**
  27.      * This method initializes sShell
  28.       */
  29.      private   void  createSShell() {
  30.         sShell  =   new  Shell();
  31.         
  32.         GridData gridData  =   new  GridData();
  33.         gridData.horizontalSpan  =   2 ;
  34.         GridLayout gridLayout  =   new  GridLayout();
  35.         gridLayout.numColumns  =   3 ;   
  36.         sShell.setText( " 用户登录 " );
  37.         sShell.setLayout(gridLayout);
  38.         sShell.setSize( new  Point( 300 ,  200 ));
  39.         sShell.addShellListener( new  org.eclipse.swt.events.ShellAdapter() {
  40.              public   void  shellClosed(org.eclipse.swt.events.ShellEvent e) {
  41.                 sShell.dispose();  //  TODO Auto-generated Event stub shellClosed()
  42.             }
  43.         });
  44.         lbUserName  =   new  Label(sShell, SWT.NONE);
  45.         lbUserName.setText( " 用户名: " );
  46.         txtUserName  =   new  Text(sShell, SWT.BORDER);
  47.         txtUserName.setLayoutData(gridData);
  48.         lbPassword  =   new  Label(sShell, SWT.NONE);
  49.         lbPassword.setText( " 密码: " );
  50.         txtPassword  =   new  Text(sShell, SWT.BORDER);
  51.         Label filler1  =   new  Label(getSShell(), SWT.NONE);
  52.         btnOK  =   new  Button(getSShell(), SWT.NONE);
  53.         btnOK.setText( " 确定 " );
  54.         btnCancel  =   new  Button(getSShell(), SWT.NONE);
  55.         btnCancel.setText( " 取消 " );
  56.     }

  57. }
复制代码



在上面的代码中,我们需要自己设计 OK按钮和Cancel按钮,需要自己处理事件,需要模式对话框的时候,还需要自己构建事件循环。

其实,我们可以使用JFace提供的Dialog基类来简化我们的开发,使用JFace的Dialog类时,只需要重写几个方法就行了,重写 createDialogArea来添加控件,而且不需要我们自己设计OK按钮和Cancel按钮,重写okPressed来处理事件。所以,我写了了另 外一个LoginDialog2类,其代码如下:

  1. package opengsp;

  2. import org.eclipse.jface.dialogs.Dialog;
  3. import org.eclipse.jface.window.IShellProvider;
  4. import org.eclipse.swt.SWT;
  5. import org.eclipse.swt.widgets.Composite;
  6. import org.eclipse.swt.widgets.Control;
  7. import org.eclipse.swt.widgets.Label;
  8. import org.eclipse.swt.widgets.Shell;
  9. import org.eclipse.swt.widgets.Text;

  10. public class LoginDialog2 extends Dialog {
  11.     private Label lbUserName = null;
  12.     private Text txtUserName = null;
  13.     private Label lbPassword = null;
  14.     private Text txtPassword = null;

  15.     public LoginDialog2(IShellProvider parentShell) {
  16.         super(parentShell);
  17.         // TODO Auto-generated constructor stub
  18.     }

  19.     public LoginDialog2(Shell parentShell) {
  20.         super(parentShell);
  21.         // TODO Auto-generated constructor stub
  22.     }

  23.     @Override
  24.     protected Control createDialogArea(Composite parent) {
  25.         // TODO Auto-generated method stub
  26.         Composite composite = (Composite) super.createDialogArea(parent);
  27.         lbUserName = new Label(composite, SWT.NONE);
  28.         lbUserName.setText("用户名:");
  29.         txtUserName = new Text(composite, SWT.BORDER);
  30.         lbPassword = new Label(composite, SWT.NONE);
  31.         lbPassword.setText("密码:");
  32.         txtPassword = new Text(composite, SWT.BORDER);
  33.         return composite;
  34.     }

  35. }
复制代码



而这个时候,在Application类中的代码如下:

  1. public Object start(IApplicationContext context) {
  2.         Display display = PlatformUI.createDisplay();
  3.         
  4.         //下面是我的代码
  5.         LoginDialog2 loginDialog = new LoginDialog2(new Shell());
  6.         loginDialog.open();
  7.         //我的代码结束
  8.         
  9.         try {
  10.             int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
  11.             if (returnCode == PlatformUI.RETURN_RESTART) {
  12.                 return IApplication.EXIT_RESTART;
  13.             }
  14.             return IApplication.EXIT_OK;
  15.         } finally {
  16.             display.dispose();
  17.         }
  18.     }
复制代码



最后说一点,关于可是化编辑环境 的。VE是在是太不能让人满意了,根本没有办法对Dialog进行可视化编辑,而且用于Eclispe 3.4的VE 1.4非官方版也不能对ViewPart进行编辑。建议打击使用SWT Designer。

还有,我觉得不应该把这个对话框插入到Application类中,而是应该放到ApplicationWorkbenchWindowAdvisor类的preWindowOpen方法中。

你可能感兴趣的:(eclipse,null,application,dialog,Constructor,SWT)