Search code examples
pythonwxpython

Using wxPython Menus, ID: What does it mean and Which should I use for X?


I have a wxPython app I'm working on. I'm working on the menu and menu bars.

So far here is the code I have:

class Frame(wx.Frame):
    def __init__(self, title):
        wx.Frame.__init__(self, None, title=title, pos=(150,150), size=    (350,200))
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        menuBar = wx.MenuBar()

        menu = wx.Menu()
        m_exit = menu.Append(wx.ID_EXIT, "&Exit\tAlt-X", 
            "Close window and exit program.")
        self.Bind(wx.EVT_MENU, self.OnClose, m_exit)

        menu = wx.Menu()
        m_about = menu.Append(wx.ID_ABOUT, "&About", "Information about this     program")
        self.Bind(wx.EVT_MENU, self.OnAbout, m_about)
        menuBar.Append(menu, "&Help")

        self.SetMenuBar(menuBar)

        self.statusbar = self.CreateStatusBar()

        panel = wx.Panel(self)
        box = wx.BoxSizer(wx.VERTICAL)

        m_text = wx.StaticText(panel, -1, "Hello World!")
        m_text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
        m_text.SetSize(m_text.GetBestSize())
        box.Add(m_text, 0, wx.ALL, 10)

        m_close = wx.Button(panel, wx.ID_CLOSE, "Close")
        m_close.Bind(wx.EVT_BUTTON, self.OnClose)
        box.Add(m_close, 0, wx.ALL, 10)

        panel.SetSizer(box)
        panel.Layout()

    def OnClose(self, event):
        dlg = wx.MessageDialog(self, 
            "Do you really want to close this application?",
            "Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION)
        result = dlg.ShowModal()
        dlg.Destroy()
        if result == wx.ID_OK:
            self.Destroy()

    def OnAbout(self, event):
        dlg = AboutBox()
        dlg.ShowModal()
        dlg.Destroy()

I wanted to add New, Open, Save, SaveAs to the File menu, add an Actions menu and within it put Action1, Action2 (temp names). My only questions are these:

First of all, in the statement m_exit = menu.Append(wx.ID_EXIT, etc... what does the wx.ID_EXIT mean? And where can I get a list of possible ID's? I'm not sure what I would set for New, Open, Save, SaveAs, etc. I will make OnNew, OnOpen, etc functions, but I'm not sure of what the ID should be. I heard about custom ID's but I need an example because I can't seem to find anything that makes sense to me about them. I see you use them in Append() but not Bind(). Why id this? What exactly is the reason for passing it to Append()? like in m_exit (see below), I use wx.ID_EXIT, but then in Bind I never mentioned it again I just mention wx.EVT_MENU, the function to be executed and the name of the menu.Append(). How do these items interact??

EDIT:

I tried this code:

    menu = wx.Menu()
    ID_FILE_NEW = wx.NewId()
    m_new = menu.Append(wx.ID_FILE_NEW, "&New\tCtrl+N", "txt")
    self.Bind(wx.EVT_MENU, self.OnNew, m_new)     

    ID_FILE_OPEN  = wx.NewId()
    m_open = menu.Append(wx.ID_FILE_OPEN, "&Open\tCtrl+O", "txt")
    self.Bind(wx.EVT_MENU, self.OnOpen, m_open)

    ID_FILE_SAVE = wx.NewId()
    m_save = menu.Append(wx.ID_FILE_SAVE, "&Save\tCtrl+S", "txt")
    self.Bind(wx.EVT_MENU, self.OnSave, m_save)

    ID_FILE_SAVE_AS = wx.NewId()
    m_save_as = menu.Append(wx.FILE_SAVE_AS, "&SaveAs", "txt")
    self.Bind(wx.EVT_MENU, self.OnSaveAs, m_save_as)

    m_exit = menu.Append(wx.ID_EXIT, "&Exit\tCtrl+X", 
        "Close window and exit program.")
    self.Bind(wx.EVT_MENU, self.OnClose, m_exit)

    menuBar.Append(menu, "&File")

It didn't work, the application opened and then closed instantly, when I remove everything but m_close, it works again. Obviously my wx.NewId() variables aren't working? What am I doing wrong in either creating my own wx.NewId's or setting up the menubar for more menus and menuitems. I know I'm doing at least one of these things wrong. Can anyone help me figure out what it is and suggest the best way to accomplish the goal?

EDIT Cont'd:

I have two classes that derive from wx.Dialog. Here is the code:

class AboutBox(wx.Dialog):
def __init__(self):
    wx.Dialog.__init__(self, None, -1, "About",
        style=wx.DEFAULT_DIALOG_STYLE|wx.THICK_FRAME|wx.RESIZE_BORDER|
            wx.TAB_TRAVERSAL)
    hwin = HtmlWindow(self, -1, size=(400,200))
    vers = {}
    vers["python"] = sys.version.split()[0]
    vers["wxpy"] = wx.VERSION_STRING
    hwin.SetPage(aboutText % vers)
    btn = hwin.FindWindowById(wx.ID_OK)
    irep = hwin.GetInternalRepresentation()
    hwin.SetSize((irep.GetWidth() + 25, irep.GetHeight() + 10))
    self.SetClientSize(hwin.GetSize())
    self.CentreOnParent(wx.BOTH)
    self.SetFocus()

class VersionBox(wx.Dialog):
def __init__(self):
    wx.Dialog.__init__(self, None, -1, "Version",
        style=wx.DEFAULT_DIALOG_STYLE|wx.THICK_FRAME|wx.RESIZE_BORDER|
            wx.TAB_TRAVERSAL)
    hwin = HtmlWindow(self, -1, size=(400, 200))
    vers = {}
    vers["python"] = sys.version.split()[0]
    vers["wxpy"] = wx.VERSION_STRING
    hwin.SetPage(versionText % vers)
    btn = hwin.FindWindowById(wx.ID_OK)
    irep = hwin.GetInternalRepresentation()
    hwin.SetSize((irep.GetWidth() + 25, irep.GetHeight() + 10))
    self.SetClientSize(hwin.GetSize())
    self.CentreOnParent(wx.BOTH)
    self.SetFocus()

Where it cals hwin.FindWindowById(wx.ID_OK) I don't see how it's finding the correct window. For example the AboutBox displays the about window but in it's menu.Append it uses wx.ID_ABOUT, but wx.ID_OK works fine. I am now trying to implement VersionBox. But I'm not sure what to change that to. ID_ABOUT like in the menu.Append()? Or something else.

If it's not obvious by now I don't understand these ID's, when to use them, which ones to use, and how they interact with GetWindowById() please clarify...

I try to follow the pattern I see in examples and tutorials, but when I add new menuitems to a menu it never seems to work. I don't get an error message but the program opens and closes right away. I'm following this code: http://wiki.wxpython.org/wxPython%20by%20Example But I can't seem to get any menuitems to work other than what they've already added. Any ideas ?

Thanks A Million, sorry for putting up with me but I'm having some trouble. Even answering some of my questions or some clarification would help big time in my understanding of how wxPython works.

FINAL EDIT:

I got it working with version, by using wx.ID_ABOUT like the OnAbout() method. Seems that if I add other OnX() methods, even if they aren't bound to anything messes the program up, why would that be?

Thanks a ton, your help has guided me the right way, if anybody can supply some small examples it would help a lot. If not, thanks anyway I'm finally on the right track.


Solution

    1. wx.ID_EXIT is an integer ID that wx has reserved an knows the meaning of ... there are others such as wx.Button(self,wx.ID_OK) notice I do not need to pass a string label (with wx.ID_EXIT it should automagically trigger the close event as well (unless you catch the menu event and short circuit it), these work differently depending on the context that they are used in

      you could get a list of all the wx.ID_XXX with [n for n in dir(wx) if n.startswith("ID_")]

    2. wx.EVT_MENU is the command event generated by any menu selection (including accelerator table hotkeys) , you can see what it has by using dir(event) inside the function handler ... but I think it only has the default commandevent fields.

    3. APP_EXIT is a string the programmer wrote somewhere something like APP_EXIT=_("&Exit") ... this is a common practice to use all capital letters for constants (it also helps simplify localization).

    4. wxPython will try to parse whatever hotkey combination you give it so +/- probably does not matter(although this is not documented well so I dont know ... you could do some trial and error and find out pretty easy)

      menu = wx.Menu()
      ID_PRG_NFO = wx.NewId()
      m_about = menu.Append(ID_PRG_NFO, "Program &Info\tAlt-I", "Information about this     program")
      

    for a OPEN menu option you could do

        m_open = menu.Append(wx.ID_OPEN,"&Open\tAlt-O)
    

    ok here is the list of special ID's in wxPython2.8

    >>> import wx
    >>> [n for n in dir(wx) if n.startswith("ID_")]
    ['ID_ABORT', 'ID_ABOUT', 'ID_ADD', 'ID_ANY', 'ID_APPLY', 'ID_BACKWARD', 'ID_BOLD
    ', 'ID_CANCEL', 'ID_CLEAR', 'ID_CLOSE', 'ID_CLOSE_ALL', 'ID_CONTEXT_HELP', 'ID_C
    OPY', 'ID_CUT', 'ID_DEFAULT', 'ID_DELETE', 'ID_DOWN', 'ID_DUPLICATE', 'ID_EDIT',
     'ID_EXIT', 'ID_FILE', 'ID_FILE1', 'ID_FILE2', 'ID_FILE3', 'ID_FILE4', 'ID_FILE5
    ', 'ID_FILE6', 'ID_FILE7', 'ID_FILE8', 'ID_FILE9', 'ID_FIND', 'ID_FORWARD', 'ID_
    HELP', 'ID_HELP_COMMANDS', 'ID_HELP_CONTENTS', 'ID_HELP_CONTEXT', 'ID_HELP_INDEX
    ', 'ID_HELP_PROCEDURES', 'ID_HELP_SEARCH', 'ID_HIGHEST', 'ID_HOME', 'ID_IGNORE',
     'ID_INDENT', 'ID_INDEX', 'ID_ITALIC', 'ID_JUSTIFY_CENTER', 'ID_JUSTIFY_FILL', '
    ID_JUSTIFY_LEFT', 'ID_JUSTIFY_RIGHT', 'ID_LOWEST', 'ID_MORE', 'ID_NEW', 'ID_NO',
     'ID_NONE', 'ID_NOTOALL', 'ID_OK', 'ID_OPEN', 'ID_PAGE_SETUP', 'ID_PASTE', 'ID_P
    REFERENCES', 'ID_PREVIEW', 'ID_PREVIEW_CLOSE', 'ID_PREVIEW_FIRST', 'ID_PREVIEW_G
    OTO', 'ID_PREVIEW_LAST', 'ID_PREVIEW_NEXT', 'ID_PREVIEW_PREVIOUS', 'ID_PREVIEW_P
    RINT', 'ID_PREVIEW_ZOOM', 'ID_PRINT', 'ID_PRINT_SETUP', 'ID_PROPERTIES', 'ID_RED
    O', 'ID_REFRESH', 'ID_REMOVE', 'ID_REPLACE', 'ID_REPLACE_ALL', 'ID_RESET', 'ID_R
    ETRY', 'ID_REVERT', 'ID_REVERT_TO_SAVED', 'ID_SAVE', 'ID_SAVEAS', 'ID_SELECTALL'
    , 'ID_SEPARATOR', 'ID_SETUP', 'ID_STATIC', 'ID_STOP', 'ID_UNDELETE', 'ID_UNDERLI
    NE', 'ID_UNDO', 'ID_UNINDENT', 'ID_UP', 'ID_VIEW_DETAILS', 'ID_VIEW_LARGEICONS',
     'ID_VIEW_LIST', 'ID_VIEW_SMALLICONS', 'ID_VIEW_SORTDATE', 'ID_VIEW_SORTNAME', '
    ID_VIEW_SORTSIZE', 'ID_VIEW_SORTTYPE', 'ID_YES', 'ID_YESTOALL', 'ID_ZOOM_100', '
    ID_ZOOM_FIT', 'ID_ZOOM_IN', 'ID_ZOOM_OUT']
    >>>