分享

Eclipse RCP 开发:从登录对话框说开去 - 海边沫沫 - BlogJava

 gyb98 2010-12-08

以前我写过一系列的关于Eclipse RCP编程的文章,内容包含了从RCP入门到高级的OpenGL编程。而且我坚信,RCP编程会越来越流行。如果大家需要从头了解RCP,可以看我以前的文章:
使用Eclipse RCP进行桌面程序开发(一):快速起步
使用Eclipse RCP进行桌面程序开发(二):菜单、工具栏和对话框
使用Eclipse RCP进行桌面程序开发(三):视图和透视图
使用Eclipse RCP进行桌面程序开发(四):在Windows中使用Active X控件
使用Eclipse RCP进行桌面程序开发(五):2D绘图
使用Eclipse RCP进行桌面程序开发(六):向OpenGL进军

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

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

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

public  Object start(IApplicationContext context) {
        Display display 
=  PlatformUI.createDisplay();
        
        
// 下面是我的代码
        LoginDialog loginDialog  =   new  LoginDialog();
        
// 我的代码结束
        
        
try  {
            
int  returnCode  =  PlatformUI.createAndRunWorkbench(display,  new  ApplicationWorkbenchAdvisor());
            
if  (returnCode  ==  PlatformUI.RETURN_RESTART) {
                
return  IApplication.EXIT_RESTART;
            }
            
return  IApplication.EXIT_OK;
        } 
finally  {
            display.dispose();
        }
    }

我的本意是只弹出登录对话框,登录对话框关闭后才出现工作台窗口,可是事实证明,即使登录等话框不关闭,也不会影响工作台窗口的创建,效果如下图:
001.jpg

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

public  Object start(IApplicationContext context) {
        Display display 
=  PlatformUI.createDisplay();
        
        
// 下面是我的代码
        LoginDialog loginDialog  =   new  LoginDialog();
        
while ( ! loginDialog.getSShell().isDisposed()){
            
try {
                Thread.sleep(
100 );
            }
catch (Exception e){
                
            }
        }
        
// 我的代码结束
        
        
try  {
            
int  returnCode  =  PlatformUI.createAndRunWorkbench(display,  new  ApplicationWorkbenchAdvisor());
            
if  (returnCode  ==  PlatformUI.RETURN_RESTART) {
                
return  IApplication.EXIT_RESTART;
            }
            
return  IApplication.EXIT_OK;
        } 
finally  {
            display.dispose();
        }
    }

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

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

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

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

所以,正确的代码如下:

public  Object start(IApplicationContext context) {
        Display display 
=  PlatformUI.createDisplay();
        
        
// 下面是我的代码
        LoginDialog loginDialog  =   new  LoginDialog();
        
while ( ! loginDialog.getSShell().isDisposed()){
            
if  ( ! display.readAndDispatch ())
                display.sleep ();
        }
        
// 我的代码结束
        
        
try  {
            
int  returnCode  =  PlatformUI.createAndRunWorkbench(display,  new  ApplicationWorkbenchAdvisor());
            
if  (returnCode  ==  PlatformUI.RETURN_RESTART) {
                
return  IApplication.EXIT_RESTART;
            }
            
return  IApplication.EXIT_OK;
        } 
finally  {
            display.dispose();
        }
    }

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

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

package  opengsp;

import  org.eclipse.swt.layout.GridLayout;
import  org.eclipse.swt.graphics.Point;
import  org.eclipse.swt.widgets.Shell;
import  org.eclipse.swt.widgets.Label;
import  org.eclipse.swt.SWT;
import  org.eclipse.swt.widgets.Text;
import  org.eclipse.swt.widgets.Button;
import  org.eclipse.swt.layout.GridData;

public   class  LoginDialog {

    
private  Shell sShell  =   null ;  
    
private  Label lbUserName  =   null ;
    
private  Text txtUserName  =   null ;
    
private  Label lbPassword  =   null ;
    
private  Text txtPassword  =   null ;
    
private  Button btnOK  =   null ;
    
private  Button btnCancel  =   null ;

    
public  LoginDialog() {
        
//  TODO Auto-generated constructor stub
        createSShell();
        sShell.open();
    }

    
public  Shell getSShell() {
        
return  sShell;
    }

    
/**
     * This method initializes sShell
     
*/
    
private   void  createSShell() {
        sShell 
=   new  Shell();
        
        GridData gridData 
=   new  GridData();
        gridData.horizontalSpan 
=   2 ;
        GridLayout gridLayout 
=   new  GridLayout();
        gridLayout.numColumns 
=   3 ;    
        sShell.setText(
" 用户登录 " );
        sShell.setLayout(gridLayout);
        sShell.setSize(
new  Point( 300 200 ));
        sShell.addShellListener(
new  org.eclipse.swt.events.ShellAdapter() {
            
public   void  shellClosed(org.eclipse.swt.events.ShellEvent e) {
                sShell.dispose(); 
//  TODO Auto-generated Event stub shellClosed()
            }
        });
        lbUserName 
=   new  Label(sShell, SWT.NONE);
        lbUserName.setText(
" 用户名: " );
        txtUserName 
=   new  Text(sShell, SWT.BORDER);
        txtUserName.setLayoutData(gridData);
        lbPassword 
=   new  Label(sShell, SWT.NONE);
        lbPassword.setText(
" 密码: " );
        txtPassword 
=   new  Text(sShell, SWT.BORDER);
        Label filler1 
=   new  Label(getSShell(), SWT.NONE);
        btnOK 
=   new  Button(getSShell(), SWT.NONE);
        btnOK.setText(
" 确定 " );
        btnCancel 
=   new  Button(getSShell(), SWT.NONE);
        btnCancel.setText(
" 取消 " );
    }

}

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

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

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class LoginDialog2 extends Dialog {
    
private Label lbUserName = null;
    
private Text txtUserName = null;
    
private Label lbPassword = null;
    
private Text txtPassword = null;

    
public LoginDialog2(IShellProvider parentShell) {
        
super(parentShell);
        
// TODO Auto-generated constructor stub
    }

    
public LoginDialog2(Shell parentShell) {
        
super(parentShell);
        
// TODO Auto-generated constructor stub
    }

    @Override
    
protected Control createDialogArea(Composite parent) {
        
// TODO Auto-generated method stub
        Composite composite = (Composite) super.createDialogArea(parent);
        lbUserName 
= new Label(composite, SWT.NONE);
        lbUserName.setText(
"用户名:");
        txtUserName 
= new Text(composite, SWT.BORDER);
        lbPassword 
= new Label(composite, SWT.NONE);
        lbPassword.setText(
"密码:");
        txtPassword 
= new Text(composite, SWT.BORDER);
        
return composite;
    }

}

而这个时候,在Application类中的代码如下:
public Object start(IApplicationContext context) {
        Display display 
= PlatformUI.createDisplay();
        
        
//下面是我的代码
        LoginDialog2 loginDialog = new LoginDialog2(new Shell());
        loginDialog.open();
        
//我的代码结束
        
        
try {
            
int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
            
if (returnCode == PlatformUI.RETURN_RESTART) {
                
return IApplication.EXIT_RESTART;
            }
            
return IApplication.EXIT_OK;
        } 
finally {
            display.dispose();
        }
    }

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

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

评论

# re: 谈一谈 Eclipse RCP 中的对话框  回复  更多评论   

2009-05-20 12:24 by 阳衡锋
// 下面是我的代码
LoginDialog loginDialog = new LoginDialog();
while ( ! loginDialog.getSShell().isDisposed()){
if ( ! display.readAndDispatch ())
display.sleep ();
}
// 我的代码结束
写一个 模态框都这么麻烦。在我看来,用java做c/s麻烦是真的。不如用别的平台了。

# re: 谈一谈 Eclipse RCP 中的对话框  回复  更多评论   

2009-05-20 12:39 by 阳衡锋
不过lz写的这个系列确实写的很好。

# re: 谈一谈 Eclipse RCP 中的对话框  回复  更多评论   

2009-05-20 13:14 by 隔叶黄莺
@阳衡锋
这样写法比起 Windows SDK 写窗口程序还是简单些。

# re: Eclipse RCP 开发:从登录对话框说开去  回复  更多评论   

2010-01-25 12:09 by 初学
修改了一下LZ的程序,JFACE的DLG返回结果没有起到作用,不知道改成这样对不对,还请lZ指教1、2.
Display display = PlatformUI.createDisplay();

UserLogin userdlg=new UserLogin(new Shell());
try
{
int rescode=userdlg.open();
if (rescode!=IDialogConstants.OK_ID) return IApplication.EXIT_OK;
else {
try {

int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
if (returnCode == PlatformUI.RETURN_RESTART) {
return IApplication.EXIT_RESTART;
}
return IApplication.EXIT_OK;
} finally
{
display.dispose();
}
}
}
finally
{
display.dispose();
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多