XMLMenuLoader uses XML definitions to create a wx.MenuBar. It is based on the code in this post:
http://mail.python.org/pipermail/python-list/2001-June/046912.html
and adds checkable menus and submenus.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | """
XMLMenuLoader: Class to handle loading a wxMenuBar with menu items
defined in XML
author: Tom Jenkins <tjenkins@devis.com>
based on code by Bjorn Pettersen <BPettersen@NAREX.com>
20041010: Egor Zindy <ezindy@gmail.com> Code now uses pulldom, supports
submenus and checkable items.
#*COULDDO*# Add new XML definitions (menu images, greyed menus...)
#*TODO*: refactor load into base class that handles loading the menu
in another way so we can have ListMenuLoader, StreamMenuLoader, etc...
usage:
self.menubar=wx.MenuBar()
loader = XMLMenuLoader(MenuBar = self.menubar, controller=self)
loader.load("menu.xml")
self.SetMenuBar(self.menubar)
"""
import wx
from xml.dom import pulldom
class XMLMenuLoader:
def __init__(self, MenuBar = None, controller = None):
"""Load the menubar with the menu items stored in XML format in
the instance"s filename property
wxMenuBar -> the root menubar of the frame; if given will become
the instances new menubar property
controller -> the class instance that will receive any callbacks
stored in the menu items" callback attribute
"""
self.MenuBar=MenuBar
self.controller=controller
def load(self,filename="menu.xml"):
events = pulldom.parse(filename)
self.parse(events)
def loadString(self,xml_string):
events = pulldom.parseString(xml_string)
self.parse(events)
def parse(self,events):
#menu_list is used as a stack: the last element
#is the parent menu to which items are appended.
menu_list=[]
menu_list.append(self.MenuBar)
for (event,node) in events:
if event=="START_ELEMENT" and node.nodeName=="menu":
menu = wx.Menu()
parent_menu=menu_list[-1]
menu_name=node.getAttribute("name").replace("_", "&")
if len(menu_list)==1:
parent_menu.Append(menu, menu_name)
else:
parent_menu.AppendMenu(-1,menu_name,menu)
menu_list.append(menu)
elif event=="END_ELEMENT" and node.nodeName=="menu":
#Encountered the end of a menu definition.
#Remove the wx.menu from the stack.
menu_list.pop(-1)
elif event=="START_ELEMENT" and node.nodeName=="separator":
#separator definition
menu=menu_list[-1]
menu.AppendSeparator()
elif event=="START_ELEMENT" and node.nodeName=="menuitem":
#the current menu
menu=menu_list[-1]
#checking all the attributes.
#the menuitem name
name=node.getAttribute("name").replace("_", "&")
#if id is not defined, check for one available
if node.hasAttribute("id"):
id=int(node.getAttribute("id"))
else:
id = wx.NewId()
#menuitem info attribute
if node.hasAttribute("info"):
info=node.getAttribute("info")
else:
info=""
#callback
if self.controller and node.hasAttribute("callback"):
callback=node.getAttribute("callback")
handler = getattr(self.controller, callback, None)
if handler:
wx.EVT_MENU(self.controller, id, handler)
#checkable menu?
if node.hasAttribute("chk"):
chk=int(node.getAttribute("chk"))
menu.AppendCheckItem(id,name,info)
menu.Check(id,chk)
else:
menu.Append(id,name,info)
#----------------------------------------------------------------------
class MyFrame(wx.Frame):
def __init__(self, parent, id, title,
pos, size, style = wx.DEFAULT_FRAME_STYLE ):
wx.Frame.__init__(self, parent, id, title, pos, size, style)
self.CreateMyMenuBar()
self.CreateStatusBar(1)
def CreateMyMenuBar(self):
self.mainmenu=wx.MenuBar()
loader = XMLMenuLoader(MenuBar = self.mainmenu, controller=self)
loader.loadString(xml_string)
self.SetMenuBar(self.mainmenu)
def OnChoice(self, event):
id=event.GetId()
menu_item=self.mainmenu.FindItemById(id)
self.SetStatusText("item name: %s" % menu_item.GetLabel())
def OnOptions(self, event):
id=event.GetId()
menu_item=self.mainmenu.FindItemById(id)
is_checked=menu_item.IsChecked()
if id==15000:
my_str="apple"
elif id==15001:
my_str="pear"
elif id==15002:
my_str="orange"
self.SetStatusText("item: %s | checked: %d" % (my_str,is_checked))
def OnCloseWindow(self, event):
self.Destroy()
def main(argv=None):
app = wx.PySimpleApp()
f = MyFrame(None, -1, "XMLMenuLoader: XML menu creation", wx.Point(20,20), wx.Size(400,300) )
f.Show()
app.MainLoop()
#----------------------------------------------------------------------
if __name__ == "__main__":
global xml_string
xml_string="""
<menubar>
<menu name='_File'>
<menuitem name='_New' callback='OnChoice'/>
<menuitem name='_Open...' callback='OnChoice'/>
<menu name='_Export to'>
<menuitem name='Jpeg...' callback='OnChoice'/>
<menuitem name='Png...' callback='OnChoice'/>
</menu>
<separator/>
<menuitem name='E_xit' callback='OnCloseWindow'/>
</menu>
<menu name='_Edit'>
<menuitem name='Undo' callback='OnChoice'/>
<menuitem name='Redo' callback='OnChoice'/>
</menu>
<menu name='_Checkable'>
<menuitem id='15000' name='Item 1' callback='OnOptions' chk='1'/>
<menuitem id='15001' name='Item 2' callback='OnOptions' chk='0'/>
<menuitem id='15002' name='Item 3' callback='OnOptions' chk='0'/>
</menu>
<menu name='_Help'>
<menuitem name='About...' info='Read more about it' callback='OnAbout'/>
</menu>
</menubar>"""
main()
|
To try it out, just launch the code.
usage: self.menubar=wx.MenuBar() loader = XMLMenuLoader(MenuBar = self.menubar, controller=self) loader.load("menu.xml") self.SetMenuBar(self.menubar)
if you call the file XMLMenuLoader.py then you will need a
from XMLMenuLoader import XMLMenuLoader
at the top of your code.
Do the submenus work in Linux?
Resource Editor. How is this different to or better than using XRCed resource editor and the xrc module that comes with wxPython?
Re: Resource Editor. The problem is that the xrc module is not well documented (well, there's an example in the demo and that's about it).
I found a wiki with a reasonably well explained example here: http://wiki.wxpython.org/index.cgi/UsingXmlResources
The difference between the two XML structures is that XMLMenuLoader focusses on the menu structure only, rather than the whole application.
From the example again, this is how the menu xml looks like
I think the structure in XMLMenuLoader is a bit lighter (easy enough to be edited by hand if needed):
XMLMenuLoader also connects elements in the menu with function names and handles non-existing functions gracefully.
Resource Editor. Thank you for responding to my question Egor. I think you raise valid issues. I'll have to look a little more closely.