Have you ever wondered if you could create a wxPython program using XML? Well, I never did either, but there is a way and its name is XRC. In fact, wxPython comes with an editor called XRCed that you can use to layout your GUI and generate the XML code with. In this article, we’ll give you a quick walk-through of XRC and how to use it to create a couple GUI skeletons. We will look at two examples that use only XRC controls and then a third that mixes in some additional non-XRC widgets.

Creating a Login Screen with XRC

A common dialog that we often see is a login dialog. I used XRCed to create the following XML code:

<?xml version="1.0" encoding="cp1252"?>
<resource>
  <object class="wxFrame" name="mainFrame">
    <object class="wxPanel" name="panel">
      <object class="wxBoxSizer">
        <orient>wxVERTICAL</orient>
        <object class="sizeritem">
          <object class="wxStaticText" name="handle">
            <label/>
          </object>
        </object>
        <object class="sizeritem">
          <object class="wxFlexGridSizer">
            <object class="sizeritem">
              <object class="wxStaticText" name="userLbl">
                <label>Username:</label>
              </object>
              <flag>wxALL</flag>
              <border>5</border>
            </object>
            <object class="sizeritem">
              <object class="wxTextCtrl" name="userTxt"/>
            </object>
            <object class="sizeritem">
              <object class="wxStaticText" name="passwordLbl">
                <label>Password:</label>
              </object>
              <flag>wxALL</flag>
              <border>5</border>
            </object>
            <object class="sizeritem">
              <object class="wxTextCtrl" name="passwordTxt">
                <style>wxTE_PROCESS_ENTER|wxTE_PASSWORD</style>
              </object>
            </object>
            <object class="sizeritem">
              <object class="wxButton" name="loginBtn">
                <label>Login</label>
              </object>
              <flag>wxALL|wxALIGN_CENTRE</flag>
              <border>5</border>
            </object>
            <object class="sizeritem">
              <object class="wxButton" name="cancelBtn">
                <label>Cancel</label>
              </object>
              <flag>wxALL|wxALIGN_CENTRE</flag>
              <border>5</border>
            </object>
            <cols>2</cols>
            <rows>3</rows>
            <vgap>4</vgap>
            <hgap>2</hgap>
          </object>
          <border>5</border>
        </object>
      </object>
      <style/>
    </object>
    <size>200,100</size>
    <title>Login</title>
    <centered>1</centered>
  </object>
</resource>

To use XRC code in your wxPython, all you need to do is “import wx.xrc” or use “from wx import xrc”. Let’s see what the Python code looks like:

import wx
from wx import xrc
 
class MyApp(wx.App):
    def OnInit(self):
        res = xrc.XmlResource("login.xrc")
 
        frame = res.LoadFrame(None, 'mainFrame')
 
        frame.Show()
        return True
 
if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

In the code above, we use xrc’s XmlResource method to open our XML file and load it in our program. Next, we use the resulting variable to load specific widgets from the file. In this case, we load just the frame by calling LoadFrame. Note that we passed None into the LoadFrame call. That first argument is the parent argument and since this frame shouldn’t have a parent, we passed it None. Lastly, we call the frame’s Show method so we can actually see our program. That’s all there is to it! Now let’s move onto something a little bit more complex.

Creating a Notebook with XRC

Creating a Notebook widget is a little trickier than just creating a frame. For one thing, when using a Notebook, you usually stick multiple panels on it. This can get confusing if your panels are complex. Thus, we’ll look at how to create a simple notebook and a slightly more complex version. Let’s start with the simple one first. If you want to follow along, open XRCed and see if you can copy the layout in the screenshot below:

XRCed layout of a Notebook

XRCed layout of a Notebook

The trick to adding pages to your notebook in XRCed is that you need to select the child panel and choose the NotebookPage tab that appears on the right. In there you can set the labels for the tabs. Let’s take a look at the generated XML:

<?xml version="1.0" ?>
<resource>
  <object class="wxFrame" name="DemoFrame">
    <object class="wxPanel" name="DemoPanel">
      <object class="wxBoxSizer">
        <orient>wxVERTICAL</orient>
        <object class="sizeritem">
          <object class="wxNotebook" name="DemoNotebook">
            <object class="notebookpage">
              <object class="wxPanel" name="tabOne"/>
              <label>tabOne</label>
            </object>
            <object class="notebookpage">
              <object class="wxPanel" name="tabTwo"/>
              <label>tabTwo</label>
            </object>
          </object>
          <option>1</option>
          <flag>wxALL|wxEXPAND</flag>
          <border>5</border>
        </object>
      </object>
    </object>
    <title>XRC Notebook Demo</title>
  </object>
</resource>

It’s pretty much the same as the code we saw previously. Note that we can embed sizer flags in the XML itself (e.g. wxALL|wx.EXPAND). That’s pretty neat! The code to load this notebook is almost exactly the same as the code we used for the login dialog earlier:

# notebookXrcDemo.py
import wx
from wx import xrc
 
class MyApp(wx.App):
    def OnInit(self):
        self.res = xrc.XmlResource("notebook2.xrc")
 
        self.frame = self.res.LoadFrame(None, 'DemoFrame')
 
        self.frame.Show()
        return True
 
if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

The only differences here are the names of the frame and the XRC file. Now let’s move on to our slightly more complex Notebook example. In this example, we will create a notebook XRC file and two Panel XRC files that we can use as tabs for the notebook. Our new Notebook’s XML is pretty much like the old one, so we’ll skip that. Instead, we’ll look at our Python code instead:

Notebook Demo

# notebookXrcDemo2.py
import wx
from wx import xrc
 
class MyApp(wx.App):
    def OnInit(self):
        res = xrc.XmlResource("notebook.xrc")
        frame = res.LoadFrame(None, "DemoFrame")
        panel = xrc.XRCCTRL(frame, "DemoPanel")
        notebook = xrc.XRCCTRL(panel, "DemoNotebook")
 
        # load another xrc file
        res = xrc.XmlResource("panelOne.xrc")
        tabOne = res.LoadPanel(notebook, "panelOne")
        notebook.AddPage(tabOne, "TabOne")
 
        # load the last xrc file
        res = xrc.XmlResource("panelTwo.xrc")
        tabTwo = res.LoadPanel(notebook, "panelTwo")
        notebook.AddPage(tabTwo, "tabTwo")
 
        frame.Show()
        return True
 
if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

Here we just extract the frame, panel and notebook objects from the first XRC file and use those as our basis for adding other controls. Loading the other two panels is a cinch as we just do what we did to load the original panel. Then we add our new panels to the notebook using the familiar AddPage methodology. Once that’s done, we show the frame and we’re done! The second panel has an empty ListCtrl in it and when I first created it, I kept getting error messages because I forgot to set its style. Make sure you tell it that you want it to be in List, Report or one of its other modes or you’ll have issues too.

Adding Controls Outside of XRC

One of the issues with XRC is that it only supports a small subset of the widgets available. Fortunately, there are ways to “teach” XRC how to use new controls, but that is beyond the scope of this introductory article. Instead, I’ll show you how to add the controls outside of XRC. The concept is the same as using normal widgets, so it’s really easy to understand. In fact, we’re going to take the second notebook example and add a PlateButton to it:

# notebookXrcDemo3.py
 
import wx
from wx import xrc
import wx.lib.platebtn as platebtn
 
class MyApp(wx.App):
    def OnInit(self):
        self.res = xrc.XmlResource("notebook2.xrc")
 
        frame = self.res.LoadFrame(None, 'DemoFrame')
        panel = xrc.XRCCTRL(frame, "DemoPanel")
        notebook = xrc.XRCCTRL(panel, "DemoNotebook")
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        btn = platebtn.PlateButton(panel, label="Test", style=platebtn.PB_STYLE_DEFAULT)
        sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
        sizer.Add(btn)
        panel.SetSizer(sizer)
 
        frame.Show()
        return True
 
if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

Notice that all we had to do was take the XRC Panel widget and make it the PlateButton’s parent. Then we added the notebook and the button to a vertically oriented sizer. Now we know how to combine normal widgets with XRC widgets in our applications. I hope you’ve learned a lot from this article and will find it helpful in your own work.

Further Reading

Downloads

Print Friendly