返回介绍

9.5 使用 wxWidgets 资源文件

发布于 2025-03-08 15:26:46 字数 8962 浏览 0 评论 0 收藏 0

你可以从一个 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 程序的命令行参数:

短命令格式长命令格式描述
-hhelp显式帮助信息。
-vverbose打印执行过程信息。
-ccpp-code编译目标为 C++代码,而不是 XRS 文件。
-ppython-code编译目标为 Python 代码而不是 XRS 文件。
-eextra-cpp-code和-c 一起使用,指示为 XRC 定义的窗口生成头文件。
-uuncompressed不要压缩 Xml 文件(C++ only)。
-ggettext将相关的字符串翻译为 poEdit 或者 gettext 可以识别的格式.输出到标准输出或者某个文件中(如果指定了-o 参数的话)。
-nfunction <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>

使用这种技术,你可以既不用创建新的资源处理类,又可以在资源文件中使用未知的控件。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文