SWT编程基础-Display和Shell

来源:互联网 发布:数据对接和系统过渡 编辑:程序博客网 时间:2024/05/17 05:06

Display和Shell

首先我们来看一段代码:

package com.gujin.swt;import org.eclipse.swt.SWT;import org.eclipse.swt.widgets.Display;import org.eclipse.swt.widgets.Label;import org.eclipse.swt.widgets.Shell;public class HelloSWT{   public static void main(String[] args)   {      Display display = Display.getDefault();      Shell shell = new Shell(display);      Label hello = new Label(shell, SWT.NONE);      hello.setBounds(10, 10, 100, 30);      hello.setText("Hello SWT");      shell.open();      shell.pack();      while (!shell.isDisposed())      {         if (!display.readAndDispatch())         {            display.sleep();         }      }      display.dispose();   }}

这是一段很简单的示例代码,我们可以将其分为三个部分:
第一部分:创建Display对象

Display display = Display.getDefault();

第二部分:创建并配置Shell

Shell shell = new Shell(display);
Label hello = new Label(shell, SWT.NONE);
hello.setBounds(10, 10, 100, 30);
hello.setText(“Hello SWT”);
shell.open();
shell.pack();

第三部分:进入一个循环

while (!shell.isDisposed())
{
    if (!display.readAndDispatch())
   {
       display.sleep();
    }
}

这三部分也是一个SWT程序都需要的三部分。

Dispaly的创建

所有的SWT程序在开始运行时都必须获取Display对象,没有Display,SWT程序就无法和操作系统交互,一个SWT程序中至少含有一个Display对象,创建Display对象的线程称为UI线程。
在SWT程序中,我们可以通过如下方式获取一个Display对象:

  • new Display:创建一个新的Display实例
  • Display.getDefault():第一次调用会创建一个Display对象,之后再次调用会获得之前创建的Display对象,需要注意的是,如果在调用之前已经通过new Display获得了Display对象,那么调用Display.getDefault()将获得之前新创建的Display对象

// 创建一个Display对象
Display create = Display.getDefault();
// 获得Display对象
Display display = Display.getDefault();
// 两次获得对象为同一个
System.out.println(create == display);

这段代码的输出结果为true,同样的下面这段代码也是一样的效果

// 创建一个Display对象
Display create = new Display();
// 获得Display对象
Display display = Display.getDefault();
// 两次获得对象为同一个
System.out.println(create == display);

一个线程中不能同时存在两个活动的Display,如果这样做的话,程序在运行时就会抛出一个SWT异常。如果确实需要创建多个同时活动的Display实例,则必须在不同的线程中创建。我们可以通过Display.getCurrent()函数来获取当前线程对应的Display实例,使用Display.findDisplay(Thread)则可以找到任意线程中的Display对象。
在多线程中,我们不应该使用Display.getDefault()来获得Display实例,这样很容易出现“非法线程访问”的异常,我们可以通过如下方法来获得Display实例:

public static Display getThreadDisplay(){   return Display.getCurrent() == null ? new Display() : Display.getCurrent();}

Shell的创建

在SWT程序中,一个Shell就代表一个窗体,在SWT程序中,为了使资源管理更方便而做了规定,当父资源释放时,它要负责释放属于自己的子资源。从Display直接创建而来的Shell是Display的子资源,而从某个Shell(Shell A)创建而来的Shell B则是Shell A的子资源。当作为父资源的Shell被释放时,作为子资源的Shell也会同时被释放,而由于所有的Shell都是由Display直接或间接创建的,因此Display是所有Shell的最高层父资源,当一个Display被释放时,所有存在于这个UI线程中的Shell窗口都会被释放掉。

Display的事件队列和事件循环

当我们创建了一个窗口并打开它后,就进入事件循环

while (!shell.isDisposed()){    if (!display.readAndDispatch())    {       display.sleep();    }}

这段代码就是用于处理事件的循环。通过这段代码我们也可以发现,SWT是通过循环不断的从事件队列中获得信息并处理,所以当我们有耗时较长的事件要处理时,很容易造成程序堵塞,用户的体验度较低,我们通过一段代码来演示一下:

package com.gujin.swt;import org.eclipse.swt.SWT;import org.eclipse.swt.events.SelectionAdapter;import org.eclipse.swt.events.SelectionEvent;import org.eclipse.swt.widgets.Button;import org.eclipse.swt.widgets.Display;import org.eclipse.swt.widgets.Label;import org.eclipse.swt.widgets.Shell;public class HelloSWT{   public static void main(String[] args)   {      Display display = Display.getDefault();      Shell shell = new Shell(display);      final Label label = new Label(shell, SWT.NONE);      label.setBounds(10, 10, 200, 30);      label.setText("before");      Button button = new Button(shell, SWT.BORDER);      button.setBounds(10, 50, 200, 30);      button.setText("deal");      button.addSelectionListener(new SelectionAdapter() {         @Override         public void widgetSelected(SelectionEvent e)         {            try            {               Thread.sleep(10000);            }            catch (Exception e2)            {            }            label.setText("after");         }      });      shell.open();      shell.pack();      while (!shell.isDisposed())      {         if (!display.readAndDispatch())         {            display.sleep();         }      }      display.dispose();   }}

运行这段代码,我们可以发现,当我们点击按钮后,窗口会堵塞无响应,直至事件处理结束,这在实际应用中肯定是不可取的,在Display中为我们提供了syncExec(Runnable r)和asyncExec(Runnable r)来向队列中插入事件,我们可以对上面的代码进行如下修改:

package com.gujin.swt;import org.eclipse.swt.SWT;import org.eclipse.swt.events.SelectionAdapter;import org.eclipse.swt.events.SelectionEvent;import org.eclipse.swt.widgets.Button;import org.eclipse.swt.widgets.Display;import org.eclipse.swt.widgets.Label;import org.eclipse.swt.widgets.Shell;public class HelloSWT{   public static void main(String[] args)   {      final Display display = Display.getDefault();      Shell shell = new Shell(display);      final Label label = new Label(shell, SWT.NONE);      label.setBounds(10, 10, 200, 30);      label.setText("before");      Button button = new Button(shell, SWT.BORDER);      button.setBounds(10, 50, 200, 30);      button.setText("deal");      button.addSelectionListener(new SelectionAdapter() {         @Override         public void widgetSelected(SelectionEvent e)         {            new Thread() {               @Override               public void run()               {                  try                  {                     Thread.sleep(10000);                  }                  catch (Exception e2)                  {                  }                  display.syncExec(new Runnable() {                     @Override                     public void run()                     {                        label.setText("after");                     }                  });               }            }.start();         }      });      shell.open();      shell.pack();      while (!shell.isDisposed())      {         if (!display.readAndDispatch())         {            display.sleep();         }      }      display.dispose();   }}

我们启动一个新的线程来处理耗时较多的逻辑,在处理完成后想时间队列中添加一个事件完成后续操作,这时候我们发现单击按钮后,窗口不会出现之前的阻塞现象,在事件处理完成后Label上的文本同样会被改变。

0 0