APP下载

wxWidgets事件处理机制

2014-03-14张晓民

电脑与电信 2014年4期
关键词:窗体跨平台静态

张 枫 张晓民

(南阳理工学院软件学院,河南 南阳 473000)

wxWidgets事件处理机制

张 枫 张晓民

(南阳理工学院软件学院,河南 南阳 473000)

wxWidgets是一个可以在多个平台上进行图形用户界面开发的C++语言框架库。如同其它所有的GUI框架一样,wxWidgets的程序控制采用事件驱动的方式。wxWidgets主要有两种方法来处理事件:一种通过事件表宏定义静态地把事件和事件处理过程绑定在一起;另外一种通过函数动态地绑定事件和事件处理过程。本文介绍了这两种事件处理方法并对其作出分析比较,说明了动态事件处理机制代码量稍大但比静态事件表机制更加灵活方便。

跨平台;事件驱动;绑定

1.引言

wxWidgets是一个开源跨平台(操作系统)的C++框架(framework)库,它可以提供丰富的图形用户界面(GUI:Graphical User Interface)应用程序接口(API:Application Programming Interface)。用户的程序开发工作在很大程度上是在进行界面设计,其工作量往往占整个开发工作的60%-70%[1],wx-Widgets通过隐藏平台相关代码,与不同系统及编译器各自对应的库相连接,用wxWidgets开发的应用程序将会呈现出最适合于那个平台的外观和感觉,增强程序的灵活性和可移植性,在很大程度上减少用户在进行界面设计时的开发工作。这种平台无关性,即"Write Once Run Everywhere"的理念,使得wxWidgets在程序开发中具有实际的意义。你不需要重新学习某个特定平台的API,而且,你的程序可能在将来很长时间仍然不会过时,因为随着计算机科技的演进,wx-Widgets也将会进行相应的演进,这样你的程序将会很方便地移植到将来的最新的操作系统以支持最新的特性而不需要学习[2,3]。WxWidgets提供了对现今几乎所有流行操作系统平台和移动开发平台的支持,但不同于java的跨平台特性。Java语言的跨平台是建立在“中间代码”的基础上的,而wxWidgets被直接翻译成机器码,从而获得速度上的优势[3]。在GUI编程方面,这意味着很大的不同。wxWidgets在世界范围内广泛应用于各个领域,用户有个体软件开发者、科研院校,以及AOL、AMD等大公司[4]。在我国,wxWidgets的研究还很少,使用者也非常有限[5]。但随着苹果的崛起,Linux操作系统的大力推广以及移动设备种类的日渐丰富,免费、开源、灵活易用的wxWidgets已经成为程序员进行跨平台软件开发的一个重要选择。本文就wxWidgets事件处理的方法进行了研究和探讨。

2.事件驱动

事件是GUI程序的重要部分,所有的GUI程序都是事件驱动的[2,6,7]。事件可以定义为程序发生了某些事情的信号。外部用户行为,如移动鼠标、点击鼠标和按下键盘等,操作系统或程序行为,如时钟,网络等也可以引发事件。事件驱动可以定义为:应用程序开始运行之后就设置一个循环等待用户或其它的事件源产生事件,一旦接收到某个事件,就将它交给某个事件函数进行处理[7]。虽然看上去不同的窗口是同时被刷新的,但实际上,绝大多数的GUI程序仍然是单线程的[2]。

事件机制是GUI程序开发的重中之重。wxWidgets事件处理系统比起通常的虚方法机制来说要稍微复杂一点,但它的一个好处是可以避免需要实现基类中所有的虚方法,因为实现所有的基类虚方法有时候是不切实际的或者是效率很低的[2]。wxWidgets的事件可以由三部分来描述:

(1)事件类型(Event Type):唯一标示事件类型的整型值,在wxWidgets中的定义如下:typedef int wxEventType。当应用程序发生不同的事情会产生不同类型的事件。

(2)事件类(Event Class):wxWidgets封装了诸如Windows里的message、GTK+的signal,将事件统一抽象成为事件类wxEvent,又从这个基类派生了很多的事件类。事件类携带了和事件相关的一些信息,从而使得不同的事件都有相应的事件类与之对应[2];不同的事件也可以使用相同的事件类。总之,不管事件是如何产生的,都可以以相同的方式来进行处理。wxWidgets能处理的事件有按钮事件、键盘事件和鼠标事件等,它们都是wxEvent的子类。

(3)事件源(Event Source):wxEvent存储了产生事件的对象和它的窗体标示符(Window Identifier)。窗体标示符是事件系统中唯一确定事件产生源(窗口)的整数。不同的窗体可能会产生相同类型的事件,通过窗体标示符可以对它们加以区分。

wxWidgets有两种主要的事件处理方法,一是静态事件表机制,二是动态事件处理方法。下文将介绍两种方法并对其进行比较分析。

3.静态事件表

wxWidgets对事件的处理是通过事件处理器(Event Handler)来完成的,每一个wxEvtHandler的派生类,例如窗体、按钮、菜单等,都会在其内部维护一个事件表,即事件哈希表(Event Hash Table),用来告诉wxWidgets事件和事件处理过程的映射关系。因此,要创建静态的事件表,首先需要一个处理事件的类,而该类必须是wxEvtHandler的派生类。wxWidgets中有各种能接收消息的控件都是wxEventHandler的派生类,表明它们具备事件处理能力[1],这也是在我们的程序中自定义的窗体类或控件类需要继承自wxFrame或者wxDialog类的原因。wxWidgets将相应的函数指针保存在事件哈希表中,当事件发生时,从这个哈希表中,找到相应的事件函数指针,然后通过事件函数指针调用函数[8]。在使用事件哈希表之前必须定义它,而每一个需要处理事件的类都需要相同的代码,因此,wxWidgets用 一 个 宏 DECLARE_EVENT_TABLE()或 wxDECLARE_EVENT_TABLE()来定义哈希表这个复杂的过程。该宏定义可以放置在窗体类定义的任何位置。同样,wxWidgets使用宏BEGIN_EVENT_TABLE和END_EVENT_TABLE或者wxBEGIN_EVENT_TABLE和wxEND_EVENT_TABLE实现一个事件表,其中BEGIN_EVENT_TABLE或者wxBEGIN_EVENT_TABLE将事件表进行了初始化,事件关连宏根据程序员提供的窗口ID,以及事件处理函数最终生成事件表项,并添加到事件表项数组中。加入事件的最后一步是声明和实现事件处理函数。

如下几个步骤和代码展示了静态事件表的使用方法:

定义一个类直接或间接继承自wxEvtHandler的类。wx-Window窗体或控件子类、wxApp都是wxEvtHandler的直接或者间接派生类;

(1)在类中为每一个要处理的事件定义一个方法作为事件处理器。事件处理方法都具有相同的形式:它们都不是虚函数,需要一个non-const wxEvent引用类型的参数。方法的返回值是void类型,因为引用类型的参数将带回事件的返回信息;

(2)在类的声明中使用DECLARE_EVENT_TABLE()或wx DECLARE_EVENT_TABLE()声明事件表;

(3)类的实现代码中,使用宏BEGIN_EVENT_TABLE和END_EVENT_TABLE或者wxBEGIN_EVENT_TABLE和wxEND_EVENT_TABLE实现一个事件表;

(4)实现事件处理函数,并在事件表中使用事件关联宏实现从事件到事件函数的映射。

如下代码展示了wxWidgets静态事件表编程的核心部分。图1显示的是一个基于wxWidgets的跨平台计算器在Win7下的运行结果,它和动态事件处理方法在Win7下的运行效果一样,展示了Win7本地风格和观感。程序运行环境:Win7,使用wxWidgets3.0动态链接库,编译选项UNICODE=

/*事件处理函数,函数原型都一样,返回值为void,一个wx-Event派生类对象的非常量(non-const)引用*/

void OnClose(wxCloseEvent&event);

void OnButton(wxCommandEvent&event);

void OnSize(wxSizeEvent&event);

void OnTimer(wxTimerEvent&event);

void OnChar(wxKeyEvent&event);

wxDECLARE_EVENT_TABLE();//此处也可用DECLARE_EVENT_TABLE()

};

//MyFrame.cxx

/*可以用BEGIN_EVENT_TABLE和END_EVENT_TABLE代替wxEND_EVENT_TABLE和 wxBEGIN_EVENT_TABLE*/

/*ID_MENU和ID_BUTTON分别对应程序菜单项和按钮的窗体标示符*/

wxBEGIN_EVENT_TABLE(MyFrame,wxFrame)

EVT_BUTTON(ID_BUTTON,MyFrame::OnButton)

EVT_SIZE(MyFrame::OnSize)

EVT_CLOSE_WINDOW(MyFrame::OnClose)

EVT_TIMER(MyFrame::OnTimer)

EVT_CHAR(MyFrame::OnChar)

wxEND_EVENT_TABLE()

图1 Win7下程序运行结果

4.动态事件处理

尽管静态事件表机制比较简单,但并不意味着这是一种比较好的选择,因为静态事件表的映射通过宏定义在编译期间进行的,这就意味着我们没有办法改变这种映射关系[6]。而程序在运行期的不同阶段又确切需要不同的映射关系,或者需要在不同的类之间共享事件处理函数,比如我们的程序需要先进行初始化,只有在初始化成功之后才进行事件和事件处理函数的映射;或者我们需要在不同的类之间共享事件处理函数。这时,使用wxWidgets强大的动态事件处理机制,可以更加精确地处理事件表细节。

和动态事件处理相关的API函数有两个:wxEvtHandler::Connect()和wxEvtHandler::DisConnect()。依据事件的处理策略,这两个函数又有三种重载形式。wxEvtHandle::Connect()函数只能够映射事件到wxEvtHandler派生类的方法。在wxWidgets2.9.0之后,wxWidgets提供了一组更加灵活的动态事件处理函数wxEvtHandler::Bind()和wx-EvtHandler::UnBind(),它们在功能上同wxEvtHandler::Connect()和wxEvtHandler::DisConnect()是相同的,但可以用任意类的方法、普通函数或专有功能组件作为事件处理函数[6]。在大多数情况下,无需手动调用UnBind()或DisConnect(),因为窗口类被释放的时候,它们将自动被调用[2]。以下代码展示了动态事件的绑定过程。图2展示了程序在Ubuntu12.04下的运行结果,它和静态事件表方法在Ubuntu12.04下的运行效果一样,展示的是Ubuntu本地风格和观感。程序运行环境:Ubuntu12.04,使用wxWidgets3.0类库。

};

//MyFrame.cxx

//没有事件宏定义

图2 Ubuntu12.04下程序运行结果

5.结论

wxWidgets的事件处理方法和C++中的虚函数多态比较相似:允许在事件处理器(wxEvtHandler)的派生类中重新定义函数来改变基类的行为[6]。而这两种机制最大的不同在于派生类调用基类方法的方式:派生类的虚函数可以直接调用基类的方法,所以可以在派生类事件处理函数的开始先调用基类方法再处理事件(post-process the event),也可以在派生类事件处理函数中先处理事件(pre-process the event)再在结束时调用基类方法;实际上,默认的事件处理行为可能由底层操作系统的平台相关代码来实现,并不属于wxWidgets的API,因此wxWidgets不允许直接调用基类默认事件处理函数,只能够采用pre-process the event方法处理事件,要想调用基类默认行为的话需要使用wxEvent::Skip()函数。

wxWidgets可以使用静态事件表机制和动态事件处理方法把事件和事件处理函数绑定在一块,并产生同操作系统平台一致的本地观感。静态事件表通过宏定义把所有的事件映射书写在同一个地方,简单明了,但不能够在运行期改变事件的映射关系,处理事件的类只能够是wxEvtHandler的派生类,适合在简单的应用程序中使用。动态事件的处理方法则根据需要以更加精确地事处理事件表的细节,显得更加灵活。表1对静态事件表和动态事件表在各个方面做出了一系列的比较。

表1 静态事件表和动态事件综合比较

总之,wxWidgets封装了底层平台的差异,采用了更加灵活的事件处理机制,既可以使用静态事件表代替虚函数,也可以动态地进行事件处理,获得较高的效率和良好的可移植性。

[1]熊凯,高茂庭,于仁师.C++语言开发跨平台程序的研究与实现[J].电脑知识与技术,2006(2):127-128.

[2]Kevin Hock,Stefan Csomor,Julian Smart.Cross-Platform GUI Programming with WxWidgets[M].UK:Prentice Hall PTR,2005.7.

[3]王艳红,张宪生.基于wxWidgets的跨平台软件开发[J].中国科技博览,2012(26):388.

[4]张长亮.wxWidgets跨平台程序开发[M].北京:机械工业出版社,2012.9.

[5]李森林,邓小武.一种跨平台类库WxWidgets应用开发研究[J].电脑与电信,2009(3):32-34. [6]Julian Smart,Robert Roebling,et al.wxWidgets Cross-Platform GUI Toolkit[M/OL].https://sourceforge.net/projects/wxwindows/files/ 3.0.0/wxWidgets-docs-chm-3.0.0.zip 2013.11.

[7]李宁.wxWidgets:全能的跨平台软件开发包[J].程序员,2006 (11):126-127.

[8]张羽.WxWidgets框架应用及其模块化开发[J].硅谷,2009(22):67.

The Mechanism of Handling Events in wxWidgets

Zhang Feng Zhang Xiaomin
(School of Software,Nanyang Institute of Technology,Nanyang 473000,Henan)

wxWidgets is a C++framework providing GUI(Graphical User Interface)and other facilities on multi-platforms. Like other GUI frameworks,the control of flow in wxWidgets applications is event-based.There are two principal ways to handle events in wxWidgets.One of them uses event table macros and allows you to define the binding between events and their handlers only statically;the other way maps events and their handlers dynamically.The paper introduces and analyzes the two methods and summarizes that the dynamical way requires slightly more codes but is much more flexible than the static way.

cross-platform;event-based;binding

张枫,男,河南南阳人,硕士研究生,讲师,研究方向:跨平台软件开发,数字图像处理。

猜你喜欢

窗体跨平台静态
跨层级网络、跨架构、跨平台的数据共享交换关键技术研究与系统建设
最新进展!中老铁路开始静态验收
静态随机存储器在轨自检算法
一款游戏怎么挣到全平台的钱?
试谈Access 2007数据库在林业档案管理中的应用
跨平台APEX接口组件的设计与实现
油罐车静态侧倾稳定角的多体仿真计算
基于B/S的跨平台用户界面可配置算法研究
巧设WPS窗体控件让表格填写更规范
WinCE.net下图形用户界面的开发