- 浏览: 150101 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
EclipseEye:
<div class="quote_title ...
SWT/JFace专题 --- SWT中Display和多线程 -
fair_jm:
不错 蛮详细的 谢谢分享
SWT/JFace专题 --- SWT中Display和多线程
Display
1.Display 的创建
一个SWT程序至少需要一个Display对象,创建Display对象的线程被称为UI线程,一个UI线程只有唯一的display对象(如果需要,可以在其他线程中创建)。
Display.getDefault()方法首次调用时会创建Display实例,随后再次调用这个方法可获取已创建的实例。
Display.getCurrent()方法可以获得当前线程对应的Display实例;
Display.findDisplay(Thread)则可以找到线程对应的Display对象;
在并发多线程的程序中推荐如下的代码来管理Display实例:
2.Display的事件循环队列和事件循环
创建一个窗口后并打开后,就进入了事件循环部分(Event Loop)
下面看一下readAndDispatch()的实现:
其中调用OS类的PeekMessage()方法,从系统队列中取出一条消息
实现如下:
在调用系统API转发消息之前,SWT还使用filterMessage方法对消息事件做了过滤。在通常情况下,发送到窗口中控件的事件都会交由窗口的消息处理函数统一处理。如果事件是发送到某一个特定控件(如按钮,菜单等),这个方法允许过滤掉这个事件而不发送到主窗口。如果在菜单和他的窗口中为同一个快捷键设置了不同的功能,打开菜单式,快捷键消息就会被菜单捕捉下来不发送到窗口。
SWT的事件循环中,方法runDeferredEvents()和runAsyncMessages()负责处理Display的自定义事件队列中的事件。
现在可以整理出Display.readAndDispathch的流程:
首先冲系统事件队列中读取消息,如果在程序的事件队列中读到事件,就将它发送到窗口去处理;如果在线程交互的事件队列中有需要执行的事件,就去执行它。如果readAndDispatch
返回false,事件循环就会调用Display的sleep方法。sleep方法调用了OS.WaitMessage,
WaitMessage会使当前线程(UI线程)休眠,这样就可以将处理器交给别的线程使用。当事件队列中有新的事件传来时,UI线程会被唤醒并恢复事件循环过程。
3.监视器(Monitor)、边界(Bounds)和客户区域(ClientArea)
使用Display.getMonitor()方法可以取得与这个Display相连的所有监视器的信息,而Display.getPrimaryMonitor()可以得到主监视器对象。监视器包括两个部分:Bounds和ClientArea。
边界代表了这个监视器的屏幕的大小,而客户区域则代表可以用来显示窗口的区域的大小。
Monitor monitor= disp.getPrimaryMonitor();
monitor.getClientArea();
monitor.getBounds();
4.SWT程序中的多线程
SWT采用单线程模型管理绘图操作,只有UI线程才能进行控件重绘和处理事件循环等直接访问Display的操作,非UI线程试图直接操作Display会抛出一个SWT异常。
Display维护着一个自定义的事件队列,这个队列就是用来供后台线程和UI线程同步的。后台线程用Runnable对象将绘图操作包装起来,然后将对象插入到事件队列中,这样Display执行信息循环时就会执行这些操作。
Display提供了如下两个方法向队列中插入事件:
1)Display.syncExec(Runnable runnable)
同步调用。调用这个方法会通知UI线程在下一次事件循环时执行runnable参数的run方法。调用这个方法的线程将被阻塞到runnable执行完为止。如果参数是null,调用这个方法会唤醒休眠中的UI线程。
2)Display.asyncExec(Runnable runnable)
异步调用。调用这个方法同样通知UI线程在下一次事件循环时执行runnable参数的run方法。调用这个方法的线程不会阻塞,而且在runnable执行完成后不会得到通知。如果参数是null,调用这个方法会唤醒休眠中的UI线程。
可以看出syncExec/asyncExec 都是通过Synchronizer 这个类实现的.
下面看一下
----------
5.其他
---------------
使用系统托盘
SWT允许Java程序像本地程序一样直接访问系统托盘。在SWT中,系统托盘资源由Tray类型管理,调用Display.getSystemTray方法,可以得到唯一的Tray实例并操作它,如果所在的平台上没有系统托盘,就返回null。
得到Tray后,就可以创建TrayItem向系统托盘添加一个Item,并设置图标和提示信息,例子代码如下:
用Region构造不规则窗口
Region类型代表的是平面坐标系上的由任意个多边形组成的一个区域。
... ...
Region region=new Region(display);
region.add(new Rectangle(10,10,10,100));
region.add(new Rectangle(10,100,10,10));
region.add(new Rectangle(10,10,100,10));
region.add(new Rectangle(100,10,10,100));
... ...
1.Display 的创建
一个SWT程序至少需要一个Display对象,创建Display对象的线程被称为UI线程,一个UI线程只有唯一的display对象(如果需要,可以在其他线程中创建)。
Display.getDefault()方法首次调用时会创建Display实例,随后再次调用这个方法可获取已创建的实例。
Display.getCurrent()方法可以获得当前线程对应的Display实例;
Display.findDisplay(Thread)则可以找到线程对应的Display对象;
在并发多线程的程序中推荐如下的代码来管理Display实例:
public static Display getThreadDisplay(){ return Display.getCurrent()==null?new Display():Display.getCurrent(); }
2.Display的事件循环队列和事件循环
创建一个窗口后并打开后,就进入了事件循环部分(Event Loop)
while(!shell.isDisposed()){ if(!display.readAndDispatch()){ display.sleep(); } }常见的图形操作系统会为每一个GUI程序分配一个事件队列,用户操作鼠标和键盘时,操作系统负责将这些事件放到对应程序的事件队列中。但同时,程序可能希望在事件循环中处理一些自定义事件。为此,Display额外维护了一个应用程序级别的事件队列,自定义的事件可以被添加到这个队列中。在Display的事件循环中,同时处理着系统队列和这个自定义队列中的事件。
下面看一下readAndDispatch()的实现:
public boolean readAndDispatch () { checkDevice (); lpStartupInfo = null; drawMenuBars (); runSkin (); runDeferredLayouts (); runPopups (); if (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) {//1.从事件队列中取出消息事件 if (!filterMessage (msg)) { //2.对消息事件过滤 OS.TranslateMessage (msg); //3.翻译msg OS.DispatchMessage (msg); //4.发送到窗口处理 } //5.runDeferredEvents()和runAsyncMessages()处理自定义队列中的事件 runDeferredEvents (); return true; } return isDisposed () || (runMessages && runAsyncMessages (false)); }这里调用了很多org.eclipse.swt.internal.win32.OS类的方法。OS是SWT用来包装操作系统API的类型,它的方法都是JNI接口,而且与操作系统的API一一对应。
其中调用OS类的PeekMessage()方法,从系统队列中取出一条消息
实现如下:
public static final boolean PeekMessage (MSG lpMsg, int /*long*/ hWnd, int wMsgFilterMin, int wMsgFilterMax, int wRemoveMsg) { if (IsUnicode) return PeekMessageW (lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); return PeekMessageA (lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); }调用PeekMessageW (),PeekMessageA():就是native声明的JNI接口
public static final native boolean PeekMessageW (MSG lpMsg, int /*long*/ hWnd, int wMsgFilterMin, int wMsgFilterMax, int wRemoveMsg); public static final native boolean PeekMessageA (MSG lpMsg, int /*long*/ hWnd, int wMsgFilterMin, int wMsgFilterMax, int wRemoveMsg);
在调用系统API转发消息之前,SWT还使用filterMessage方法对消息事件做了过滤。在通常情况下,发送到窗口中控件的事件都会交由窗口的消息处理函数统一处理。如果事件是发送到某一个特定控件(如按钮,菜单等),这个方法允许过滤掉这个事件而不发送到主窗口。如果在菜单和他的窗口中为同一个快捷键设置了不同的功能,打开菜单式,快捷键消息就会被菜单捕捉下来不发送到窗口。
SWT的事件循环中,方法runDeferredEvents()和runAsyncMessages()负责处理Display的自定义事件队列中的事件。
boolean runDeferredEvents () { boolean run = false; /* * eventQueue就是Display维护的事件循环队列,由于在一个UI线程中只存在一个Display对象, *只有一个display可以操作这个eventQueue,所以这里不需要做同步处理。 */ while (eventQueue != null) { /* 取出队列中的event事件 */ Event event = eventQueue [0]; if (event == null) break; int length = eventQueue.length; System.arraycopy (eventQueue, 1, eventQueue, 0, --length); eventQueue [length] = null; /* 把event事件发到对应的控件去处理 */ Widget widget = event.widget; if (widget != null && !widget.isDisposed ()) { Widget item = event.item; if (item == null || !item.isDisposed ()) { run = true; widget.sendEvent (event); } } } /* 设为null,便于jvm及时回收对象 */ eventQueue = null; return run; }
/* Synchronizer 是Display中实现非ui线程和ui线程同步操作的关键实现类(下文有该类的详细说明)*/ Synchronizer synchronizer = new Synchronizer (this);
boolean runAsyncMessages (boolean all) { return synchronizer.runAsyncMessages (all); }
现在可以整理出Display.readAndDispathch的流程:
首先冲系统事件队列中读取消息,如果在程序的事件队列中读到事件,就将它发送到窗口去处理;如果在线程交互的事件队列中有需要执行的事件,就去执行它。如果readAndDispatch
返回false,事件循环就会调用Display的sleep方法。sleep方法调用了OS.WaitMessage,
WaitMessage会使当前线程(UI线程)休眠,这样就可以将处理器交给别的线程使用。当事件队列中有新的事件传来时,UI线程会被唤醒并恢复事件循环过程。
public boolean sleep () { checkDevice (); if (runMessages && getMessageCount () != 0) return true; if (OS.IsWinCE) { OS.MsgWaitForMultipleObjectsEx (0, 0, OS.INFINITE, OS.QS_ALLINPUT, OS.MWMO_INPUTAVAILABLE); return true; } return OS.WaitMessage (); }
3.监视器(Monitor)、边界(Bounds)和客户区域(ClientArea)
使用Display.getMonitor()方法可以取得与这个Display相连的所有监视器的信息,而Display.getPrimaryMonitor()可以得到主监视器对象。监视器包括两个部分:Bounds和ClientArea。
边界代表了这个监视器的屏幕的大小,而客户区域则代表可以用来显示窗口的区域的大小。
Monitor monitor= disp.getPrimaryMonitor();
monitor.getClientArea();
monitor.getBounds();
4.SWT程序中的多线程
SWT采用单线程模型管理绘图操作,只有UI线程才能进行控件重绘和处理事件循环等直接访问Display的操作,非UI线程试图直接操作Display会抛出一个SWT异常。
Display维护着一个自定义的事件队列,这个队列就是用来供后台线程和UI线程同步的。后台线程用Runnable对象将绘图操作包装起来,然后将对象插入到事件队列中,这样Display执行信息循环时就会执行这些操作。
Display提供了如下两个方法向队列中插入事件:
1)Display.syncExec(Runnable runnable)
同步调用。调用这个方法会通知UI线程在下一次事件循环时执行runnable参数的run方法。调用这个方法的线程将被阻塞到runnable执行完为止。如果参数是null,调用这个方法会唤醒休眠中的UI线程。
public void syncExec (Runnable runnable) { Synchronizer synchronizer; synchronized (Device.class) { if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); synchronizer = this.synchronizer; } synchronizer.syncExec (runnable); }
2)Display.asyncExec(Runnable runnable)
异步调用。调用这个方法同样通知UI线程在下一次事件循环时执行runnable参数的run方法。调用这个方法的线程不会阻塞,而且在runnable执行完成后不会得到通知。如果参数是null,调用这个方法会唤醒休眠中的UI线程。
public void asyncExec (Runnable runnable) { synchronized (Device.class) { if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED); synchronizer.asyncExec (runnable); } }
可以看出syncExec/asyncExec 都是通过Synchronizer 这个类实现的.
下面看一下
/** * Synchronizer 为Display的消息队列的处理提供了同步的支持 */ public class Synchronizer { Display display;//需要控制的display对象 int messageCount;//当前消息数量 RunnableLock [] messages;//Runnable对象数组,存放着要处理的消息事件 Object messageLock = new Object ();//同步时的一个参照对象(一个事件只能被一个线程拥有) Thread syncThread;//在runAsyncMessages()异步消息处理时会用到(没看明白它有什么作用) static final int GROW_SIZE = 4;//messages数组的增量 static final int MESSAGE_LIMIT = 64;//messages数组的最大容量 //TEMPORARY CODE static final boolean IS_CARBON = "carbon".equals (SWT.getPlatform ()); static final boolean IS_COCOA = "cocoa".equals (SWT.getPlatform ()); static final boolean IS_GTK = "gtk".equals (SWT.getPlatform ()); /** * Constructs a new instance of this class. */ public Synchronizer (Display display) { this.display = display; } /** * 把runnable消息对象添加到messages数组中,等待UI线程处理 */ void addLast (RunnableLock lock) { boolean wake = false; synchronized (messageLock) { if (messages == null) messages = new RunnableLock [GROW_SIZE];//初始容量为GROW_SIZE if (messageCount == messages.length) {//当message个数和数组长度相同时,就扩容 RunnableLock[] newMessages = new RunnableLock [messageCount + GROW_SIZE]; System.arraycopy (messages, 0, newMessages, 0, messageCount); messages = newMessages; } messages [messageCount++] = lock;//添加runnable对象 //当message对象从无到有时,也就是刚刚有一个进来,就该唤醒UI线程了 //如果大于1个,所有UI线程已经在活动状态,就无效唤醒了 wake = messageCount == 1; } if (wake) display.wakeThread (); } /** * *异步调用。调用这个方法同样通知UI线程在下一次事件循环时执行runnable参数的run *方法。调用这个方法的线程不会阻塞,而且在runnable执行完成后不会得到通知。如果参 * 数是null,调用这个方法会唤醒休眠中的UI线程。 */ protected void asyncExec (Runnable runnable) { if (runnable == null) { //TEMPORARY CODE if (!(IS_CARBON || IS_GTK || IS_COCOA)) { display.wake (); return; } } addLast (new RunnableLock (runnable)); } int getMessageCount () { synchronized (messageLock) { return messageCount; } } void releaseSynchronizer () { display = null; messages = null; messageLock = null; syncThread = null; } RunnableLock removeFirst () { synchronized (messageLock) { if (messageCount == 0) return null; RunnableLock lock = messages [0]; System.arraycopy (messages, 1, messages, 0, --messageCount); messages [messageCount] = null; if (messageCount == 0) { if (messages.length > MESSAGE_LIMIT) messages = null; } return lock; } } boolean runAsyncMessages () { return runAsyncMessages (false); } /** *运行message数组中runnable对象的run方法对消息事件进行异步处理 */ boolean runAsyncMessages (boolean all) { boolean run = false; do { RunnableLock lock = removeFirst (); if (lock == null) return run; run = true; synchronized (lock) {//同步runnable对象,表示该runnable对象仅能被一个线程处理 syncThread = lock.thread; try { lock.run (); } catch (Throwable t) { lock.throwable = t; SWT.error (SWT.ERROR_FAILED_EXEC, t); } finally { syncThread = null; lock.notifyAll (); } } } while (all); return run; } /** *同步调用。调用这个方法会通知UI线程在下一次事件循环时执行runnable参数的run方 *法。调用这个方法的线程将被阻塞到runnable执行完为止。如果参数是null,调用这个 *方法会唤醒休眠中的UI线程。 */ protected void syncExec (Runnable runnable) { RunnableLock lock = null; synchronized (Device.class) { if (display == null || display.isDisposed ()) SWT.error (SWT.ERROR_DEVICE_DISPOSED); if (!display.isValidThread ()) { if (runnable == null) { display.wake (); return; } lock = new RunnableLock (runnable); /* * Only remember the syncThread for syncExec. */ lock.thread = Thread.currentThread(); addLast (lock); } } if (lock == null) { if (runnable != null) runnable.run (); return; } synchronized (lock) { boolean interrupted = false; while (!lock.done ()) { try { lock.wait (); } catch (InterruptedException e) { interrupted = true; } } if (interrupted) { Compatibility.interrupt(); } if (lock.throwable != null) { SWT.error (SWT.ERROR_FAILED_EXEC, lock.throwable); } } } }其中RunnableLock类的实现
/** * Instances of this class are used to ensure that an * application cannot interfere with the locking mechanism * used to implement asynchronous and synchronous communication * between widgets and background threads. */ class RunnableLock { Runnable runnable; Thread thread; Throwable throwable; RunnableLock (Runnable runnable) { this.runnable = runnable; } boolean done () { return runnable == null || throwable != null; } void run () { if (runnable != null) runnable.run (); runnable = null; } }
----------
5.其他
---------------
使用系统托盘
SWT允许Java程序像本地程序一样直接访问系统托盘。在SWT中,系统托盘资源由Tray类型管理,调用Display.getSystemTray方法,可以得到唯一的Tray实例并操作它,如果所在的平台上没有系统托盘,就返回null。
得到Tray后,就可以创建TrayItem向系统托盘添加一个Item,并设置图标和提示信息,例子代码如下:
public static void main(String[] args){ Display display=Display.getDefault(); Shell shell= new Shell(display,SWT.NO_TRIM);//SWT.NO_TRIM表示不带边框 shell.setSize(120,80); shell.open(); Tray systemTray=display.getSystemTray(); TrayItem newItem=new TrayItem(systemTray,SWT.NONE); newItem.setImage(...); newItem.setToolTipText("Text Tray!"); while(!shell.isDisposed()){ if(!display.readAndDispatch()){ display.sleep(); } } newItem.dispose(); display.dispose(); }-----------
用Region构造不规则窗口
Region类型代表的是平面坐标系上的由任意个多边形组成的一个区域。
... ...
Region region=new Region(display);
region.add(new Rectangle(10,10,10,100));
region.add(new Rectangle(10,100,10,10));
region.add(new Rectangle(10,10,100,10));
region.add(new Rectangle(100,10,10,100));
... ...
发表评论
-
再说SWT中的滚动面板ScrolledComposite实现
2013-06-19 15:43 2339记得以前写过一篇关于滚动面板的文章 SWT中 Scrolle ... -
OSGi参考资料
2013-04-18 01:11 688基于 OSGi 的面向服务的组件编程 探索 OSGi 框架的组 ... -
CDT(编辑、调试)参考资料
2013-04-17 02:15 1119CDT编辑器 --------- 构建基于 CDT 的编辑器, ... -
Workspace Resource框架专题(3)处理工作空间资源更改事件
2013-04-17 01:44 13893 处理工作空间资源更改事件 工作空间API允许工具对它 ... -
Workspace Resource框架专题(2)workspace 框架API
2013-04-17 01:27 14962 工作空间API 本 ... -
Workspace Resource框架专题(1)Resource的概念
2013-04-17 01:12 13991 Resource的概念 如 ... -
如何恢复断点及Marker
2013-03-05 00:41 0如何恢复断点及Marker -
深入Workbench框架
2013-03-01 02:10 1749深入Workbench框架(结合UIPersistent) 1 ... -
Eclipse插件开发中的Action
2013-02-24 23:10 2015插入点用来定义菜单出 ... -
Eclipse开发中编辑器(Editors)和视图(View)总结
2013-02-24 22:58 29201.视图(Views) 视图( ... -
SWT/JFace专题 --- 对话框向导(Dialogs Wizards)
2013-02-24 22:42 2253对话框向导(Dialogs Wizar ... -
SWT/JFace专题 --- JFace
2013-02-24 22:37 1659JFace JFace是基于SWT的一套图形工具包,它没有为 ... -
SWT/JFace专题 --- SWT API 结构
2013-02-23 18:31 1085SWT API 结构 1.布局类(l ... -
Eclipse启动过程(源码级剖析)
2013-02-20 03:24 3425双击eclipse安装目录下的eclipse.exe运行后,会 ... -
SWT/JFace专题 --- SWT结构浅析
2013-02-23 17:02 1027SWT技术是一套基于Java的 ... -
Eclipse平台体系结构
2013-02-21 23:56 19181.Eclipse平台体系结构 1 ... -
RCP平台架构
2013-02-23 14:11 1494RCP 富客户端通常是指具有独立用户界面的客户端程序。富客户 ... -
Ant构建脚本相关
2013-02-18 01:26 0Ant构建脚本相关 -
CDT源码架构研究
2013-02-18 01:24 0CDT源码架构研究 -
SWT/JFace专题 --- SWT/JFace概述
2013-02-23 16:59 873SWT(Standerd Widget Toolkit,标准图 ...
相关推荐
11.5 多线程程序设计 238 11.6 本章小结 243 第12章 SWT系统资源 244 12.1 系统资源概述 244 12.1.1 什么是系统资源 244 12.1.2 释放资源的原则 245 12.1.3 访问资源的原则 246 12.1.4 何时释放资源 ...
6. **Threads**:由于Java的多线程特性,SWT也支持在后台线程中更新界面,但需注意线程安全问题。 7. **Data Binding**:数据绑定允许将控件的值与应用程序的数据模型关联起来,简化了用户界面和业务逻辑之间的交互...
在SWT中,`Display` 类管理着所有窗口和控件的显示,而 `Shell` 是顶级窗口,可以包含多个控件。`addPaintListener()` 方法用于监听控件的绘画事件,`PaintEvent` 包含了画笔对象 `gc`,通过它可以进行绘图操作。`...
`SWT线程.pdf`文件可能是详细讲解SWT中线程管理的教程,可以帮助读者深入理解如何在SWT应用中正确处理多线程。 总的来说,SWT是一个强大且灵活的GUI库,对于希望开发高性能、原生感的Java桌面应用的开发者来说,是...
在给定的代码示例中,我们看到了如何使用 SWT 和 JFace 创建并显示多个窗体(Form)以及一个对话框(Dialog)。下面将详细解释这些知识点: 1. **SWT**: SWT 是一个开源的 Java 库,它提供了一个跨平台的 API,...
首先,我们需要创建一个`Display`对象,它是SWT中用于管理GUI线程和绘制的上下文。然后创建一个`Shell`对象,它是SWT窗口的基本构建块。`Shell`的实例化通常伴随着`Display`的实例,这样它们就能一起工作。 接下来...
在`FileBrowser`类中,我们创建了一个`Display`对象,它是SWT中用于管理线程和事件的基本构造。接着,我们创建了一个`Shell`,它是SWT窗口的基础。`Table`对象用于展示文件和目录,我们可以设置它的样式(例如边框,...
在编程时,Display 是 SWT 应用程序的入口点,它负责事件循环和线程间的通信。Shell 是 SWT 界面中的窗口对象,可以有多种样式,如普通窗口或对话框。Widgets 是 SWT 中的具体控件,如按钮、文本框等,它们通过 ...
1. GUI线程:在SWT中,所有对GUI的操作必须在事件调度线程(Event Dispatch Thread,EDT)中进行,以保证界面的同步更新和避免数据竞争。 2. 回调机制:使用`Display.asyncExec`或`Display.syncExec`方法来确保在...
在SWT(Standard Widget Toolkit)和JFace这两个Java图形用户界面库中,布局管理是非常关键的一部分,用于控制控件在窗口中的排列方式。本篇主要关注的是`RowLayout`,这是一种更加灵活且功能强大的布局方式,相比`...
Swt API中的`Display`类是整个GUI的主要入口点,负责管理窗口和线程。`Shell`类代表一个顶级窗口,所有控件都必须包含在一个Shell内。开发者需要注意及时关闭和释放资源,防止内存泄漏。 **7. 文件对话框** Swt...
8. **线程与异步编程**:由于GUI操作通常需要在事件循环中执行,教程会介绍如何正确处理线程,避免阻塞用户界面,以及利用Display和Job类进行异步任务处理。 9. **示例代码**:教程中应包含丰富的代码示例,帮助...
8. **Threads**: SWT是线程安全的,但通常推荐在UI线程(即Display的线程)中执行对小部件的操作,以避免线程同步问题。 **SWT与AWT/Swing的区别**: SWT与Java标准库中的AWT(Abstract Window Toolkit)和Swing相比...
SWT支持多语言环境,开发者可以使用ResourceBundle和MessageBundle来实现应用程序的本地化。 10. **实例分析**: 博文可能包含了一些简单的SWT应用实例,比如创建一个带有“Hello, SWT!”按钮的基本窗口,通过...
12. **线程管理**:在GUI应用程序中,正确地管理线程以避免阻塞UI是至关重要的,可能会涉及到Display和Job类的使用。 通过对这些知识点的学习和实践,开发者可以更深入地理解并掌握Eclipse SWT和JFace的使用,从而...
- **Display**是Eclipse中用于管理GUI线程的类,负责处理所有与GUI相关的操作,如事件分发等。 - **Shell**是应用程序的顶级容器,相当于窗口。一个应用程序至少包含一个Shell实例。 **3.2 控件** - SWT提供了丰富...