第一章 介绍

在这一章中,我们会回答这样一些基本的问题:wxWidgets是什么,它和别的类似的开发库有什么不同。我们还会大概说一下这个项目的历史,以及wxWidgets社区的工作,它采用的许可协议,它的体系架构以及目前拥有的各种版本等。

什么是wxWidgets

wxWidgets是一个程序员的开发工具包,这个工具包用来开发用于桌面或者移动设备的图形界面应用程序。或者说它提供了一个框架,它作了很多底层的管家婆似的工作以便给应用程序提供一些默认的行为。wxWidgets库给程序员提供了大量的类以及类的方法,以供其使用和定制。一个典型图形界面应用程序所作的事情包括:显示一个包含各种标准控件的窗口,也可能需要在窗口中绘制某种特别的图形或者图像,并且还要响应来自鼠标,键盘以及其它输入设备的输入。很可能这个应用程序还要和其它的进程通信,甚至还要驱动别的应用程序,换句话说,wxWidgets可以让程序员编写一个拥有所有通用特性的时髦应用程序的工作变的相对容易。

虽然wxWidgets经常被打上图形界面程序开发的标签,但是它在很多其它的应用程序开发方面也提供了很多特性支持。这样作的目的是为了让使用wxWidgets编写的程序的各个部分都可以是跨平台的,而不仅仅是图形界面的部分。这些部分包括:文件和流操作,多线程,程序设置,进程间通讯,在线帮助,数据库访问等等。

1.1 为什么要使用wxWidgets?

wxWidgets和其它类似的GUI(图形用户界面,下同)库比如MFC或者OWL一个最本质的区别在于,它是跨平台的。wxWidgets提供的 API函数在它支持的所有平台上是想同的或者是非常相似的。这意味着你可以编写一个windows上运行的程序,这个程序不需要经过任何改动,或者只需要 很少的改动(这种情况并不常见),只需要通过重新编译,就可以在Linux或者Max OSX上运行。比起为另外的平台从头编写代码,这显然有很大的好处,另外一个附带的好处是,你不需要重新学习那个平台的API。 而且,你的程序可能在将来很长时间仍然可以使用。因为随着计算机科技的演进,wxWidgets将会随之一起演进,这样你的程序将会很方便的移植到最新的 操作系统以支持最新的特性。

另外一个与众不同的地方在于,wxWidgets可以给你的应用程序提供本地观感。一些其它的可以跨平台的开发框架在不同的平台使用同 样的窗口组件代码(译者注:难到他指的是JAVA?),也许它会通过类似窗口主题这样的方式来模拟本地观感。而wxWidgets则尽可能的使用本地的窗 口控件(当然wxWidgets也提供自己的控件集,这是另外一个话题了),所以wxWidgets的程序不只是看上去象是本操作系统上的原生程序,它实 际上就是原生程序。对于使用应用程序的用户来说,这是非常重要的,因为和本地操作系统标准的任何一点细微的甚至是几乎难以察觉的不同,都会让他们产生避而 远之的想法。

让我们来举例说明。下图演示了一个叫做StoryLines的小程序运行在Windows XP上的样子:

正象大家看到的那样,这是一个典型的Windows应用程序,有典型的Windows的GUI控件例如标签页,滚动条以及下拉列表。类似了,下图演示了这个程序在Max OSX上的样子,正象我们期待的那样,它有着水晶外形图标,没有菜单条(因为按照苹果的风格,当前窗口的菜单条应该显示在屏幕的最顶层。)

最后,我们还将演示一下同样的程序在小红帽Linux上作为一个GTK+程序的样子:

为什么不直接使用JAVA呢?对于基于Web的应用来说,JAVA的确很不错,但是对于桌面应用程序来说,JAVA有时候并不是一个很好的选择。一般来 讲,基于C++的wxWidgets程序会运行更快,感观上更象本地原生程序并且更容易安装,因为它并不依赖于你的机器一定要有JAVA虚拟机。C++也 更容易访问操作系统提供的底层函数并且更容易和已有的C++或者C代码集成。基于以上原因,您现在经常用到的桌面程序中,很少有全部基于JAVA开发的。 而wxWidgets则可以让你开发高性能的,本地原生的应用程序。而这可能正是你的用户所期待的。

wxWidgets是一个开放源代码的项目。毫无疑问,这意味着使用wxWidgets是免费,它不需要额外花费你1分钱(除非您愿意大方的向这个项目进 行捐助),但是,开放源代码并不仅仅意味着免费,它有着更重要的意义。开源项目通常可以持续比它的创建团队或者通常意义上的拥有者更长久的时间。使用 wxWidgets开发程序,你的代码永远不会过时,你的代码所依赖的开发平台永远不会消失。你可以通过直接修改源代码来修正基础库中的问题(译者注:使 用Delphi的开发者对此可能有更深的体会,由于众所周知的原因,很多开发工具慢慢的被淘汰了)。你甚至可以自己抽点时间加入到wxWidgets的开 发团队中来,维护其中的一部分代码,这也是一件非常有趣的事情。开源项目的团队成员之所以加入某个团队是因为他们热爱他们正在作的事情,并且迫不及待的想 把他们的知识和别人分享,而商业项目的客服支持人员通常不具有这种理想主义的情节,当你使用wxWidgets开始编程时,你其实是把自己放入一个令人惊 讶的有艺术天赋的一堆天才中间(译者注:我只是按照字面意思翻译,虽然我自己用wxWidgets开发程序,但是这样的话还是让我觉的有一点点善意的恶 心。可能是我的英文太差了,没有理解原话的意思,原话是这样的:When you use wxWidgets, you tap into an astonishing talent pool, with contributors from a wide range of backgrounds.),这些天才来自世界的各个角落,有着各种各样的背景。开发应用程序需要考虑的很多细节都被这些天才封装在了你可以直接拿来很简 单就可以使用的类中,如果不是这些天才的劳动,你可能要花费很大的精力才能应付。一个开放和活跃的社区将会通过邮件列表对你提供帮助,在这里,你会享受到 讨论的乐趣。这些讨论并不全是和wxWidgets相关的。更多情形下,你是和社区那些有经验的或者没有经验的开发者进行心灵的交流.也许有一天,你会发 现自己成为wxWidgets之所以成功的一分子。

wxWidgets已经被广泛的应用在各种工业领域。它的用户包含了象AOL,AMD,CALTECH, Lockheed Martin, NASA, the Open Source Applications Foundation, Xerox等等这些大的商业和团体机构。wxWidgets拥有很广泛的使用者,从个体的软件开发者到大的商业团体,从计算机科学领域到医疗研究领域,从社会生态学到电信领域。当然,还有数不清的开源项目在使用它,例如Audacity声音编辑项目和pgAdmin III数据库设计和维护项目等。

人们出于各种各样的目的而使用wxWidgets,一些人只是把它作为单平台开发上MFC的优雅的替代者,一些则是为了让他们的程序可以方便的从微软的Windows移植到Linux或者是苹果的OSX。 wxWidgets还正致力于移动终端的支持,包括嵌入式linux,微软的Pocket PC,在不久的将来还会支持Palm OS。

1.2 wxWidgets的历史

1992年,Julian Smart在Edinburgh大学开始制作一个叫做Hardy的图表工具的时候,为了避免其发行版本在Sun的工作站和各种PC之间作选择,他决定使用跨平台的编程框架。但是当时可选的跨平台的编程框架不多,而他的部门也不可能给他很多的预算,所以他只能自己创建一个自己的跨平台编程框架。这样, wxWidgets 1.0诞生了。 1992年9月,学校允许他把他的wxWidgets 1.0上传到部门的FTP服务器,因此别的一些开发者也开始使用他的代码。最开始的时候,wxWidgets是面向XView和MFC 1.0的,由于Borland C++的适用者抱怨其对MFC的依赖,所以Julian Smart用纯Win32的代码重写了wxWidgets。又因为XView很快被Motif取代,很快,Widgets提供了对Motif的支持。

不久以后,一个很小但是却很付有激情的wxWidgets用户社区成立了并且拥有了自己的邮件列表。大量的新代码和补丁开始融入到wxWidgets中,其中包括Markus Holzem提供的Xt的支持。wxWidgets也自然的拥有了越来越多的来自世界各地的使用者:独立工作者,学术机构,政府机构以及很多企业用户等,他们认为wxWidgets提供的产品质量和产品支持甚至好过他们见过的或者用过的其它商业的产品。

1997年,在Markus Holzem的帮助下,新版的wxWidgets 2 API问世。此时,Wolfram Gloger建议应该提供GTK+的支持。GTK+是被GNOME桌面系统采纳的一套窗口控件。于是,Robert Roebling开始领导GTK版本的wxWidgets的开发,现在wxWidgets的GTK版本已经成为其在UNIX/LINUX下的最主要的版本。到了1998年,Windows和GTK+的版本被合入版本控制工具CVS。Vadim Zeitlin加入到项目中来帮助管理和维护如此大量的设计和代码,同年,Stefan Csomor开始着手增加对Mac OS的支持。

1999年,Vaclav Slavik的令人印象深刻的wxHTML类和HTML帮助文件显示控件被加入进来。2000年,SciTech公司开始开发wxUniversal版本,这个版本提供属于wxWidgets自己的不依赖于任何其它图形库的窗口控件,以便支持那些没有原生窗口控件库的操作系统。wxUniversal最初被用于SciTech公司的MGL产品,这个产品为图形用户界面提供了底层支持。

到了2002年,Julian Smart和Robert Roebling在wxUniversal的基础上提供了wxX11版本,这个版本仅依赖于Unix和X11,因此它几乎适用于任何的类Unix环境,所以,它可以被用在相当底层的系统中。

2003年,wxWidgets开始了对Windows CE的支持,同年Robert Roebling在GPE嵌入式Linux平台上演示了使用wxGTK编写的程序。

2004年,因为收到微软的商标方面的威胁,wxWidgets被迫从它原来的名字"wxWindows"改名。

同样是在2004年,Stefan Csomor和一大群热心的参与者彻底的修改了wxMac OSX版本,OSX版本的功能和性能都得到了极大的提升。而David Elliot领导的小组正在稳步的开发一个基于Cocoa的版本,William Osborne也着手开发一个可以支持wxWidgets的"minimal"例子的Palm OS 6的版本。 2005年4月,2.6版的wxWidgets发布了,几乎所有的平台版本在这个版本都有了大幅的改进和提高。

wxWidgets将来的计划包括:

  • 一个包管理工具,使得集成第三方工具变得容易。
  • 更好的嵌入式支持。
  • 更好的事件处理机制。
  • 增强型控件支持:比如一种捆绑了树形控件和列表控件的控件。
  • wxHTML 2提供在各种平台下的完整的Web能力支持。
  • STL标准兼容
  • 完整的Palm OS支持

1.3 wxWidgets社区

wxWidgets社区是非常活跃的。它拥有两个邮件列表: wx-users (用于普通用户)和 wx-dev (用于wxWidgets的开发者).一个网站,网站上有最新消息,一些文章以及和wxWidgets有关的链接,还拥有一个"Wiki," 所谓"Wiki"是一个网页的集合,这些网页可以被任何人修改和增加信息.还有一个论坛可以用来就某一话题发起讨论. 这些网络资源的网址列举在下面:

和大多数开放源代码的项目一样,wxWidgets采用CVS来进行代码的管理和修改记录的跟踪.为了保证开发的有序进行,只有少数几个开发者拥有对 CVS库的修改权限,其它的开发者可以通过提交补丁和提交缺陷报告的方式参与开发,wxWidgets目前使用的补丁和缺陷管理系统是由 SourceForge提供的。CVS库主要有两个分枝,稳定版分支和开发分支。CVS的稳定版分支只允许为了修改缺陷而进行修改。新功能的开发需要在开发分支进行。稳定版的版本号都是双数的,例如2.4.x等,而开发分支的版本好都是单数的,比如2.5.x等。对于单数版本,使用者最好等待新的稳定版本的发布再使用。当然直接从CVS下载最新的开发版本也是可以的。

wxWidgets的API的修改通常是由开发者在wx-dev邮件列表里讨论以后决定的。

除了以上这些,很多其它的wxWidget相关的项目都拥有他们自己的社区。例如wxPython社区和wxPerl(参见附录E:wxWidgets的第三方工具)社区.

1.4 wxWidgets和面向对象编程

和大多数现代的GUI编程框架一样,wxWidgets大量使用了面向对象编程的概念。每一个窗口都是一个C++的对象。这些对象已经被预置了很好的处理机制,可以接收事件并对事件作出相应的反应。用户所看到的,就是这个对象的交互系统中可视化那一部分。作为一个程序开发人员,你所要作的事情就是合理的安排这些可视的行为集来让它们作出的反应看上去更合理。wxWidgets已经实现了很多默认的行为来让这个工作变的更容易。

当然,面向对象的思想和GUI编程并不是同时产生的。但是在20世纪70年代,由Alan Kay和其它一些人设计的面向对象的语言SmallTalk却是GUI编程历史上的一个重要的里程碑。无论对于用户界面设计来说,还是对计算机程序语言来说,它都是一个创新。虽然wxWidgets使用不同的语言和不同的API,但是就面向对象的原理来说,本质上都是一样的。

1.5 wxWidgets的体系结构

下表展示的wxWidgets的四层体系结构: wxWidgets公用API层,各个平台发行版,用于各个平台的API和操作系统层。

| wxWidgets API |   |   |   |   |   |   |   |   |   |
|:--- |:--- |:--- |:--- |:--- |:--- |:--- |:--- |:--- |
| wxWidgets Port | wxMSW | wxGTK | wxX11 | wxMotif | wxMac | wxCocoa | wxOS2 | wxPalmOS | wxMGL |
| Platform API | Win32 | GTK+ | Xlib | Motif/Lesstif | Carbon | Cocoa | PM | Palm OS Protein APIs | MGL |
| Operating System | Windows/Windows CE | Unix/Linux |   |   | Mac OS 9/ Mac OS X | Mac OS X | OS/2 | Palm OS | Unix/DOS |

下面依次说明目前已有的各个平台发行版本:

wxMSW

这个版本编译和运行在各个版本的微软的Windows操作系统上,包括:Windows95,Windows98,WinMe,Windows NT,Windows 2000,Windows Xp以及Windows 2003.在linux平台上,这个版本也可以使用Wine的库进行编译,并且可以被配置成在WinCE上运行。除了使用本地原生窗口控件,这个版本也可以配置成使用wxWidgets自己的窗口控件。

wxGTK

wxWidgets的GTK+版本可以使用GTK的1.x或者2.x版本,支持所有可以运行X11和GTK的类Unix平台 (比如: Linux, Solaris, HP-UX, IRIX, FreeBSD, OpenBSD, AIX等). 它也可以运行在那些有足够资源的嵌入式平台,比如GPE Palmtop环境 (如下图所示). wxWidgets的GTK版本是类Unix系统的推荐版本.

wxX11

wxWidgets的X11版本使用了wxUniversal的窗口控件集,直接运行在Xlib上。这使得它很适合嵌入式系统,当然它也可以运行在那些不喜欢GTK+的桌面系统上。它支持所有可以运行X11的Unix系统,当然wxX11并不像wxGTK那样完善。下图演示了Life程序使用wxX11版本编译运行在一个iPAQ PDA的类似Linux/TinyX环境下的样子。

wxMotif

这个版本的wxWidgets可以在大多数拥有 Motif, OpenMotif, 或者Lesstif的Unix系统上. 既然连Sun自己都正准备把它的窗口控件集转向GNOME和GTK+,对于大多数开发者来说,Motif并不是一个很可靠的选择.

wxMac

wxMac是为Mac OS 9 (9.1以后的版本)和Mac OS X (10.2.8以后的版本)准备的. 如果在Mac OS 9上编译,你需要 Metrowerks CodeWarrior的工具包, 如果在Mac OS X上编译, 你可以选择Metrowerks CodeWarrior工具包或者苹果公司的工具包. 如果使用苹果公司的工具包,你应该使用Xcode 1.5或者更高的版本,或者你可以考虑直接使用命令行工具toolsGCC 3.3或者其后续的版本.

wxCocoa

这是一个正在进行中的版本, 它使用Mac OS X的Cocoa API. 虽然Carbon和Cocoa的功能很相似,但是这个版本有可能会支持除Mac以外的其它支持GNUStep的操作系统。

wxWinCE

Windows CE版本的wxWidgets封装了WindowsCE平台上的各种不同的开发包,包括Pocket PC和Smartphone等. 这个版本包含在wxMSW的Win32版本中。下面第一副图演示了Life程序在Pocket PC 2003模拟器上运行的样子。第二副图则演示了wxWidgets中的对话框例子运行在一个拥有四个屏幕,分辨率为176x220的 SmartPhone2003上的样子。wxWidgets作了大量的用户界面适配方面的工作,比如因为Smartphone只支持两个菜单按钮,所以在通常显示菜单的地方wxWidgets构建了可以折叠的菜单。尽管如此,一些地方仍然需要依靠编程者使用不同的代码,比如应该使用SetLeftMenu 和SetRightMenu函数来代替直接在对话框上增加两个确定和取消按钮。

wxPalmOS

这个版本是为Palm OS 6准备的。到作者写这本书的时候为止,这个版本还处在很初级的阶段,但是已经可以在Palm OS 6的模拟器中运行一个很简单的小程序了。参见下图:

wxOS2

wxOS2是一个由别人维护的用于OS/2或者eComStation的版本(is a Presentation Manager port for OS/2 or eComStation).

wxMGL

这个版本使用了SciTech公司的底层图形库,窗口控件使用的是wxUniversal中的版本。

内部组织

在内部,wxWidgets的代码大致分为6层:

  1. 通用代码被所有的版本使用,包括类的数据结构,运行期类型信息,和一些公共基类比如wxWindowBase等,这些基类的代码将被所有它的子类所继承。
  2. 一般代码用来实现独立于各个平台的高级窗口控件,在某个平台不具有某种控件的时候将使用这部分代码,比如wxWizard和wxCalendarCtrl。
  3. 通用组件基本的窗口控件集,这套控件可以在某个平台(比如X11或者MGL)不具有它自己的窗口控件的时候使用。
  4. 平台相关代码调用特定平台的API来实现某个类的代码。比如在wxMSW中的wxTextCtrl控件的实现是封装了Win32的edit控件。
  5. 外来代码存放在一个单独的contrib目录中,提供一些非必要但是很有用的类实现。比如wxStyledTextCtrl
  6. 第三方代码不是由wxWidgets开发维护但是被wxWidgets使用以提供一些很重要的特性的代码,比如JPEG, Zlib, PNG和Expat库。

每一个平台版本所需要作的事情就是提取它需要的那些层的代码,然后用它那个平台的底层的API来实现wxWidgets的API。

当你编译你的代码的时候,wxWidgets怎么知道要编译哪一个平台的类呢?当你包含一个wxWidgets的头文件(比如 wx/textctrl.h)的时候,由于使用的不同的宏定义,实际上你包含的是一个特定平台的头文件(wx/msw/textctrl.h)。然后,当你链接wxWidgets的库文件的时候,当然这个库文件也需要是用同样的宏定义编译的。你可以同时拥有多套宏定义,例如你可以有调试版本和发布版本两套宏定义。通过这些宏定义的不同,你可以控制编译器链接wxWidgets的动态或者是静态版本,也可以禁止编译某个特定组件,或者是决定你要编译的是 Unicode版本还是ANSI版本。为了达到这个目的,你需要修改setup.h或者增加不同的编译选项,这取决于你的编译器。有关这些问题更详细的描述,请参见附录A,???安装wxWidgets???

另外一点提示是:虽然wxWidgets封装了本地的平台相关的API,并不意味着在你的wxWidgets程序中不可以使用这些API。只是,在绝大多数情况下,你用不着使用这些API。

1.6 许可协议

wxWidgets采用的是L-GPL的许可协议外加一个附加条款。你可以从wxWidgets的网站上或者wxWidgets的分发包的docs目录里看到这份许可协议。这份协议总体上来说就是:你既可以使用wxWidgets开发自由软件,也可以使用它开发商业软件,并且不需要支付任何版权费用。你既可以动态链接wxWidgets的运行期库文件,也可以使用静态链接。如果你对wxWidgets本身进行了任何改动,你必须公开这一部分的源代码。而完全属于你自己的那部分代码或者库文件则不需要公开。另外,在发布使用wxWidgets编写的软件的时候,你还需要考虑wxWidgets的一些可选组件自己的许可协议,比如PNG和JPEG图形库的许可协议,它们和wxWidgets的许可协议可能并不完全相同。

本书中所有的例子和源代码同样使用wxWidgets的许可协议。

第一章小结

在这一章里,我们试图向你解释wxWidgets是什么,简单的描述了它的历史,大概支持哪些平台以及大概说了一下它的内部的组织结构。

在下一章 "开始使用"里,我们会通过几个例子来展示一下用wxWidgets编写应用程序大概是怎样一个样子。

第二章 开始使用

在这一章里,我们将使用一个很小的例子,来建立你对用wxWidgets编程的一个大致的印象。我们将会来看看一个用wxWidgets编写的程序是怎么开始的又是怎样结束的,怎样创建主窗口,怎样响应用户的命令。正如wxWidgets所追求的短小精悍的哲学一样,这就是所有这一章我们要涉及的内容。不过在这之前,你可能先要参考附录A来安装wxWidgets.

2.1 一个小例子

下图演示了我们的小例子在Windows上运行的样子

这个小例子创建了一个主窗口(是一个wxFrame类的实例),这个主窗口有一个菜单条和一个状态条。菜单条上的菜单按照你的命令显示一个关于窗口或者退出这个小程序。显然这算不上什么杀手级的大程序,但是足以给你展示wxWodgets的一些基本原则,并且让你可以相信,随着知识的慢慢积累,有一天你将作出更复杂的(那些杀手级的)程序,

2.2 应用程序类

每一个wxWidgets程序都需要定义一个wxApp类的子类,并且需要并且只能构造一个这个类的实例,这个实例控制着整个程序的执行。你的这个继承自 wxApp的子类至少需要定义一个OnInit函数,当wxWidgets准备好运行你写的代码的时候,它将会调用这个函数(和一个典型的Win32程序中的main函数或者WinMain函数类似)。

你定义这个子类的代码可能和下面的代码类似:

class MyApp : public wxApp
{
public:
virtual bool OnInit();
};

在这个OnInit函数中,你通常应该创建至少一个窗口,对传入的命令行参数进行解析,为应用程序进行数据设置和其它的一些初始化的操作.如果这个函数返回真,wxWidgets将开始事件循环用来处理用户输入并且在必要的情况下处理这些输入。如果OnInit函数返回假, wxWidgets将会释放它内部已经分配的资源,然后结束整个程序的运行。

接下来我们看一个最简单的OnInit函数的实现:

bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame(wxT("Minimal wxWidgets App"));
frame->Show(true);
return true;
}

你可能还会注意到上面例子中的wxT这个宏,在接下来的例子中,这个宏还会被频繁用到。它的作用是让你的代码兼容Unicode模式。这个宏和另外一个_T宏的作用是完全一样的。使用这个宏也不会带来运行期的性能损失。(你可能还会遇到另外一个类似的"_()"标记,这个标记是用来告诉 wxWidgets将其中的字符串翻译成其它语言的版本,参见第16章“编写国际化程序”)。

那么创建MyApp的实例的代码在哪里呢?实际上,这是在wxWidgets内部实现的,不过你仍然需要告诉wxWidgets需要创建哪一个App类的实例,所以你还需要增加下面的一个宏:

IMPLEMENT_APP(MyApp)

如果没有实现这个类,wxWidgets就不知道怎样创建一个新的应用程序对象。这个宏除了上述的功能以外,还会检查编译应用程序使用的库文件是否和当前的库文件的版本相匹配,如果没有这种检查,由此而产生的一些运行期的错误可能很难被查出原因。

当wxWidgets创建这个MyApp类的实例的时候,会将创建的结果赋值给一个全局变量wxTheApp.你当然可以在你的程序中使用这个变量,但是你可能不得不一遍又一遍的进行从wxApp到MyApp的类型强制转换。增加下面的这一行声明以后,你就可以调用wxGetApp()函数,这个函数会返回一个到这个MyApp实例的引用,这样用起来就方便多了。

DECLARE_APP(MyApp)

一点提示:

即使没有声明DECLARE_APP,你仍然可以不用进行类型强制转化就直接对wxTheApp变量调用wxApp的方法.这可以避免在所有的头文件中包含MyApp的头文件,对于那些库文件而不是应用程序的代码来说也更有意义,而且还可以缩短编译的时间。

2.3 Frame窗口类

我们来看一看自定义的Frame窗口类MyFrame.一个Frame窗口是一个可以容纳别的窗口的顶级窗口,通常拥有一个标题栏和一个菜单栏。下面是我们的例子中这个类的定义,可以将其放在MyApp的定义之后:

class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
private:
DECLARE_EVENT_TABLE()
};

这个窗口类的定义有一个构造函数,两个用来把菜单命令和C++代码相连的事件处理函数,还有一个宏来告诉wxWidgets这个类想要自己处理某些事件。

2.4 事件处理函数

你也许已经注意到了,事件处理函数在MyFrame类中不是虚函数。如果不是虚函数,他们是怎样被调用的呢?答案就在下面的事件表里:

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
END_EVENT_TABLE()

所谓事件表,是一组位于类的实现文件(.cpp文件)中的宏,用来告诉wxWidgets来自用户或者其它地方的事件应该怎样和类的成员函数对应起来。

前面展示的事件表表明,要把鼠标点击标识分别为wxID_EXIT和wxID_ABOUT的菜单项的事件和MyFrame的成员函数OnAbout和 OnQuit关联起来。这里的EVT_MENU宏只是很多中事件宏的其中之一,事件宏的作用是告诉wxWidgets哪种事件应该被关联到哪个成员函数。这里的两个标识wxID_ABOUT和wxID_EXIT是wxWidgets预定义的宏,通常你应该通过枚举,常量或者预编译宏的方式定义你自己的标识。

用上面的方法定义的时间表是一种静态的事件表,它不可以在运行期改变,在下一章里,我们将描述怎样定义可以在运行期改变的动态事件表。

现在,让我们来看看这两个事件处理函数.

void MyFrame::OnAbout(wxCommandEvent& event)
{
wxString msg;
msg.Printf(wxT("Hello and welcome to %s"),
wxVERSION_STRING);
wxMessageBox(msg, wxT("About Minimal"),
wxOK | wxICON_INFORMATION, this);
} void MyFrame::OnQuit(wxCommandEvent& event)
{
Close();
}

当用户点击关于菜单项的时候,MyFrame::OnAbout函数弹出一个消息框。这用到了wxWidgets提供的API wxMessageBox,它的四个参数分别代表消息内容,标题,窗口类型以及父窗口。

当用户点击退出菜单项的时候,MyFrame::OnQuit函数被调用(你已经意识到了,这是事件表的功劳)。它调用wxFrame类的Close函数来释放frame窗口。因为没有别的窗口存在了,这触发了应用程序的退出,实际上,wxFrame类的Close函数并不直接关闭 frame窗口,而是产生一个wxEVT_CLOSE_WINDOW事件,这个事件默认的处理函数调用wxWindow::Destroy函数释放了 frame窗口。

用户还可以通过别的方法关掉应用程序,比如通过点击标题栏上的关闭按钮或者是通过系统菜单中的关闭菜单,在这种情况下,OnQuit函数是怎样被调用的呢?事实上,在这种情况,OnQuit函数并没有被调用。这时,wxWidgets会通过Close函数(象OnQuit中的那样),给 frame窗口发送一个wxEVT_CLOSE_WINDOW事件,这个事件默认的处理函数会释放掉frame窗口。在你的应用程序中,可以通过重载这个处理函数来增加改变这种默认的行为,比如:如果你想问一问你的用户是不是真的要关闭窗口。关于这种处理的细节,可以参见第4章,"窗口基础"。

另外,大多数的应用程序类还应该重载一个OnExit函数,以便在任何时候程序退出时,执行一下清理和资源回收的动作。需要注意的是,这个函数只有在OnInit函数返回真的时候才会被执行。当然,我们这个小例子程序就用不着定义这个函数了。

2.5 Frame窗口的构造函数

最后,让我们来看看Frame窗口的构造函数,正是它实现了frame窗口的图标,菜单条和状态条。

#include "mondrian.xpm"

MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title)
{
SetIcon(wxIcon(mondrian_xpm));
wxMenu *fileMenu = new wxMenu;
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(wxID_ABOUT, wxT("&About...\tF1"),
wxT("Show about dialog"));
fileMenu->Append(wxID_EXIT, wxT("E&xit\tAlt-X"),
wxT("Quit this program"));
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(fileMenu, wxT("&File"));
menuBar->Append(helpMenu, wxT("&Help"));
SetMenuBar(menuBar);
CreateStatusBar(2);
SetStatusText(wxT("Welcome to wxWidgets!"));
}

这个构造函数首先调用它的基类(wxFrame)的构造函数,使用的参数是父窗口(还没有父窗口,所以用NULL),窗口标识(wxID_ANY标识让 wxWidgets自己选择一个)和标题。这个基类的构造函数才真正创建了一个窗口的实例。除了这样的调用方法,还有另外一种方法是直接在构造函数里面显式调用基类默认的构造函数,然后调用wxFrame::Create函数来创建一个frame窗口的实例。

小图片或者是图标在所有的平台上都可以用XPM格式来表示。XPM文件其实是一个ASCII编码的完全符合C++语法的文本文件,所以可以直接用C++的方式包含到代码中(译者注:显然这样的包含方式在分发软件的时候是不需要分发这个图片文件的)。SetIcon那一行代码使用 mondrian_xpm变量在堆栈上创建了一个图标(这个mondrian变量是在mondrian.xpm文件里定义的)。然后将这个图标和 frame窗口关联在一起。

接下来创建了菜单条。增加菜单项的Append函数的三个参数的意义分别为:菜单项标识,菜单上的文本以及一个稍微长一些的帮助字符串。这个帮助字符串会自动在菜单项被高亮显示的时候自动显示在状态栏上。菜单上的文本中由"&"符号前导的字符将成为菜单的快捷操作符,在实际的显示中用下划线表示。而"\t"符号则前导一个全局的快捷键,这个快捷键甚至可以在菜单项没有显示的时候触发菜单功能。

这个构造函数所做的最后一件事是创建一个由两个区域组成的状态条并且在状态条的第一个区域写上欢迎的字样。

2.6 完整的例子

现在是时候把所有的代码放在一起了,通常,我们应该把头文件和实现文件分开,但是对于这样小的一个程序,就没有这个必要了。

// Name:        minimal.cpp
// Purpose: Minimal wxWidgets sample
// Author: Julian Smart #include "wx/wx.h" // 定义应用程序类
class MyApp : public wxApp
{
public:
// 这个函数将会在程序启动的时候被调用
virtual bool OnInit();
}; // 定义主窗口类
class MyFrame : public wxFrame
{
public:
// 主窗口类的构造函数
MyFrame(const wxString& title); // 事件处理函数
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event); private:
// 声明事件表
DECLARE_EVENT_TABLE()
}; // 有了这一行就可以使用 MyApp& wxGetApp()了
DECLARE_APP(MyApp) // 告诉wxWidgets主应用程序是哪个类
IMPLEMENT_APP(MyApp) // 初始化程序
bool MyApp::OnInit()
{
// 创建主窗口
MyFrame *frame = new MyFrame(wxT("Minimal wxWidgets App")); // 显示主窗口
frame->Show(true); // 开始事件处理循环
return true;
} // MyFrame类的事件表
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
END_EVENT_TABLE() void MyFrame::OnAbout(wxCommandEvent& event)
{
wxString msg;
msg.Printf(wxT("Hello and welcome to %s"),
wxVERSION_STRING); wxMessageBox(msg, wxT("About Minimal"),
wxOK | wxICON_INFORMATION, this);
} void MyFrame::OnQuit(wxCommandEvent& event)
{
// 释放主窗口
Close();
} #include "mondrian.xpm" MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title)
{
// 设置窗口图标
SetIcon(wxIcon(mondrian_xpm)); // 创建菜单条
wxMenu *fileMenu = new wxMenu; // 添加“关于”菜单项
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(wxID_ABOUT, wxT("&About...\tF1"),
wxT("Show about dialog")); fileMenu->Append(wxID_EXIT, wxT("E&xit\tAlt-X"),
wxT("Quit this program")); // 将菜单项添加到菜单条中
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(fileMenu, wxT("&File"));
menuBar->Append(helpMenu, wxT("&Help")); // ...然后将菜单条放置在主窗口上
SetMenuBar(menuBar); // 创建一个状态条来让一切更有趣些。
CreateStatusBar(2);
SetStatusText(wxT("Welcome to wxWidgets!"));
}

2.7 wxWidgets程序一般执行过程

下面大概的描述一下整个程序的执行过程:

  1. 依照系统平台的不同,不同的main函数或者winmain函数或者其它类似的函数被调用(这个函数是由wxWidgets提供的,而不是由应用程序提供的).wxWidgets 初始化它自己的数据结构并且创建一个MyApp的实例.
  2. wxWidgets调用MyApp::OnInit函数, 这个函数会创建一个MyFrame的实例.
  3. MyFrame的构造函数通过它的基类wxFrame的构造函数创建一个窗口,然后给这个窗口增加图标,菜单栏和状态栏.
  4. MyApp::OnInit函数显示主窗口并且返回真.
  5. wxWidgets开始事件循环,等待事件发生并且将事件分发给相应的处理过程.

就目前我们所知道的,应用程序会在以下情况下退出:主窗口被关闭,用户选择退出菜单或者系统按钮和系统菜单中的关闭选项(这些系统菜单和系统按钮在不同的系统中就往往千差万别了)。

2.8 编译和运行程序

你可以在附带光盘的examples/chap02里找到这个例子。你可能需要把它拷贝到你硬盘上的某个目录中才能进行编译。因为要提供适合所有读者的软件环境的Makefile几乎是不可能的,我们提供了一个DialogBlocks的项目文件,并且把我们能想到的大多数系统平台进行了配置。你可以参考附录C,“使用DialogBlocks创建应用程序”来找到适合你自己的编译器的编译方法。在附录B中,我们也大概的描述了直接用wxWidgets编译的方法。

你可以从CDRom上安装wxWidgets和DialogBlocks,如果你使用的是windows操作系统并且你的系统中还没有可以用的编译器,你还需要安装一个编译器。然后运行DialogBlocks,并且在其设置叶面设置你的wxWidgets和编译器的路径,然后打开 examples/chap02/minimal.pj,选择适合你的编译器和编译平台,比如Mingw Debug或者VC++ Debug(Windows),GCC Debug GTK+(Linux),或者GCC DEBUG Max(Max Os X)等,然后点击绿色的编译和运行工程按钮。如果你的wxWidgets库还没有被编译,它将首先编译wxWidgets库。

你也可以在wxWidgets的sample/minimal目录中找到一个类似的例子。如果你不想使用DialogBlocks编译,你也可以试着编译这个例子。你可以在附录A“安装wxWidgets”中找到编译这个例子的方法,

第二章小结

本章向你展示了一个非常简单的wxWidgets小程序是怎样工作的。我们已经接触到了wxFrame类,事件处理,应用程序初始化,并且创建了一个菜单条和状态条。虽然代码看上去有些复杂,但是任何其它更复杂的程序设计和我们展示在这个小例子中的一些公共的原则都是相同的。在下一章里,我们会更近距离的看一看事件表机制以及我们的应用程序是怎样处理事件表的。

第三章 事件处理

3.1 事件驱动编程

当程序员们首次面对苹果公司的第一个图形界面个人电脑的时候,他们为这种和以前所有的经验都不同的电脑操作方法感到惊奇。鼠标指针在一个个的窗口之间移来移去,滚动条,菜单,文本编辑框等等等等,真的很难以想象,这么多让人眼花缭乱的东西,其背后的代码该是多么复杂和不可思议。似乎所有这一切都是以完全并行的方式运行的,虽然这只是一个假象。对于很多人来说,苹果个人电脑是他们对事件驱动编程的第一印象。

所有的GUI程序都是事件驱动的。换句话说,应用程序一直停留在一个循环中,等待着来自用户或者其他地方(比如窗口刷新或网络连接)的事件,一旦收到某种事件,应用程序就将其扔给处理这个事件的函数。虽然看上去不同的窗口是同时被刷新的,但实际上,绝大多数的GUI程序都是单线程的,因此窗口的刷新是依次按顺序进行的。如果由于某种意外你的电脑变得很慢导致窗口刷新的过程变的很明显,你就会注意到这一点。

不同的GUI编程架构用不同的方法将它内部的事件处理机制展现给程序开发者。对于wxWidgets来说,事件表机制是最主要的方法。在下一小节我们会对此进行进一步的解释。

3.2 事件表和事件处理过程

wxWidgets事件处理系统比起通常的虚方法机制来说要稍微复杂一点,但它的一个好处是可以避免需要实现基类中所有的虚方法,因为实现所有的基类虚方法有时候是不切实际的或者是效率很低的。

每一个wxEvtHandler的派生类,例如frame,按钮,菜单以及文档等,都会在其内部维护一个事件表,用来告诉wxWidgets事件和事件处理过程的对应关系。所有继承自wxWindow的窗口类,以及应用程序类都是wxEvtHandler的派生类.

要创建一个静态的事件表(意味着它是在编译期间创建的),你需要下面几个步骤:

  1. 定义一个直接或者间接继承自wxEvtHandler的类.
  2. 为每一个你想要处理的事件定义一个处理函数。
  3. 在这个类中使用DECLARE_EVENT_TABLE声明事件表。
  4. 在.cpp文件中使用使用BEGIN_EVENT_TABLE和END_EVENT_TABLE实现一个事件表。
  5. 在事件表的实现中增加事件宏,来实现从事件到事件处理过程的映射。

所有的事件处理函数拥有相同的形式。他们的返回值都是void,他们都不是虚函数,他们都只有一个事件对象作为参数。(如果你熟悉MFC,这可能会让你觉得轻松,因为在MFC中消息处理函数并没有一个统一的形式。)这个事件对象的类型是随这个处理函数要处理的事件的变化而变化的。例如简单控件(比如按钮)的命令处理函数和菜单命令的处理函数的参数都是wxCommandEvent类型,而size事件(这个事件通常是由用户改变窗口的客户区尺寸而引起的)处理函数的参数则是wxSizeEvent的类型。不同的事件参数类型可以调用的方法也不相同,通过这些方法,你可以获得事件产生的原因以及产生这个事件的控件的值的改变情况(比如,文本框中的值的改变)。当然最简单的情形是你完全不需要访问这个参数的任何方法,比如按钮点击事件。

让我们来扩展一下前一章中的例子,来增加一个窗口大小改变事件的处理和一个确定按钮的处理。下面是扩展以后的MyFrame的定义:

class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnSize(wxSizeEvent& event);
void OnButtonOK(wxCommandEvent& event);
private:
DECLARE_EVENT_TABLE()
};

增加菜单项的代码和前一章的代码类似,而在frame窗口增加一个按钮的代码也只需要在MyFrame的构造函数中增加下面的代码:

wxButton* button = new wxButton(this, wxID_OK, wxT("OK"),
wxPoint(200, 200));

类似的,在事件表中也需要相应的增加事件映射宏:

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU (wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU (wxID_EXIT, MyFrame::OnQuit)
EVT_SIZE ( MyFrame::OnSize)
EVT_BUTTON (wxID_OK, MyFrame::OnButtonOK)
END_EVENT_TABLE()

当用户点击关于菜单或者退出菜单的时候,这个事件被发送到frame窗口,而MyFrame的事件表告诉wxWidgets,对于标识符为 wxID_ABOUT的菜单事件应该发送到MyFrame::OnAbout函数,而标识符为wxID_EXIT的菜单事件应该发送到MyFrame:: OnQuit函数。换句话说:当事件处理循环处理这两个事件的时候,相应的函数将会以一个的wxCommandEvent类型的参数被调用。

EVT_SIZE事件映射宏不需要标识符参数因为这个事件只会被产生这个事件的控件所处理。

EVT_BUTTON这一行将导致当frame窗口及其子窗口中标识符为wxID_OK的按钮被点击的时候,OnButtonOK函数被调用。这个例子表明,事件可以不必被产生这个事件的控件所处理。让我们假定这个按钮是MyFrame的子窗口。当按钮被点击的时候,wxWidgets会首先检查wxButton类是否指定了相应的处理函数,如果找不到,则在其父亲的类所属的事件表中进行查找,在这个例子中,按钮的父亲是MyFrame类型的一个实例,在其事件表中指明了一个对应的处理函数,因此MyFrame::OnButtonOK函数就被调用了。类似的搜索过程不光发生在窗口控件的父子继承树中,也发生在普通的类继承关系中,这意味着你可以选择在哪里定义事件的处理函数。举例来说,如果你设计了一个对话框,这个对话框需要响应的类似标识符为wxID_OK的command事件。但是你可能需要把这个控件的创建工作留给使用你的代码的其他的程序员,只要他在创建这个控件的时候使用同样的标识符,你仍然可以给这个控件为这个事件定义默认的处理函数。

下图中演示了一次普通的按钮点击事件发生以后,wxWidgets搜索所有事件表的顺序。图中只演示了wxButton和 MyFrame两次继承关系。当用户点击了确认按钮的时候,一个新的wxCommandEvent事件被创建,其中包含标识符wxID_OK和事件类型 wxEVT_COMMAND_BUTTON_CLICKED,然后这个按钮的事件表开始通过wxEvtHandler::ProcessEvent函数进行匹配,事件表中的每一个条目都会去尝试匹配,然后是其父类wxControl的事件表,然后是wxWindow的。如果都没有匹配到, wxWidgets会搜索其父亲的类事件表,然后就找到了一条匹配条目:

EVT_BUTTON(wxID_OK,MyFrame::OnButtonOK)

因此MyFrame::OnButtonOK被调用了。

需要注意的事:只有Command事件(其事件类型直接或者间接的继承自wxCommandEvent)才会被递归的应用到其父亲的事件表。通常这是wxWidgets的用户经常会感到困惑的地方,因此我们把那些不会传递给其父亲的事件表的事件列举如下:wxActivate, wxCloseEvent, wxEraseEvent, wxFocusEvent, wxKeyEvent, wxIdleEvent, wxInitDialogEvent, wxJoystickEvent, wxMenuEvent, wxMouseEvent, wxMoveEvent, wxPaintEvent, wxQueryLayoutInfoEvent, wxSizeEvent, wxScrollWinEvent, 和wxSysColourChangedEvent,这些事件都不会传给事件源控件的父亲.

这些事件不会传递给其父亲,是因为这些事件仅对产生这个事件的窗口才有意义,举例来说,把一个子窗口的重绘事件发送给它的父亲,其实是没有任何意义的。

3.3 过滤某个事件

wxWidgets事件处理系统实现了一些和C++中的虚方法非常类似的机制,通过这种机制,你可以通过重载某种基类的事件表的方法来改变基类的默认的事件处理过程。在多数情况下,通过这种方法,你甚至可以改变本地原生控件的默认行为。举例来说,你可以过滤某些按键事件以便本地原生的编辑框控件不处理这些按键。要达到这个目的,你需要实现一个继承自wxTextCtrl的新的类,然后在其事件表中使用EVT_KEY_DOWN事件映射宏。过滤所有的按键事件也许不是你想要的,这时候,你可以通过调用wxEvent::Skip函数来提示事件处理过程对于其中的某些按键事件应该继续寻找其父类的事件表。

总的来说,在wxWidgets中,你应该通过调用事件的Skip方法,而不是通过显式直接调用其父类对应函数的方法来实现对特殊事件的过滤。

下面的这个例子演示怎样让你自己的文本框控件只接受"a"到"z"和"A"到"Z"的按键,而忽略其它按键的方法:

void MyTextCtrl::OnChar(wxKeyEvent& event)
{
if ( wxIsalpha( event.KeyCode() ) )
{
//这些按键在可以接受的范围,所以按照正常的流程处理
event.Skip();
}
else
{
// 这些事件不在我们可以接受的范围,所以我们不调用Skip函数
// 由于事件表已经匹配并且没有调用Skip函数,所以事件处理过程不会
// 再继续匹配别的事件表,而是认为事件处理已经结束
wxBell();
}
}


3.4 挂载事件表

你并不一定非要实现继承自某个类的新类,才可以改变它的事件表。对于那些继承自wxWindow的类来说,有另外一种可取代的方法。你可以通过实现一个新的直接继承自wxEvtHandler的新类,然后定义这个新类事件表,然后使用wxWindow::PushEventHandler函数将这个事件表压入到某个窗口类的事件表栈中。最后压入的那个事件表在事件匹配过程中将会被最先匹配,如果在其中没有匹配到对应的事件处理过程,那么栈中以前的事件表仍将被匹配,如此类推。你还可以用wxWindow::PopEventHandler函数来弹出最顶层的事件表,如果你给wxWindow:: PopEventHandler函数传递的是True的参数,那么这个弹出的事件表将被删除。

这种方法可以避免大量的类重载,也使不同的类的实例共享同一个事件表成为可能。

有时候,你需要手动调用窗口类的事件表,这时候你应该使用wxWindow::GetEventHandler方法,而不是直接使用调用这个窗口类的成员函数。虽然wxWindow::GetEventHandler通常返回这个窗口类本身。但是如果你之前曾经使用 PushEventHandler压入另外一个事件表,那么函数将会返回处于最顶层的事件表。因此使用wxWindow:: GetEventHandler函数才可以保证事件被正确的处理。

PushEventHandler的方法通常用来临时的或者永久的改变图形界面的行为。举例来说,加入你想在你的应用程序实现对话框编辑的功能。你可以捕获这个对话框和它的内部控件的所有的鼠标事件,先使用你自己的事件表处理这些事件,来进行类似拖拽控件,改变控件大小以及移动控件动作。这在联机教学中也是很有用技术。你可以在你自己的事件表中过滤收到的事件,如果是可以接受的,则调用wxEvent::Skip函数正常处理。

3.5 动态事件处理方法

前面我们讨论的事件处理方法,都是静态的事件表,这也是我们处理事件最常用的方式。接下来,我们来讨论一下事件表的动态处理,也就是说在运行期改变事件表的映射关系。使用这种事件映射方法的原因,可能是你想在程序运行的不同时刻使用不同的映射关系,或者因为你使用的那种语言(例如python)不支持静态映射,或者仅仅是因为你更喜欢动态映射。因为动态映射的方法可以使你更精确的控制事件表的细节,你甚至可以单独的将事件表中的某一个条目在运行期打开或者关闭,而前面说的PushEventHandler和PopEventHandler的方法只能针对整个事件表进行处理。除此以外,动态事件处理还允许你在不同的类之间共享事件函数。

和动态事件处理相关的API有两个:wxEvtHandler::Connect和wxEvtHandler::Disconnect。大多数情况下你不需要手动调用wxEvtHandler::Disconnect函数,这个函数将在窗口类被释放的时候自动。

下面我们还用前面的MyFrame类来举例说明:

class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
private:
};

你可能已经注意到,这次我们没有使用DECLARE_EVENT_TABLE来声明一个事件表。为了动态进行事件映射,我们需要在OnInit函数中增加下面的代码:

frame->Connect( wxID_EXIT,
wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnQuit) );
frame->Connect( wxID_ABOUT,
wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnAbout) );

我们传递给Connect函数的三个参数分别为菜单标识符,事件标识符和事件处理函数指针。要注意这里的事件标识符 wxEVT_COMMAND_MENU_SELECTED不同于前面在静态事件表中用于表示事件映射的宏EVT_MENU,实际上EVT_MENU内部也使用了wxEVT_COMMAND_MENU_SELECTED.EVT_MENU其实也自动包含了用于对事件处理指针类型强制转换的宏 wxCommandEventHandler()。一般说来,如果事件处理函数的参数类型是wxXYZEvent,那么其处理函数的类型就应该用 wxXYZEventHandler宏进行强制转换.

如果我们希望在某个时候中止上面的事件映射,可以使用下面的方法:

frame->Disconnect( wxID_EXIT,
wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(MyFrame::OnQuit) );
frame->Disconnect( wxID_ABOUT,
wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventFunction(MyFrame::OnAbout) );

3.6 窗口标识符

窗口标识符是在事件系统中用来唯一确定窗口的整数。事实上,在整个应用程序的范围内,窗口标识符不必一定是唯一的,而只要在某个固定的上下文(比如说,在一个frame窗口和它的所有子窗口)内是唯一的就可以了。举例来说:你可以在无数个对话框中使用wxID_OK这个标识符,只要在某个对话框内不要重复使用就可以了。

如果在窗口的构造函数中使用wxID_ANY作为其标识符,则意味着你希望wxWidgets自动为你生成一个标识符。这或者是因为你不关心这个标识符的值,或者是因为这个窗口不需要处理任何事件,或者是因为你将在同一个地方处理所有的事件。如果是最后一种情况,在使用 wxEvtHandler::Connect函数或者在静态事件表中,你应该使用wxID_ANY作为窗口的标识符。wxWidgets自动创建的标识符是总是一个负数,所以永远不会和用户定义的窗口标识符重复,用户定义的窗口标识符只能是正整数。

下表列举了wxWidgets提供的一些标准的标识符。你应该尽可能的使用这些标识符,这是由于下面一些原因。某些系统会给特定的标识符提供一些小图片(例如GTK+系统上的OK和取消按钮)或者提供默认的处理函数(例如自动产生wxID_CANCEL事件来响应Escape键)。在 Mac OS X系统上,wxID_ABOUT, wxID_PREFERENCES和wxID_EXIT菜单项也有特别的处理。另外一些wxWidgets的控件也会自动处理标识符为 wxID_COPY, wxID_PASTE或 wxID_UNDO等的一些菜单或者按钮的命令。

标识符名称 描述
wxID_ANY 让wxWidgets自动产生一个标识符
wxID_LOWEST 最小的系统标识符值 (4999)
wxID_HIGHEST 最大的系统标识符值 (5999)
wxID_OPEN 打开文件
wxID_CLOSE 关闭窗口
wxID_NEW 新建窗口文件或者文档
wxID_SAVE 保存文件
wxID_SAVEAS 文件另存为(应该弹出文件位置对话框)
wxID_REVERT 恢复文件在磁盘上的状态
wxID_EXIT 退出应用程序
wxID_UNDO 撤消最近一次操作
wxID_REDO 重复最近一次操作
wxID_HELP 帮助 (例如对话框上的帮助按钮可以用这个标识符)
wxID_PRINT 打印
wxID_PRINT_SETUP 打印设置
wxID_PREVIEW 打印预览
wxID_ABOUT 显示一个用来描述整个程序的对话框
wxID_HELP_CONTENTS 显示上下文帮助
wxID_HELP_COMMANDS 显示应用程序命令
wxID_HELP_PROCEDURES 显示应用程序过程
wxID_HELP_CONTEXT 未使用
wxID_CUT 剪切
wxID_COPY 复制到剪贴板
wxID_PASTE 粘贴
wxID_CLEAR 清除
wxID_FIND 查找
wxID_DUPLICATE 复制
wxID_SELECTALL 全选
wxID_DELETE 删除
wxID_REPLACE 覆盖
wxID_REPLACE_ALL 全部覆盖
wxID_PROPERTIES 查看属性
wxID_VIEW_DETAILS 列表框中的按照详细信息方式显示
wxID_VIEW_LARGEICONS 列表框按照大图标的方式显示
wxID_VIEW_SMALLICONS 列表框中按照小图标的方式显示
wxID_VIEW_LIST 列表框中按照列表的的方式显示
wxID_VIEW_SORTDATE 按照日期排序
wxID_VIEW_SORTNAME 按照名称排序
wxID_VIEW_SORTSIZE 按照大小排序
wxID_VIEW_SORTTYPE 按照类型排序
wxID_FILE1 to wxID_FILE9 显示最近使用的文件
wxID_OK 确定
wxID_CANCEL 取消
wxID_APPLY 应用变更
wxID_YES YES
wxID_NO No
wxID_STATIC 静态文本或者静态图片可以用这个标识符
wxID_FORWARD 向前
wxID_BACKWARD 向后
wxID_DEFAULT 恢复默认设置
wxID_MORE 显示更多选项
wxID_SETUP 显示一个设置对话框
wxID_RESET 重置所有选项
wxID_CONTEXT_HELP 显示上下文帮助
wxID_YESTOALL 全部选是
wxID_NOTOALL 全部选否
wxID_ABORT 中止当前操作
wxID_RETRY 重试
wxID_IGNORE 忽略错误
wxID_UP 向上
wxID_DOWN 向下
wxID_HOME 首页
wxID_REFRESH 刷新
wxID_STOP 停止正在进行的操作
wxID_INDEX 显示一个索引
wxID_BOLD 加粗显示
wxID_ITALIC 斜体显示
wxID_JUSTIFY_CENTER 居中
wxID_JUSTIFY_FILL 格式
wxID_JUSTIFY_RIGHT 右对齐
wxID_JUSTIFY_LEFT 左对齐
wxID_UNDERLINE 下划线
wxID_INDENT 缩进
wxID_UNINDENT 反缩进
wxID_ZOOM_100 放大到100%
wxID_ZOOM_FIT 缩放到整页
wxID_ZOOM_IN 放大
wxID_ZOOM_OUT 缩小
wxID_UNDELETE 反删除
wxID_REVERT_TO_SAVED 恢复到上次保存的状态

为了避免你自己定义的标识符和这些预定义的标识符重复,你可以使用大于wxID_HIGHEST的标识符或者小于wxID_LOWEST的标识符。

3.7 自定义事件

如果你要使用自定义的事件,你需要下面的几个步骤:

  1. 从一个合适的事件类派生一个你自己的事件类,声明动态类型信息并且实现一个Clone函数,按照你自己的意愿增加新的数据成员和函数成员.如果你希望这个事件在窗口继承关系之间传递,你应该使用的wxCommandEvent派生类,如果你希望这个事件的处理函数可以调用Veto(译者注:某些事件可以调用这个函数来阻止后续可能对这个事件进行的任何操作(如果有的话),最典型的例子是关闭窗口事件wxEVT_CLOSE),你应该使用 wxNotifyEvent的派生类.
  2. 为这个事件类的处理函数定义类型.
  3. 定义一个你的事件类支持的事件类型的表。这个表应该定义在你的头文件中。用BEGIN_DECLARE_EVENT_TYPES()宏和END_DECLARE_EVENT_TYPES()宏包含起来。其中的每一个支持的事件的声明应该使用DECLARE_EVENT_TABLE (name, integer)格式的宏.然后在你的.cpp文件中使用DEFINE_EVENT_TYPE(name)来实现这个事件类.
  4. 为每个你的事件类支持的事件定义一个事件映射宏。

我们还是通过例子来让上面这段绕口的话显的更生动一些。假如我们要实现一个新的控件wxFontSelectorCtrl,这个控件将可以显示字体的预览。用户通过点击字体的预览来弹出一个对话框让用户可以更改字体。应用程序也许想拦截这个字体改变事件,因此我们在我们的底层鼠标消息处理过程中将会给应用程序发送一个自定义的字体改变事件。

因此我们需要定义一个新的事件wxFontSelectorCtrlEvent.应用程序可以通过事件映射宏 EVT_FONT_SELECTION_CHANGED(id, func)来增加对这个事件的处理。我们还需要给这个事件定义一个事件类型: wxEVT_COMMAND_FONT_SELECTION_CHANGED. 这样,我们的头文件看上去就象下面的样子:

/*!
* Font selector event class
*/
class wxFontSelectorCtrlEvent : public wxNotifyEvent
{
public:
wxFontSelectorCtrlEvent(wxEventType commandType = wxEVT_NULL,
int id = 0): wxNotifyEvent(commandType, id)
{}
wxFontSelectorCtrlEvent(const wxFontSelectorCtrlEvent& event): wxNotifyEvent(event)
{}
virtual wxEvent *Clone() const
{ return new wxFontSelectorCtrlEvent(*this); }
DECLARE_DYNAMIC_CLASS(wxFontSelectorCtrlEvent);
};
typedef void (wxEvtHandler::*wxFontSelectorCtrlEventFunction)
(wxFontSelectorCtrlEvent&);
/*!
* Font selector control events and macros for handling them
*/
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(wxEVT_COMMAND_FONT_SELECTION_CHANGED, 801)
END_DECLARE_EVENT_TYPES()
#define EVT_FONT_SELECTION_CHANGED(id, fn) DECLARE_EVENT_TABLE_ENTRY(
wxEVT_COMMAND_FONT_SELECTION_CHANGED, id, -1, (wxObjectEventFunction) (wxEventFunction)
(wxFontSelectorCtrlEventFunction) & fn,
(wxObject *) NULL ),

而在我们的.cpp文件中,看上去则象下面的样子:

DEFINE_EVENT_TYPE(wxEVT_COMMAND_FONT_SELECTION_CHANGED)
IMPLEMENT_DYNAMIC_CLASS(wxFontSelectorCtrlEvent, wxNotifyEvent)

然后,在我们的新控件的鼠标处理函数中,可以通过下面的方法在检测到用户选择了一个新的字体的时候,发送一个自定义的事件:

wxFontSelectorCtrlEvent event(
wxEVT_COMMAND_FONT_SELECTION_CHANGED, GetId());
event.SetEventObject(this);
GetEventHandler()->ProcessEvent(event);

现在,在使用我们的新控件的应用程序的代码中就可以通过下面代码来处理我们定义的这个新事件了:

BEGIN_EVENT_TABLE(MyDialog, wxDialog)
EVT_FONT_SELECTION_CHANGED(ID_FONTSEL, MyDialog::OnChangeFont)
END_EVENT_TABLE()
void MyDialog::OnChangeFont(wxFontSelectorCtrlEvent& event)
{
// 字体已经更改了,可以作一些必要的处理。
...
}

上面用到的事件标识符801在最新的版本中已经没有用处了,之所以这样写只是为了兼容wxWidgets2.4的版本。

让我们再来看一眼事件映射宏的定义:

#define EVT_FONT_SELECTION_CHANGED(id, fn) DECLARE_EVENT_TABLE_ENTRY(
wxEVT_COMMAND_FONT_SELECTION_CHANGED, id, -1, (wxObjectEventFunction) (wxEventFunction)
(wxFontSelectorCtrlEventFunction) & fn,
(wxObject *) NULL ),

这个宏的作用其实是把一个五元组放入到一个数组中,所以这段代码的语法看上去是奇怪了一些,这个五元组的意义分别解释如下:

  • 事件类型:一个事件类可以处理多种事件类型,但是在我们的例子中,只处理了一个事件类型,所以就只有一个事件映射宏的记录。这个事件类型必须要和事件处理函数所有处理的事件的类型一致。
  • 传递给事件映射宏的标识符:只有当事件的标识符和事件表中的标识符一致的时候,相应的事件处理函数才会被调用。
  • 第二标识符。在这里-1表示没有第二标识符。
  • 事件处理函数。如果没有类型的强制转换,一些编译器会报错,这也就是我们要定义事件处理函数类型的原因。
  • 用户数据,通常都是NULL;

随书附带光盘中的examples/chap03目录中有一个完整的自定义事件的例子,其中包括了一个字体选择控件和一个简单验证类,你可以在你的应用程序中使用她们。你还可以阅读你的wxWidgets发行版目录中的include/wx/event.h来获得更多的灵感。

 

第三章小结

在这一章里,我们讨论了wxWidgets中的事件分发机制,以及怎样进行动态事件处理,还谈到了窗口标识符以及怎样使用自定义的事件。更多关于事件处理的内容,你可以参考附录H,"wxWidgets怎样处理事件"以及附录I"事件处理类和宏",那里列举了主要的事件处理的类和宏。你还可以参考大量的 wxWidgets的例子来学习事件的用法,尤其是wxWidgets发行版目录samples/event这个例子。

在下一章里,我们将讨论一系列重要的GUI控件以及如何在你的程序中使用这些控件.

第四章 窗口的基础知识

在这一章中,在详细描述应用程序中大量使用的各种窗口类之前,我们首先来看看一个窗口的主要元素。这些要素中的大部分相信你以前在各种平台上的编程经验或者使用经验足以让你对它们非常的熟悉。虽然总会有一些平台相关的差异,但是你会发现,单就功能上来说,各个平台之间有着惊人的相似,而wxWidgets 已经屏蔽了几乎所有这些平台上的差异,还剩下的一小部分差异,主要体现在各个平台可选的窗口类型上的不同。

 

4.1 窗口解析

你当然大略的知道一个窗口指的是什么,但是为了更好的理解wxWidgets的API,你应该更精通wxWidgets使用的窗口模型的细节。它可能和你在某个特定平台上的窗口概念有些许的不同。下图演示了一个窗口中的各个基本元素:

窗口的概念

一个窗口指的是屏幕上的任何一个拥有以下特征的规则区域:它可以被改变大小,可以自我刷新,可以被显示和隐藏等等。它可以包含别的窗口(比如frame窗口就可以包含菜单条窗口,工具条窗口以及状态条窗口),也可以子窗口(比如一个静态的文本或者一副静态图片)。通常你在使用wxWidgets编写的程序运行的屏幕上看到的窗口,都和一个wxWindow类或者它的派生类对应,当然也不总是这样。比如本地原生下拉框就不总是用wxWindow建模的。

客户区和非客户区

当我们谈到窗口的大小,我们通常指的是它整个的大小,包括一些用于修饰的边框和标题栏等。而当我们谈到一个窗口的客户区大小,通常都只意味着窗口里面那些能被绘制或者它的子窗口能被放置的位置的大小。例如一个frame窗口的客户区大小就不包括那些菜单栏,状态栏和工具栏所占用的地方。

滚动条

大多数窗口都有显示滚动条的能力,这些滚动条通常是窗口自己增加的而不是有应用程序手动增加的。在这种情况下,客户区的大小还应该减去滚动条所占用的空间。为了优化性能,只有那些拥有wxHSCROLL和wxVSCROLL类型的窗口才会自动生成它们自己的滚动条。关于滚动条更多的情形我们会在本章稍候讨论wxScrolledWindow的时候讨论。

光标和鼠标指针

一个窗口可以拥有一个光标(wxCaret,用来显示当前的文本位置)和一个鼠标指针(wxCursor,用来显示当前鼠标指针的位置).当鼠标移入某个窗口时,wxWidgets会自动显示这个窗口的鼠标指针。当一个窗口变为当前焦点窗口时,如果可以的话,光标将会显示在当前文本的插入位置,或者如果这个焦点是由于鼠标点击造成的,光标将会显示在鼠标对应的位置。

顶层窗口

窗口通常分为象wxFrame,wxDialog,wxPopup这样的顶层窗口和其它窗口。只有顶层窗口创建的时候可以使用NULL作为其父窗口,也指有顶层窗口是延迟删除的(所谓延迟删除的意思是说,它们只有在系统空闲的时候才会被删除,也就是说只有当所有的事件都被处理完以后才会被删除)。而且出了Popup窗口以外,顶层窗口通常都有一个标题栏和一个关闭按钮,只要应用程序允许,它们就可以被拽着满屏幕的跑或者被改变大小。

座标体系

窗口的座标体系通常是左上角为原点(0,0),单位是象素。在某个用于窗口绘制的特定的上下文中,原点和比例允许被改变。这方面详细的可以参考第5章,"窗口绘制和打印".

窗口绘制

当一个窗口需要重绘的时候,它将收到两个事件,wxEVT_ERASE_BACKGROUND事件用于通知应用程序重新绘制背景, wxEVT_PAINT则用于通知重新绘制前景。那些已经准备好使用的窗口空间比如wxButton(按钮)通常知道怎么处理这两个事件,但是如果你是要创建自己的窗口控件,你就需要自己处理这两个事件了。通过获取窗口的变动区域你可以优化你的绘制代码。

颜色和字体

每一个窗口都有一个前景色和一个背景色。默认的背景擦除函数会使用背景色来清除窗口背景,如果没有设置背景色,则会使用当前的系统皮肤推荐的颜色进行背景的清除。前景色则相对来说很少被用到。每一个窗口也拥有一个字体设置,是否用到这个字体设置要取决于这个窗口本身的类型。

窗口变体

在Mac OS X上,一个窗口有一个窗口变体的概念,通过这个概念窗口可以被以不同级别的大小显示:wxWINDOW_VARIANT_NORMAL(默认显示级别), wxWINDOW_VARIANT_SMALL, wxWINDOW_VARIANT_MINI, 或者wxWINDOW_VARIANT_LARGE.当你有很多信息要展示而屏幕空间不够的时候,你可以使用相对较小的级别,但是这个显示级别的使用应该适可而止。有些程序总是喜欢使用小的显示级别。

改变大小

当一个窗口的大小,无论是来自用户还是应用程序本身的原因,发生变化时,它将收到一个wxEVT_SIZE事件。如果这个窗口拥有子窗口,它们可能需要被重新放置和重新计算大小。处理这种情况推荐的方法是使用sizer类,关于这个类的细节将在第7章,"使用Sizer确定窗口的布局" 这一章详细介绍。大多数已经确定的窗口类都有一个默认的大小和位置,这需要你在创建这些窗口的时候使用wxDefaultSize和 wxDefaultPosition这两个特殊的值。到目前为止,每一个控件都实现了DoGetBestSize函数,这个函数会返回一个基于控件的内容,当前字体以及其它各方面来说最合理的大小。

输入

任何窗口在任何时候都可以接收到鼠标事件,除非某个窗口已经临时捕获了鼠标或者这个窗口已经被禁止使用了,而对于键盘事件来说,只有当前处于活动状态的窗口才可以收到。应用程序自己可以设置自己为活动状态,wxWidgets也会在用户点击某个窗口的时候将其设置为活动状态。正变成活动状态的窗口会收到wxEVT_SET_FOCUS事件,而正失去焦点的窗口会收到wxEVT_KILL_FOCUS事件。请参考第6章:处理输入。

空闲事件处理和用户界面更新

所有的窗口(除非特殊声明)都将收到空闲事件wxEVT_IDLE,这个事件是在所有其它的事件都已经被处理完以后发出的。使用EVT_IDLE事件映射宏来处理。更多的信息请参卡第17章,"多线程编程"中的"空闲时间处理"小节.

其中一个特殊的空闲时间操作就是进行用户界面更新,在这个操作中所有的窗口都可以定义一个函数来更新自己的状态。这个函数将会被周期性的在系统空闲时调用。而EVT_UPDATE_UI(id, func)这个宏则通常不需要作什么事情。更多关于用户界面更新的细节请参考第9章,"创建自定义对话框".

窗口的创建和删除

一般来说,窗口都是在堆上使用new方法创建的,第15章,"内存管理,调试和错误检查"这一章对此有详细的描述,也会提到一些例外情况。大多数的窗口类都可以通过两种方法被创建:单步创建和两步创建。比如wxButton的两种构造函数如下:

wxButton();
wxButton(wxWindow* parent,
wxWindowID id,
const wxString& label = wxEmptyString,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxT("button"));

下面演示了使用一步创建的方法创建一个wxButton的实例(其中多数参数采用默认值):

wxButton* button  = new wxButton(parent, wxID_OK);

除非是frame或者dialog窗口,对于别的窗口,都必须在构造函数中传入一个非空的父窗口。这会自动把这个新窗口作为这个父窗口的子窗口。当父窗口被释放的时候,它的所有的子窗口也将被释放。正象我们前面提到的那样,你必须输入一个自定义的或者系统内建的标识符给这个窗口以便唯一标识这个窗口。你也可以使用wxID_ANY宏让wxWidgets帮你生成一个。你可以传递位置和大小参数给这个窗口,一个校验类(参考第9章),一个类型 (接下来会提到),和一个字符串的名字。这个字符串的名字可以是任意的值或者干脆不填也可以。只有在Xt和Motif系统上这个参数才有意义,因为在这些系统上控件是通过它们的名字来区分的,平常情况下则很少用到。

两步创建的意思是说,你先使用默认的构造函数创建一个实例,然后再使用这个实例Create方法实际创建这个对象。Create的参数和前面使用的构造函数的参数完全相同,还是用wxButton作为例子:

wxButton* button  = new wxButton;
button->Create(parent, wxID_OK);

窗口在你调用Create函数的时候会收到wxEVT_CREATE事件,你可以对这个事件进行进一步的处理。

使用两步创建的原因是什么呢?第一个原因是有时侯你可能想在晚些时候,在真正需要的时候才完整的创建窗口。另外一个原因是你希望在调用 Create函数之前设置窗口的某些属性值。尤其是这些属性值被Create函数使用的时候就显的更有意义。例如你可能想在窗口被创建之前设置 wxWS_EX_VALIDATE_RECURSIVELY扩展类型,而这个类型必须通过SetExtraStyle函数才可以设置。在这种情况下,对某些对话框类而言,validation必须在Create函数被调用之前被初始化。所以如果你需要这个功能,就必须在调用Create之前初始化这个值。

当你创建一个窗口类,或者其它任何非顶层窗口的派生类的时候,如果它的父窗口是可见的,那么它也总是可见的,你可以通过Show(false)来使它不可见。这和wxDialog或者wxFrame这样的顶层窗口是不一样的。顶层窗口在创建时通常是不可见的,这样可以避免绘制那些子窗口和排列子控件的时候发生闪烁。你需要通过Show或者(对于模式对话框来说)ShowModal的调用让它可见。

窗口是通过调用其Destroy函数(对于顶层窗口来说)或者delete函数(对于其子窗口来说)来释放的。wxEVT_DESTROY事件会在窗口刚刚要被释放之前被调用。实际上,子窗口是被自动释放的,所以delete函数是很少直接被手动调用的。

窗口类型

窗口拥有一个类型和一个扩展类型。窗口类型是设置窗口创建时的行为和外观的一种简洁的方法。这些类型的值被设置成可以使用类似比特位的方法操作,例如下面的例子:

wxCAPTION | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxTHICK_FRAME

wxWindow类有一组基本的类型值,例如边框的类型等,每一个派生类可以增加它们自己的类型。需要特别指出的是,扩展类型的值是不可以拿来给类型用的。

4.2 窗口类概览

在接下来的章节中,我们会介绍最常用的那些窗口类以便你可以在你的应用程序中使用它们。然而如果你是第一次阅读这本书,你可以直接跳到第5章阅读后面的内容,而在晚些时候你需要使用的时候再回过头来阅读本章的内容。

为了让你先大致浏览一下本章的内容,我们把本章将会讨论的窗口类列举如下。对于其他一些窗口类,请参考第12章,"高级窗口类"以及附录E,"wxWidgets中的第三方工具".

基本窗口类

下面的这些基本的窗口类实现了一些最基本的功能。这些类主要是用来作为别的类型的基类以生成更实用的派生类。

  • wxWindow. 这是所有窗口类的基类。
  • wxControl. 所有控件(比如wxButton)的基类.
  • wxControlWithItems. 是那些拥有多个子项目的控件的基类.

顶层窗口类

顶层窗口类通常指那些独立的位于桌面上的类。

  • wxFrame. 一个可以包含其他窗口,并且大小可变的窗口类。
  • wxMDIParentFrame. 是一个可以管理其他Frame类的类.
  • wxMDIChildFrame. 是一个可以被其父窗口管理的frame类.
  • wxDialog. 是一种可变大小的用于给用户提供选项的窗口类.
  • wxPopupWindow. 是一种暂态的只有很少修饰的顶层窗口.

容器窗口类

容器窗口类可以管理其他窗口

  • wxPanel. 这是一个给其它窗口提供布局的窗口.
  • wxNotebook. 可以实用TAB页面进行切换的窗口.
  • wxScrolledWindow. 可以有滚动条的窗口.
  • wxSplitterWindow. 可以管理两个子窗口的一种特殊窗口类.

非静态控件窗口类

这些控件是用户可以操作或者编辑的。

  • wxButton. 一种拥有一个标签的按钮控件.
  • wxBitmapButton. 一种拥有图片和标签的按钮控件.
  • wxChoice. 拥有一个下拉列表的选择控件.
  • wxComboBox. 拥有一组选项的可编辑的选择控件.
  • wxCheckBox. 拥有一个复选框的控件,复选框有选中和未选中两种状态.
  • wxListBox. 拥有一组可选择的字符串项目的列表框.
  • wxRadioBox. 拥有一组选项的单选框.
  • wxRadioButton. 单选框.
  • wxScrollBar. 滚动条控件。
  • wxSpinButton. 一个拥有增加和减小两个选项的按钮.
  • wxSpinCtrl. 拥有一个文本编辑框和一个wxSpinButton用来编辑整数.
  • wxSlider. 这个控件用来在一个固定的范围内选择一个整数.
  • wxTextCtrl. 单行或者多行的文本编辑框.
  • wxToggleButton. 两态按钮.

静态控件

这些控件提供不能被最终用户编辑的静态信息

  • wxGauge. 用来显式数量的控件.
  • wxStaticText. 文字标签控件.
  • wxStaticBitmap. 用来显示一幅静态图片.
  • wxStaticLine. 用来显式静态的一行.
  • wxStaticBox. 用来在别的控件周围显示一个静态的方框.

菜单

菜单是一种包含一组命令列表的窗口

控件条

控件条通常在Frame窗口中使用,用来为信息或者命令的访问提供快捷操作

  • wxMenuBar. wxFrame上的菜单条.
  • wxToolBar. 工具条.
  • wxStatusBar. 状态条用来在程序运行过程中显示运行期信息

4.3 基础窗口类

虽然你不一定有机会直接使用基础窗口类(wxWindow),但是由于这个类是很多窗口控件的基类,它实现的很多方法在它的子类型中都可以直接拿来使用,所以有必要介绍一下这个基础窗口类。

窗口类wxWindow

wxWindow窗口类既是一个重要的基类,也是一个你可以直接在代码中使用的类。当然,前者使用的频度要比后者大很多。

和前面提到的一样,wxWindow也可以使用单步创建或者两步创建两种方式。单步创建的构造函数原型如下:

wxWindow(wxWindow* parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = wxT("panel"));

可以象下面这样使用:

wxWindow* win  = new wxWindow(parent, wxID_ANY,
wxPoint(100, 100), wxSize(200, 200));

窗口类型

每一个窗口类都可以使用定义在下表中的这些窗口基类中的窗口类型。这些类型中不是所有的类些都被所有的控件所支持。例如对于边框的类型。如果在创建窗口的时候你没有指定窗口的边框类型,那么在不同的平台上将会有不同的边框类型的缺省值。在windows平台上,控件边框的缺省值为 wxSUNKEN_BORDER,意为使用当前系统风格的边框。你可以使用类似wxNO_BORDER这样的值来覆盖系统的默认值。

wxSIMPLE_BORDER 在窗口周围显示一个瘦边框.
wxDOUBLE_BORDER 显示一个双层边框.
wxSUNKEN_BORDER 显示一个凹陷的边框,或者使用当前窗口风格设置.
wxRAISED_BORDER 显示一个凸起的边框.
wxSTATIC_BORDER 显示一个适合静态控件的边框. 只支持Windows平台.
wxNO_BORDER 不显示任何边框.
wxTRANSPARENT_WINDOW 定义一个透明窗口 (意思是这个窗口不接收paint事件).只支持windows平台.
wxTAB_TRAVERSAL 使用这个类型允许非Dialog窗口支持使用TAB进行遍历.
wxWANTS_CHARS 使用这个类型来允许窗口接收包括回车和TAB在内的所有的键盘事件。TAB用来在Dialog类型的窗口中遍历各控件。如果没有设置这个类型,这些特殊的按键事件将不会被产生。
wxFULL_REPAINT_ON_RESIZE 在默认情况下,在窗口客户区大小发生改变时,wxWidgets并不会重画整个客户区。设置这个类型将使得wxWidgets改变这种默认的作法,而保持整个客户区的刷新
wxVSCROLL 显示垂直滚动条.
wxHSCROLL 显示水平滚动条.
wxALWAYS_SHOW_SB 如果一个窗口有滚动条,那么在不需要滚动条的时候(当窗口足够大不需要使用滚动条的时候),禁止滚条而不隐藏滚动条。这个类型目前只支持Windows平台和wxWidgets的wxUniversal版本.
wxCLIP_CHILDREN 只支持Windows平台,用于消除由于擦除子窗口的背景而引起的闪铄.

下表列出了窗口的扩展类型,这些扩展类型不能直接和类型混用,而要使用wxWindow::SetExtraStyle函数来进行设置。

wxWS_EX_VALIDATE_RECURSIVELY 在默认情况下,Validate,transferDataToWindow,和transferDataFromWindow只在窗口的直接子窗口上才可以使用。如果设置了这个扩展类型,那么将可以递归的在各个子窗口上使用。
wxWS_EX_BLOCK_EVENTS wxCommandEvents事件将会在无法在当前事件表中找到匹配的时候在其父窗口中尝试匹配,设置这个扩展属性可以阻止这个行为。Dialog类型的窗口默认设置了这个类型,但是如果SetExtraStyle被应用程序类调用过的话,默认设置可能被覆盖.
wxWS_EX_TRANSIENT 不要使用这个窗口作为其它窗口的父窗口.这个类型一定只能用于瞬间窗口;否则,如果使用它作为一个dialog或者frame类型窗口的父窗口,如果父窗口在子窗口之前释放,可能导致系统崩溃。
wxWS_EX_PROCESS_IDLE 这个窗口应该处理所有的idle事件,包括那些设置了wxIDLE_PROCESS_SPECIFIED模式的idle事件。
wxWS_EX_PROCESS_UI_UPDATES 这个窗口将处理所有的Ui刷新事件,包括那些设置了wxUPDATE_UI_PROCESS_SPECIFIED的UI刷新事件。参考第9章获得和界面刷新有关的更多的内容.

窗口事件

窗口类和它的派生类可以产生下面的事件(鼠标,键盘和游戏手柄产生的事件将会在第6章描述)。

EVT_WINDOW_CREATE(func) 用于处理wxEVT_CREATE事件, 这个事件在窗口刚刚被产生的时候生成,处理函数的参数类型是wxWindowCreateEvent.
EVT_WINDOW_DESTROY(func) 用于处理wxEVT_DELETE事件,在这个窗口即将被删除的时候产生,处理函数的参数类型是wxWindowDestroyEvent.
EVT_PAINT(func) 用于处理wxEVT_PAINT事件,在窗口需要被刷新的时候产生.处理函数的参数类型是wxPaintEvent.
EVT_ERASE_BACKGROUND(func) 用于处理wxEVT_ERASE_BACKGROUND事件,在窗口背景需要被更新的时候产生. 处理函数的参数是wxEraseEvent.
EVT_MOVE(func) 用于处理wxEVT_MOVE事件, 在窗口移动的时候产生.处理函数的参数类型是wxMoveEvent.
EVT_SIZE(func) 用于处理wxEVT_SIZE事件, 在窗口大小发生变化以后产生.处理函数的参数类型是wxSizeEvent.
EVT_SET_FOCUS(func)EVT_KILL_FOCUS(func) 用于处理wxEVT_SET_FOCUS和wxEVT_KILL_FOCUS事件,在窗口得到或者失去键盘焦点的时候产生. 处理函数参数类型是wxFocusEvent.
EVT_SYS_COLOUR_CHANGED(func) 用于处理wxEVT_SYS_COLOUR_CHANGED事件,当用户在控制面板里更改了系统颜色的时候产生(只支持windows平台). 处理函数参数类型为wxSysColourChangedEvent.
EVT_IDLE(func) 用于处理wxEVT_IDLE事件,在空闲事件产生.处理函数参数类型位wxIdleEvent.
EVT_UPDATE_UI(func) 用于处理wxEVT_UPDATE_UI事件,在系统空闲时间产生用来给窗口一个更新自己的机会.

wxWindow类的成员函数

因为wxWindow类是其它所有窗口类的基类,它拥有很多的成员函数。我们不可能在这里作一一的说明,只能拣其中最重要的一些作简要的说明。不过我们还是推荐你浏览一下wxWidgets手册中的相关部分,以便能够彻底了解wxWindow类提供的所有功能,以及要使用这个功能你需要提供的参数等。

CaptureMouse函数可以捕获所有的鼠标输入(将其限制在本窗口以内),ReleaseMouse则可以释放前一次的捕获.在绘图程序中,这两个函数是很有用的。它可以让你在鼠标移动到窗口边缘的时候来滚动窗口的客户区,而不是任由鼠标跑到别的窗口并且激活别的窗口。另外的两个函数GetCapture用来获取当前正在使用的捕获设备(如果是被当前的应用程序设置的话),而HasCapture可以用来检测是否鼠标正被这个窗口捕获。

Centre, CentreOnParent和CentreOnScreen三个函数可以让窗口调整自己的位置使其位于屏幕或者是其父窗口的正中间位置。

ClearBackground函数将使用当前的背景色清除当前窗口.

ClientToScreen和ScreenToClient可以将座标在相对于屏幕左上角和相对于客户区左上角之间进行互相转换.

Close函数产生一个wxCloseEvent事件,这个事件通常的处理过程将关闭窗口,释放内存。当然如果应用程序为这个事件定义了特殊的处理函数,那么窗口也有可能不被关闭和释放。

ConvertDialogToPixels和ConvertPixelsToDialog函数可以对数值进行对话框单位和象素单位之间的转换。这在基于字体大小以便应用程序的显示更合理的操作中是很有用的.

Destroy函数将安全的释放窗口.使用这个函数代替直接调用delete操作符因为这个函数下不同的窗口类型的表现是不一样的。对于对话框和frame这样的顶层窗口来说,这个函数会将窗口放入一个等待删除的额窗口列表中,等到这个窗口的所有的事件都处理完毕的时候才会被删除,这可以避免一些事件在已经不存在的窗口上被执行.

Enable允许或者禁止窗口和它的子窗口处理输入事件。在禁止状态下一些窗口会有不同的颜色和外观。Disable函数和Enable函数使用false作为参数的效果是完全一样的。

FindFocus函数是一个静态函数,用它可以找到当前拥有键盘焦点的窗口。

FindWindow函数可以通过标识符或者名字在它的窗口关系树中寻找某个窗口. 返回值可能是它的一个子窗口或者它自己.如果你可以确定你要找的窗口的类型,可以安全的使用wxDynamicCast进行类型强制转换,转换的结果将是一个指向那个类型的指针或者是NULL:

MyWindow* window = wxDynamicCast(FindWindow(ID_MYWINDOW), MyWindow);

Fit函数会自动改变窗口的大小以便刚好可以容纳它的所有子窗口.这个函数应用被应用在使用基于sizer的布局的时候。FitInside函数是一个类似的函数,区别在于它使用的是虚大小(应用在那些包含滚动条的窗口).

Freeze和Thaw,这两个函数的作用是告诉wxWidgets,在这两个函数之间进行的刷新界面的操作是允许被优化的。举例来说,如果你要在一个文本框控件逐行中加入多行文本,则可以用这个方法来优化显示效果,避免闪烁,这两个函数已经在GTK+版本的wxTextCtrl控件上实现,也适用于 Windows和Max Os X平台的所有类.

GetAcceleratorTable和SetAcceleratorTable用来获取和设置某个窗口的加速键表.

GetBackgroundColour和SetBackgroundColour用来访问窗口的背景颜色属性。这个属性被 wxEVT_ERASE_BACKGROUND事件用来绘制窗口背景.如果你更改了背景颜色设置,你应该调用Refresh或者 ClearBackground函数来刷新背景. SetOwnBackgroundColour的作用和SetBackgroundColour基本相同,但是前者不会更改当前窗口的子窗口的背景属性.

GetBackgroundStyle和SetBackgroundStyle用来设置窗口的背景类型. 默认的窗口背景类型是wxBG_STYLE_SYSTEM,它的含义是wxWidgets按照系统默认设置来进行背景绘制。系统默认的背景绘制方法根据控件的不同而不同,比如wxDialog的默认背景绘制方法是什么纹理绘制的方法,而wxListBox则是使用固定颜色的绘制方法。如果你设置了窗口的背景绘制方法类型是wxBG_STYLE_COLOUR,那么wxWidgets会全部用单一颜色的方法绘制背景。而如果你将其设置为 wxBG_STYLE_CUSTOM, wxWidgets将不进行任何的背景绘制工作,你可以自己在擦除背景事件中或者重画事件中自己绘制背景.如果你希望自己绘制背景,请一定设置 wxBG_STYLE_CUSTOM为背景类型,否则很容易引起画面的闪烁.

GetBestSize函数以象素为单位返回窗口最合适的大小(因为每个窗口类都需要实现DoGetBestSize函数).这个函数用来提示Sizer系统不要让这个窗口的尺寸太小以致不能正确的显示和使用。举例来说,对于静态文本控件来说,不要让它的字符只能显示一半。对于包含其它子窗口的窗口比如wxPanel来说,这个尺寸等于对这个窗口调用Fit函数以后的尺寸的大小。

GetCaret和SetCaret函数用来访问或者设置窗口的光标.

GetClientSize和SetClientSize用来访问和设置窗口的客户区大小,单位是象素. 客户区大小指的是不包括边框和修饰的区域,或者说你用户可以绘制或者用来放置子窗口的区域.

GetCursor和SetCursor函数用来访问和设置窗口的鼠标指针.

GetDefaultItem函数返回一个指向这个窗口默认的子按钮的指针或者返回NULL。默认子按钮是当用户按回车键的时候默认激活的按钮,可以通过wxButton::SetDefault函数进行设置.

GetDropTarget和SetDropTarget函数用来取得或者设置和窗口关联的wxDropTarget对象,这个对象用来处理和控制窗口的拖放操作. 我们将在第11章"剪贴板和拖放"中详细介绍拖放有关的操作。

GetEventHandler和SetEventHandler函数用来访问和设置窗口的第一事件表.默认情况下,窗口的事件表就是窗口自己,但是你可以指定不同的事件表,也可以通过PushEventHandler和PopEventHandler函数设置一个事件表链。然后让不同的事件表处理不同的事件. wxWidgets将搜索整个事件表链来寻找匹配的事件处理函数处理新收到的事件,详情请参阅第3章,“事件处理”.

GetExtraStyle和SetExtraStyle函数用来获取和设置窗口的扩展类型位。扩展类型宏通常以wxWS_EX_开头.

GetFont和SetFont函数获取和设置和窗口相关的字体. SetOwnFont和SetFont的功能相似,唯一的区别在于前者设置的字体不会被子窗口继承.

GetForegroundColour和SetForegroundColour函数用来操作窗口的前景颜色属性. 和SetOwnForegroundColour函数的区别仅在于是否改变子窗口的这个属性。

GetHelpText和SetHelpText用来获取和设置窗口相关的上下文帮助.这个文本属性实际上存储在当前的wxHelpProvider实现中,而不是存在于窗口类中.

GetId和SetId用来操作窗口标识符.

GetLabel函数返回窗口相关联的标签.具体的含义取决的特定的窗口类.

GetName和SetName用来操作窗口名称,这个名字不需要是唯一的。这个名称对wxWidgets来说没有什么意义,但是在Motif系统中被用于窗口资源的名称.

GetParent返回窗口的父窗口.

GetPosition以象素单位返回相对于父窗口的窗口左上角座标。

GetRect返回一个wxRect对象 (参考第13章, "数据结构和类型"),其中包含了象素单位的这个窗口的大小和位置信息.

GetSize和SetSize函数获取和设置窗口象素单位的窗口长宽.

GetSizer和SetSizer函数用来操作这个窗口绑定的最顶级的窗口布局对象.

GetTextExtent用于返回当前字体下某个字符串的象素长度.

GetToolTip和SetToolTip用来操作这个窗口的tooltip对象.

GetUpdateRegion函数返回窗口自上次Paint事件以来需要刷新的区域.

GetValidator和SetValidator函数用来操作这个窗口可选的wxValidator对象。详情请参考第9章.

GetVirtualSize返回窗口的虚大小,通常就是和滚动条绑定的那个大小.

GetWindowStyle和SetWindowStyle用来操作窗口类型比特位。

InitDialog函数发送一个wxEVT_INIT_DIALOG事件来来开始对话框数据传送.

IsEnabled用来检测当前窗口的使能状态.

IsExposed用来检测一个点或者一个矩形区域是否位于需要刷新的范围。

IsShown用来指示窗口是否可见.

IsTopLevel用来指示窗口是否是顶层窗口(仅用于wxFrame或者wxDialog).

Layout函数用来在窗口已经指定一个布局对象的情况下更新窗口布局。参考第7章.

Lower函数用来将窗口移到窗口树的最低层,而Raise则把窗口移动到窗口树的最顶层.这两个函数既可以用于顶层窗口,也可以用于子窗口.

MakeModal函数禁用其它所有的顶层窗口,以便用户只能和当前这个顶层窗口交互.

Move函数用来移动窗口.

MoveAfterInTabOrder更改窗口的TAB顺序到作为参数的窗口的后面, 而MoveBeforeInTabOrder则将其挪到参数窗口的前面.

PushEventHandler压入一个事件表到当前的事件表栈, PopEventHandler函数弹出并且返回事件表栈最顶层的事件表. RemoveEventHandler则查找事件表栈中的一个事件表并且将其出栈.

PopupMenu函数在某个位置弹出一个菜单.

Refresh和RefreshRect函数导致窗口收到一个重画事件(和一个可选的擦除背景事件).

SetFocus函数让本窗口收到键盘焦点.

SetScrollbar函数用来设置窗口内建的滚动条的属性.

SetSizeHints函数用来定义窗口的最小最大尺寸,依次窗口尺寸增量的大小,仅对顶层窗口适用.

Show函数用来显示和隐藏窗口; Hide函数的作用相当于适用false作为参数调用Show函数.

transferDataFromWindow和transferDataToWindow获取和传输数据到窗口对象,并且在没有被重载的情况下会调用验证函数.

Update立即重画窗口已经过期的区域.

UpdateWindowUI函数发送wxUpdateUIEvents事件到窗口,以便给窗口一个更新窗口元素(比如工具条和菜单)的机会.

Validate使用当前的验证对象验证窗口数据.

wxControl类

wxControl是一个虚类。它继承自wxWindow,用来作为控件的基类: 所谓控件指的是那些可以显示数据项并且通常需要响应鼠标或者键盘事件的那些窗口类.

wxControlWithItems类

wxControlWithItems也是一个虚类,用来作为wxWidgets的一些包含多个数据项的控件(比如wxListBox, wxCheckListBox,wxChoice和wxComboBox等)的基类。使用这个基类的目的为了给这些具有相似功能的控件提供一致的API函数。

wxControlWithItems的数据项拥有一个字符串和一个和这个字符串绑定的可选的客户数据。客户数据可以是两种类型,要么是无类型指针(void*),这意味这这个控件只帮忙存储客户数据但是永远不会使用客户数据。另外一种是有类型(wxClientData)指针,对于后一种情况,客户数据会在控件被释放或者数据项被清除的时候被自动释放。同一个控件的所有数据项必须拥有同样的客户区数据类型:要么是前者,要么是后者。客户区数据的类型是在第一次调用Append函数或者,SetClientData函数或者SetClientObject函数的时候被确定的。如果要使用有类型指针客户数据,你应该自定义一个继承自wxClientData的类,然后将它的实例指针传递给Append函数或者 SetClientObject函数。

wxControlWithItems的成员函数

Append函数给这个控件增加一个或者一组数据项. 如果是增加一个数据项,你可以用第二个参数指定有类型客户数据指针或者无类型客户数据指针。比如:

wxArrayString strArr;
strArr.Add(wxT("First string"));
strArr.Add(wxT("Second string"));
controlA->Append(strArr);
controlA->Append(wxT("Third string"));
controlB->Append(wxT("First string"), (void *) myPtr);
controlC->Append(wxT("First string"), new MyTypedData(1));

Clear函数清除控件所有数据项并且清除所有的客户数据.

Delete函数使用基于0的索引清除一个数据项以及它的客户数据。

FindString返回一个和某个字符串基于0的数据项的索引。如果找不到则返回wxNOT_FOUND.

GetClientData和GetClientObject返回某个数据项的客户区数据指针(如果有的话). SetClientData和SetClientObject则可以用来设置这个指针.

GetCount数据项的总数.

GetSelection返回选中的数据项或wxNOT_FOUND. SetSelection则用来设置某个数据项为选中状态.

GetString用来获取某个数据项的字符串; SetString则用来设置它.

GetStringSelection用来返回选中的数据项的字符串或者一个空的字符串; SetStringSelection则用来设置选中的字符串。你应该先调用FindString函数保证这个字符串是存在于某个数据项的,否则可能引发断言失败.

Insert在控件数据项的某个位置插入一个数据项(可以包含也可以不包含客户数据).

IsEmpty则用来检测一个控件的数据项是否为空.

4.4 顶层窗口

顶层窗口直接被放置在桌面上而不是包含在其它窗口之内。如果应用程序允许,他们可以被移动或者重新改变大小。总共有三种基础的顶层窗口类型。 wxFrame和wxDialog都是从一个虚类wxTopLevelWindow继承来的。另外一个wxPopupWindow功能相对简单,是直接从 wxWindow继承过来的。一个dialog既可以是模式的也可以是非模式的,而frame通常都是非模式的。模式对话框的意思是说当这个对话框弹出时,应用程序除了等待用户关闭这个对话框以外不再作别的事情。对于那些要等待用户响应以后才能继续的操作来说。这是比较合适的。但是寻找替代的解决方案通常也不是一件坏事。举例来说,在工具条上设置一个字体选项可能比弹出一个模式的对话框让用户选择字体更能是整个应用程序看上去更流畅。

顶层窗口通常都拥有一个标题栏,这个标题栏上有一些按钮或者菜单或者别的修饰用来关闭,或者最小化,或者恢复这个窗口。而frame窗口则通常还会拥有菜单条,工具条和状态条。但是通常对话框则没有这些。在Mac OS X上,frame窗口的菜单条通常不是显示在frame窗口的顶部而是显示在整个屏幕的顶部。

不要被"顶层窗口"的叫法和wxApp::GetTopWindow函数给搞糊涂了。wxWidgets使用后者来得到应用程序的主窗口,这个主窗口通常是你在应用程序里创建的第一个frame窗口或者dialog窗口。

如果需要,你可以使用全局变量wxTopLevelWindows来访问所有的顶层窗口,这个全局变量是一个wxWindowList类型.

wxFrame

wxFrame是主应用程序窗口的一个通用的选择。下图演示了常见的各个frame窗口元素。frame窗口拥有一个可选的标题栏(上面有一些类似关闭功能的按钮),一个菜单条,一个工具栏,一个状态栏。剩下的区域则称为客户区,如果拥有超过一个子窗口,那么他们的尺寸和位置是由应用程序决定的。你应该通过第7章中会讲到的窗口布局控件来作相关的事情。或者如果只有两个子窗口的话,你可以考虑使用一个分割窗口。后者我们稍后就会讲到。

在某些平台上不允许直接绘制frame窗口,因此你应该增加一个wxPanel作为容器来放置别的子窗口控件,以便可以通过键盘来遍历各个控件。

frame窗口是允许包含多个工具条的,但是它只会自动放置第一个工具条。因此你需要自己明确的布局另外的工具条。

你很应该基于wxFrame实现你自己的frame类而不是直接使用wxFrame类,并且在其构造函数中创建自己的菜单条和子窗口。这会让你更方便处理类似wxEVT_CLOSE事件和别的命令事件。

你可以给frame窗口指定一个图标以便让系统显示在任务栏上或者文件管理器中。在windows平台上,你最好指定一组16x16和 32x32并且拥有不同颜色深度的图标。在linux平台上也一样,windows系统上的图标已经足够。而在Max OsX上,你需要更多的不同颜色和深度的图标。关于图标和图标列表的更多信息,可以参考第10章,�在应用程序中使用图片�。

除了默认的构造函数以外,wxFrame还拥有下面的构造函数

wxFrame(wxWindow* parent, wxWindowID id, const wxString& title,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxDEFAULT_FRAME_STYLE,
const wxString& name = wxT("frame"));

例如:

wxFrame* frame = new wxFrame(NULL, ID_MYFRAME,
wxT("Hello wxWidgets"), wxDefaultPosition,
wxSize(500, 300));
frame->Show(true);

注意在你显式的调用Show函数之前,frame是不会被显示的。以便应用程序有机会在所有的子窗口还没有被显示的时候进行窗口布局。

没有必要给这个新建的frame窗口指定父窗口。如果指定了父窗口并且指定了wxFRAME_FLOAT_ON_PARENT类型位,这个新建的窗口将会显示在它的父窗口的上层。

要删除一个frame窗口,应该使用Destroy方法而不是使用delete操作符,因为Destroy会在处理完这个窗口的所有事件以后才真正释放这个窗口。调用Close函数会导致这个窗口收到wxEVT_CLOSE事件,这个事件默认的行为是调用Destroy方法。当一个 frame窗口被释放时。它的所有的子窗口都将被释放,谁叫他们自己不是顶层窗口呢。

当最后一个顶层窗口被释放的时候,应用程序就退出了(这种默认的行为可以通过调用wxApp:: SetExitOnFrameDelete改变)。你应该在你的主窗口的wxEVT_CLSE事件处理函数中调用Destroy释放其它的顶层窗口(比如一个查找对话框之类),否则可能出现主窗口已经关闭但是应用程序却不退出的情况。

frame窗口没有类似dialog窗口那样的wxDialog::ShowModal方法可以禁止其它顶层窗口处理消息,然后,还是可以通过别的方法达到类似的目的。一个方法是通过创建一个wxWindowDisabler对象,另外一个方法是通过wxModalEventLoop对象,传递这个frame窗口的指针作为参数,然后调用Run函数开始一个本地的事件循环,然后调用Exit函数(通常是在这个Frame窗口的某个事件处理函数里)退出这个循环。

下图演示了一个用户应用程序的frame窗口在windows上运行的样子。这个frame窗口拥有一个标题栏,一个菜单条,一个五彩的工具栏,在frame窗口的客户区有个分割窗口,底端有一个状态条,正显示着当用户的鼠标划过工具条上的按钮或者菜单项的时候显示的提示。

wxFrame的窗口类型比特位

Frame窗口比起基本窗口类,增加了下面一些类型:

wxDEFAULT_FRAME_STYLE 其值为 wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX.
wxICONIZE 以最小化的方式显示窗口。目前只适用于Windows平台.
wxCAPTION 在窗口上显示标题.
wxMINIMIZE 和wxICONIZE的意义相同. 也只适用于Windows平台.
wxMINIMIZE_BOX 显示一个最小化按钮.
wxMAXIMIZE 以最大化方式显示窗口. 仅适用于Windows.
wxMAXIMIZE_BOX 在窗口上显示最大化按钮.
wxCLOSE_BOX 在窗口上显示关闭按钮.
wxSTAY_ON_TOP 这个窗口显示在其它所有顶层窗口之上.仅适用于Windows.
wxSYSTEM_MENU 显示系统菜单.
wxRESIZE_BORDER 边框可改变大小.
wxFRAME_TOOL_WINDOW 窗口的标题栏比正常情况要小,而且在windows平台上,这个窗口不会在任务栏上显示.
wxFRAME_NO_TASKBAR 创建一个标题栏是正常大小,但是不在任务栏显示的窗口。目前支持windows和linux。需要注意在windows平台上,这个窗口最小化时将会最小化到桌面上,这有时候会让用户觉得不习惯,因此这种类型的窗口最好不要使用wxMINIMIZE_BOX类型。
wxFRAME_FLOAT_ON_PARENT 拥有这种类型的窗口总会显示在它的父窗口的上层。使用这种类型的窗口必须拥有非空父窗口,否则可能引发断言错误.
wxFRAME_SHAPED 拥有这种类型的窗口可以通过调用SetShape函数来使其呈现不规则窗口外貌.

下表列出的是frame窗口的扩展类型,需要使用wxWindow::SetExtraStyle函数才可以设置扩展类型

wxFRAME_EX_CONTEXTHELP 在windows平台上,这个扩展类型导致标题栏增加一个帮助按钮。当这个按钮被点击以后,窗口会进入一种上下文帮助模式。在这种模式下,任何应用程序被点击时,将会发送一个wxEVT_HELP事件。
wxFRAME_EX_METAL 在Mac OS X平台上,这个扩展类型将会导致窗口使用金属外观。请小心使用这个扩展类型,因为它可能会假定你的应用程序用户默认安装类声卡之类的多媒体设备。

wxFrame的事件

下表列出了frame窗口比基本窗口类增加的事件类型:

EVT_ACTIVATE(func) 用来处理wxEVT_ACTIVATE事件, 在frame窗口被激活或者去激活的时候产生.处理函数的参数类型为wxActivateEvent.
EVT_CLOSE(func) 用来处理wxEVT_CLOSE事件, 在应用程序准备关闭窗口的时候产生. 处理函数的参数类型是wxCloseEvent。这个类型支持Veto函数调用以阻止这个事件的进一步处理.
EVT_ICONIZE(func) 用来处理wxEVT_ICONIZE事件, 当窗口被最小化或者被恢复普通大小的时候产生.处理函数的参数类型为wxIconizeEvent. 通过使用IsIconized函数来判断究竟是最小化事件还是恢复事件.
EVT_MAXIMIZE(func) 用来处理wxEVT_MAXIMIZE事件, 当窗口被最大化或者从最大化恢复的时候产生. 处理函数的参数类型为wxMaximizeEvent. 通过IsMaximized函数判断究竟是最大化还是从最大化状态恢复.

wxFrame的成员函数

下面将介绍wxFrame类的主要的成员函数。因为wxFrame类是从wxTopLevelWindow和wxWindow继承过来的,请同样参考这两个类的成员函数。

CreateStatusBar函数在frame窗口的底部创建一个拥有一个或多个域的状态栏. 可以使用SetStatusText函数来设置状态栏上的文字,使用SetStatusWidths函数来设置状态栏每个域的宽度(参考本章稍后对 wxStatusBar的介绍). 使用举例:

frame->CreateStatusBar(2, wxST_SIZEGRIP);
int widths[3] = { 100, 100, -1 };
frame->SetStatusWidths(3, widths);
frame->SetStatusText(wxT("Ready"), 0);

CreateToolBar 函数在frame窗口的菜单条下创建一个工具栏. 当然你也可以直接创建一个wxToolBar实例,然后调用wxFrame::SetToolBar函数以便让frame类来管理你刚创建的toolbar.

GetMenuBar用来SetMenuBar操作frame和绑定的菜单条.一个frame窗口只可以有一个菜单条。如果你新创建了一个,那么就的那个将被删除和释放。

GetTitle和SetTitle函数用来访问窗口标题栏上的文本.

Iconize函数使得frame窗口最小化或者从最小化状态恢复.你可以使用IsIconized函数检查当前的最小化状态.

Maximize函数使得frame窗口最大化或者从最大化状态恢复. 你可以用IsMaximized函数来检查当前的最大化状态.

SetIcon函数可以设置在frame窗口最小化的时候显示的图标。各个平台的窗口管理器也可能把这个图标用于别的用途,比如显示在任务条上或者在显示在窗口浏览器中。你还可以通过SetIcons函数指定一组不同颜色和深度的图标列表.

SetShape函数用来给frame窗口设置特定的显示区域。目前支持这个函数的平台有Windows, Mac OS X, 和GTK+以及打开某种X11扩展功能的X11版本.

ShowFullScreen函数使用窗口在全屏和正常状态下切换,所谓全屏状态指的是frame窗口将隐藏尽可能多的修饰元素,而尽可能把客户区以最大化的方式显示。可以使用IsFullScreen函数来检测当前的全屏状态.

不规则的Frame窗口

如果你希望制作一个外貌奇特的应用程序,比如一个时钟程序或者一个媒体播放器,你可以给frame窗口指定一个不规则的区域,只有这个区域范围内的部分才会被显示出来。下图演示了一个没有任何标题栏,状态栏,菜单条的frame窗口,它的重画函数显示了一个企鹅,这个窗口被设置了一个区域使得只有这个企鹅才会显示。

显示一个有着奇特外观的程序的代码并不复杂,你可以在附带光盘的samples/shaped目录里找到一个使用不同图片的完整的例子。总的来说,当窗口被创建时,它加载了一幅图片,并且使用这幅图片的轮廓创建了一个区域。在GTK+版本上,设置窗口区域的动作必须在发送了窗口创建事件之后,所以你需要使用宏定义区别对待GTK+的版本。下面的例子演示了你需要怎样对事件表,窗口的构造函数和窗口的创建事件处理函数进行修改:

BEGIN_EVENT_TABLE(ShapedFrame, wxFrame)
EVT_MOTION(ShapedFrame::OnMouseMove)
EVT_PAINT(ShapedFrame::OnPaint)
#ifdef __WXGTK__
EVT_WINDOW_CREATE(ShapedFrame::OnWindowCreate)
#endif
END_EVENT_TABLE()
ShapedFrame::ShapedFrame()
: wxFrame((wxFrame *)NULL, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxSize(250, 300),
| wxFRAME_SHAPED
| wxSIMPLE_BORDER
| wxFRAME_NO_TASKBAR
| wxSTAY_ON_TOP
)
{
m_hasShape = false;
m_bmp = wxBitmap(wxT("penguin.png"), wxBITMAP_TYPE_PNG);
SetSize(wxSize(m_bmp.GetWidth(), m_bmp.GetHeight()));
#ifndef __WXGTK__
// On wxGTK we can't do this yet because the window hasn't
// been created yet so we wait until the EVT_WINDOW_CREATE
// event happens. On wxMSW and wxMac the window has been created
// at this point so we go ahead and set the shape now.
SetWindowShape();
#endif
}
// Used on GTK+ only
void ShapedFrame::OnWindowCreate(wxWindowCreateEvent& WXUNUSED(evt))
{
SetWindowShape();
}

为了创建一个区域模板,我们从一幅图片的创建了一个区域,其中的颜色参数用来指定需要透明显示的颜色,然后调用frame窗口的SetShape函数设置这个区域模板。

void ShapedFrame::SetWindowShape()
{
wxRegion region(m_bmp, *wxWHITE);
m_hasShape = SetShape(region);
}

为了让这个窗口可以通过鼠标拖拽来实现在窗口上的移动,我们可以通过下面的鼠标移动事件处理函数:

void ShapedFrame::OnMouseMove(wxMouseEvent& evt)
{
wxPoint pt = evt.GetPosition();
if (evt.Dragging() && evt.LeftIsDown())
{
wxPoint pos = ClientToScreen(pt);
Move(wxPoint(pos.x - m_delta.x, pos.y - m_delta.y));
}
}

而重画事件处理函数就比较简单了,当然在你的真实的应用程序里,你可以在其中放置更多的图形元素。

void ShapedFrame::OnPaint(wxPaintEvent& evt)
{
wxPaintDC dc(this);
dc.DrawBitmap(m_bmp, 0, 0, true);
}

你也可以参考wxWidgets的发行版中的samples/shaped目录中的例子。

小型frame窗口

在Window平台和GTK+平台上,你可以使用wxMiniFrame来实现那些必须使用小的标题栏的窗口,例如,来实现一个调色板工具。下图(译者注:这个图片应该是搞错了,是下一个例子中的图片,不过作者的翻译源使用的就是这个图片,没有办法了。)演示了windows平台上的这种小窗口的样子。而在Max Os X平台上,这个小窗口则是直接使用的普通的frame窗口代替。其它方面wxMiniFrame和wxFrame都是一样的。

wxMDIParentFrame

这个窗口类继承自wxFrame,是wxWidgets的MDI(多文档界面)体系的组成部分。MDI的意思是由一个父窗口管理零个或多个子窗口的一种界面架构。这种MDI界面结构依平台的不同而有不同的外观,下图演示了两种主要的外观。在windows平台上,子文档窗口是排列在其父窗口内的一组frame窗口,这些文档窗口可以被平铺,层叠或者把其中的某一个在父窗口的范围内最大化以便在某个时刻仅显示一个文档窗口。 wxWidgets会自动在主菜单上增加一组菜单用来控制文档窗口的这些操作。MDI界面的一个优点是使得整个应用程序界面相对来说显得不那么零乱。这一方面是由于因为所有的文档窗口被限制在父窗口以内,另外一个方面,父窗口的菜单条会被活动的文档窗口的菜单条替代,这使得多个菜单条的杂乱性也有所减轻。

而在GTK+平台上,wxWidgets则通过TAB页面控件来模拟多文档界面。在某个时刻只能有一个窗口被显示,但是用户可以通过TAB页面在窗口之间进行切换。在Mac Os上,MDI的父窗口和文档窗口则都使用和普通的Frame窗口一样的外观,这符合这样的一个事实就是在Mac Os上,文档总是在一个新的窗口中被打开。

在那些MDI的文档窗口包含在父窗口之中的平台上,父窗口将把它的所有的文档窗口排列在一个子窗口中,而这个窗口可以和frame窗口中的其它控件子窗口和平共处。在上图中,父窗口将一个文本框控件和那些文档窗口进行了这样的排列。更详细的情形请参考wxWidgets发行版本的 samples/mdi目录中的例子。

除了父窗口的菜单条外,每一个文档窗口都可以有自己的菜单条。当某个文档窗口被激活时,它的菜单条将显示在父窗口上,而没有子文档窗口被激活时,父窗口显示自己的菜单条。在构建子文档窗口的菜单条时,你应该主要要包含那些同样命令的父窗口的菜单项,再加上文档窗口自己的命令菜单项。父窗口和文档窗口也可以拥有各自的工具条和状态栏,但是这两者并没有类似菜单条这样的机制。

wxMDIParentFrame类的构造函数和wxFrame类的构造函数是完全一样的.

wxMDIParentFrame的窗口类型

wxMDIParentFrame类额外的窗口类型列举如下:

| --- | --- |
| wxFRAME_NO_WINDOW_MENU | Under Windows, removes the Window menu that is normally added automatically. |

wxMDIParentFrame的成员函数

下面列出了wxMDIParentFrame类除了wxFrame的函数以外的主要成员函数。

ActivateNext和ActivatePrevious函数激活前一个或者后一个子文档窗口。

Cascade和Tile层叠或者平铺所有的子窗口. ArrangeIcons函数以图标的方式平铺所有最小化的文档窗口.这三个函数都只适用于Windows平台.

GetActiveChild获取当前活动窗口的指针(如果有的话).

GetClientWindow函数返回那个包含所有文档窗口的子窗口的指针.这个窗口是自动创建的,但是你还是可以通过重载 OnCreateClient函数来返回一个你自己的继承自wxMDIClientWindow的类的实例。如果你这样作,你就需要使用两步创建的方法创建这个多文档父窗口类.

wxMDIChildFrame

wxMDIChildFrame窗口应该总被创建为一个wxMDIParentFrame类型窗口的子窗口. 正如我们前面已经提到的那样,依平台的不同,这种类型的窗口既可以是在其父窗口范围内的一组窗口列表,也有可能是在桌面上自由飞翔的普通的frame窗口。

除了父窗口必须不能为空,它的构造函数和普通的frame的构造函数是一样的。

#include "wx/mdi.h"
wxMDIParentFrame* parentFrame = new wxMDIParentFrame(
NULL, ID_MYFRAME, wxT("Hello wxWidgets"));
wxMDIChildFrame* childFrame = new wxMDIChildFrame(
parentFrame, ID_MYCHILD, wxT("Child 1"));
childFrame->Show(true);
parentFrame->Show(true);

wxMDIChildFrame的窗口类型

wxMDIChildFrame和wxFrame的窗口类型是一样的。尽管如此,不是所有的窗口类型的值在各个平台上都是有效的。

wxMDIChildFrame的成员函数

wxMDIChildFrame的除其基类wxFrame以外的主要成员函数列举如下:

Activate函数激活本窗口,将其带到前台并且使得其父窗口显示它的菜单条。

Maximize函数将其在父窗口范围内最大化(仅适用于windows平台).

Restore函数使其从最大化的状态恢复为普通状态(仅适用于windows平台)。

wxDialog

对话框是一种用来提供信息或者选项的顶层窗口。他可以有一个可选的标题栏,标题栏上可以有可选的关闭或者最小化按钮,和frame窗口一样,你也可以给它指定一个图标以便显示在任务栏或者其它的地方。一个对话框可以组合任何多个非顶层窗口或者控件,举例来说,可以包含一个wxNoteBook控件和位于底部的两个确定和取消按钮。正如它的名字所指示的那样,对话框的目的相比较于整个应用程序的主窗口,只是为了给用户提示一些信息,提供一些选项等。

有两种类型的对话框:模式的或者非模式的。一个模式的对话框在应用程序关闭自己之前阻止其它的窗口处理来自应用程序的消息。而一个非模式的对话框的行为则更像一个frame窗口。它不会阻止应用程序中的别的窗口继续处理消息和事件。要使用模式对话框,你应该调用ShowModal函数来显示对话框,否则就应该象frame窗口那样,使用Show函数来显示对话框。

模式对话框是wxWindow派生类中极少数允许在堆栈上创建的对象之一,换句话说,你既可以使用下面的方法使用模式对话框:

void AskUser()
{
MyAskDialog *dlg = new MyAskDialog(...);
if ( dlg->ShowModal() == wxID_OK )
...
dlg->Destroy();
}

也可以直接使用下面的方法:

void AskUser()
{
MyAskDialog dlg(...);
if ( dlg.ShowModal() == wxID_OK )
...
//这里不需要调用Destroy()函数
}

通常你应该从wxDialog类派生一个你自己的类,以便你可以更方便的处理类似wxEVT_CLOSE这样的事件以及其它命令类型的事件。通常你应该在派生类的构造函数中创建你的对话框需要的其它控件。

和wxFrame类一样,如果你的对话框只有一个子窗口,那么wxWidgets会自动为你布局这个窗口,但是如果有超过一个子窗口,应用程序应该自己负责窗口的布局(参考第7章)

当你调用Show函数时,wxWidgets会调用InitDialog函数来发送一个wxInitDialog事件到这个窗口,以便开始对话框数据传输和验证以及其它的事情。

除了默认的构造函数,wxDialog还拥有下面的构造函数:

wxDialog(wxWindow* parent, wxWindowID id, const wxString& title,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE,
const wxString& name = wxT("dialog"));

例如:

wxDialog* dialog = new wxDialog(NULL, ID_MYDIALOG,
wxT("Hello wxWidgets"), wxDefaultPosition,
wxSize(500, 300));
dialog->Show(true);

在调用Show(true)函数或者ShowModal函数之前,对话框都是不可见的,以便其可以以不可见的方式进行窗口布局。

默认情况下,如果对话框的父窗口为NULL,应用程序会自动指定其主窗口为其父窗口,你可以通过指定wxDIALOG_NO_PARENT类型来创建一个无父无母的孤儿dialog类,不过对于模式对话框来说,最好不要这样作。

和wxFrame类一样,最好不要直接使用delete操作符删除一个对话框,取而代之使用Destroy或者Close。以便在对话框的所有事件处理完毕以后才释放对话框,当你调用Cloes函数时,默认发送的wxEVT_CLOSE事件的处理函数其实也是调用了Destroy函数.

需要注意,当一个模式的对话框被释放的时候,它的某个事件处理函数需要调用EndModal函数,这个函数的参数是导致这次释放的那个事件所属窗口的窗口标识符(例如wxID_OK或者wxID_CANCEL).这样才能使得应用程序退出这个模式对话框的事件处理循环,从而使得调用 ShowModal的代码能够释放这个窗口。而这个标识符则作为ShowModal函数的返回值。我们举下面一个OnCancel函数为例:

//wxID_CANCEL的命令事件处理函数
void MyDialog::OnCancel(wxCommandEvent& event)
{
EndModal(wxID_CANCEL);
}
//显示一个模式对话框
void ShowDialog()
{
//创建这个对话框
MyDialog dialog;
// OnCancel函数将会在ShowModal执行的过程中被调用。
if (dialog.ShowModal() == wxID_CANCEL)
{
...
}
}

下图展示了几个典型的对话框,我们会在第9章阐述它们的创建过程。当然,对话框可以远比图中的那些更复杂,例如下面第二个图中的那样,它包含一个分割窗口,一个树形控件以便显示不同的面板(panels),以及一个扮演属性编辑器的网格控件

wxDialog的窗口类型

除了基本窗口类型以外,wxDialog还有下面一些窗口类型可以使用:

wxDEFAULT_DIALOG_STYLE 其值等于wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX.
wxCAPTION 在对话框上显示标题.
wxMINIMIZE_BOX 在标题栏显示最小化按钮.
wxMAXIMIZE_BOX 在标题栏显示最大化按钮.
wxCLOSE_BOX 在标题栏显示关闭按钮.
wxSTAY_ON_TOP 对呼框总在最前. 仅支持windows平台.
wxSYSTEM_MENU 显示系统菜单.
wxRESIZE_BORDER 显示可变大小边框.
wxDIALOG_NO_PARENT 如果创建对话框的时候父窗口为NULL,则应用程序会使用其主窗口作为对话框的父窗口,使用这个类型可以使得在这种情况下,对话框强制使用NULL作为其父窗口。模式窗口不推荐强制使用NULL作为父窗口。

下表解释了对话框类额外的扩展窗口类型。虽然wxWS_EX_BLOCK_EVENTS类型是窗口基类的窗口类型,但是由于它是对话框类的默认扩展类型,我们在这里也进行了描述。

wxDIALOG_EX_CONTEXTHELP 在Windows平台上,这个扩展类型使得对话框增加一个查询按钮.如果这个按钮被按下,对话框将进入一种帮助模式,在这种模式下,无论用户点击哪个子窗口,都将发送一个wxEVT_HELP事件. 这个扩展类型不可以和wxMAXIMIZE_BOX和wxMINIMIZE_BOX类型混用.
wxWS_EX_BLOCK_EVENTS 这是一个默认被设置的扩展类型。意思是阻止命令类型事件向更上一级的窗口传播.要注意调用SetExtraStyle函数可以重置这个扩展类型.
wxDIALOG_EX_METAL 在Mac OS X平台上,这个扩展类型导致对话框使用金属外观.不要滥用这个类型,因为它默认用户的机器有各种多媒体硬件

wxDialog事件

下表解释了相对于窗口基类来说额外的对话框事件:

EVT_ACTIVATE(func) 用于处理wxEVT_ACTIVATE事件,在对话框即将激活或者去激活的时候产生。处理函数的参数类型是wxActivateEvent。
EVT_CLOSE(func) 用户处理wxEVT_CLOSE事件,在应用程序或者操作系统即将关闭窗口的时候产生.处理函数的参数类型是wxCloseEvent,这个事件可以调用Veto函数来放弃事件的后续处理.
EVT_ICONIZE(func) 用来处理wxEVT_ICONIZE事件, 在窗口被最小化或者从最小化状态恢复的时候产生.处理函数的参数类型是wxIconizeEvent. 调用IsIconized来检测到底是最小化还是从最小化恢复.
EVT_MAXIMIZE(func) 用来处理wxEVT_MAXIMIZE事件,这个事件在窗口被最大化或者从最大化状态恢复的时候产生,处理函数的参数类型是wxMaximizeEvent.调用IsMaximized函数确定具体的状态类型.
EVT_INIT_DIALOG(func) 用于处理wxEVT_INIT_DIALOG事件,在对话框初始化自己之前被产生。处理函数的参数类型是wxInitDialogEvent.wxPanel也会产生这个事件.默认执行的操作是调用TransferDataToWindow.

4.5 容器窗口

容器窗口是用来装载别的可见元素的窗口,所谓可见元素指的是子窗口或者是画在这个窗口上的图案。

wxPanel

wxPanel是一个在某些方面有点象dialog窗口的窗口。这个窗口通常被用来摆放那些除了对话框或者frame窗口以外的其它控件窗口。它也常被用来作为wxNoteBook控件的页面。它通常使用系统默认的颜色。

和对话框一样,可以使用它的InitDialog方法来产生一个wxInitDialogEvent事件。如果设置了wxTAB_TRAVERSAL类型,那么它通常可以通过使用类似TAB键的导航键遍历所有它上面的子控件。

除了默认的构造函数以外,wxPanel还拥有下面的构造函数:

wxPanel(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxTAB_TRAVERSAL|wxNO_BORDER,
const wxString& name = wxT("panel"));

用法如下:

wxPanel* panel = new wxPanel(frame, wxID_ANY,
wxDefaultPosition, (500, 300));

wxPanel的窗口类型

wxPanel没有额外的窗口类型。

wxPanel的成员函数

wxPanel也没有额外的成员函数。

wxNotebook

这个类提供了一个有多个页面的窗口,页面之间可以通过边上的TAB按钮来切换。每个页面通常是一个普通的wxPanel窗口或者其派生类,当然你完全可以使用别的窗口。

NoteBook的TAB按钮可以包含一个图片,也可以包含一个文本标签。图片是由wxImageList(参考第10章)提供的,是通过在列表中的位置和页面对应的。

使用notebook的方法是,创建一个wxNotebook对象,然后调用其AddPage方法或者InserPage方法,传递一个用来作为页面的窗口指针。不要手动释放那些已经被wxNoteBook作为页面的窗口,你应该使用DeletePage来删除某个页面或者干脆等到notebook释放它自己的时候。notebook会一并释放那些页面.

下面举例说明怎样创建一个有三个页面,包含文本和图片的TAB标签的notebook:

#include "wx/notebook.h"
#include "copy.xpm"
#include "cut.xpm"
#include "paste.xpm"
//创建notebook
wxNotebook* notebook = new wxNotebook(parent, wxID_ANY,
wxDefaultPosition, wxSize(300, 200));
// 创建图片列表
wxImageList* imageList = new wxImageList(16, 16, true, 3);
imageList->Add(wxIcon(copy_xpm));
imageList->Add(wxIcon(paste_xpm));
imageList->Add(wxIcon(cut_xpm));
// 创建页面
wxPanel1* window1 = new wxPanel(notebook, wxID_ANY);
wxPanel2* window2 = new wxPanel(notebook, wxID_ANY);
wxPanel3* window3 = new wxPanel(notebook, wxID_ANY);
notebook->AddPage(window1, wxT("Tab one"), true, 0);
notebook->AddPage(window2, wxT("Tab two"), false, 1);
notebook->AddPage(window3, wxT("Tab three"), false 2);

下图演示了上述代码在windows平台上的结果:

在大多数的平台上,当TAB页面数量太多不能被完全显示的时候,都会自动出现导航按钮。但是在Mac OS上,这个导航按钮是不会出现的,因此在这个系统上,可以使用的页面数目将受到窗口大小和TAB标签大小的限制。

如果你在每个页面上都使用布局控件,并且在创建notebook的时候使用默认的大小wxDefaultSize,那么wxNotebook将会自动调整大小以适合它的页面。

Notebook窗口主题管理

在Windows Xp系统中,默认的窗口主题会给notebook的页面添加过渡色。虽然这是预期的本地行为,但是它可能会降低性能。出于审美方面的原因,你可能想直接使用单一的色彩,尤其是当notebook不是位于一个对话框以内的时候。如果你想阻止这种默认的行为,可以采用以下三种办法:第一:你可以给你的 notebook指定wxNB_NOPAGETHEM类型来禁止某个特定的notebook的这种效果,或者你可以使用 wxSystemOptions::SetOption来在应用程序范围内禁止它,或者你可以使用SetBackgroundColour函数来给某个单独的页面禁止它。要在应用程序范围内禁止它,你可以使用下面的代码:
wxSystemOptions::SetOption(wxT("msw.notebook.themed-background"), 0);
将它的值设置成1可以再次允许这种效果。要让某个单独的页面禁用这种效果可以使用下面的方法:

wxColour col = notebook->GetThemeBackgroundColour();
if (col.Ok())
{
page->SetBackgroundColour(col);
}

在windows系统以外的平台,或者如果一个应用程序没有使用主题风格,GetThemeBackgroundColour都将返回一个未初始化的颜色,因此上面的代码在各个平台都是可以使用的。另外就是上述的这部分代码的语法和行为是有可能在将来的wxWidgets版本中发生变化,请参考你本地wxWidgets发行版中的wxNotebook类的文档来获取最新的信息。

wxNotebook的窗口类型

wxNotebook可以拥有下面的额外窗口类型:

wxNB_TOP 标签放在顶部.
wxNB_LEFT 标签放在左面. 不是所有的WindowsXp的主题都支持这个选项.
wxNB_RIGHT 标签在右面. 不是所有的WindowsXp的主题都支持这个选项.
wxNB_BOTTOM 标签在底部. 不是所有的WindowsXp的主题都支持这个选项.
wxNB_FIXEDWIDTH 所有标签宽度相同.仅适用于windows.
wxNB_MULTILINE 可以有多行的标签. 仅适用于windows
wxNB_NOPAGETHEME 在windos上禁用主题风格.以提高性能和提供另外一种审美选择.

wxNotebook的事件

wxNotebook可以产生wxNotebookEvent事件,这个事件可以被它和它的继承者处理.

EVT_NOTEBOOK_PAGE_CHANGED(id,func) 当前页面已经改变.
EVT_NOTEBOOK_PAGE_CHANGING(id,func) 当前页面即将改变。你可以使用Veto函数阻止这种改变.

wxNotebook的成员函数

AddPage增加一个页面,InsertPage在某个固定位置插入一个页面.你可以使用文本标签或者图片标签或者两者都有,用法如下:

//增加一个使用了文本和图片两种标签都有的页面,并且当前处于未选中状态。
// (其中图片使用的是图片列表中的索引为2的那个).
notebook->AddPage(page, wxT("My tab"), false, 2);

DeletePage函数移除并且释放某个特定的页面,而RemovePage函数则仅仅是移除这个页面。DeleteAllPages函数用来删除所有的页面。当wxNoteBook被释放时,它也会自动删除所有的页面。

AdvanceSelection函数循环选择页面。 SetSelection函数以基于0的索引选择特定的页面. GetSelection取得当前选中页面的索引或者返回wxNOT_FOUND.

SetImageList函数用来给Notebook设置一个图标列表,这个函数仅是设置而不绑定图片列表,这意味着在notebook 控件被删除的时候,这个图片列表控件并不会被删除,如果你希望使用绑定,则可以使用AssignImageList函数. GetImageList函数返回notebook相关的图片列表对象.图片列表对象用来提供每个页面标签中使用的图片,详情请参考第10章。

GetPage函数用来返回和某个索引对应的页面窗口指针, GetPageCount函数则返回页面总数.

SetPageText和GetPageText用来操作页面标签上的文本.

SetPageImage和GetPageImage用来操作页面标签上的图片在图片列表中的索引,

wxNotebook的替代选择

wxNotebook是wxBookCtrlBase的派生类, 这个基类是用来提供用于管理一组页面的所有数据和方法的抽象类。和wxNotebook有着相似API的类还有两个,一个是wxListbook,一个是 wxChoicebook,你也可以实现你自己的类,比如你可以实现一个wxTreebook.

wxListbook使用一个wxListCtrl变量来控制页面;ListCtrl控件是一种在内部显示一组带有标签的图片的控件. 在wxListbook中,相关的ListCtrl控件可以显示在上下左右四个方向,默认是在左边。这是wxNotebook的一个很有吸引力的替代者。因为即使在Mac Os X平台上,ListCtrl可以管理的项目数量也几乎没有限制,因此wxListbook可以管理的页面数也不受窗口大小和标签长度的限制。

wxChoicebook则使用一个选择控件(一个下拉列表)来管理页面。比较适用于窗口空间比较小的场合。这个控件不会在页面标签处显示图片,而且默认情况下,选择控件显示在整个控件的上方。

上述这两个控件的头文件分别为wx/listbook.h和wx/choicebk.h.它们的事件处理函数的参数类型分别为 wxListbookEvent和wxChoicebookEvent类型,事件影射宏则分别为EVT_XXX_PAGE_CHANGED(id, func)和EVT_XXX_PAGE_CHANGING(id,func),其中XXX代表LISTBOOK或者CHOICEBOOK.

你可以使用类似wxNotebook定义的那些窗口类型,也可以类似用wxCHB_TOP或者wxLB_TOP取代wxNB_TOP这样的窗口类型,它们的值都是一样的,

wxScrolledWindow

尽管所有的窗口都可以拥有滚动条,但是为了让滚动条工作,还需要一些额外的代码。这个类则为让不同类型的窗口类中的滚动条正确工作提供了足够的灵活性。 wxScrolledWindow主要实现了那些让窗口可以以一定的单位连续的滚动(而不是不定大小的跳跃),也可以定义在使用翻页键进行翻页的时候的大小。这种实现通常仅适用于类似画图程序中的那种翻页功能,对于一些复杂功能的文本编辑程序则不一定适合。因为富文本编辑器中每一行的高度和宽度都有可能不同。wxGrid网格控件就是这样的一个例子(在网格控件中,每一行和每一列的宽度都有可能不同),在这种情况下,你应该自己直接从wxWindow类实现自己的派生类,从而实现自己的窗口滚动机制。

要适用滚动窗口,你需要提供每次逻辑移动的单位(也就是当处理上滚一行和下滚一行时窗口应该移动的大小)以及整个窗口的虚拟逻辑大小。然后wxScrollWindow类就会自己关注是否显示滚动条,以及滚动条上的滑钮应该用多大显示等等这些细节问题.

下面演示了怎样创建一个滚动窗口:

#include "wx/scrolwin.h"
wxScrolledWindow* scrolledWindow = new wxScrolledWindow(
this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400),
wxVSCROLL|wxHSCROLL);
// 设置窗口的虚拟逻辑大小: 1000x1000
// 每次滚动10个象素
int pixelsPerUnixX = 10;
int pixelsPerUnixY = 10;
int noUnitsX = 1000;
int noUnitsY = 1000;
scrolledWindow->SetScrollbars(pixelsPerUnitX, pixelsPerUnitY,
noUnitsX, noUnitsY);

第二种设置虚拟大小的方法是使用SetVirtualSize函数,它的参数是以象素为单位的虚拟大小。然后再用SetScrollRate 函数来设置水平和垂直方向上的滚动增量。第三种方法是使用布局控件来布局窗口,滚动窗口会自动计算所有子窗口需要的窗口大小作为窗口的虚大小,你同样需要调用SetScrollRate函数来设置滚动增量。

你可以想普通窗口那样使用重画事件,但是在进行任何窗口重画动作之前你应该调用DoPrepareDC函数来保证重画动作以当前的窗口原点作为原点,就象下面演示的那样:

void MyScrolledWindow::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
DoPrepareDC(dc);
dc.SetPen(*wxBLACK_PEN);
dc.DrawLine(0, 0, 100, 100);
}

你也可以直接重载OnDraw虚函数,wxScrolledWindow在调用这个函数之前,会首先调用DoPrepareDC函数,因此你只需要象下面演示的那样作:

void MyScrolledWindow::OnDraw(wxDC& dc)
{
dc.SetPen(*wxBLACK_PEN);
dc.DrawLine(0, 0, 100, 100);
}

需要注意的是,在别的任何事件处理函数中如果要重画窗口,你同样需要调用DoPrepareDC函数。

你也可以象下面这样提供你自己的DoPrepareDC函数,这个函数默认的行为只是把设备操作原点移动到当前滚动条开始的位置:

void wxScrolledWindow::DoPrepareDC(wxDC& dc)
{
int ppuX, ppuY, startX, startY;
GetScrollPixelsPerUnit(& ppuX, & ppuY);
GetViewStart(& startX, & startY);
dc.SetDeviceOrigin( - startX * ppuX, - startY * ppuY );
}

关于在wxScrollWindow上进行作图的详细情形,包括怎样使用双缓冲区,请参考第5章,�重画和打印�中的wxPaintDC小节。

wxScrolledWindow的窗口类型

wxScrolledWindow类并没有特别的窗口类型,但是通常需要设置wxVSCROLL|wxHSCROLL类型,这也是wxScrolledWindow的默认类型。在某些平台上因为效率的原因可能不支持这两个类型。

wxScrolledWindow的事件

wxScrolledWindow会产生wxScrollWinEvent事件(参见下表).这些事件不会在父子窗口关系中传播,因此要处理这种事件,你必须定义自己的滚动窗口派生类或者挂载你自己的事件表。不过在通常情况下,你并不需要覆盖默认的处理函数来自己处理这些事件。

EVT_SCROLLWIN(func) 处理所有滚动事件.
EVT_SCROLLWIN_TOP(func) 处理事件wxEVT_SCROLLWIN_TOP,滚动到最顶端.
EVT_SCROLLWIN_BOTTOM(func) 处理事件wxEVT_SCROLLWIN_BOTTOM 滚动到最底端事件.
EVT_SCROLLWIN_LINEUP(func) 处理事件wxEVT_SCROLLWIN_LINEUP上滚一行.
EVT_SCROLLWIN_LINEDOWN(func) 处理事件wxEVT_SCROLLWIN_LINEDOWN 下滚一行.
EVT_SCROLLWIN_PAGEUP(func) 处理事件wxEVT_SCROLLWIN_PAGEUP 上滚一页.
EVT_SCROLLWIN_PAGEDOWN(func) 处理事件 wxEVT_SCROLLWIN_PAGEDOWN 下滚一页.

wxScrolledWindow的成员函数介绍

CalcScrolledPosition和CalcUnscrolledPosition函数都需要四个参数,前两个整数参数是输入需要计算的点,后两个则是指向整数的指针用来放置计算结果。第一个函数用来计算实际位置到逻辑位置的映射。如果当前的滚动条下滚了10个象素,则0这个数字作为输入将得到对应输出-10,第二个函数则实现相反的功能。

EnableScrolling函数用来允许或者禁止垂直或者水平方向的物理滚动。物理滚动的含义是说在收到滚动事件的时候对窗口进行物理上的平移。但是如果应用程序需要不等量的移动(比如,由于字体的不同为了避免滚动的时候显示半行字的情况出现),则需要禁止物理移动,在这种情况下,应用程序需要自己移动对应的子窗口。物理移动在所有支持物理移动的平台上都是默认打开的。当然不是所有的平台都支持物理移动。

GetScrollPixelsPerUnit函数在两个指向整数的指针中返回水平和垂直方向上的移动单位。返回0表示在那个方向上不能滚动。

GetViewStart函数返回窗口可视部分左上角的座标,单位是逻辑单位,你需要乘以GetScrollPixelsPerUnit的返回值以便将结果转换成象素单位。

GetVirtualSize返回当前设定的以象素为单位的虚拟窗口大小。

DoPrepareDC将画画设备的原点设置到当前的可见原点。

Scroll函数将窗口滚动到一个特定的逻辑单位位置(注意这里的单位不是象素).

SetScrollbars用来设置移动单位的象素值,各个方向的移动单位总数,水平或者垂直方向的当前滚动位置(可选)以及是否立即刷新窗口(默认否)等。

SetScrollRate用来设置滚动单位,相当于单独设置SetScrollbars中的移动单位参数.

SetTargetWindow用来滚动非wxScrolledWindow类型的其它窗口.

滚动非wxScrolledWindow类型的窗口

如果你想自己实现窗口的滚动行为,你可以直接从wxWindow派生你的窗口类,然后使用SetScrollbar函数来设置这个窗口的滚动条。

SetScrollbar函数的参数如下表的说明:

int orientation 滚动条的类型: wxVERTICAL or wxHORIZONTAL.
int position 滚动条滑块的位置,逻辑滚动单位.
int visible 可见部分大小,逻辑滚动单位. 通常会决定滑块的长度.
int range 滚动条的最大长度,逻辑滚动单位.
bool refresh 是否立即刷新窗口.

举例来说,如果你想显示一个50行文本的文本窗口,使用同样的字体,而窗口的大小只够显示16行,你可以使用下面的代码:

SetScrollbar(wxVERTICAL, 0, 16, 50)

注意在上面的例子中,滑块的位置永远不可能大于50-16=34.

你可以通过用当前窗口除以当前字体下文本的高度来得到当前窗口可以显示多少行这个值。

如果你是自己实现滚动行为,你总是需要在窗口大小发生改变的时候更改滚动条的设置。因此你可以在首次计算滚动条参数的代码中使用AdjustScrollbars函数,然后在wxSizeEvent的处理事件中使用AdjustScrollbars函数。

你可以参考wxGrid控件的代码来获得实现自定义滚动窗口的更多灵感。

你也可以参考wxWidgets手册中的wxVScrolledWindow类,它可以用来建立一个可以在垂直方向进行不固定单位滚动的窗口类。

wxSplitterWindow

这个类用来管理最多两个窗口,如果你想在更多窗口中实现分割,你可以使用多个分割窗口。当前的窗口可以被应用程序分割成两个窗口,比如通过一个菜单命令,也可以通过应用程序命令或者通过分割窗口的用户操作界面(双击分割条或者拖拽分割条使得其中一个窗口的大小变为0)重新变为一个窗口.其中拖拽分割条的方法将会受到后面会提到的SetMinimumPaneSize方法的限制。

在大多数平台上,当分割条被拖拽时,会有一个和背景颜色相反的竖条随之移动以显示分割条的最终位置,你可以通过使用 wxSP_LIVE_UPDATE窗口类型来使得分割条以实时方式通过直接改变两个窗口的大小来代替那种默认方式。实时方式是Mac OS X上默认的也是唯一的方式。

下面的代码演示了怎样创建一个分割窗口来操作两个窗口并且隐藏其中的一个:

#include "wx/splitter.h"
wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY,
wxPoint(0, 0), wxSize(400, 400), wxSP_3D);
leftWindow = new MyWindow(splitter);
leftWindow->SetScrollbars(20, 20, 50, 50);
rightWindow = new MyWindow(splitter);
rightWindow->SetScrollbars(20, 20, 50, 50);
rightWindow->Show(false);
splitter->Initialize(leftWindow);
//去掉下面的注释以便禁止窗口隐藏
// splitter->SetMinimumPaneSize(20);
下面的代码代码演示了创建分割窗口以后怎样使用它:
void MyFrame::OnSplitVertical(wxCommandEvent& event)
{
if ( splitter->IsSplit() )
splitter->Unsplit();
leftWindow->Show(true);
rightWindow->Show(true);
splitter->SplitVertically( leftWindow, rightWindow );
}
void MyFrame::OnSplitHorizontal(wxCommandEvent& event)
{
if ( splitter->IsSplit() )
splitter->Unsplit();
leftWindow->Show(true);
rightWindow->Show(true);
splitter->SplitHorizontally( leftWindow, rightWindow );
}
void MyFrame::OnUnsplit(wxCommandEvent& event)
{
if ( splitter->IsSplit() )
splitter->Unsplit();
}

下图演示了分割窗口在windows平台上的例子,在这个例子中,分割窗口没有使用wxSP_NO_XP_THEME类型。如果使用了这个类型,分割窗口将会拥有更传统的下沉边框和3维外观。

wxSplitterWindow的窗口类型

wxSP_3D 使用三维效果的边框和分割条 .
wxSP_3DSASH 使用三维效果分割条 .
wxSP_3DBORDER 和xSP_BORDER效果相同
wxSP_BORDER 使用标准边框 .
wxSP_NOBORDER 无边框(默认值).
wxSP_NO_XP_THEME 在Xp操作系统上,如果你不喜欢默认的效果,使用三维边框和分割条效果。
wxSP_PERMIT_UNSPLIT 即使设置了最小值也允许窗口被隐藏.
wxSP_LIVE_UPDATE 在分割条移动的时候实时更新窗口.

wxSplitterWindow事件

wxSplitterWindow使用wxSplitterEvent类型的事件处理函数

EVT_SPLITTER_SASH_POS_CHANGING(id,func) 用于处理wxEVT_COMMAND_SPLITTER_SASH_ POS_CHANGING事件,在分割条位置即将改变的时候产生。调用Veto函数阻止分割条移动,或者调用事件的SetSashPosition函数来更改分割条的位置.
EVT_SPLITTER_SASH(id,func) 用于处理wxEVT_COMMAND_SPLITTER_ SASH_POS_CHANGED事件,分割条的位置已经改变你可以通过事件的SetSashPosition函数阻止这种改变或者更改变化的幅度.
EVT_SPLITTER_UNSPLIT(id,func) 用于处理wxEVT_COMMAND_SPLITTER_UNSPLIT事件,在有某个窗口被隐藏的时候产生.
EVT_SPLITTER_DCLICK(id,func)  
用于处理wxEVT_COMMAND_SPLITTER_DOUBLECLICKED事件,在分割条被双击的时候产生.  

wxSplitterWindow的成员函数

GetMinimumPaneSize和SetMinimumPaneSize函数用于操作分割窗口的最小窗格大小。默认为0,意味着分割窗口的每一侧的大小都可以被缩减至0。相当于移除某一部分分割窗口.要阻止这种情形,也为了阻止拖拽分割条的时候超出边界,可以将其设置成一个大于0的值比如20个象素。虽然这样,如果设置了wxSP_PERMIT_UNSPLIT窗口类型,即使最小值设置为大于0的数,也还是可以将某个分割窗口隐藏。

GetSashPosition和SetSashPosition用来操作分割条的位置,传递true参数给SetSashPosition函数导致分割条被立刻刷新。

GetSplitMode和SetSplitMode函数用来设置分割的方向wxSPLIT_VERTICAL或者wxSPLIT_HORIZONTAL.

GetWindow1和GetWindow2用来获取两个分割窗口的指针。

Initialize函数使用一个窗口指针参数调用,用来显示两个分割窗口中的某一个窗口。

IsSplit函数用来判断窗口是否处于分割状态.

ReplaceWindow用来替换被分割窗口控制的两个窗口中的一个。使用这个函数要好过先使用Unsplit函数然后再增加另外一个窗口.

SetSashGravity用一个浮点小数来设置分割比例。0.0表示只显示右边或者下边的窗口,1.0表示只显示左边或者上边的窗口。中间的值则表示按照某种比例分配两个窗口的大小。使用GetSashGravity函数来获取当前的分割比例。

SplitHorizontally函数SplitVertically使用一个可选的分割尺寸来初始化分割窗口。

Unsplit去掉指定的那个分割窗口.

UpdateSize用来使得分割窗口立即刷新(通常情况下,这是在系统空闲的时候完成的).

布局控件中使用wxSplitterWindow的说明

在布局控件中使用分割窗口有一点细微之处需要说明一下。如果你不需要这个分割窗口的分割条是可移动的,你可以在创建两个子窗口的时候指定绝对大小。这将固定这两个子窗口的最小大小,从而使得分割条不能自由移动。如果你希望分割条能正常移动,在创建两个子窗口的时候你就需要使用默认的大小,然后在分割窗口的构造函数中指定其最小大小。然后在将这个分割窗口增加到布局控件的时候,在Add函数中使用wxFIXED_MINSIZE标记来告诉wxWidgets 将分割窗口控件当前的大小作为其最小大小。

另外一种情况是,分割窗口没有设定分割条的位置,也没有设定它的子窗口的大小,当布局控件完成布局时,分割窗口不愿意过早的设置分割条的位置,而要等到系统空闲的时候(这是它的默认行为)。在这种情况下,我们可能会看到当窗口刚刚可见的时候分割条复位自己的位置。为了避免出现这种情景,我们应该在对布局控件调用Fit之后,马上调用分割窗口的UpdateSize函数让其作立即更新。

默认情况下,当用户或者应用程序改变分割窗口的大小时,只有底端(或者右端)的子窗口改变自己的大小以便获取或减小额外的空间,要改变这种默认的行为,你可以使用前面说过的SetSashGravity函数指定一个固定的分割比例。

wxSplitterWindow的替代者

如果在应用程序中你需要使用很多的分割窗口,不妨考虑使用wxSashWindow.这个窗口允许它的任何边成为一个分割条,以便将其分割成多个窗口。而新分的这些窗口通常是用户创建的wxSashWindow的子窗口。

当wxSashWindow的分割条被拖动时,会向应用程序发送wxSashEvent事件以便事件处理函数可以相应的进行窗口布局。布局是通过一个称为wxLayoutAlgorithm的类来完成的,这个类可以根据父窗口的不同提供LayoutWindow,LayoutFrame, LayoutMDIFrame等多种不同的排列方法。

你还可以使用wxSashLayoutWindow类,这个类通过wxQueryLayoutInfoEvent事件来给wxLayoutAlgorithm提供布局方向和尺寸方面的信息。

关于wxSashWindow,wxLayoutAlgorithm和wxSashLayoutWindow更多的信息请参考手册中的相关内容。wxSashWindow不允许子窗口被移动或者分离,因此在不久的将来,这个类可能被一个通用的支持合并和分离的布局框架体系所代替。

下图演示了samples/sashtest目录中的例子在windows上执行的效果:

4.6 非静态控件

非静态控件指的是那些可以响应鼠标和键盘事件的类似wxButton和wxListBox之类的控件。这里我们会对其中最基本的那些进行一些描述。更高级的内容被安排在第12章,你可以参考附录E中的方法从网上下载或者创建你自己的更高级的控件。

wxButton

wxButton控件看上去象是一个物理上可以按下的按钮,它拥有一个文本标签,是用户界面中最常用的一个元素,它可以被放置在对话框,或者面板(wxPanel)或者几乎任何其它的窗口中。当用户点击按钮的时候,会产生一个命令类型的事件。

下面是创建按钮的一个简单的例子:

#include "wx/button.h"
wxButton* button = new wxButton(panel, wxID_OK, wxT("OK"),
wxPoint(10, 10), wxDefaultSize);

下图演示了按钮在Windows Xp系统上的默认外观:

wxWidgets创建的按钮的默认大小是依靠静态函数wxButton::GetDefaultSize指定的,这个函数在不同的平台上返回不同的值。不过你可以通过给按钮指定wxBU_EXACTFIT类型来使其产生刚好符合其标签尺寸的大小。

wxButton的窗口类型

wxBU_LEFT 标签文本作对齐. 仅适用于Windows和GTK+平台.
wxBU_TOP 标签文本上对齐. 仅适用于Windows和GTK+平台.
wxBU_RIGHT 标签文本右对齐。仅适用于Windows和GTK+.
wxBU_BOTTOM 标签文本对齐按钮底部. 仅适用于Windows和GTK+平台.
wxBU_EXACTFIT 不使用默认大小创建按钮,而是按照其标签文本的大小来创建按钮.
wxNO_BORDER 创建一个平面按钮。仅适用于Windows和GTK+平台.

wxButton的事件

wxButton可以创建类型为wxCommandEvent的事件,如下表所示:

   
EVT_BUTTON(id,func) 用于处理wxEVT_COMMAND_BUTTON_CLICKED事件,在用户用左键单击按钮的时候产生.

wxButton的成员函数

wxButton的成员函数

SetLabel和GetLabel函数用来操作按钮标签文本.你可以使用"&"前导符来指示用于这个按钮的快捷键,仅适用于Windows和GTK+平台. SetDefault将按钮设置为其父窗口的默认按钮,这样用户按回车键就相当于用左键点击了这个按钮.

wxButton的标签

你可以使用一个前导符"&"来指定其后的字符为这个按钮的快捷键,不过这个操作仅适用于Windows和GTK+的版本,在其它的平台上,这个前导符将被简单的忽略。

在某些系统,尤其是GTK+系统中,例如确定或者新建这样的标准按钮的标签以及附带显示的一些小图片都被进行了默认的定义。在wxWidgets中,通常 你只需要指定这个按钮的标识符为系统预定义的标识符,那么这些预定义的标签和小图片将被显示,不过,指定和默认值不同的标签文本以及小图片的操作都是允许 的。

推荐的对于这些预定义按钮的使用方法如下,你只需要指定预定义的标识符,不需要指定标签文本,或者指定标签文本为空字符串:

wxButton* button = new wxButton(this, wxID_OK);

wxWidgets会各种平台提供合适的标签,在上面的例子中,在windows或者Mac OSX平台上,将使用字符串"&OK",而在GTK+系统中,会使用当前语言下的OK按钮标签。然后如果你提供了自己的标签文本, wxWidgets将会按照你的指示办事:

wxButton* button = new wxButton(this, wxID_OK, wxT("&Apply"));

这会导致无论在哪种平台上,这个按钮都将覆盖标准标识符的定义,使用"Apply"作为这个按钮的标签。

你可以使用wxGetStockLabel函数(需要包含wx/stockitem.h这个头文件)来获得某个预定义窗口标识符的标签,使用窗口标识符和true(如果你希望在返回的结果中包含前导符(&))作为参数.

下表列出了预定义标识符和其默认标签的对应关系。

预定义标识符 预定义标签
wxID_ADD "Add"
wxID_APPLY "&Apply"
wxID_BOLD "&Bold"
wxID_CANCEL "&Cancel"
wxID_CLEAR "&Clear"
wxID_CLOSE "&Close"
wxID_COPY "&Copy"
wxID_CUT "Cu&t"
wxID_DELETE "&Delete"
wxID_FIND "&Find"
wxID_REPLACE "Rep&lace"
wxID_BACKWARD "&Back"
wxID_DOWN "&Down"
wxID_FORWARD "&Forward"
wxID_UP "&Up"
wxID_HELP "&Help"
wxID_HOME "&Home"
wxID_INDENT "Indent"
wxID_INDEX "&Index"
wxID_ITALIC "&Italic"
wxID_JUSTIFY_CENTER "Centered"
wxID_JUSTIFY_FILL "Justified"
wxID_JUSTIFY_LEFT "Align Left"
wxID_JUSTIFY_RIGHT "Align Right"
wxID_NEW "&New"
wxID_NO "&No"
wxID_OK "&OK"
wxID_OPEN "&Open"
wxID_PASTE "&Paste"
wxID_PREFERENCES "&Preferences"
wxID_PRINT "&Print"
wxID_PREVIEW "Print previe&w"
wxID_PROPERTIES "&Properties"
wxID_EXIT "&Quit"
wxID_REDO "&Redo"
wxID_REFRESH "Refresh"
wxID_REMOVE "Remove"
wxID_REVERT_TO_SAVED "Revert to Saved"
wxID_SAVE "&Save"
wxID_SAVEAS "Save &As..."
wxID_STOP "&Stop"
wxID_UNDELETE "Undelete"
wxID_UNDERLINE "&Underline"
wxID_UNDO "&Undo"
wxID_UNINDENT "&Unindent"
wxID_YES "&Yes"
wxID_ZOOM_100 "&Actual Size"
wxID_ZOOM_FIT "Zoom to &Fit"
wxID_ZOOM_IN "Zoom &In"
wxID_ZOOM_OUT "Zoom &Out"

wxBitmapButton

和普通按钮唯一不同的地方在于,它将显示一个小图片而不是标签文本。

下面演示了创建一个图片按钮的方法:

#include "wx/bmpbuttn.h"
wxBitmap bitmap(wxT("print.xpm"), wxBITMAP_TYPE_XPM);
wxBitmapButton* button = new wxBitmapButton(panel, wxID_OK,
bitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);

以及在Windows平台上的显示效果:

图片按钮通常只需要指定一个拥有透明背景的图片,wxWidgets将会在任何状态的时候都使用这个图片,不过如果你愿意,你可以给不同的按钮状态指定不同的图片,比如选中状态,释放按钮状态以及不可用状态等。

XPM图片格式是推荐的图片格式,因为它可以很容易的提供透明相关的信息以及可以被包含在C++代码中。不过加载别的格式的图片也是可以的,比如常见的JPEG,PNG,GIF以及BMP格式。

wxBitmapButton的窗口类型

wxBU_AUTODRAW 如果指定了这个类型,图片按钮将只会使用标签图片以系统默认的方式自动被绘制,它将包含边框和3D的外观。如果没有指定这个类型,图片按钮将按照不同状态提供的不同的图片来进行绘制. 这个类型仅适用于Windows和Mac OS.
wxBU_LEFT 图片左对齐. 在Mac OS上忽略这个类型.
wxBU_TOP 图片顶端对齐. Mac OS不适用.
wxBU_RIGHT 图片右对齐. Mac OS不适用.
wxBU_BOTTOM 图片底端对齐. Mac OS不适用.

wxBitmapButton事件

图片按钮的事件和wxButton完全相同.

wxBitmapButton的成员函数

SetBitmapLabel和GetBitmapLabel用来操作按钮的主要标签图片. 你还可以使用SetBitmapFocus, SetBitmapSelected, 和SetBitmapDisabled函数来设置按钮被激活,被按下以及被禁用状态下对应的图片。

SetDefault将按钮设置为其父窗口的默认按钮,这样用户按回车键就相当于用左键点击了这个按钮.

wxChoice

选择控件由一个只读的文本区域组成,这个区域的文本通过对一个附属的下拉列表框中值进行选择来进行赋值。这个下拉列表框是默认不可见的,只有在用户用鼠标点击下拉按钮以后才会显示。

创建一个选择控件的代码如下,其中的参数含义依次为:父窗口,标识符,位置,大小,窗口类型以及文本选项。

#include "wx/choice.h"
wxArrayString strings;
strings.Add(wxT("One"));
strings.Add(wxT("Two"));
strings.Add(wxT("Three"));
wxChoice* choice = new wxChoice(panel, ID_COMBOBOX,
wxDefaultPosition, wxDefaultSize, strings);

在大多数平台上,除了文本区域的文本是只读的以外,选择控件和wxComboBox(如下图所示)是非常相似的。在GTK+平台上,选择控件是一个拥有一 个下拉菜单的按钮。你可以用过设置wxComboBox窗口的只读属性来模拟选择控件,以便能够使得在选项过多的时候使用滚动条。

wxChoice的窗口类型

wxChoice控件没有特别的窗口类型.

wxChoice的事件

wxChoice控件产生wxCommandEvent类型的事件,如下表所示:

   
VT_CHOICE(id,func) 用于处理wxEVT_COMMAND_CHOICE_SELECTED事件,当有用户通过列表选择某个选项的时候产生.

wxChoice的成员函数

wxChoice相关的成员函数都是前面介绍过的wxControlWithItems类的函数,如: Clear, Delete, FindString, GetClientData, GetClientObject, SetClientData, SetClientObject, GetCount, GetSelection, SetSelection, GetString, SetString, GetStringSelection, SetStringSelection, Insert, 和IsEmpty.

wxComboBox

ComboBox是一个单行编辑框和一个列表框的组合,用来允许用户以和下拉框中的文本没有关系的方式设置或者获取编辑框中的文本。其中 的单行文本域可以是只读的,在这种情况下就和选择控件非常相似了。和选择控件一样,通常列表框是隐藏的,除非用户单击了编辑框旁边的小按钮。这个控件的目 的在提供一种紧凑的方法给用户,使他们既可以自己输入文本,也可以很方便的选择预定义好的文本。

创建一个ComboBox的方法和创建选择控件的方法很类似,如下所示:

#include "wx/combobox.h"
wxArrayString strings;
strings.Add(wxT("Apple"));
strings.Add(wxT("Orange"));
strings.Add(wxT("Pear"));
strings.Add(wxT("Grapefruit"));
wxComboBox* combo = new wxComboBox(panel, ID_COMBOBOX,
wxT("Apple"), wxDefaultPosition, wxDefaultSize,
strings, wxCB_DROPDOWN);

wxComboBox的窗口类型

| wxCB_SIMPLE | 列表框永远显示. 仅适用于Windows平台. |
|:--- |:--- |
| wxCB_DROPDOWN | 列表框下拉显示. |
| wxCB_READONLY | 和wxCB_DROPDOWN的含义相同,只不过编辑框的文本只能通过列表框进行选择,即使在应用程序的代码里,也不允许编辑框中的文本不存在于列表框内。 |
| wxCB_SORT | 列表框中的选项自动按照子母顺序排序. |

wxComboBox的事件

wxComboBox产生的是wxCommandEvent类型的事件,如下表所示:

EVT_TEXT(id, func) 用来处理wxEVT_COMMAND_TEXT_UPDATED事件,当编辑框内文本变化时产生.
EVT_COMBOBOX(id, func) 用来处理wxEVT_COMMAND_COMBOBOX_SELECTED事件,当用户通过列表框选择一个选项的时候产生.

wxComboBox的成员函数

除了wxControlWithItems的成员函数以外,额外的成员函数还包括:

Copy函数将编辑框中的文本拷贝到剪贴板, Cut函数除了作Copy函数的动作,还将编辑框中的文本清空, Paste函数则把剪贴板内的文本复制到编辑框中。

GetInsertionPoint函数返回当前编辑框中插入点的位置(长整型),SetInsertionPoint则用来设置这个位置.SetInsertionPointEnd用来将其设置到编辑框末尾。

GetLastPosition返回编辑框中的最后位置。

GetValue函数用来返回编辑框中的文本,SetValue则用来设置它.如果combobox拥有wxCB_READONLY类型,则参数中的文本必须在列表框内,否则在发表版本中,这条语句将被忽略,而在调试版本中,则会出现一个告警。

SetSelection用来设置文本框中的一部分为选中状态,Replace则将文本框中的某一部分由给定的参数取代。Remove则移除文本框中的给定部分.

请同时参考wxControlWithItems类的这些成员函数: Clear, Delete, FindString, GetClientData, GetClientObject, SetClientData, SetClientObject, GetCount, GetSelection, SetSelection, GetString, SetString, GetStringSelection, SetStringSelection, Insert, 和IsEmpty.

wxCheckBox

CheckBox是一个通常拥有两种状态:选中或者未选中的控件。通常情况下,如果是选中的状态,则控件上显示一个小的叉号或者对号。通 常这个控件还包含一个标签,这个标签可以显示在控件的左边,也可以显示在控件的右边。CheckBox控件甚至还可以有第三个状态,姑且称之为混合状态或 者不确定状态。一个典型的用法是在安装程序中,对于某个组件来说除了要安装和不要安装两种状态以后,还可以有必须安装这种状态。

下面是创建CheckBox的例子代码:

#include "wx/checkbox.h"
wxCheckBox* checkbox = new wxCheckBox(panel, ID_CHECKBOX,
wxT("&Check me"), wxDefaultPosition, wxDefaultSize);
checkBox->SetValue(true);

以及在windows平台上控件的样子:

以及另外的一个三态的wxCheckBox第三态的样子:

wxCheckBox的窗口类型

wxCHK_2STATE 创建一个二态选择框,这是默认类型.
wxCHK_3STATE 创建一个三态选择框.
wxCHK_ALLOW_3RD_STATE_FOR_USER 默认情况下,用户是不能设置第三种状态的,第三种状态只能在程序中通过代码来设置,使用这个类型可使得用户也可以通过鼠标设置第三态.
wxALIGN_RIGHT 使标签显示在选择框的左边.

wxCheckBox的事件

wxCheckBox产生wxCommandEvent类型的事件,如下表所示:

   
EVT_CHECKBOX(id, func) 用于处理 wxEVT_COMMAND_CHECKBOX_CLICKED事件, 当wxCheckBox的选择状态改变时产生.

wxCheckBox的成员函数

SetLabel和GetLabel用来设置选择框的标签文本. 在Windows和GTK+平台上,可以通过"&"前导字符设置快捷键.

GetValue和SetValue用来操作Bool型的当前选择状态. 使用Get3StateValue和Set3StateValue来操作三种状态wxCHK_UNCHECKED, wxCHK_CHECKED,或wxCHK_UNDETERMINED.

Is3State用于检测是否是三态选择框。

IsChecked用于检测当前是否为选中状态。

wxListBox 和 wxCheckListBox

wxListBox用来从一组基于0索引的字符串列表中选择一个或者多个。这一组备选的字符串列表显示在一个滚动窗口中,选中的文本被高 亮显示.列表框可以是单选的,这种情况下,如果一个选项被选中,以前被选中的选项就自动变为未选中状态,也可以是多选框,这种状态下,对某个选项的点击只 会导致这个选项的选中状态进行切换。

使用下面的代码创建一个列表框:

#include "wx/listbox.h"
wxArrayString strings;
strings.Add(wxT("First string"));
strings.Add(wxT("Second string"));
strings.Add(wxT("Third string"));
strings.Add(wxT("Fourth string"));
strings.Add(wxT("Fifth string"));
strings.Add(wxT("Sixth string"));
wxListBox* listBox = new wxListBox(panel, ID_LISTBOX,
wxDefaultPosition, wxSize(180, 80), strings, wxLB_SINGLE);

在windows下的样子:

wxCheckListBox是wxListBox的派生类,继承了它的功能,另外它还在每个选项上额外显示一个复选框. 这个类包含在头文件wx/checklst.h中,下图演示了wxCheckListBox控件在windows上的样子:

如果有很多选项要显示,你可以考虑使用wxVListBox。这是一个虚的列表框类,它用来显示每个选项的方法是你在继承自这个类的派生类中实现的OnDrawItem函数和OnMeasureItem函数,而它的事件映射宏的格式则和wxListBox的一模一样。

wxHtmlListBox就是一个wxVListBox的派生类,它提供了显示复杂选项的一种简单的方法。你需要定义一个 wxHtmlListBox的派生类,然后在其OnGetItem函数中,为每个选项定义一段HTML文本,然后wxHtmlListBox则按照这段 HTML文本来显示各个选项。下图演示了光盘例子samples/htlbox在Windows Xp上的效果,在这个例子中,通过重载OnDrawSeparator函数实现不同选项的不同显示。

wxListBox和wxCheckListBox的窗口类型

wxLB_SINGLE 单选列表.
wxLB_MULTIPLE 多选列表.
wxLB_EXTENDED 扩展选择选项,用户可以通过Shift键或者鼠标以及其它一些键盘绑定进行快速多选.
wxLB_HSCROLL 创建水平滚动条如果选项的值过长. Windows only.
wxLB_ALWAYS_SB 总显示垂直滚动条.
wxLB_NEEDED_SB 只在需要的时候显示垂直滚动条.
wxLB_SORT 所有选项自动按照子母顺序排序.

wxListBox的wxCheckListBox事件

wxListBox和wxCheckListBox产生的事件类型wxCommandEvent类型的事件.

EVT_LISTBOX(id, func) 用于处理wxEVT_COMMAND_LISTBOX_SELECTED事件,当用户选择某个选项的时候产生.
EVT_LISTBOX_DCLICK(id, func) 用于处理wxEVT_COMMAND_LISTBOX_DOUBLECLICKED事件, 当某个选项被双击的时候产生.
EVT_CHECKLISTBOX (id, func) 用于处理wxEVT_COMMAND_CHECKLISTBOX_TOGGLED事件,当wxCheckListBox的某个选项的选中状态发生改变的时候产生。

wxListBox 成员函数

Deselect不选则某个选项。 GetSelections使用wxArrayInt类型返回一组选中的索引。 InsertItems用来插入一组选项,参数可以是个数和C++的wxString数组,以及插入点的组合,也可以是wxArrayString对象和插入点的组合.

Selected用来判断某个选项是否被选中。

Set用来清除选项并且用参数中的选项组重置选项。参数可以是个数和C++的wxString数组,以及插入点的组合,也可以是wxArrayString对象和插入点的组合.

SetFirstItem将某个选项设置为第一个可见的选项。

SetSelection和SetStringSelection用索引或者字符创值的方式指定某个选项的选中状态。

请同时参考wxControlWithItems的下列成员函数: Clear, Delete, FindString, GetClientData, GetClientObject, SetClientData, SetClientObject, GetCount, GetSelection, GetString, SetString, GetStringSelection, Insert, 和IsEmpty.

wxCheckListBox的成员函数

除了wxListBox的成员函数以外,wxCheckListBox还另外拥有下面的函数:

Check函数使用选项索引和一个bool值作为参数也控制选项的选中状态。

IsChecked用来判断某个选项是否为选中状态。

wxRadioBox

Radio Box用来在一组相关但是互斥的选项中进行选择。通常显示为一个可以拥有一个文本标签的静态框中的一组垂直的或者水平的选项按钮。

这些选项按钮的排列方式取决于构造函数中的两个参数,栏数和方向窗口有关的类型,方向有关的窗口类型包括wxRA_SPECIFY_COLS(默认值)和 wxRA_SPECIFY_ROWS,举例来说,如果你的radio box共有8个选项,栏数为2,方向为wxRA_SPECIFY_COLS,那么这些选项将会以两列四行的方式显示,而假如方向为 wxRA_SPECIFY_ROWS,则显示为四列两行。

下面演示了怎样创建一个有三栏的RadioBox:

#include "wx/radiobox.h"
wxArrayString strings;
strings.Add(wxT("&One"));
strings.Add(wxT("&Two"));
strings.Add(wxT("T&hree"));
strings.Add(wxT("&Four "));
strings.Add(wxT("F&ive "));
strings.Add(wxT("&Six "));
wxRadioBox* radioBox = new wxRadioBox(panel, ID_RADIOBOX,
wxT("Radiobox"), wxDefaultPosition, wxDefaultSize,
strings, 3, wxRA_SPECIFY_COLS);

以及它在windows系统中的样子:

wxRadioBox的窗口类型

wxRadioBox额外的窗口类型如下表所示:

wxRA_SPECIFY_ROWS 横向分栏.
wxRA_SPECIFIY_COLS 纵向分栏.

wxRadioBox事件

wxRadioBox产生wxCommandEvent类型的事件,如下表所示:

   
EVT_RADIOBOX(id, func) 用来处理wxEVT_COMMAND_RADIOBOX_SELECTED事件,当用户点击选项的时候产生.

wxRadioBox成员函数

Enable选中(或不选)某个选项.

FindString查找匹配的选项,返回这个选项的索引或者wxNOT_FOUND.

GetCount返回选项的总数。

GetString和SetString用来操作某个选项的标签文本. GetLabel和SetLabel则用来操作整个radio box的标签.

GetSelection返回基于0的选中的选项的索引. GetStringSelection则返回选中的选项的标签文本. SetSelection和SetStringSelection则用来设置对应的选项,通过这两个函数进行设置时不会发送任何事件。

Show函数用来显示或者隐藏某个特定的选项或者整个radio box控件。

wxRadioButton

一个radio按钮通常用来表示一组相关但是互斥的选项中的一个,它拥有一个按钮和一个文本标签,这个按钮的外观通常是一个圆形。

radio按钮有两种状态:选中和未选中。你可以创建一组radio按钮来代替前面介绍的radiobox控件,这样作的方法是,给第一个radio按钮 指定wxRB_GROUP类型,然后直到另外一个组被创建,或者没有同级的子控件的时候,这个组才结束,组中的控件既可以是radio按钮,也可以是别的 控件。

为什么要代替radiobox呢,其中一个原因是有时候你需要使用更复杂的布局,例如,你可能希望给每一个radio按钮增加一个额外的描述或者增加一个额外的窗口控件,又或者你不希望在一组radio周围显示一个静态的边框等。

下面是创建一组(两个按钮的代码):

#include "wx/radiobut.h"
wxRadioButton* radioButton1 = new wxRadioButton (panel,
ID_RADIOBUTTON1, wxT("&Male"), wxDefaultPosition,
wxDefaultSize, wxRB_GROUP);
radioButton1->SetValue(true);
wxRadioButton* radioButton2 = new wxRadioButton (panel,
ID_RADIOBUTTON2, wxT("&Female"));
//用于水平放置两个按钮的布局控件使用的代码
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(radioButton1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
sizer->Add(radioButton2, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
parentSizer->Add(sizer, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

wxRadioButton的窗口类型

wxRB_GROUP 用来标识一组radio按钮的开始.
wxRB_USE_CHECKBOX 使用checkbox的按钮取代radio按钮(仅适用于Palm OS).

wxRadioButton的事件

wxRadioButton产生wxCommandEvent类型的事件,如下表所示:

   
EVT_RADIOBUTTON(id, func) 用于处理wxEVT_COMMAND_RADIOBUTTON_SELECTED事件,当用户点击某个radio按钮的时候产生。

wxRadioButton的成员函数

GetValue和SetValue使用bool类型来操作radio按钮的状态.

wxScrollBar

wxScrollBar用来单独的增加一个滚动条,这个滚动条和某些窗口自动增加的两个滚动条是有区别的,但是它们处理事件的方式是一样的。滚动条主要有下面四个属性:范围(range),滑块大小,页大小和当前位置。

范围的含义指的是和这个滚动条绑定的窗口的逻辑单位的大小。比如一个表格有15行,那么和这个表格绑定的滚动条的范围就可以设置为15。

滑块大小通常用来反映当前可视部分的大小,还用表格来作为例子,如果因为窗口大小的原因表格只能显示5行,那么滚动条的滑块大小就可以设置成5。如果滑块大小大于或者等于范围,在多数平台上,这个滚动条将自动隐藏。

页大小指的是当滚动条执行翻页命令时需要滚动的单位数目。

当前位置指的是滑块当前所处的位置。

使用下面的代码来创建一个滚动条,其中构造函数的参数含义依次为父窗口,标识符,位置,大小和窗口类型:

#include "wx/scrolbar.h"
wxScrollBar* scrollBar = new wxScrollBar(panel, ID_SCROLLBAR,
wxDefaultPosition, wxSize(200, 20), wxSB_HORIZONTAL);

在windows平台上,显示结果如下图所示:

创建一个滚动条以后,可以使用SetScrollbar函数来设置它的属性。前面在介绍wxScrolledWindow的时候已经介绍这个函数的用法。

wxScrollBar的窗口类型

wxSB_HORIZONTAL 指定滚动条为水平方向.
wxSB_VERTICAL 指定滚动条为垂直方向.

wxScrollBar的事件

wxScrollBar产生wxScrollEvent类型的事件。你可以使用EVT_COMMAND_SCROLL...事件映射宏加 窗口标识符来拦截由特定滚动条产生的相关事件,或者使用EVT_SCROLL...不带窗口标识符的事件映射宏来拦截除了本窗口以外来自其他的窗口的滚动 条事件。使用EVT_SCROLL(func)可以响应所有的滚动条事件。在附录I�事件类型和相关宏�中详细列举了所有的滚动事件,你也可以参考手册中 的相关内容。

wxScrollBar的成员函数

Getrange返回范围大小.

GetPageSize返回页大小.通常这个大小和滑块大小相同。

GetThumbPosition和SetThumbPosition用来操作滑块当前位置.

GetThumbLength返回滑块或者当前可是区域的大小.

SetScrollbar 用来设置滚动条的所有属性.比如滑块位置(逻辑单位),滑块大小,范围,页大小以及一个可选的bool参数用来指示是否立即更新滚动条的显示。

wxSpinButton

wxSpinButton拥有两个小的按钮用来表示上下或者左右.这个控件通常和一个文本编辑框控件一起使用,以用来增加或者减少某个值。为了方便移植, 应该尽可能使用wxSpinCtrl来代替wxSpinButton,因为不是所有的平台都支持wxSpinButton.

这个控件(以及wxSpinCtrl)所支持的值的范围是平台相关的,但是至少在-32768到32768之间,

使用下面的代码来创建一个wxSpinButton,其中构造函数的参数的含义分别为:父窗口,标识符,位置,大小以及窗口类型:

#include "wx/spinbutt.h"
wxSpinButton* spinButton = new wxSpinButton(panel, ID_SPINBUTTON,
wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL);

在windows平台上,显示的结果如下图所示:

wxSpinButton的窗口类型

下表列出了wxSpinButton的窗口类型

wxSP_HORIZONTAL spin按钮为左右方向. 不支持wxGTK.
wxSP_VERTICAL spin按钮为上下垂直方向.
wxSP_ARROW_KEYS 用户可以使用方向键来改变相关值.
wxSP_WRAP 将最大值和最小值首尾相连,比如最小值的更小的下一个值将是最大值.

wxSpinButton的事件

wxSpinButton产生wxSpinEvent类型的事件, 如下表所示:

EVT_SPIN(id, func) 用来处理wxEVT_SCROLL_THUMBTRACK事件, 当上下(或左右)按钮被点击的时候产生.
EVT_SPIN_UP(id, func) 用来处理wxEVT_SCROLL_LINEUP事件,当向上(或者向左)的按钮被点击的时候产生.
EVT_SPIN_DOWN(id, func) 用来处理wxEVT_SCROLL_LINEDOWN事件,当向下(或者向右)按钮被点击的额时候产生.

wxSpinButton的成员函数

GetMax返回设置的最大值. GetMin返回设置的最小值. GetValue和SetValue用来操作当前值. SetRange用来设置最大和最小值.

wxSpinCtrl

wxSpinCtrl是wxTextCtrl和wxSpinButton控件的组合。当用户点击wxSpinButton的向上或者向下按钮的时候,wxTextCtrl中的值将会随之变化。用户也可以直接在wxTextCtrl中输入他想要的值。

下面的代码用来创建一个值范围在0到100之间,初始值为5的wxSpinCtrl。

#include "wx/spinctrl.h"
wxSpinCtrl* spinCtrl = new wxSpinCtrl(panel, ID_SPINCTRL,
wxT("5"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS,
0, 100, 5);

在windows平台上的显示效果如下图所示:

wxSpinCtrl的窗口类型

wxSP_ARROW_KEYS 用户可以通过方向键改变相关值.
wxSP_WRAP 将最大值和最小值首尾相连.

wxSpinCtrl事件

wxSpinCtrl产生wxSpinEvent类型的事件,如下表所示. 你也可以使用EVT_TEXT事件映射宏来处理其文本框的文本更新事件,处理函数的额参数类型为wxCommandEvent.

EVT_SPIN(id, func) Handles a wxEVT_SCROLL_THUMBTRACK event, generated whenever the up or down arrow is clicked.
EVT_SPIN_UP(id, func) Handles a wxEVT_SCROLL_LINEUP event, generated when the up arrow is clicked.
EVT_SPIN_DOWN(id, func) Handles a wxEVT_SCROLL_LINEDOWN event, generated when the down arrow is clicked.
EVT_SPINCTRL(id, func) Handles all events generated for the wxSpinCtrl.

wxSpinCtrl成员函数

GetMax返回设置的最大值. GetMin返回设置的最小值. GetValue和SetValue用来操作当前值. SetRange用来设置最大值和最小值.

wxSlider

slider控件拥有一个滑条和一个滑块,可以通过移动滑条上的滑块来改变控件的当前值.

使用下面的代码创建一个取值范围为0到40,初始位置为16的wxSlider.

#include "wx/slider.h"
wxSlider* slider = new wxSlider(panel, ID_SLIDER, 16, 0, 40,
wxDefaultPosition, wxSize(200, -1),
wxSL_HORIZONTAL|wxSL_AUTOTICKS|wxSL_LABELS);

在windows平台上显示的外观如下所示:

wxSlider的窗口类型

wxSL_HORIZONTAL 显示水平方向的滑条.
wxSL_VERTICAL 显示垂直方向的滑条.
wxSL_AUTOTICKS 显示刻度标记.
wxSL_LABELS 显示最大、最小以及当前值的标签.
wxSL_LEFT 对于垂直滑条,将刻度显示在左面.
wxSL_RIGHT 对于垂直滑条,将刻度显示在右面.
wxSL_TOP 对于水平滑条,刻度显示在上面. 默认值为显示在底部.
wxSL_SELRANGE 允许用户通过滑条选择一个范围.仅适用于Windows.

wxSlider的事件

wxSlider产生wxCommandEvent类型的事件,如下表所示,但是如果你希望更好的控制,你可以使用EVT_COMMAND_SCROLL_...宏,其处理函数的参数类型为wxScrollEvent,参见附录I.

   
EVT_SLIDER(id, func) 用于处理wxEVT_COMMAND_SLIDER_UPDATED事件,当用户移动滑块的时候产生.

wxSlider的成员函数

ClearSel用来在设置了wxSL_SELRANGE类型的滑条上清除选择区域. ClearTicks则用来清除刻度,仅适用于windows系统.

GetLineSize和SetLineSize用来操作移动单位,这个移动单位用来在用户使用方向键操作滑块的时候使用. GetPageSize和SetPageSize也用来操作移动单位,这个单位在用户用鼠标点击滑条的任一边的时候使用.

GetMax返回当前设置的最大值.

GetMin返回当前设置的最小值.

GetSelEnd和GetSelStart用来返回选择区域的起点和终点; SetSelection用来设置选择区域的起点和终点. 这些函数都只适用于Windows.

GetThumbLength和SetThumbLength用来操作滑块的大小.

GetTickFreq和SetTickFreq用来操作滑条上刻度的密度.SetTick用来设置刻度的位置。这些函数仅适用于Windows.

GetValue返回滑条的当前值, SetValue用来设置滑条的当前值.

SetRange用来设置滑条的最大值和最小值.

wxTextCtrl

文本控件是用来显示和编辑文本的控件,它支持单行和多行的文本编辑。在某些平台上,支持给文本控件中的文本设置一些简单的格式和风格。在windows平台,GTK+平台以及Mac OS X平台上通过使用wxTextAttr类来设置和获取文本的当前格式。

使用下面的代码创建一个支持多行文本的文本框控件:

#include "wx/textctrl.h"
wxTextCtrl* textCtrl = new wxTextCtrl(panel, ID_TEXTCTRL,
wxEmptyString, wxDefaultPosition, wxSize(240, 100),
wxTE_MULTILINE);

在windows平台上,多行文本框控件的外观如下:

多行文本框允许使用以"\n"分割的一组文本行的方式来处理一段文本,即使在非Unix的平台上,使用"\n"作为分割符也是允许的。这使得你可以忽略平 台之间换行符的差异。不过,作为代价,你将不能直接使用那些GetInsertionPoint函数或者GetSelection函数返回的索引,作为 GetValue返回的字符串中的索引,因为在前者返回的索引中,操作系统可能会进行了一点点的偏移以便对应上平台使用的"\r\n"换行符,就象 windows平台上的那样。

如果你想从上面例子的函数的返回值中得到一个子字符串应该怎么办呢?你可以使用GetRange函数。它们返回的索引可以被用于它们自 己别的成员函数,比如SetInsertionPoint或者SetSelection。总而言之,不要将用多行文本框的成员函数返回的索引直接用于它的 内容字符串的索引,但是可以将其用于其它成员函数作为参数。

多行文本框支持设置文本格式:你可以为部分文本设置单独的文本颜色和字体。在windows平台上,这需要窗口使用wxTE_RICH 窗口类型。你可以在插入文本之前使用SetDefaultStyle函数,或者在插入文本以后使用SetStyle来改变已经存在于文本框中的文本的格 式。前者更有效率。

无论在哪种情况下,如果指定的格式中的部分格式是没有被指定的,则默认格式中相应的值将被使用,如果没有默认的格式,那个文本控件自己的属性将会被使用。

在下面的代码中,第二次调用SetDefaultStyle不会改变文本的前景颜色(仍然是红色),最后一次调用SetDefaultStyle则不会改变文本的背景颜色(仍然是灰色的)。

text->SetDefaultStyle(wxTextAttr(*wxRED));
text->AppendText(wxT("Red text\n"));
text->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY));
text->AppendText(wxT("Red on gray text\n"));
text->SetDefaultStyle(wxTextAttr(*wxBLUE));
text->AppendText(wxT("Blue on gray text\n"));

wxTextCtrl的窗口类型

wxTE_PROCESS_ENTER 这个控件将会产生wxEVT_COMMAND_TEXT_ENTER事件.如果没有设置这个类型,回车键将会或者被空家内部处理,或者被dialog窗口用来遍历所有子窗口.
wxTE_PROCESS_TAB 这个控件将会在TAB键被按下的时候处理wxEVT_CHAR事件,否则TAB键用来在dialog的所有子窗口之间遍历.
wxTE_MULTILINE 支持多行文本.
wxTE_PASSWORD 文本将会以"*"显示.
wxTE_READONLY 文本只读.
wxTE_RICH 在Windows下使用富文本编辑控件. 这将允许控件存储超过64KB的文本;并且垂直滚动条自动在需要的时候显示.这个类型在别的平台上将被忽略.
wxTE_RICH2 在Windows下使用富文本编辑控件的2.0或者3.0版本; 垂直滚动条将始终被显示. 在其它平台忽略这个类型.
wxTE_AUTO_URL 高亮显示URL字符串,并且在其被点击的时候产生wxTextUrlEvents事件. 在windows平台上需要设置wxTE_RICH类型.仅适用于Windows和GTK+平台.
wxTE_NOHIDESEL 默认情况下,在windows平台上,如果文本框当前没有得到焦点,将不会高亮显示文本框中文本的选中部分,使用这个类型可以使其即使在文本框没有获得焦点的时候,被选部分也会高亮显示。其它平台则忽略这个类型。
wxHSCROLL 显示水平滚动条,这样就不需要文本自动换行了. 在GTK+平台上无效.
wxTE_LEFT 文本框中的文本左对齐 (默认).
wxTE_CENTRE 文本框中的文本居中对齐.
wxTE_RIGHT 文本框中的文本右对齐.
wxTE_DONTWRAP 等同于wxHSCROLL类型.
wxTE_LINEWRAP 如果文本行的长度超出可以显示的部分,则允许在文本行的任何位置换行,目前只支持wxUniversal版本.
wxTE_WORDWRAP 如果文本行的长度超出可以显示的部分,则允许在文本行的单词边界位置换行,目前只支持wxUniversal版本.
wxTE_NO_VSCROLL Removes the vertical scrollbar. No effect on GTK+.

wxTextCtrl的事件

wxTextCtrl主要产生wxCommandEvent类型的事件,如下表所示:

EVT_TEXT(id, func) 用于处理wxEVT_COMMAND_TEXT_UPDATED事件,文本框内文本值改变的时候产生.
EVT_TEXT_ENTER(id, func) 用于处理wxEVT_COMMAND_TEXT_ENTER事件,在用户按下回车键的时候产生并且文本框设置了wxTE_PROCESS_ENTER窗口类型.
EVT_TEXT_MAXLEN(id, func) 用于处理wxEVT_COMMAND_TEXT_MAXLEN事件,当用户试图输入的文本长度超过SetMaxLength设置的长度的时候产生.仅适用于Windows和GTK+平台.

wxTextCtrl的成员函数

AppendText将文本添加在文本框最后, WriteText在当前的插入点插入文本. SetValue清除文本框中的当前文本然后赋值,赋值后IsModified函数返回False.在所有这些函数中,如果文本框支持多行文本,则可以使 用换行符。需要注意这些函数都会产生文本更新事件.

GetValue函数返回文本框的所有文本,如果是多行文本类型,则其中可以包含换行符. GetLineText返回其中一行. GetRange返回某个位置范围内的文本。

Copy函数拷贝选中的文本到剪贴板. Cut除了Copy以外还清除选中的文本. Paste则将剪贴板上的文本替换当前的选中文本,在用户界面刷新事件处理函数中,你还可以使用CanCopy, CanCut和CanPaste函数。

Clear清除所有文本.产生文本更新事件。

DiscardEdits复位内部的�已修改�标记,就好像文本框的文本已经被保存了那样。

EmulateKeyPress用来模拟按键输入,以便在文本框中进行某些修改.

GetdefaultStyle和SetDefaultStyle用来操作当前的默认文本格式. GetStyle返回某个位置文本的当前格式,SetStyle则用来设置某个范围内的文本格式e.

GetInsertionPoint和SetInsertionPoint函数用来操作新文本的插入点. GetLastPosition用来返回当前文本的最末位置,SetInsertionPointEnd用来将插入点设置在文本最末。

GetLineLength返回某个特定行的字符创长度。

GetNumberOfLines返回总行数。

GetStringSelection返回当前选中的文本。如果当前没有选中任何文本,则返回空字符串。GetSelection返回当前选中部分的索引. SetSelection则用两个整数参数来设置当前的选中部分。

IsEditable返回当前控件是否可以被编辑. SetEditable用来设置控件的可编辑状态以便让其只读或者可编辑. IsModified当文本框内的文本已经被编辑过的时候返回True. IsMultiline用来检测当前文本框是否是多行文本框。

LoadFile将文件内容读入文本框, SaveFile将文本框内容存入文件.

PositionToXY将象素值转换成文本的行号和位置, 而XYToPosition则刚好相反。

Remove删除给定区域的文本,. Replace则替换给定区域的文本。

ShowPosition使得文本控件显示包含给定位置的部分。

Undo撤消最近一次编辑, Redo重复最近一次编辑. 在某些平台上,这些操作可能什么也不作. 你可以用CanUndo和CanRedo来测试当前的平台是否支持撤消和重做动作。

wxToggleButton

wxToggleButton在用户点击以后保持按下状态.换句话说,除了长的象按钮,它其实更可以说是一个wxCheckBox.

创建wxToggleButton的代码如下:

#include "wx/tglbtn.h"
wxToggleButton* toggleButton = new wxToggleButton(panel, ID_TOGGLE,
wxT("&Toggle label"), wxDefaultPosition, wxDefaultSize);
toggleButton->SetValue(true);

下图则显示了其在windows平台上的样子:

wxToggleButton的窗口类型

wxToggleButton没有特别的窗口类型.

wxToggleButton事件

wxToggleButton产生wxCommandEvent类型的事件。

   
EVT_TOGGLEBUTTON(id, func) 用于处理wxEVT_COMMAND_TOGGLEBUTTON_CLICKED事件, 用户点击该按钮的时候产生.

wxToggleButton的成员函数

SetLabel和GetLabel用来操作按钮上的标签. 在windows和GTK+平台上你可以使用"&"前导符来指定一个加速键.

GetValue和SetValue用来获取和设置按钮的状态。

4.7 静态控件

静态控件不响应任何用户输入,只用来显示一些信息或者增加应用程序的美感。

进度条

这是一个水平或者垂直的用来显示进度(通常是时间的进度)的控件。它不产生任何命令事件。下面的代码用来创建一个进度条:

#include "wx/gauge.h"
wxGauge* gauge = new wxGauge(panel, ID_GAUGE,
200, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL);
gauge->SetValue(50);

在windows平台上的外观:

wxGauge的窗口类型

wxGA_HORIZONTAL 水平进度条.
wxGA_VERTICAL 垂直进度条.
wxGA_SMOOTH 创建一个光滑的进度条,进度条的每一段之间没有空格. 仅适用于Windows.

wxGauge事件

因为进度条只是用来显示信息,因此不产生任何事件。

wxGauge成员函数

GetRange和SetRange用来设置进度条的最大值。

GetValue和SetValue用来获取和设置进度条的当前值。

IsVertical用来检测是否是垂直进度条(否则就是水平的)。

wxStaticText

静态文本控件用来显示一行或者多行的静态文本。

下面的例子创建了一个静态文本控件:

#include "wx/stattext.h"
wxStaticText* staticText = new wxStaticText(panel, wxID_STATIC,
wxT("This is my &static label"),
wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);

以及它在windows平台上的外观:

在静态文本控件标签中的前导符"&",在某些平台(比如Windows和GTK+)上用来定义一个快捷键,通过这个快捷键可以直接访问到下一个非静态的控件。

wxStaticText的窗口类型

wxALIGN_LEFT 标签左对齐.
wxALIGN_RIGHT 标签右对齐.
wxALIGN_CENTRE 标签在水平方向上居中对齐.
wxST_NO_AUTORESIZE 默认情况下,静态文本控件会在调用SetLabel以后自动改变大小以使得其大小刚好满足标签文本的需要,如果设置了这个类型,则标签不会改变自己的大小。通常这个类型应该和上面的对齐类型一起使用因为如果没有设置这个类型,自动调整大小使得对齐没有任何意义。

wxStaticText的成员函数

GetLabel和SetLabel用户获取和设置文本标签。

wxStaticBitmap

静态图片控件显示一个图片。

使用下面的代码创建静态图片控件。

#include "wx/statbmp.h"
#include "print.xpm"
wxBitmap bitmap(print_xpm);
wxStaticBitmap* staticBitmap = new wxStaticBitmap(panel, wxID_STATIC,
bitmap);

这会在作为父窗口的面板或者对话框上显示一个图片,如下图所示:

wxStaticBitmap的窗口类型

没有特别的窗口类型.

wxStaticBitmap的成员函数

GetBitmap和SetBitmap用来获取和设置其显示的图片。

wxStaticLine

这个控件用来在其父窗口上显示一个水平或者垂直的长条,以便作为子窗口的静态分割条。

下面是创建wxStaticLine的代码:

#include "wx/statline.h"
wxStaticLine* staticLine = new wxStaticLine(panel, wxID_STATIC,
wxDefaultPosition, wxSize(150, -1), wxLI_HORIZONTAL);

以及其在windows平台上的外观:

wxStaticLine的窗口类型

wxLI_HORIZONTAL 水平长条.
wxLI_VERTICAL 垂直长条.

wxStaticLine的成员函数

IsVertical用来检测是否为垂直长条.

wxStaticBox

这个控件用来在一组控件周围显示一个静态的拥有一个可选标签的矩形方框。到目前为止,这个控件不可以作为其它控件的父窗口。它围绕的那些控件是它的的兄弟窗口而非子窗口。它们应该在它后面创建,但是它们拥有同样的父窗口。在将来的版本中,也许会更改这个限制以便它可以同时容纳兄弟窗口和子窗口。

下面是创建一个wxStaticBox的例子代码:

#include "wx/statbox.h"
wxStaticBox* staticBox = new wxStaticBox(panel, wxID_STATIC,
wxT("&Static box"), wxDefaultPosition, wxSize(100, 100));

以及它在windows平台上的样子:

wxStaticBox的窗口类型

没有特别的窗口类型

wxStaticBox的成员函数

GetLabel和SetLabel用来获取和设置其静态标签。

4.8 菜单

在这一小节中,我们来介绍一下怎样使用wxMenu,它用一种相对简单的办法来提供一组命令但是却不占用大量的空间。在下一小节,我们会描述一下怎样在菜单条上使用菜单。

wxMenu

菜单是指的一串命令,它可以从菜单条弹出,也可以从任何一个窗口上作为关联菜单,通过通常是右键单击来弹出。菜单项可以是一个普通的命令,也可以是一个复选框或者是一个单选框。菜单项可以被禁用,这是它将不能触发任何命令。某些菜单项可以通过一下特殊的三角符号来带出一个新的菜单,菜单中有可以有新的特殊菜单项,这种循环可以使用任意多次。另外一种特殊的菜单项是一个分割条,它只是简单的显示一行或者一段空白以便把两组菜单项进行分割。

下图显示了一个典型的拥有普通菜单项,复选框,单选框以及子菜单的菜单:

上面的例子还演示了两种快捷操作方法:加速键和快捷键。加速键是通过前导符"&"指定的,使用下划线表示,当菜单显示的时候可以通过这个键来执行相应的命令。而快捷键则是一个组合键,它可以在菜单没有显示的时候执行菜单命令,它通过一个TAB加一个组合键被定义。例如,上图中的 New菜单是通过下面的代码实现的:

menu->Append(wxID_NEW, wxT("&New...\tCtrl+N"));

更多通过菜单或者wxAcceleratorTable创建快捷键的方法将会在第6章说明。

菜单中的复选框和单选框的状态是由菜单类自动维护的。它们会在单击的时候自动将自己的状态改变,并且在下一次菜单展示的时候以新的状态展示。对于单选框来说,在改变自己状态的同时,它还会将其它同一组中的单选框更改为未选中的状态。你也可以在自己的代码中设置它们的状态(参考第9章)。

你可以通过wxWindow::PopupMenu函数将某个菜单在一个窗口的特定位置显示,比如下面的代码:

void wxWindow::OnRightClick(wxMouseEvent& event)
{
if (!m_menu)
{
m_menu = new wxMenu;
m_menu->Append(wxID_OPEN, wxT("&Open"));
m_menu->AppendSeparator();
m_menu->Append(wxID_EXIT, wxT("E&xit"));
}
PopupMenu(m_menu, event.GetPosition());
}

菜单事件在发送给它的父窗口以及进行其它的事件表匹配之前会首先发送给菜单自己。PopupMenu函数会导致程序短暂堵塞,直到用户关闭这个菜单。如果你愿意,你可以每次都释放旧的并且重新创建新的菜单,也可以每次都使用同一个菜单。

你应该尽可能的使用系统预定义的菜单标识符,比如wxID_OPEN, wxID_ABOUT, wxID_PRINT等等,在第三章中有一个完整的列表。需要特别指出的是,在Mac OS X上,标识符为wxID_ABOUT, wxID_PREFERENCES和 wxID_EXIT的菜单项不是显示在你定义的菜单中,而是显示在系统菜单中,wxWidgets自动为你的菜单作了这个调整。但是你需要注意在调整以后不要留下类似空的帮助菜单,或者是两个菜单分割条连在一起这样的不专业的情况出现。

在wxWidgets的发行版的例子中的samples/menu例子演示了所有菜单的功能,而在另外一个samples/ownerdrw例子中,则演示了怎样在菜单中使用定制的字体和图片。

wxMenu的事件

wxMenu相关的事件类型总共有四种: wxCommandEvent, wxUpdateUIEvent, wxMenuEvent, 和 wxContextMenuEvent.

下表列出了处理函数使用wxCommandEvent作为参数类型的事件映射宏。可用其来处理菜单命令,无论是弹出菜单命令还是主菜单(来自类似Frame窗口的菜单条上的菜单)命令。这种事件宏和工具条上的事件映射宏是一致的,这使得菜单和工具条上的按钮产生的事件可以通过同一个处理函数处理。

EVT_MENU(id, func) 用来处理wxEVT_COMMAND_MENU_SELECTED事件,某个菜单项被选中的时候产生.
EVT_MENU_RANGE(id1, id2, func) 用来处理wxEVT_COMMAND_MENU_RANGE事件,在某个范围内的菜单项被选中的时候产生.

下表列出了处理函数使用wxUpdateUIEvent作为参数类型的事件映射宏。这种宏对应的事件是在系统空闲的时候产生的,以便给应用程序一个更新用户界面的机会。例如,允许或者禁止一个菜单项等。尽管wxUpdateUIEvent可以被任何窗口产生,但是菜单产生的 wxUpdateUIEvent还是有一些不同的地方,在于在这个事件中可以调用Check函数,SetText函数和Enable函数等。Check函数用来选中或者不选某个菜单项,而SetText函数用来设置菜单项的标签。如果菜单项的标签需要根据某种条件动态改变的话会比较有用。例如:

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_UPDATE_UI(ID_TOGGLE_TOOLBAR, MyFrame::OnUpdateToggleToolbar)
END_EVENT_TABLE()
void MyFrame::OnUpdateToggleToolbar(wxUpdateUIEvent& event)
{
event.Enable(true);
event.Check(m_showToolBar);
event.SetText(m_showToolBar ?
wxT("Show &Toolbar (shown)") :
wxT("Show &Toolbar (hidden)"));
}
EVT_UPDATE_UI(id, func) 用来处理wxEVT_UPDATE_UI事件. 处理函数可以调用Enable, Check, 和SetText以及其它函数.
EVT_UPDATE_UI_RANGE(id1, id2, func) 用来处理wxEVT_UPDATE_UI事件,以便同时处理一组标识符.

关于界面更新的更多详情请参考第9章。

下表列出另外一些事件映射宏,其中EVT_CONTEXT_MENU的参数类型为wxContextMenuEvent,这是一个从 wxCommandEvent继承的事件类型,因此这个事件可以在父子窗口继承关系中传播。使用这个宏来拦截类似鼠标右键单击以产生关联菜单的事件,然后调用事件的GetPosition函数来获得菜单应该显示的准确位置。剩下的事件映射宏的处理函数采用wxMenuEvent对象作为参数,这些事件只从菜单条发送给其frame窗口,来告诉应用程序一个菜单已经被打开或者关闭了,或者某个菜单项正被高亮显示,默认的EVT_MENU_HIGHLIGHT 的处理函数是在主程序的状态栏显示这个菜单项的帮助信息,不过你可以提供你自己的处理函数来作一些不同的事情。

EVT_CONTEXT_MENU(func) 用来处理由于用户或者通过某个特殊按键(在windows平台上),或者通过单击鼠标右键产生的弹出一个上下文菜单的请求。处理函数的参数类型为wxContextMenuEvent.
EVT_COMMAND_CONTEXT_MENU(id, func) 和EVT_CONTEXT_MENU相似,不过多了一个窗口标识符参数.
EVT_MENU_OPEN(func) 用来处理wxEVT_MENU_OPEN事件,在一个菜单即将被打开的时候产生。在windows平台上主菜单上每次遍历只会导致一次这个事件产生.
EVT_MENU_CLOSE(func) 用来处理wxEVT_MENU_CLOSE事件,它在某个菜单被关闭的时候产生.
EVT_MENU_HIGHLIGHT(id, func) 用来处理wxEVT_MENU_HIGHLIGHT事件,当某个菜单项被高亮显示的时候产生,通常用来在主程序的状态栏上产生关于这个菜单项的帮助信息。
EVT_MENU_HIGHLIGHT_ALL(func) 用来处理wxEVT_MENU_HIGHLIGHT事件,在任何一个菜单项被高亮显示的时候产生。

wxMenu的成员函数

Append增加一个菜单项: 参数为标识符,菜单项标签, 帮助信息和菜单项类型(wxITEM_NORMAL, wxITEM_SEPARATOR, wxITEM_CHECK 或 wxITEM_ RADIO). 你也可以使用AppendCheckItem和AppendRadioItem来避免手动指定wxITEM_CHECK或wxITEM_RADIO参数类型.例如:

// 增加一个普通菜单项
menu->Append(wxID_NEW, wxT("&New...\tCtrl+N"));
// 增加一个复选框菜单项
menu->AppendCheckItem(ID_SHOW_STATUS, wxT("&Show Status"));
// 增加一个单选框菜单项
menu->AppendRadioItem(ID_PAGE_MODE, wxT("&Page Mode"));
Another overload of Append enables you to append a submenu, for example:
// 增加一个子菜单
menu->Append(ID_SUBMENU, wxT("&More options..."), subMenu);

还有一种Append的重载函数允许你直接使用一个wxMenuItem来增加一个菜单项,这是在菜单中增加图片或者使用自定义字体唯一的方法:

// 初始化图片和字体
wxBitmap bmpEnabled, bmpDisabled;
wxFont fontLarge;
// 创建一个菜单项
wxMenuItem* pItem = new wxMenuItem(menu, wxID_OPEN, wxT("&Open..."));
// 设置图片和字体
pItem->SetBitmaps(bmpEnabled, bmpDisabled);
pItem->SetFont(fontLarge);
// 增加到菜单中
menu->Append(pItem);

使用Insert函数来在特定的位置插入一个菜单项。使用Prepend, PrependCheckItem, PrependRadioItem和PrependSeparator在菜单最开始的地方插入一个菜单项。

AppendSeparator插入一个分割条, InsertSeparator在特定位置插入一个分割条.

// 插入一个分割条
menu->AppendSeparator();

Break函数在菜单里插入一个中断点,导致下一个插入的菜单项出现在另外一栏。

使用Check函数是标记复选框或者单选框的状态,参数为菜单项标识符和一个bool值。使用IsChecked函数来获取当前状态.

Delete函数删除并且释放一个菜单项使用菜单项标识符或者wxMenuItem指针。如果这个菜单项是一个子菜单,则子菜单将不被删除。如果你想删除并且释放一个子菜单,使用Destroy函数。Remove函数移除一个菜单项但是并不释放它,而是返回指向它的指针。

使用Enable函数来允许或者禁止一个菜单项。但是比直接调用这个方法更好的作法是处理用户界面更新事件(参考第9章)。IsEnabled函数返回当前的可用状态。

使用FindItem函数根据标签或者标识符找到一个菜单项,使用FindItemByPosition函数通过位置找到一个菜单项。

GetHelpString和SetHelpString函数用来访问菜单项的帮助信息.当菜单是菜单条的一部分时,高亮显示的菜单项的帮助信息将会显示在状态栏上。

GetLabel和SetLabel用来获取或者设置菜单项的标签.

GetMenuCount返回菜单项的个数。

GetMenuItems返回一个wxMenuItemList类型的菜单项的列表。

GetTitle和SetTitle用来获取或者设置菜单的可选标题,这个标题通常只在弹出菜单中有意义。

UpdateUI发送用户界面更新事件到其参数只是的事件处理对象,如果参数为NULL则发往菜单的父窗口.这个函数在菜单即将显示之前会被调用,但是应用程序也可以在自己认为需要的时候调用它。

4.9 控制条

控制条提供了一个比较直观而方便的方法来排列多个控件。目前有三种类型的控制条,分别是:菜单条,工具条和状态条,其中菜单条只能在frame窗口上使用,工具条和状态条通常也是在frame窗口上使用,不过它们也可以作为别的窗口的子窗口。

wxMenuBar

菜单条包含一系列的菜单,显示在frame窗口顶部标题栏的下方。你可以通过调用SetMenuBar函数覆盖frame窗口当前的菜单条,使用下面的代码创建一个菜单条:

wxMenuBar* menuBar = new wxMenuBar;
wxMenu* fileMenu = new wxMenu;
fileMenu->Append(wxID_OPEN, wxT("&Open..."), wxT("Opens a file"));
fileMenu->AppendSeparator();
fileMenu->Append(wxID_EXIT, wxT("E&xit"), wxT("Quits the program"));
menuBar->Append(fileMenu);
frame->SetMenuBar(menuBar, wxT("&File"));

上述代码创建了一个只有一个菜单的菜单条,如下图所示:

你可以给菜单增加子菜单,也可以在菜单上增加单选框和复选框,还可以给菜单项增加快捷键和加速键(请参考本章前一章节的内容)。

如果你提供了一个帮助字符创,那么默认的EVT_MENU_HIGHLIGHT函数会将其显示到frame的窗口的状态栏上(如果有的话)。

wxMenuBar的窗口类型

wxMenuBar拥有一个wxMB_DOCKABLE类型,在GTK+平台上允许菜单条从主窗口分离出来。

wxMenuBar事件

参考本章前一小节中的相关内容。

wxMenuBar成员函数

Append函数在菜单条的末尾增加一个菜单项,一旦菜单项被成功增加,那么它将被菜单条管理并且在合适的时候被释放。这个函数参数为要增加的菜单以及一个标签。Insert函数则在给定的位置插入一个菜单。

Enable函数允许或者禁止给定标识符的菜单项. 使用IsEnabled函数判断菜单项是否被允许.

Check函数选中或者去选中一个复选框或者单选框菜单项. 使用IsChecked函数来获得当前的选择状态。

EnableTop函数允许或者禁止一整个菜单。参数为基于0的菜单索引。

FindMenu使用给定的标签字符串查找某个菜单,其中参数字符串可以带有前导字符也可以不带有前导字符.如果找不到则返回wxNOT_FOUND.

FindMenuItem通过菜单名和菜单项返回菜单项的索引。

FindItem通过给定的菜单项标识符返回wxMenuItem类型的菜单项,如果这个菜单项是一个子菜单,那么第二个参数将会返回这个子菜单的指针。

GetHelpString和SetHelpString用来获取或者设置某个菜单项的帮助信息。

GetLabel和SetLabel用来设置某个菜单项的标签,

GetLabelTop和 SetLabelTop用来获取和设置某个菜单在菜单条上的标签,参数为基于0的索引。

GetMenu根据索引返回对应菜单指针,

GetMenuCount返回菜单条上所有菜单的个数,

Refresh重画菜单条。

Remove移除一个菜单项并且返回对应的菜单指针,然后用户应该自己释放这个已经移除的菜单。

Replace函数则将某个位置的菜单使用新的菜单代替并且返回老的菜单的指针。 用户应该自己释放这个老的菜单,

wxToolBar

工具条用来放置各种按钮和控件。工具条可以是水平的也可以是垂直的,其上的按钮可以是单选,复选以及push按钮。这些按钮可以显示标签也可以显示图片。如果你使用wxFrame::CreateToolBar函数创建工具条,或者使用wxFrame::SetToolBar函数将你创建的工具条和 frame窗口绑定,那么frame窗口将会管理这个工具条的位置和大小以及释放,并且工具条的大小也不算作frame的客户区大小。。任何其它方法创建的工具条,你都需要自己使用布局控件或者其它方法来来负责这个工具条的位置和大小,

下面是创建工具条以及和frame窗口绑定的例子代码:

#include "wx/toolbar.h"
#include "open.xpm"
#include "save.xpm"
wxToolBar* toolBar = new wxToolBar(frame, wxID_ANY,
wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL|wxNO_BORDER);
wxBitmap bmpOpen(open_xpm);
wxBitmap bmpSave(save_xpm);
toolBar->AddTool(wxID_OPEN, bmpOpen, wxT("Open"));
toolBar->AddTool(wxID_SAVE, bmpSave, wxT("Save"));
toolBar->AddSeparator();
wxComboBox* comboBox = new wxComboBox(toolBar, ID_COMBOBOX);
toolBar->AddControl(comboBox);
toolBar->Realize();
frame->SetToolBar(toolBar);

在windows平台上的外观如下图所示:

注意调用Realize之前,所有位于其上的按钮和控件应该已经被增加到了工具条中,否则工具条上将什么也不会显示。

你可以查看wxWidgets的samples/toolbar中的例子,来了解怎样更改工具条的方向,增加显示按钮上的标签以及更改按钮的大小等等其它方面的内容。

Windows平台上工具按钮上的图片的颜色

在windows平台上,wxWidgets试图将工具按钮上的图片的颜色映射到当前桌面风格下的标准颜色。通常来说,亮灰色(light gray)用来表示透明颜色。下表列出了所有这些颜色。事实上,工具按钮上的图片的颜色只需要接近于标准颜色。接近意味着RGB三原色的值和标准颜色的值差别在10个单位范围内。

颜色值 颜色名 用于
wxColour(0, 0, 0) 黑色 深色阴影
wxColour(128, 128, 128) 深灰 亮物体的3维边缘的阴面
wxColour(192, 192, 192) 亮灰 3维物体的表面(按钮背景), 表示透明区域
wxColour(255, 255, 255) 白色 亮物体的3维边缘的高亮面

对于16色的图片来说,这中映射是没有问题的,但是如果你使用的是颜色更丰富的按钮图片甚至真彩图片,那么这种映射可能会让你的按钮上的图片变的非常丑陋,在这种情况下,你可以使用下面的代码禁止这种映射:

wxSystemOptions::SetOption(wxT("msw.remap"), 0);

要使用上面的代码,你需要包含"wx/sysopt.h"头文件。

wxToolBar的窗口类型

wxTB_HORIZONTAL 创建水平工具条.
wxTB_VERTICAL 创建垂直工具条.
wxTB_FLAT 给工具条一个浮动外观. Windows and GTK+ only.
wxTB_DOCKABLE 使得工具条可以支持浮动和放置. GTK+ only.
wxTB_TEXT 显示按钮上的标签;默认情况下只显示图标.
wxTB_NOICONS 不显示按钮上的图标;默认情况下是显示的.
wxTB_NODIVIDER 指示工具条上没有分割线. Windows only.
wxTB_HORZ_LAYOUT 指示文本显示在图片的旁边而不是下面. 仅适用于Windows和GTK+. 这个类型必须和wxTB_TEXT共同使用.
wxTB_HORZ_TEXT 是wxTB_HORZ_LAYOUT和wxTB_TEXT的组合.

wxToolBar的事件

工具条事件映射宏如下表所示。象前面说过的那样,工具条的事件宏和菜单的事件宏是完全一样的,你既可以使用EVT_MENU宏,也可以使用EVT_TOOL宏。它们的事件处理函数的参数类型都是wxCommandEvent。对于它们中的大多数来说,窗口标识符指的都是工具按钮的窗口标识符,只有EVT_TOOL_ENTER事件宏的窗口标识符指的是工具条的窗口标识符,而对应的工具按钮的窗口标识符要从wxCommandEvent事件中获取,这是因为在表示鼠标离开一个工具按钮的时候,窗口标识符可能为-1,而在wxWidgets的事件体系中是不允许标识符为-1的。

EVT_TOOL(id, func) 用于处理wxEVT_COMMAND_TOOL_CLICKED事件 (和wxEVT_COMMAND_MENU_SELECTED的值相同), 在用户点击工具按钮的时候产生. 在宏中使用工具按钮的标识符.
EVT_TOOL_RANGE(id1, id2, func) 用于处理一组工具按钮标识符的wxEVT_COMMAND_TOOL_CLICKED事件.
EVT_TOOL_RCLICKED(id, func) 用于处理wxEVT_COMMAND_TOOL_RCLICKED事件,用户在工具按钮上点击右键的时候产生.使用工具按钮的窗口标识符.
EVT_TOOL_RCLICKED_RANGE (id1, id2, func) 用于处理一组工具按钮标识符的wxEVT_COMMAND_TOOL_RCLICKED事件.
EVT_TOOL_ENTER(id, func) 用于处理wxEVT_COMMAND_TOOL_ENTER事件, 用户的鼠标移入或者移出某个工具按钮的额时候产生,宏中使用工具条的窗口标识符,使用wxCommandEvent::GetSelection函数来获得移入的工具按钮的标识符,如果是移出则这个值是-1

wxToolBar的成员函数

AddTool增加一个工具按钮:指定按钮的标识符,一个可选的标签,一个图片,一个帮助信息,以及按钮的类型(wxITEM_NORMAL, wxITEM_CHECK, 或者wxITEM_RADIO). 使用InsertTool在某个特定的位置插入一个工具按钮. 也可以使用AddCheckTool和AddRadioTool来避免指定wxITEM_CHECK或wxITEM_RADIO类型. AddSeparator 增加一个分割线,基于实现的不同,它可能显示为一条线或者一段空白. 使用InsertSeparator在某个特定的位置插入一个分割线,例如下面的代码将增加一个复选框工具按钮,它包含一个标签("Save"), 一个图片,一个帮助字符串 ("Toggle button 1"):

toolBar->AddTool(wxID_SAVE, wxT("Save"), bitmap,
wxT("Toggle button 1"), wxITEM_CHECK);

AddControl增加一个控件,比如combo框. InsertControl在某个位置插入一个控件,

DeleteTool删除给定标识符的工具按钮. DeleteToolByPos删除指定位置的按钮. RemoveTool移除给定位置的那个按钮但是并不释放它,而是将它的指针返回给.

EnableTool用来允许或者禁止某个工具按钮,你可能想参考第9章中的方法,使用用户界面更新事件来更合理的作这个动作。 GetToolEnabled函数返回某个工具按钮的当前可用状态。

FindById和FindControl来通过窗口标识符寻找某个按钮或者某个控件。

如果你想使用非默认大小16x15的图标。你可以使用SetToolBitmapSize函数. 使用GetToolBitmapSize函数获得当前设置的图片尺寸. GetToolSize返回当前工具按钮整个的大小。

GetMargins和SetMargins用来获取和设置工具条的左右和上下的边界。

GetToolClientData和SetToolClientData通过窗口标识符获取和设置工具按钮绑定的某个继承自wxObject的类.

GetToolLongHelp和SetToolLongHelp用来获取或者设置和工具按钮绑定的长的帮助信息。这个信息可以被显示在状态条或者别的什么位置。GetToolShortHelp则用来获取或者设置工具按钮的短的帮助信息。

GetToolPacking 和 SetToolPacking用来获取和设置两排工具按钮之间的间隔。如果是垂直工具条,则为水平工具按钮之间的间隔,如果为水平工具条,则为垂直工具按钮之间的间隔。

GetToolPosition返回某个由窗口标识符指定的工具按钮在工具条上的位置。

GetToolSeparation和 SetToolSeparation用来获取或者设置工具条上分割线的大小。

GetToolState和SetToolState用来获取或者设置某个单选框或者复选框的状态。

Realize必须在任何按钮被增加以后调用。

ToggleTool反选某个单选或者复选按钮的状态。

wxStatusBar

状态条是一个狭长的窗口,这个窗口通常被放置在一个Frame窗口的底部,用来提供一些状态信息。状态条可以包含一个或多个区域,区域可以拥有固定的或者可变的宽度。如果你使用wxFrame::CreateStatusBar或者wxFrame::SetStatusBar创建或者将某个状态条和frame窗口绑定,那么这个状态条的大小,位置以及其释放都由这个frame窗口负责,并且这个状态条的大小也不包含在frame窗口的客户区域内,反之,任何其它的方式创建的状态条,这些事情都要由你自己的代码去做。

下面是创建一个状态条的例子代码,这个状态条有三个区域,前两个的大小固定是60象素,第三个将使用剩余的区域:

#include "wx/statusbr.h"
wxStatusBar* statusBar = new wxStatusBar(frame, wxID_ANY,
wxST_SIZEGRIP);
frame->SetStatusBar(statusBar);
int widths[] = { 60, 60, -1 };
statusBar->SetFieldWidths(WXSIZEOF(widths), widths);
statusBar->SetStatusText(wxT("Ready"), 0);

这段代码产生的结果如下图所示:

如果你想,你甚至可以在状态条的区域里面放置一些小的控件。这需要你自己管理它们的尺寸和大小,例如在你继承自wxStatusBar的新类的size事件的处理函数中。

wxStatusBar的窗口类型,注意你可以使用SetStatusStyles函数设置某个区域的类型

|:--- |:--- |
| wxST_SIZEGRIP | 在状态条的右边显示一个小的修饰. |

wxStatusBar的事件

没有特别的事件。

wxStatusBar的成员函数

GetFieldRect返回某个区域的内部的大小和位置.

GetFieldsCount返回当前区域的个数. SetFieldsCount用来设置区域的个数。

GetStatusText返回当前某个区域的文本, SetStatusText用来设置某个区域的文本.

PushStatusText保存当前的区域的文本到一个堆栈中,并且把参数的字符串显示在状态条上. PopStatusText则将堆栈中最顶层的字符串显示在状态条上。

SetMinHeight设置状态条最小的合理高度。

SetStatusWidths采用一个整数数组来设置各个区域的宽度。其中整数代表绝对值,而负数则代表比例,比如说,如果你希望创建一个有三个区域的状态条,其中最右边的区域固定为100个象素,左边的两个区域按照2/3和1/3的比例瓜分剩下的区域,则你应该使用一个包含-2,- 1,100三个整数的数组作为这个函数的参数。

SetStatusStyles使用区域个数和一组整数的类型数组来给各个区域设置不同的类型,这些整数的类型决定了区域的外观。其中 wxSB_NORMAL用来显示标准的拥有三维边界的下沉区域,而wxSB_FLAT显示一个没有边框的区域,而wxSB_RAISED则显示一个鼓起的拥有三维边框的区域。

第四章小结

这一章中,我们介绍了足够的关于各种窗口类和控件的知识,相信这足以是你可以开始构建你的各种应用程序。如果你还希望了解这个窗口或者控件的更多的知识,请参考wxWidgets的相关手册。对于那些更深入的窗口类,以及怎样创建自己的控件,我们将在第12章介绍。阅读wxWidgets发行版中的各个例子也将是非常有帮助的,比如samples/widgets, samples/toolbar, samples/text和 samples/listbox中的那些例子。

在接下来的一章里,我们将介绍一下应用程序怎样在不同的表面上作画,包括在窗口上,图片上或者是打印机上。

4.3 基础窗口类

虽然你不一定有机会直接使用基础窗口类(wxWindow),但是由于这个类是很多窗口控件的基类,它实现的很多方法在它的子类型中都可以直接拿来使用,所以有必要介绍一下这个基础窗口类。

窗口类wxWindow

wxWindow窗口类既是一个重要的基类,也是一个你可以直接在代码中使用的类。当然,前者使用的频度要比后者大很多。

和前面提到的一样,wxWindow也可以使用单步创建或者两步创建两种方式。单步创建的构造函数原型如下:

wxWindow(wxWindow* parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = wxT("panel"));

可以象下面这样使用:

wxWindow* win  = new wxWindow(parent, wxID_ANY,
wxPoint(100, 100), wxSize(200, 200));

窗口类型

每一个窗口类都可以使用定义在下表中的这些窗口基类中的窗口类型。这些类型中不是所有的类些都被所有的控件所支持。例如对于边框的类型。如果在创建窗口的时候你没有指定窗口的边框类型,那么在不同的平台上将会有不同的边框类型的缺省值。在windows平台上,控件边框的缺省值为 wxSUNKEN_BORDER,意为使用当前系统风格的边框。你可以使用类似wxNO_BORDER这样的值来覆盖系统的默认值。

wxSIMPLE_BORDER 在窗口周围显示一个瘦边框.
wxDOUBLE_BORDER 显示一个双层边框.
wxSUNKEN_BORDER 显示一个凹陷的边框,或者使用当前窗口风格设置.
wxRAISED_BORDER 显示一个凸起的边框.
wxSTATIC_BORDER 显示一个适合静态控件的边框. 只支持Windows平台.
wxNO_BORDER 不显示任何边框.
wxTRANSPARENT_WINDOW 定义一个透明窗口 (意思是这个窗口不接收paint事件).只支持windows平台.
wxTAB_TRAVERSAL 使用这个类型允许非Dialog窗口支持使用TAB进行遍历.
wxWANTS_CHARS 使用这个类型来允许窗口接收包括回车和TAB在内的所有的键盘事件。TAB用来在Dialog类型的窗口中遍历各控件。如果没有设置这个类型,这些特殊的按键事件将不会被产生。
wxFULL_REPAINT_ON_RESIZE 在默认情况下,在窗口客户区大小发生改变时,wxWidgets并不会重画整个客户区。设置这个类型将使得wxWidgets改变这种默认的作法,而保持整个客户区的刷新
wxVSCROLL 显示垂直滚动条.
wxHSCROLL 显示水平滚动条.
wxALWAYS_SHOW_SB 如果一个窗口有滚动条,那么在不需要滚动条的时候(当窗口足够大不需要使用滚动条的时候),禁止滚条而不隐藏滚动条。这个类型目前只支持Windows平台和wxWidgets的wxUniversal版本.
wxCLIP_CHILDREN 只支持Windows平台,用于消除由于擦除子窗口的背景而引起的闪铄.

下表列出了窗口的扩展类型,这些扩展类型不能直接和类型混用,而要使用wxWindow::SetExtraStyle函数来进行设置。

wxWS_EX_VALIDATE_RECURSIVELY 在默认情况下,Validate,transferDataToWindow,和transferDataFromWindow只在窗口的直接子窗口上才可以使用。如果设置了这个扩展类型,那么将可以递归的在各个子窗口上使用。
wxWS_EX_BLOCK_EVENTS wxCommandEvents事件将会在无法在当前事件表中找到匹配的时候在其父窗口中尝试匹配,设置这个扩展属性可以阻止这个行为。Dialog类型的窗口默认设置了这个类型,但是如果SetExtraStyle被应用程序类调用过的话,默认设置可能被覆盖.
wxWS_EX_TRANSIENT 不要使用这个窗口作为其它窗口的父窗口.这个类型一定只能用于瞬间窗口;否则,如果使用它作为一个dialog或者frame类型窗口的父窗口,如果父窗口在子窗口之前释放,可能导致系统崩溃。
wxWS_EX_PROCESS_IDLE 这个窗口应该处理所有的idle事件,包括那些设置了wxIDLE_PROCESS_SPECIFIED模式的idle事件。
wxWS_EX_PROCESS_UI_UPDATES 这个窗口将处理所有的Ui刷新事件,包括那些设置了wxUPDATE_UI_PROCESS_SPECIFIED的UI刷新事件。参考第9章获得和界面刷新有关的更多的内容.

窗口事件

窗口类和它的派生类可以产生下面的事件(鼠标,键盘和游戏手柄产生的事件将会在第6章描述)。

EVT_WINDOW_CREATE(func) 用于处理wxEVT_CREATE事件, 这个事件在窗口刚刚被产生的时候生成,处理函数的参数类型是wxWindowCreateEvent.
EVT_WINDOW_DESTROY(func) 用于处理wxEVT_DELETE事件,在这个窗口即将被删除的时候产生,处理函数的参数类型是wxWindowDestroyEvent.
EVT_PAINT(func) 用于处理wxEVT_PAINT事件,在窗口需要被刷新的时候产生.处理函数的参数类型是wxPaintEvent.
EVT_ERASE_BACKGROUND(func) 用于处理wxEVT_ERASE_BACKGROUND事件,在窗口背景需要被更新的时候产生. 处理函数的参数是wxEraseEvent.
EVT_MOVE(func) 用于处理wxEVT_MOVE事件, 在窗口移动的时候产生.处理函数的参数类型是wxMoveEvent.
EVT_SIZE(func) 用于处理wxEVT_SIZE事件, 在窗口大小发生变化以后产生.处理函数的参数类型是wxSizeEvent.
EVT_SET_FOCUS(func)EVT_KILL_FOCUS(func) 用于处理wxEVT_SET_FOCUS和wxEVT_KILL_FOCUS事件,在窗口得到或者失去键盘焦点的时候产生. 处理函数参数类型是wxFocusEvent.
EVT_SYS_COLOUR_CHANGED(func) 用于处理wxEVT_SYS_COLOUR_CHANGED事件,当用户在控制面板里更改了系统颜色的时候产生(只支持windows平台). 处理函数参数类型为wxSysColourChangedEvent.
EVT_IDLE(func) 用于处理wxEVT_IDLE事件,在空闲事件产生.处理函数参数类型位wxIdleEvent.
EVT_UPDATE_UI(func) 用于处理wxEVT_UPDATE_UI事件,在系统空闲时间产生用来给窗口一个更新自己的机会.

wxWindow类的成员函数

因为wxWindow类是其它所有窗口类的基类,它拥有很多的成员函数。我们不可能在这里作一一的说明,只能拣其中最重要的一些作简要的说明。不过我们还是推荐你浏览一下wxWidgets手册中的相关部分,以便能够彻底了解wxWindow类提供的所有功能,以及要使用这个功能你需要提供的参数等。

CaptureMouse函数可以捕获所有的鼠标输入(将其限制在本窗口以内),ReleaseMouse则可以释放前一次的捕获.在绘图程序中,这两个函数是很有用的。它可以让你在鼠标移动到窗口边缘的时候来滚动窗口的客户区,而不是任由鼠标跑到别的窗口并且激活别的窗口。另外的两个函数GetCapture用来获取当前正在使用的捕获设备(如果是被当前的应用程序设置的话),而HasCapture可以用来检测是否鼠标正被这个窗口捕获。

Centre, CentreOnParent和CentreOnScreen三个函数可以让窗口调整自己的位置使其位于屏幕或者是其父窗口的正中间位置。

ClearBackground函数将使用当前的背景色清除当前窗口.

ClientToScreen和ScreenToClient可以将座标在相对于屏幕左上角和相对于客户区左上角之间进行互相转换.

Close函数产生一个wxCloseEvent事件,这个事件通常的处理过程将关闭窗口,释放内存。当然如果应用程序为这个事件定义了特殊的处理函数,那么窗口也有可能不被关闭和释放。

ConvertDialogToPixels和ConvertPixelsToDialog函数可以对数值进行对话框单位和象素单位之间的转换。这在基于字体大小以便应用程序的显示更合理的操作中是很有用的.

Destroy函数将安全的释放窗口.使用这个函数代替直接调用delete操作符因为这个函数下不同的窗口类型的表现是不一样的。对于对话框和frame这样的顶层窗口来说,这个函数会将窗口放入一个等待删除的额窗口列表中,等到这个窗口的所有的事件都处理完毕的时候才会被删除,这可以避免一些事件在已经不存在的窗口上被执行.

Enable允许或者禁止窗口和它的子窗口处理输入事件。在禁止状态下一些窗口会有不同的颜色和外观。Disable函数和Enable函数使用false作为参数的效果是完全一样的。

FindFocus函数是一个静态函数,用它可以找到当前拥有键盘焦点的窗口。

FindWindow函数可以通过标识符或者名字在它的窗口关系树中寻找某个窗口. 返回值可能是它的一个子窗口或者它自己.如果你可以确定你要找的窗口的类型,可以安全的使用wxDynamicCast进行类型强制转换,转换的结果将是一个指向那个类型的指针或者是NULL:

MyWindow* window = wxDynamicCast(FindWindow(ID_MYWINDOW), MyWindow);

Fit函数会自动改变窗口的大小以便刚好可以容纳它的所有子窗口.这个函数应用被应用在使用基于sizer的布局的时候。FitInside函数是一个类似的函数,区别在于它使用的是虚大小(应用在那些包含滚动条的窗口).

Freeze和Thaw,这两个函数的作用是告诉wxWidgets,在这两个函数之间进行的刷新界面的操作是允许被优化的。举例来说,如果你要在一个文本框控件逐行中加入多行文本,则可以用这个方法来优化显示效果,避免闪烁,这两个函数已经在GTK+版本的wxTextCtrl控件上实现,也适用于 Windows和Max Os X平台的所有类.

GetAcceleratorTable和SetAcceleratorTable用来获取和设置某个窗口的加速键表.

GetBackgroundColour和SetBackgroundColour用来访问窗口的背景颜色属性。这个属性被 wxEVT_ERASE_BACKGROUND事件用来绘制窗口背景.如果你更改了背景颜色设置,你应该调用Refresh或者 ClearBackground函数来刷新背景. SetOwnBackgroundColour的作用和SetBackgroundColour基本相同,但是前者不会更改当前窗口的子窗口的背景属性.

GetBackgroundStyle和SetBackgroundStyle用来设置窗口的背景类型. 默认的窗口背景类型是wxBG_STYLE_SYSTEM,它的含义是wxWidgets按照系统默认设置来进行背景绘制。系统默认的背景绘制方法根据控件的不同而不同,比如wxDialog的默认背景绘制方法是什么纹理绘制的方法,而wxListBox则是使用固定颜色的绘制方法。如果你设置了窗口的背景绘制方法类型是wxBG_STYLE_COLOUR,那么wxWidgets会全部用单一颜色的方法绘制背景。而如果你将其设置为 wxBG_STYLE_CUSTOM, wxWidgets将不进行任何的背景绘制工作,你可以自己在擦除背景事件中或者重画事件中自己绘制背景.如果你希望自己绘制背景,请一定设置 wxBG_STYLE_CUSTOM为背景类型,否则很容易引起画面的闪烁.

GetBestSize函数以象素为单位返回窗口最合适的大小(因为每个窗口类都需要实现DoGetBestSize函数).这个函数用来提示Sizer系统不要让这个窗口的尺寸太小以致不能正确的显示和使用。举例来说,对于静态文本控件来说,不要让它的字符只能显示一半。对于包含其它子窗口的窗口比如wxPanel来说,这个尺寸等于对这个窗口调用Fit函数以后的尺寸的大小。

GetCaret和SetCaret函数用来访问或者设置窗口的光标.

GetClientSize和SetClientSize用来访问和设置窗口的客户区大小,单位是象素. 客户区大小指的是不包括边框和修饰的区域,或者说你用户可以绘制或者用来放置子窗口的区域.

GetCursor和SetCursor函数用来访问和设置窗口的鼠标指针.

GetDefaultItem函数返回一个指向这个窗口默认的子按钮的指针或者返回NULL。默认子按钮是当用户按回车键的时候默认激活的按钮,可以通过wxButton::SetDefault函数进行设置.

GetDropTarget和SetDropTarget函数用来取得或者设置和窗口关联的wxDropTarget对象,这个对象用来处理和控制窗口的拖放操作. 我们将在第11章"剪贴板和拖放"中详细介绍拖放有关的操作。

GetEventHandler和SetEventHandler函数用来访问和设置窗口的第一事件表.默认情况下,窗口的事件表就是窗口自己,但是你可以指定不同的事件表,也可以通过PushEventHandler和PopEventHandler函数设置一个事件表链。然后让不同的事件表处理不同的事件. wxWidgets将搜索整个事件表链来寻找匹配的事件处理函数处理新收到的事件,详情请参阅第3章,“事件处理”.

GetExtraStyle和SetExtraStyle函数用来获取和设置窗口的扩展类型位。扩展类型宏通常以wxWS_EX_开头.

GetFont和SetFont函数获取和设置和窗口相关的字体. SetOwnFont和SetFont的功能相似,唯一的区别在于前者设置的字体不会被子窗口继承.

GetForegroundColour和SetForegroundColour函数用来操作窗口的前景颜色属性. 和SetOwnForegroundColour函数的区别仅在于是否改变子窗口的这个属性。

GetHelpText和SetHelpText用来获取和设置窗口相关的上下文帮助.这个文本属性实际上存储在当前的wxHelpProvider实现中,而不是存在于窗口类中.

GetId和SetId用来操作窗口标识符.

GetLabel函数返回窗口相关联的标签.具体的含义取决的特定的窗口类.

GetName和SetName用来操作窗口名称,这个名字不需要是唯一的。这个名称对wxWidgets来说没有什么意义,但是在Motif系统中被用于窗口资源的名称.

GetParent返回窗口的父窗口.

GetPosition以象素单位返回相对于父窗口的窗口左上角座标。

GetRect返回一个wxRect对象 (参考第13章, "数据结构和类型"),其中包含了象素单位的这个窗口的大小和位置信息.

GetSize和SetSize函数获取和设置窗口象素单位的窗口长宽.

GetSizer和SetSizer函数用来操作这个窗口绑定的最顶级的窗口布局对象.

GetTextExtent用于返回当前字体下某个字符串的象素长度.

GetToolTip和SetToolTip用来操作这个窗口的tooltip对象.

GetUpdateRegion函数返回窗口自上次Paint事件以来需要刷新的区域.

GetValidator和SetValidator函数用来操作这个窗口可选的wxValidator对象。详情请参考第9章.

GetVirtualSize返回窗口的虚大小,通常就是和滚动条绑定的那个大小.

GetWindowStyle和SetWindowStyle用来操作窗口类型比特位。

InitDialog函数发送一个wxEVT_INIT_DIALOG事件来来开始对话框数据传送.

IsEnabled用来检测当前窗口的使能状态.

IsExposed用来检测一个点或者一个矩形区域是否位于需要刷新的范围。

IsShown用来指示窗口是否可见.

IsTopLevel用来指示窗口是否是顶层窗口(仅用于wxFrame或者wxDialog).

Layout函数用来在窗口已经指定一个布局对象的情况下更新窗口布局。参考第7章.

Lower函数用来将窗口移到窗口树的最低层,而Raise则把窗口移动到窗口树的最顶层.这两个函数既可以用于顶层窗口,也可以用于子窗口.

MakeModal函数禁用其它所有的顶层窗口,以便用户只能和当前这个顶层窗口交互.

Move函数用来移动窗口.

MoveAfterInTabOrder更改窗口的TAB顺序到作为参数的窗口的后面, 而MoveBeforeInTabOrder则将其挪到参数窗口的前面.

PushEventHandler压入一个事件表到当前的事件表栈, PopEventHandler函数弹出并且返回事件表栈最顶层的事件表. RemoveEventHandler则查找事件表栈中的一个事件表并且将其出栈.

PopupMenu函数在某个位置弹出一个菜单.

Refresh和RefreshRect函数导致窗口收到一个重画事件(和一个可选的擦除背景事件).

SetFocus函数让本窗口收到键盘焦点.

SetScrollbar函数用来设置窗口内建的滚动条的属性.

SetSizeHints函数用来定义窗口的最小最大尺寸,依次窗口尺寸增量的大小,仅对顶层窗口适用.

Show函数用来显示和隐藏窗口; Hide函数的作用相当于适用false作为参数调用Show函数.

transferDataFromWindow和transferDataToWindow获取和传输数据到窗口对象,并且在没有被重载的情况下会调用验证函数.

Update立即重画窗口已经过期的区域.

UpdateWindowUI函数发送wxUpdateUIEvents事件到窗口,以便给窗口一个更新窗口元素(比如工具条和菜单)的机会.

Validate使用当前的验证对象验证窗口数据.

wxControl类

wxControl是一个虚类。它继承自wxWindow,用来作为控件的基类: 所谓控件指的是那些可以显示数据项并且通常需要响应鼠标或者键盘事件的那些窗口类.

wxControlWithItems类

wxControlWithItems也是一个虚类,用来作为wxWidgets的一些包含多个数据项的控件(比如wxListBox, wxCheckListBox,wxChoice和wxComboBox等)的基类。使用这个基类的目的为了给这些具有相似功能的控件提供一致的API函数。

wxControlWithItems的数据项拥有一个字符串和一个和这个字符串绑定的可选的客户数据。客户数据可以是两种类型,要么是无类型指针(void*),这意味这这个控件只帮忙存储客户数据但是永远不会使用客户数据。另外一种是有类型(wxClientData)指针,对于后一种情况,客户数据会在控件被释放或者数据项被清除的时候被自动释放。同一个控件的所有数据项必须拥有同样的客户区数据类型:要么是前者,要么是后者。客户区数据的类型是在第一次调用Append函数或者,SetClientData函数或者SetClientObject函数的时候被确定的。如果要使用有类型指针客户数据,你应该自定义一个继承自wxClientData的类,然后将它的实例指针传递给Append函数或者 SetClientObject函数。

wxControlWithItems的成员函数

Append函数给这个控件增加一个或者一组数据项. 如果是增加一个数据项,你可以用第二个参数指定有类型客户数据指针或者无类型客户数据指针。比如:

wxArrayString strArr;
strArr.Add(wxT("First string"));
strArr.Add(wxT("Second string"));
controlA->Append(strArr);
controlA->Append(wxT("Third string"));
controlB->Append(wxT("First string"), (void *) myPtr);
controlC->Append(wxT("First string"), new MyTypedData(1));

Clear函数清除控件所有数据项并且清除所有的客户数据.

Delete函数使用基于0的索引清除一个数据项以及它的客户数据。

FindString返回一个和某个字符串基于0的数据项的索引。如果找不到则返回wxNOT_FOUND.

GetClientData和GetClientObject返回某个数据项的客户区数据指针(如果有的话). SetClientData和SetClientObject则可以用来设置这个指针.

GetCount数据项的总数.

GetSelection返回选中的数据项或wxNOT_FOUND. SetSelection则用来设置某个数据项为选中状态.

GetString用来获取某个数据项的字符串; SetString则用来设置它.

GetStringSelection用来返回选中的数据项的字符串或者一个空的字符串; SetStringSelection则用来设置选中的字符串。你应该先调用FindString函数保证这个字符串是存在于某个数据项的,否则可能引发断言失败.

Insert在控件数据项的某个位置插入一个数据项(可以包含也可以不包含客户数据).

IsEmpty则用来检测一个控件的数据项是否为空.

vxWidgets(二):接口文档的更多相关文章

  1. SpringSecurity权限管理系统实战—二、日志、接口文档等实现

    系列目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战 ...

  2. FastAPI 学习之路(二十)接口文档配置相关

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  3. .net core的Swagger接口文档使用教程(二):NSwag

    上一篇介绍了Swashbuckle ,地址:.net core的Swagger接口文档使用教程(一):Swashbuckle 讲的东西还挺多,怎奈微软还推荐了一个NSwag,那就继续写吧! 但是和Sw ...

  4. 【开源】.Net Api开放接口文档网站

    开源地址:http://git.oschina.net/chejiangyi/ApiView 开源QQ群: .net 开源基础服务  238543768 ApiView .net api的接口文档查看 ...

  5. ShiWangMeSDK Android版接口文档 0.2.0 版

    # ShiWangMeSDK Android版接口文档 0.2.0 版 android 总共有 14 个接口,分别涉及到初始化和对界面的一些细节的控制.下面详细介绍接口,如果没有特殊说明,接口都在 S ...

  6. 使用swagger实现web api在线接口文档

    一.前言 通常我们的项目会包含许多对外的接口,这些接口都需要文档化,标准的接口描述文档需要描述接口的地址.参数.返回值.备注等等:像我们以前的做法是写在word/excel,通常是按模块划分,例如一个 ...

  7. 推荐一款接口文档在线管理系统-MinDoc

    项目简介 MinDoc 是一款针对IT团队开发的简单好用的文档管理系统. MinDoc 的前身是 SmartWiki 文档系统.SmartWiki 是基于 PHP 框架 laravel 开发的一款文档 ...

  8. asp.net core使用Swashbuckle.AspNetCore(swagger)生成接口文档

    asp.net core中使用Swashbuckle.AspNetCore(swagger)生成接口文档 Swashbuckle.AspNetCore:swagger的asp.net core实现 项 ...

  9. Postman----基础使用篇(没有接口文档的情况下如何着手做接口测试)

    [备注说明]内文中的图片由于页面的限制,图片显示不清晰,为了能更加的看清图片,请点击"图片",点击"右键"选择"在新标签页中打开图片",可查 ...

  10. .net core 使用swagger自动生成接口文档

     前言 swagger是一个api文档自动生动工具,还集成了在线调试. 可以为项目自动生成接口文档, 非常的方便快捷 Swashbuckle.AspNetCore 是一个开源项目,用于生成 ASP.N ...

随机推荐

  1. Redis 正则扫描key并删除

    扫描key /** * @param key * @return * @Description: 通过Scan的方式迭代key */ public Set<String> scanKeys ...

  2. 方法的调用-JDK的JShell简单使用

    方法的调用 方法在定义完毕后,方法不会自己运行,必须被调用才能执行,我们可以在主方法main中来调用我们自己定义好的方法.在 主方法中,直接写要调用的方法名字就可以调用了. public static ...

  3. 【Oculus Interaction SDK】(一)设置 VR 相机与控制器 && 实现简单的抓取功能

    前言 前段时间 Oculus 的 SDK 频繁更新,很多已有的教程都不再适用于现在的版本了.本系列文章的主要目的是记录现版本常见功能的实现方法,便于自己后续开发.当然,不排除我文章刚写完 SDK 又变 ...

  4. 12月23日内容总结——csrf跨站请求伪造、校验策略、相关装饰器,auth认证模块及相关操作,拓展auth_user表

    目录 一.csrf跨站请求伪造 概念引入 概念讲解 二.csrf校验策略 概念讲解 form表单操作csrf策略 ajax请求csrf策略 三.csrf相关装饰器 四.auth认证模块 五.auth认 ...

  5. 【一句话】CAP原则

    首先一句话: 如果出现网络分区(P),持有旧数据的节点是否要继续提供服务,提供就保证了可用(A),但数据不一定新,放弃了C:如果不提供服务,则意味着放弃了可用性A,但是保证数据一致(C). 我的理解: ...

  6. mysql15 sql优化-小表驱动大表 IN和EXITS

    转:https://blog.csdn.net/qq_27409289/article/details/85963089 1.IN查询分析 select * from a  where a.id in ...

  7. P22_条件渲染

    条件渲染 wx:if 在小程序中,使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块: 也可以用 wx:elif 和 wx:else 来添加 else 判 ...

  8. 《关于我因为flink成为spark源码贡献者这件小事》

    各位读者老爷请放下手上的板砖,我可真没有标题党,且容老弟慢慢道来. spark和flink本身相信我不用做过多的介绍,后端同学不管搞没搞过大数据,应该都多多少少听过. 如果没听过,简单说,spark和 ...

  9. 编写FailServlet和SuccessServlet类

    @WebServlet("/successServlet") public class SuccessServlet extends HttpServlet { protected ...

  10. LG P2633 Count on a tree

    \(\text{Solution}\) 树上主席树板子 \(\text{Code}\) #include <cstdio> #include <algorithm> #defi ...