# 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 全書小結