Welcome, guest | Sign In | My Account | Store | Cart
"""
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()
    

History