IDL入门教程十(组件程序) 下载本文

图84:一个简单的组件程序。组件A-G已经定义。与顶级base相关联着一个事件处理程序(填充圆所示)。如果组件G产生一个事件,事件就会上浮至与组件A相关联的事

件处理程序。

然后事件结构就这样填写。ID字段标识产生事件的组件,即组件G。Top字段标识组件G所在的层次结构中的顶级组件,即组件A。Handler字段标识与处理来自组件G事件的事件处理程序相关联的组件,仍然是组件A。

如图85所示,看看一个稍微复杂的组件程序。这里有两个事件处理程序:一个与组件A相关联,一个与组件F相关联。如果组件G产生一个事件,那么事件就“上浮”至与组件F相关联的事件处理程序。这时,事件结构的Handler字段标识就是组件F而不是组件A了。

当组件G产生的事件进入到与组件F相关联的事件处理程序时,产生什么结果就与组件F的事件处理程序有关了。

事件处理函数

事件处理模块可以是过程,也可以是函数。如果与组件F相关联的事件处理模块是个过程,那么事件会被那个事件处理程序“吞掉”。也就是说,事件处理后就被终止。然后程序就等待下一个事件的发生。但是假设,事件处理程序是个函数,而不是过程。函数,回想一下,是要返回值的。如果事件处理函数的返回值是个结构,且这个结构中有三个已经定义的长整型字段ID、Top和Handler,那么IDL将把返回值作为一个事件结构上浮至层次结构图中的下一个事件处理程序。如果返回值不符合事件结构的标准,那么这个事件也称为被事件处理函数吞掉了。

图85:一个带有两个事件处理模块的、稍微复杂些的组件程序,产生于组件G的事件

由与组件F相关联的事件处理程序来处理。

因而有选择性地返回事件或处理事件及创建自己的事件是有可能的。例如,假设想将一个绘图组件用起来像一个按钮,就应该将绘图组件事件打包成一个按钮事件,然后将事件结构传递给层次结构中的下一个事件处理程序。

伪按钮事件的事件处理程序的结束或许会像如下所示:

Event={PSEUDO_BUTTON_EVENT,ID:Event.handler,TOP:Event.Top,$

Handler:0L,Select:1L} Return End

注意,Handler字段由长整型零填充,一个无效的组件标识符。IDL将获取这个返回值,检查到ID、Top和Handler字段为合适的长整型,并在Handler字段中填入适合的值。因而不必在最初时为查找一个适合Handler而担忧。最为重要的是,为字段ID和Top找到合适的组件标识符。例子里显示的值非常具有代表性。

将事件处理程序和组件联系起来

如何才能将事件处理模块和特定的组件联系起来呢?最简单的方法就是,在组件创建时,用关键字Event_Pro(如果事件处理模块是一个过程)或Event_Func(如果事件处理模块是个函数)来指定适当的事件处理模块。

例如,为quit按钮创建一个单独的事件处理过程,可以在组件定义模块中按钮创建程序中做如下修改:

QuitID=Widget_Button(fileID, Value=’Quit’, Event_Pro=’XimageBar_Quit’) 最好为事件处理模块取个独一无二的名字。作者喜欢以组件定义模块名作为事件处理模块的开端,这样就不会混淆。注意,名字不区分大小写。

一个组件除外,Event_Pro和Event_Func关键字可用于将事件处理模块和其它任何组件联系起来,这个组件就是由Xmanager直接管理的那个组件(如,顶级base的标识符就是Xmanager命令的参数之一)。Xmanager直接管理的顶级base已经有它们的事件处理程序,它是通过Xmanager命令中的Event_Handler关键字来设定的。与顶级base相联系的事件处理程序必须是一个过程而不是函数。

如果在调用Xmanager命令时没有使用Event_Handler关键字,那么与顶级base相关联的事件处理过程就是程序所注册的名字加上_Event。例如,程序XImageBar默认的事件处理

过程就是XimageBar_Event,因为注册名是’xImageBar’(Xmanager命令后的第一个参数)。注意,事件处理程序不区分大小写。

在XImageBar程序中,最好给与顶级base相关联的事件处理程序一个比较形象的名字。如它用来改变组件程序大小的程序,可以取名为XimageBar_Resize。修改组件定义语句,见下:

Xmanager, ‘xImageBar’, tlb, /No_Block, $

Event_Handler=’XimageBar_Resize’

现在,图86所示,给组件程序增加了一个事件处理程序。

图86:在XImageBar程序示意图中,事件处理模块与特定的组件联系在一起。

编写Quit按钮的事件处理程序

编写Quit按钮的事件处理程序很简单,在本程序中,它是一个过程,其唯一的功能就是销毁所有组件并从屏幕上删除。如果销毁了顶级base,那么程序将自动地销毁该层次结构中的所有组件。

唯一的技巧是如何获得顶级base的标识符,tlb,一个局部变量。由于变量tlb是组件定义模块中的局部变量,当组件定义模块一旦执行完毕,这个变量就被销毁了。在这个模块中,可以获得等同于tlb的标识符,即事件结构中的Top字段。

所有的事件处理程序,都有一个,也只有一个定位参数。如下所示。在文件xImageBar.pro中的组件定义模块之前键入以下代码:

Pro XimageBar_Quit, Event

Widget_Control, Event.Top, /Destroy

End

注意事件结构不一定非得命名为event,它可以随意取名。最好将它取名为event,以便在组件程序代码中看到它时可以知道那是代表什么。

编写改变图形窗口大小的事件处理程序

与Quit按钮的事件处理程序相比,改变图形窗口大小的事件处理程序稍微复杂一些。想想看,如果顶级base改变大小后,应该作些什么。

1. 需要知道图形窗口改变后的大小,这个信息可以来自于顶级base的事件结构中的X

和Y字段。(base的事件结构请参阅305页的附录A:组件的事件结构) 2. 需要将绘图组件(即图形窗口)拉伸到合适的尺寸。如果知道了绘图组件的标识符,

用Widget_Control命令就能做到。在这里,绘图组件标识符就是drawID。 3. 必须将绘图组件设置为当前图形窗口。如果已经知道绘图组件的图形窗口索引号用

WSet命令即可实现这个。在本程序中,绘图组件的图形窗口索引号就是wid。 4. 必须重新绘制图形。如果知道数据参数,使用ImageBar命令即可完成。

第一个要求所需的信息将来自事件结构本身。顶级base改变大小的事件件结构如下: Event={ID: 0L, Top:0L, Handler:0L, X:0L, Y:0L}

其中,字段X和Y分别为base最后的大小,以像素为单位。

第2-4所要求的信息存于结构的信息。记住,info结构保存于IDL中的全局变量中。因而必须知道,获取该变量后,将它拷贝至事件处理程序中的一个局部变量中。Info结构保存于顶级base的用户值中,其中顶级base由事件的Top字段标识。

打开文件xImageBar.pro,并在文件的首端输入以下两行代码。第一行是事件处理过程的定义行;第二行代码将顶级base的用户值所保存的info结构拷贝至一个名为info的局部变量中。键入如下:

Pro XimageBar_Resize, Event

Widget_Control, Event.Top, Get_Uvalue=info

下一步,通过使用关键字Draw_XSize和Draw_Ysize,改变绘图组件的尺寸,以便保证它和顶级base大小一致。注意,事件结构中返回的顶级base的大小既不包括菜单栏区域,也不包括窗口标题和边框的大小。这时,不仅需要获取保存在局部变量info中的绘图组件的标识符,还需要事件结构中正确尺寸。键入如下所示:

Widget_Control, info.drawID, Draw_Xsize=Event.x,$

Draw_Ysize=Event.Y

再下一步,将绘图组件设置为当前图形窗口,以便将图形显示在窗口上。

Wset, info.wid

这个重要的步骤再怎么强调都不为过。在组件程序中,必须确保知道当前所在的图形窗口,否则,将会导致在其它组件程序窗口绘制图形的操作结束。在绘图组件窗口中绘制任何图形之前一定得调用WSet命令。

最后,可以重新显示图形了。在本程序中,设置ImageBar命令中的EraseFirst关键字非常重要,否则在改变大小后,将会看到窗口中的残留显示信息。键入如下:

ImageBarm, info.image, /EraseFirst End

改变大小的事件处理程序的最终代码如下所示:

Pro XimageBar_Resize, Event

Widget_Control, Event.Top, Get_Uvalue=info