# 9.5 使用wxWidgets资源文件
你可以从一个Xml文件中加载对话框,frame窗口,菜单条,工具条等等,而不一定非要用C++代码来创建它们.这更符合界面和代码分离的原则,它可以让应用程序的界面在运行期改变.XRC文件可以通过一系列用户界面设计的工具导出,比如:wxDesigner, DialogBlocks, XRCed和wxGlade.
加载资源文件
要使用XRC文件,你需要在你的代码中包含wx/xrc/xmlres.h头文件.
如果你打算将你的XRC文件转换成二进制的XRS文件(我们很快会介绍到),你还需要增加zip文件系统的处理函数,你可以在你的OnInit函数中增加下面的代码来作到这一点:
```
#include "wx/filesys.h"
#include "wx/fs_zip.h"
wxFileSystem::AddHandler(new wxZipFSHandler);
```
首先初始化XRC处理系统,你需要在OnInit中增加下面的代码:
```
wxXmlResource::Get()->InitAllHandlers();
```
然后加载一个XRC文件:
```
wxXmlResource::Get()->Load(wxT("resources.xrc"));
```
这只是告诉wxWidgets这个资源文件的存在,要创建真实的用户界面,还需要类似下面的代码:
```
MyDialog dlg;
wxXmlResource::Get()->LoadDialog(& dlg, parent, wxT("dialog1"));
dlg.ShowModal();
```
下面的代码则演示了怎样创建菜单条,菜单,工具条,位图,图标以及面板:
```
MyFrame::MyFrame(const wxString& title): wxFrame(NULL, -1, title)
{
SetMenuBar(wxXmlResource::Get()->LoadMenuBar(wxT("mainmenu")));
SetToolBar(wxXmlResource::Get()->LoadToolBar(this,
wxT("toolbar")));
wxMenu* menu = wxXmlResource::Get()->LoadMenu(wxT("popupmenu"));
wxIcon icon = wxXmlResource::Get()->LoadIcon(wxT("appicon"));
SetIcon(icon);
wxBitmap bitmap = wxXmlResource::Get()->LoadBitmap(wxT("bmp1"));
// 既可以先创建实例再加载
MyPanel* panelA = new MyPanel;
panelA = wxXmlResource::Get()->LoadPanel(panelA, this,
wxT("panelA"));
// 又可以直接创建并加载
wxPanel* panelB = wxXmlResource::Get()->LoadPanel(this,
wxT("panelB"));
}
```
wxWidgets维护一个全局的wxXmlResource对象,你可以直接拿来使用,也可以创建一个你自己的 wxXmlResource对象,然后加载某个资源文件,然后使用和释放它.你还可以使用wxXmlResource::Set函数来让应用程序用某个 wxXmlResource对象来取代全局资源对象,并释放掉那个旧的.
要为定义在资源文件中的控件定义事件表条目,你不能直接使用整数的标识符,因为资源文件中存放的其实是字符串,你需要使用XRCID 宏,它的参数是一个资源名,返回值是这个资源对应的标识符.其实XRCID就是直接使用的wxXmlResource::GetXRCID函数,举例如下:
```
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(XRCID("menu_quit"), MyFrame::OnQuit)
EVT_MENU(XRCID("menu_about"), MyFrame::OnAbout)
END_EVENT_TABLE()
```
使用二进制和嵌入式资源文件
你可以把多个wxWidgets资源文件编译成一个二进制的压缩的xrs文件.使用的工具wxrc可以在wxWidgets的utils/wxrc目录中找到,使用方法如下:
```
wxrc resource1.xrc resource2.xrc -o resource.xrs
```
使用wxXmlResource::Load函数加载一个二进制的压缩的资源文件,和加载普通的文本Xml文件没有区别.
提示: 你可以不必把你的XRC文件单独制作一个zip压缩文件,而是把它放在其它一个可能包含HTML文件以及图片文件的普通的zip压缩文件中, wxXmlResource::Load函数支持虚拟文件系统定义(参考第14章:"文件和流"),因此你可以通过下面的方法来加载压缩文件中的XRC文件:
```
wxXmlResource::Get()->Load(wxT("resources.bin#zip:dialogs.xrc"));
```
你也可以将XRC文件编译为C++的代码,通过和别的C++的代码编译在一起,你就可以去掉某个单独的资源文件了.编译用的命令行如下所示:
```
wxrc resource1.xrc resource2.xrc c -o resource.cpp
```
编译方法和编译普通的C++代码相同,这个文件包含一个InitXmlResource函数,你必须在你的主程序中调用这个函数:
```
extern void InitXmlResource(); // defined in generated file
wxXmlResource::Get()->InitAllHandlers();
InitXmlResource();
```
下面列出了wxrc程序的命令行参数:
| 短命令格式 | 长命令格式 | 描述 |
|:--- |:--- |:--- |
| -h | help | 显式帮助信息. |
| -v | verbose | 打印执行过程信息. |
| -c | cpp-code | 编译目标为C++代码,而不是XRS文件. |
| -p | python-code | 编译目标为Python代码而不是XRS文件. |
| -e | extra-cpp-code | 和-c一起使用,指示为XRC定义的窗口生成头文件. |
| -u | uncompressed | 不要压缩Xml文件(C++ only). |
| -g | gettext | 将相关的字符串翻译为poEdit或者gettext可以识别的格式.输出到标准输出或者某个文件中(如果指定了-o参数的话). |
| -n | function <name> | 指定特定的C++初始化函数(和-c一起使用). |
| -o <filename> | output <filename> | 指定输出文件名,比如resource.xrs or resource.cpp. |
| -l <filename> | list-of-handlers <filename> | 列举这个资源文件所需要的处理函数. |
资源翻译
如果wxXmlResource对象创建的时候指定了wxXRC_USE_LOCALE标记(默认行为),所有可显示的字符串都将被认为是需要翻译的,具体内容参考第16章,"编写国际化应用程序",然后poEdit并能查找XRC文件来发现那些需要翻译的字符串,因此,必须使用"-g" 参数产生一个对应的C++文件,以供poEdit使用,命令行如下:
```
wxrc -g resources.xrc -o resource_strings.cpp
```
然后你就可以使用poEdit来搜索这个和其它的C++文件了.
XRC的文件格式
这里显然不是完整描述XRC文件格式的地方,因此我们只举一个简单的使用了布局控件的例子:
```
<?xml version="1.0"?>
<resource version="2.3.0.1">
<object class="wxDialog" name="simpledlg">
<title>A simple dialog</title>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<object class="wxTextCtrl">
<size>200,200d</size>
<style>wxTE_MULTILINE|wxSUNKEN_BORDER</style>
<value>Hello, this is an ordinary multiline\n textctrl....</value>
</object>
<option>1</option>
<flag>wxEXPAND|wxALL</flag>
<border>10</border>
</object>
<object class="sizeritem">
<object class="wxBoxSizer">
<object class="sizeritem">
<object class="wxButton" name="wxID_OK">
<label>Ok</label>
<default>1</default>
</object>
</object>
<object class="sizeritem">
<object class="wxButton" name="wxID_CANCEL">
<label>Cancel</label>
</object>
<border>10</border>
<flag>wxLEFT</flag>
</object>
</object>
<flag>wxLEFT|wxRIGHT|wxBOTTOM|wxALIGN_RIGHT</flag>
<border>10</border>
</object>
</object>
</object>
</resource>
```
XRC文件格式的详细描述可以在wxWidgets自带的文档目录docs/tech/tn0014.txt中找到.如果你使用对话框编辑器的话,你管它的文件格式干嘛呢.
你可能回问怎样在XRC文件中指定二进制的图片或者图标文件呢?实际上这些资源是通过URLs来指定的,wxWidgets的虚拟文件系统将会从合适的地方(比如一个压缩文件中)获取指定的文件.举例如下:
```
<object class="wxBitmapButton" name="wxID_OK">
<bitmap>resources.bin#zip:okimage.png</bitmap>
</object>
```
关于使用虚拟文件系统加载资源或者图片的细节,请参考第10章,"在程序中使用图片"以及第14章"文件和流".
编写资源处理类
XRC系统使用不同的资源处理类来识别Xml文件中定义的不同的资源.如果你编写了自己的控件,你就需要编写自己的资源处理类.
wxButton的资源处理类如下所示:
```
#include "wx/xrc/xmlres.h"
class wxButtonXmlHandler : public wxXmlResourceHandler
{
DECLARE_DYNAMIC_CLASS(wxButtonXmlHandler)
public:
wxButtonXmlHandler();
virtual wxObject *DoCreateResource();
virtual bool CanHandle(wxXmlNode *node);
};
```
资源处理类的实现是非常简单的.在其构造函数的实现中,使用 XRC_ADD_STYL宏来使得处理类可以识别控件相关的特殊的窗口类型,然后使用AddWindowStyles增加这些类型.然后在 DoCreateResource函数中,使用两步法创建按钮实例,其中第一步要使用XRC_MAKE_INSTANCE函数,然后调用Create函数,参数需要使用对应的函数从Xml文件中获得.而CanHandle函数则用来回答是否这个处理类可以处理某个Xml节点的问题.使用一个处理类处理多种Xml节点是允许的.
```
IMPLEMENT_DYNAMIC_CLASS(wxButtonXmlHandler, wxXmlResourceHandler)
wxButtonXmlHandler::wxButtonXmlHandler()
: wxXmlResourceHandler()
{
XRC_ADD_STYLE(wxBU_LEFT);
XRC_ADD_STYLE(wxBU_RIGHT);
XRC_ADD_STYLE(wxBU_TOP);
XRC_ADD_STYLE(wxBU_BOTTOM);
XRC_ADD_STYLE(wxBU_EXACTFIT);
AddWindowStyles();
}
wxObject *wxButtonXmlHandler::DoCreateResource()
{
XRC_MAKE_INSTANCE(button, wxButton)
button->Create(m_parentAsWindow,
GetID(),
GetText(wxT("label")),
GetPosition(), GetSize(),
GetStyle(),
wxDefaultValidator,
GetName());
if (GetBool(wxT("default"), 0))
button->SetDefault();
SetupWindow(button);
return button;
}
bool wxButtonXmlHandler::CanHandle(wxXmlNode *node)
{
return IsOfClass(node, wxT("wxButton"));
}
```
要使用某种处理类,应用程序需要包含相应的头文件并且登记这个处理类,就象下面这样:
```
#include "wx/xrc/xh_bttn.h"
wxXmlResource::AddHandler(new wxBitmapXmlHandler);
```
外来控件
XRC文件还可以通过class="unknown"来指定某个控件是外来的或者说是"未知的"控件.这可以用来实现在其父窗口已经加载到应用程序之中以后,使用C++代码来创建这个未知的控件.当XRC文件加载一个未知控件的时候,它将创建一个用来占位的窗口,然后在代码中,可以使用C ++先创建这个实际的控件,然后使用AttachUnknownControl函数替换掉那个用来占位的窗口.如下所示:
```
wxDialog dlg;
// 加载对话框
wxXmlResource::Get()->LoadDialog(&dlg, this, wxT("mydialog"));
// 创建特殊控件
MyCtrl* myCtrl = new MyCtrl(&dlg, wxID_ANY);
// 增加到对话框里
wxXmlResource::Get()->AttachUnknownControl(wxT("custctrl"), myCtrl);
// 显示整个对话框
dlg.ShowModal();
```
外来的控件在XRC文件中可以这样定义:
```
<object class="unknown" name="custctrl">
<size>100,100</size>
</object>
```
使用这种技术,你可以既不用创建新的资源处理类,又可以在资源文件中使用未知的控件.
- 第一章 介绍
- 1.1 为什么要使用wxWidgets?
- 1.2 wxWidgets的历史
- 1.3 wxWidgets社区
- 1.4 wxWidgets和面向对象编程
- 1.5 wxWidgets的体系结构
- 1.6 许可协议
- 第一章小结
- 第二章 开始使用
- 2.1 一个小例子
- 2.2 应用程序类
- 2.3 Frame窗口类
- 2.4 事件处理函数
- 2.5 Frame窗口的构造函数
- 2.6 完整的例子
- 2.7 wxWidgets程序一般执行过程
- 2.8 编译和运行程序
- 第二章小结
- 第三章 事件处理
- 3.1 事件驱动编程
- 3.2 事件表和事件处理过程
- 3.3 过滤某个事件
- 3.4 挂载事件表
- 3.5 动态事件处理方法
- 3.6 窗口标识符
- 3.7 自定义事件
- 第三章小结
- 第四章 窗口的基础知识
- 4.1 窗口解析
- 4.2 窗口类概览
- 4.3 基础窗口类
- 4.4 顶层窗口
- 4.5 容器窗口
- 4.6 非静态控件
- 4.7 静态控件
- 4.8 菜单
- 4.9 控制条
- 第四章小结
- 第五章绘画和打印
- 5.1 理解设备上下文
- 5.2 绘画工具
- 5.3 设备上下文中的绘画函数
- 5.4 使用打印框架
- 5.5 使用wxGLCanvas绘制三维图形
- 第五章小节
- 第六章处理用户输入
- 6.1 鼠标输入
- 6.2 处理键盘事件
- 6.3 处理游戏手柄事件
- 第六章小结
- 第七章使用布局控件进行窗口布局
- 7.1 窗口布局基础
- 7.2 窗口布局控件
- 7.3 使用布局控件进行编程
- 7.4 更多关于布局的话题
- 第七章小结
- 第八章使用标准对话框
- 8.1信息对话框
- 8.2 文件和目录对话框
- 8.3 选择和选项对话框
- 8.4 输入对话框
- 8.5 打印对话框
- 第八章小结
- 第九章创建定制的对话框
- 9.1 创建定制对话框的步骤
- 9.2 一个例子:PersonalRecordDialog
- 9.3 在小型设备上调整你的对话框
- 9.4 一些更深入的话题
- 9.5 使用wxWidgets资源文件
- 第九章小结
- 第十章使用图像编程
- 10.1 wxWidgets中图片相关的类
- 10.2 使用wxBitmap编程
- 10.3 使用wxIcon编程
- 10.4 使用wxCursor编程
- 10.5 使用wxImage编程
- 10.6 图片列表和图标集
- 10.7 自定义wxWidgets提供的小图片
- 第十章小结
- 第十一章剪贴板和拖放操作
- 11.1 数据对象
- 11.2 使用剪贴板
- 11.3 实现拖放操作
- 第十一章小结
- 第十二章高级窗口控件
- 12.1 wxTreeCtrl
- 12.2 wxListCtrl
- 12.3 wxWizard
- 12.4 wxHtmlWindow
- 12.5 wxGrid
- 12.6 wxTaskBarIcon
- 12.7 编写自定义的控件
- 第十二章小结
- 第十三章数据结构类
- 13.1 为什么没有使用STL?
- 13.2 字符串类型
- 13.3 wxArray
- 13.4 wxList和wxNode
- 13.5 wxHashMap
- 13.6 存储和使用日期和时间
- 13.7 其它常用的数据类型
- 第十三章小结
- 第十四章文件和流操作
- 14.1 文件类和函数
- 14.2 流操作相关类
- 第十四章小结
- 第十五章内存管理,调试和错误处理
- 15.1 内存管理基础
- 15.2 检测内存泄漏和其它错误
- 15.3 构建自防御的程序
- 15.4 错误报告
- 15.5 提供运行期类型信息
- 15.6 使用wxModule
- 15.7 加载动态链接库
- 15.8 异常处理
- 15.9 调试提示
- 第十五章小结
- 第十六章编写国际化程序
- 16.1 国际化介绍
- 16.2 从翻译说起
- 16.3 字符编码和Unicode
- 16.4 数字和日期
- 16.5 其它媒介
- 16.6 一个小例子
- 第十六章小结
- 第十七章编写多线程程序
- 17.1 什么时候使用多线程,什么时候不要使用
- 17.2 使用wxThread
- 17.3 用于线程同步的对象
- 17.4 多线程的替代方案
- 第十七章小结
- 第十八章使用wxSocket编程
- 18.1 Socket类和功能概览
- 18.2 Socket及其基本处理介绍
- 18.3 Socket标记
- 18.4 使用Socket流
- 18.5 替代wxSocket
- 第十八章小结
- 第十九章使用文档/视图框架
- 19.1 文档/视图基础
- 19.2 文档/视图框架的其它能力
- 19.3 实现Undo/Redo的策略
- 第十九章小结
- 第二十章完善你的应用程序
- 20.1 单个实例和多个实例
- 20.2 更改事件处理机制
- 20.3 降低闪烁
- 20.4 实现联机帮助
- 20.5 解析命令行参数
- 20.6 存储应用程序资源
- 20.7 调用别的应用程序
- 20.8 管理应用程序设置
- 20.9 应用程序安装
- 20.10 遵循用户界面设计规范
- 20.11 全书小结