Using wxWidgets Resource Files
You can load specifications of dialogs, frames, menu bars, toolbars, and so on from XML files with extension xrc instead of creating these elements explicitly in C++ code. This enables better separation of code and user interface, such as enabling an application's dialog design to be changed at runtime. XRC files can be exported by a range of UI design tools, including wxDesigner, DialogBlocks, XRCed, and wxGlade.
Loading Resources
To use XRC files in your application, you need to include wx/xrc/xmlres.h in your application code.If you will be converting your XRC files to binary XRS files, as we will describe shortly, install the zip file system handler by placing an AddHandler call in your OnInit function:
Initialize the XRC system by adding this to your OnInit:
#include "wx/filesys.h"
#include "wx/fs_zip.h"
wxFileSystem::AddHandler(new wxZipFSHandler);
Load the XRC file with code like this:
wxXmlResource::Get()->InitAllHandlers();
This makes wxWidgets aware of the resources in the file; to create a real UI element, we need another call. For example, the following fragment creates a dialog whose resource name is dialog1:
wxXmlResource::Get()->Load(wxT("resources.xrc"));
The following code shows how to load menu bars, menus, toolbars, bitmaps, icons, and panels.
MyDialog dlg;
wxXmlResource::Get()->LoadDialog(& dlg, parent, wxT("dialog1"));
dlg.ShowModal();
wxWidgets maintains a single wxXmlResource object that you can use, but alternatively, you can create a wxXmlResource object, load resources, and then destroy it. You can also use wxXmlResource::Set to set the current global resource object, destroying the old one.To define event tables for windows loaded from a resource file, you can't use integer identifiers because resources have string names. Instead, use the XRCID macro, which takes a resource name and returns an integer identifier associated with the name. XRCID is an alias for the function wxXmlResource:: GetXRCID. Here's an example of XRCID usage:
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"));
// Finish creating panelA after making an instance of it
MyPanel* panelA = new MyPanel;
panelA = wxXmlResource::Get()->LoadPanel(panelA, this,
wxT("panelA"));
// A second method: get XRC to both create and load panelB
wxPanel* panelB = wxXmlResource::Get()->LoadPanel(this,
wxT("panelB"));
}
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(XRCID("menu_quit"), MyFrame::OnQuit)
EVT_MENU(XRCID("menu_about"), MyFrame::OnAbout)
END_EVENT_TABLE()
Using Binary and Embedded Resource Files
It can be convenient to combine a number of resource files into one binary file (extension xrs). To compile XRC files into a zip file that the resource system can load, use the utility wxrc located in the utils/wxrc directory in your wxWidgets distribution:
Use wxXmlResource::Load to load a binary resource file in just the same way as with a plain XML file.Chapter 14, "Files and Streams," so you can write
wxrc resource1.xrc resource2.xrc -o resource.xrs
You can also compile your XRC files into C++ code that may be embedded in your application, thereby eliminating a separate resource file. Here's the wxrc command to do this:
wxXmlResource::Get()->Load(wxT("resources.bin#zip:dialogs.xrc"));
Compile this C++ file as normal and link it with your application. The file includes a function InitXmlResource, which you have to call, for example:
wxrc resource1.xrc resource2.xrc c -o resource.cpp
Table 9-2 lists the command-line options and arguments that wxrc accepts.
extern void InitXmlResource(); // defined in generated file
wxXmlResource::Get()->InitAllHandlers();
InitXmlResource();
Short Command | Long Command | Description |
---|---|---|
-h | help | Shows a help message. |
-v | verbose | Shows verbose logging information. |
-c | cpp-code | Writes C++ source rather than an XRS file. |
-p | python-code | Writes Python source rather than an XRS file. |
-e | extra-cpp-code | If used together with -c, generates a C++ header file containing class definitions for the windows defined by the XRC file. |
-u | uncompressed | Do not compress XML files (C++ only). |
-g | gettext | Outputs underscore-wrapped strings that poEdit or gettext can scan. Outputs to stdout, or a file if -o is used. |
-n | function <name> | Specifies a C++ initialization function name (use with -c). |
-o <filename> | output <filename> | Specifies the output file, such as resource.xrs or resource.cpp. |
-l <filename> | list-of-handlers <filename> | Outputs a list of resource handlers that are needed for the specified resources. |
Translating Resources
If the wxXmlResource object has been created with the wxXRC_USE_LOCALE flag (the default behavior), all displayable strings will be subject to translation, as detailed in Chapter 16, "Writing International Applications." However, poEdit cannot scan XRC files for strings to translate as it can for C++ code, so you can create a file of such strings using wxrc with the -g option. For example:
Then you can run poEdit to scan the strings in this and other files.
wxrc -g resources.xrc -o resource_strings.cpp
The XRC Format
There isn't space to describe the XRC format in detail, but here is an example showing a simple dialog with sizers:
A detailed specification of the XRC format can be found in the technical note docs/tech/tn0014.txt in your wxWidgets distribution. If you use an editor to create your user interfaces, you won't need to know about XRC's format.You may be wondering how a text XRC file can be used to specify binary bitmaps and icons. These resources may be specified as URLs, and wxWidgets' virtual file system will extract them from sources such as a zip file. For example:
<?xml version="1.0"?>
<resource version="2.3.0.1">
<object name="simpledlg">
<title>A simple dialog</title>
<object>
<orient>wxVERTICAL</orient>
<object>
<object>
<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>
<object>
<object>
<object name="wxID_OK">
<label>Ok</label>
<default>1</default>
</object>
</object>
<object>
<object 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>
See Chapter 10, "Programming with Images," and Chapter 14, "Files and Streams," for more information on using virtual file systems to load resources such as images.
<object name="wxID_OK">
<bitmap>resources.bin#zip:okimage.png</bitmap>
</object>
Writing Resource Handlers
The XRC system uses a resource handler to recognize the XML specification of each type of resource. If you write your own custom control, you may want to write a resource handler so that applications can use the custom control with XRC.As an illustration, the declaration for wxButton's handler looks like this:
The handler implementation is quite simple. In the handler's constructor, the XRC_ADD_STYLE macro is used to make the handler aware of specific button styles, and AddWindowStyles is called to add common window styles. In DoCreateResource, the button object is created in two steps, using XRC_MAKE_INSTANCE and then Create, extracting parameters such as the label, position, and size. Finally, CanHandle tests whether this handler can handle the node in question. It's permissible for a single handler class to handle more than one kind of resource.
#include "wx/xrc/xmlres.h"
class wxButtonXmlHandler : public wxXmlResourceHandler
{
DECLARE_DYNAMIC_CLASS(wxButtonXmlHandler)
public:
wxButtonXmlHandler();
virtual wxObject *DoCreateResource();
virtual bool CanHandle(wxXmlNode *node);
};
To use a handler, an application needs to include the header and register the handler, as follows:
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);
Foreign Controls
An XRC file can specify a foreign, or "unknown" control, by specifying class="unknown" in the object definition. This can stand in for a control that is actually created in the C++ code, after the parent is loaded from XRC. When XRC loads the unknown object, a placeholder window is created. Then the application calls AttachUnknownControl to superimpose the real window onto the placeholder window, with the correct position and size. For example:
The custom control definition can look like this:
wxDialog dlg;
// Load the dialog
wxXmlResource::Get()->LoadDialog(&dlg, this, wxT("mydialog"));
// Make an instance of our new custom class.
MyCtrl* myCtrl = new MyCtrl(&dlg, wxID_ANY);
// Attach it to the dialog
wxXmlResource::Get()->AttachUnknownControl(wxT("custctrl"), myCtrl);
// Show the dialog
dlg.ShowModal();
Using this technique, you can lay out interfaces in tools that don't know about your custom controls, and you also avoid the need to write a resource handler.
<object name="custctrl">
<size>100,100</size>
</object>
