Introduction

I am new to wxPython having just read Robin and Noel's excellent book "wxPython in action". In it they recommend creating menus from XML, so I came here and searched for the code but could find none. So I developed this code (as a newbie) which I am happy to share. I am sure it will benefit from re-factoring and expert comment. I have not tested it extensively but it seems to cope with the sample XML attached.

What Objects are Involved

The code is intended to be called from within your frame class init method. You still have to manually set up all the handler methods within your frame class.

Process Overview

The XML is first read from file and the "&" (if any) converted to the "&" expected by the XML parser. This allows the XML to look more like menu code as used in the "generating menus from array example". The menu creator then reads the string data and interprets the various menu attributes to create the menu system.

Special Concerns

I am not sure I am raising the correct error classes or if there is sufficient error checking.

Code Sample

   1 #this section is the main routine code
   2 import wx
   3 import xml.sax.handler
   4 import sys
   5 
   6 def readXML(xmlfile):
   7     file=open(xmlfile,'r')
   8     fixedData= file.read()
   9     file.close()
  10     return fixedData.replace( "&", "&" )
  11 
  12 
  13 def createMenus(self,menuData,frame):
  14 
  15     class MenuBuilder(xml.sax.handler.ContentHandler):
  16         def __init__(self):
  17             self.menuStack = []
  18             self.text=""
  19             self.menu=""
  20             self.itemName=""
  21             self.type=""
  22             self.handler=""
  23             self.help=""
  24             self.helpStack={}
  25             self.item=""
  26             self.nameStack=[]
  27 
  28         def startElement(self, name, attrs):
  29             self.text=""
  30             if name=="menuItem":
  31                 self.type=""
  32                 self.help=""
  33             elif name=="menubar":
  34                 menuBar=wx.MenuBar()
  35                 self.menuStack.append((menuBar,"bar"))
  36             elif name=="menuName":
  37                 self.menu=""
  38             elif name=="itemName":
  39                 self.itemName=""
  40             elif name=="kind":
  41                 self.type=""
  42             elif name=="handler":
  43                 self.handler=""
  44             elif name=="help":
  45                 self.help=""
  46             elif name=="menu":
  47                 pass
  48             elif name=="separator":
  49                 pass
  50             else:
  51                 raise ValueError, "Invalid menu component %s" % name
  52 
  53         def endElement(self, name):
  54             if name=="menuItem":
  55                 try:
  56                     self.help=self.helpStack[self.itemName]
  57                     del self.helpStack[self.itemName]
  58                 except:
  59                     self.help=""
  60                 if self.type=="" or self.type=="normal":
  61                     self.item=self.menuStack[-1][0].Append(id=wx.NewId(), text=self.itemName, help=self.help)
  62                 elif self.type=="check":
  63                     self.item=self.menuStack[-1][0].AppendCheckItem(id=wx.NewId(), text=self.itemName, help=self.help)
  64                 elif self.type=="radio":
  65                     self.item=self.menuStack[-1][0].AppendRadioItem(id=wx.NewId(), text=self.itemName, help=self.help)
  66                 else:
  67                     raise ValueError, "Unknown item type %s" % self.type
  68                 frame.Bind(wx.EVT_MENU,self.handler,self.item)
  69             elif name=="menu":
  70                 if self.menuStack[-2][1]=="bar":
  71                     self.menuStack[-2][0].Append(menu=self.menuStack[-1][0],title=self.menuStack[-1][1])
  72                 else:
  73                     try:
  74                         self.help=self.helpStack[self.menuStack[-1][1]]
  75                         del self.helpStack[self.menuStack[-1][1]]
  76                     except:
  77                         self.help=""
  78                     self.menuStack[-2][0].AppendSubMenu(submenu=self.menuStack[-1][0],text=self.menuStack[-1][1],help=self.help)
  79                 self.menuStack.pop()
  80             elif name=="separator":
  81                 self.menuStack[-1][0].AppendSeparator()
  82             elif name=="menubar":
  83                 frame.SetMenuBar(self.menuStack[-1][0])
  84             elif name=="menuName":
  85                 self.menu=wx.Menu()
  86                 self.menuStack.append((self.menu,self.text))
  87                 self.nameStack.append(self.text)
  88             elif name=="itemName":
  89                 self.itemName=self.text
  90                 self.nameStack.append(self.text)
  91             elif name=="kind":
  92                 self.type=self.text
  93             elif name=="handler":
  94                 self.handler=getattr(frame,self.text)
  95             elif name=="help":
  96                 self.help=self.text
  97                 self.helpStack[self.nameStack[-1]]=self.help
  98                 self.nameStack.pop()
  99             else:
 100                 raise ValueError, "Invalid menu component %s" % name
 101 
 102         def characters(self, content):
 103             self.text+=content
 104 
 105     builder = MenuBuilder()
 106     xml.sax.parseString(menuData, builder)

This is the sample XML I used for testing

<menubar>
    <menu>
        <menuName>&File</menuName>
        <help>File menu</help>
        <menuItem>
            <itemName>&Open</itemName>
            <handler>OnOpen</handler>
            <help>Open menu item</help>
        </menuItem>
        <menuItem>
            <itemName>&Save</itemName>
            <handler>OnSave</handler>
            <help>Save menu item</help>
        </menuItem>
        <menuItem>
            <itemName>&Close</itemName>
            <handler>OnClose</handler>
            <help>Close menu item</help>
        </menuItem>
        <separator/>
        <menuItem>
            <itemName>E&xit</itemName>
            <handler>OnExit</handler>
            <help>Exit menu item</help>
        </menuItem>
    </menu>
    <menu>
        <menuName>&Edit</menuName>
        <help>Edit menu</help>
        <menuItem>
            <itemName>&Find</itemName>
            <handler>OnFind</handler>
            <help>File menu item</help>
        </menuItem>
        <menuItem>
            <itemName>&Replace</itemName>
            <handler>OnReplace</handler>
            <help>Replace menu item</help>
        </menuItem>
    </menu>
    <menu>
        <menuName>&Others</menuName>
        <help>Others menu</help>
        <menuItem>
            <itemName>&Others item 1</itemName>
            <handler>OnOthers1</handler>
            <help>Others menu item 1</help>
        </menuItem>
        <menu>
            <menuName>&SubMenu</menuName>
            <help>Submenu</help>
            <menuItem>
                <itemName>Submenu item&1</itemName>
                <handler>OnSubmenu1</handler>
                <help>Submenu item 1</help>
            </menuItem>
            <menuItem>
                <itemName>Submenu item&2</itemName>
                <handler>OnSubmenu2</handler>
                <help>Submenu item 2</help>
            </menuItem>
            <menuItem>
                <itemName>Submenu item&3</itemName>
                <handler>OnSubmenu3</handler>
                <help>Submenu item 3</help>
            </menuItem>
        </menu>
    </menu>
    <menu>
        <menuName>&Check/Radio</menuName>
        <help>Check and radio menu</help>
        <menuItem>
            <itemName>Check item 1</itemName>
            <handler>OnCheck1</handler>
            <help>Check item 1</help>
            <kind>check</kind>
        </menuItem>
        <menuItem>
            <itemName>Check item 2</itemName>
            <handler>OnCheck2</handler>
            <help>Check item 2</help>
            <kind>check</kind>
        </menuItem>
        <menuItem>
            <itemName>Check item 3</itemName>
            <handler>OnCheck3</handler>
            <help>Check item 3</help>
            <kind>check</kind>
        </menuItem>
`       <separator/>
        <menuItem>
            <itemName>Radio item 1</itemName>
            <handler>OnRadio1</handler>
            <help>Radio item 1</help>
            <kind>radio</kind>
        </menuItem>
        <menuItem>
            <itemName>Radio item 2</itemName>
            <handler>OnRadio2</handler>
            <help>Radio item 2</help>
            <kind>radio</kind>
        </menuItem>
        <menuItem>
            <itemName>Radio item 3</itemName>
            <handler>OnRadio3</handler>
            <help>Radio item 3</help>
            <kind>radio</kind>
        </menuItem>
    </menu>
</menubar>

This is my app module

   1 import wx
   2 import TestFrame2
   3 import Library
   4 
   5 modules ={u'Library': [0, '', u'Library.py'], u'TestFrame2': [0, '', u'TestFrame2.py']}
   6 
   7 class TestApp(wx.App):
   8     def OnInit(self):
   9         self.main = TestFrame2.TestFrame(None)
  10         self.main.Show()
  11         self.SetTopWindow(self.main)
  12         return True
  13 
  14 def main():
  15     application = TestApp(0)
  16     application.MainLoop()
  17 
  18 if __name__ == '__main__':
  19     main()

And this is my Testframe2 class (with trivial handlers)

   1 import wx
   2 import Library
   3 
   4 class TestFrame(wx.Frame):
   5     def __init__(self, parent):
   6         wx.Frame.__init__(self, id=-1, name=u'TestFrame',
   7               parent = None, pos=wx.Point(480, 275), size=wx.Size(1280, 733),
   8               style=wx.DEFAULT_FRAME_STYLE, title=u'Menu testing program')
   9         self.SetClientSize(wx.Size(1280, 733))
  10         self.CreateStatusBar()
  11         menuData=Library.readXML("menu.xml")
  12         Library.createMenus(self,menuData,self)
  13 
  14     def OnOpen(self,event):
  15         wx.MessageBox("You selected the open menu item")
  16 
  17     def OnSave(self,event):
  18         wx.MessageBox("You selected the save menu item")
  19 
  20     def OnClose(self,event):
  21         wx.MessageBox("You selected the close menu item")
  22 
  23     def OnExit(self,event):
  24         wx.MessageBox("You selected the exit menu item")
  25         self.Close()
  26 
  27     def OnFind(self,event):
  28         wx.MessageBox("You selected the find menu item")
  29 
  30     def OnReplace(self,event):
  31         wx.MessageBox("You selected the replace menu item")
  32         self.Close()
  33 
  34     def OnOthers1(self,event):
  35         wx.MessageBox("You selected the others1 menu item")
  36 
  37     def OnSubmenu1(self,event):
  38         wx.MessageBox("You selected the submenu item 1")
  39 
  40     def OnSubmenu2(self,event):
  41         wx.MessageBox("You selected the submenu item 2")
  42 
  43     def OnSubmenu3(self,event):
  44         wx.MessageBox("You selected the submenu item 3")
  45 
  46     def OnCheck1(self,event):
  47         wx.MessageBox("You checked item 1")
  48 
  49     def OnCheck2(self,event):
  50         wx.MessageBox("You checked item 2")
  51 
  52     def OnCheck3(self,event):
  53         wx.MessageBox("You checked item 3")
  54 
  55     def OnRadio1(self,event):
  56         wx.MessageBox("You selected radio item 1")
  57 
  58     def OnRadio2(self,event):
  59         wx.MessageBox("You selected radio item 2")
  60 
  61     def OnRadio3(self,event):
  62         wx.MessageBox("You selected radio item 3")

Comments

I know I should be placing comments at the beginning of each module but I am not sure what level of detail is appropriate - so I chickened out and left them blank.

Menus from XML (last edited 2009-01-18 17:56:37 by c-67-168-196-237)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.