Hubro
Hubro

Reputation: 59333

Why can't I bind events directly to wx.Menu in Windows?

I have an application that allows some controllers to change the layout of the main menu bar. I do this by passing the main menu bar to the controller, then the controller adds some menus to it. My issue is binding menu click events to those added menus. It's very easy in theory:

def bind_main_menu_bar(self, main_menu_bar):
    self.main_menu_bar = main_menu_bar

    edit_menu = shared.create_menu([
        (const.ID_UNDO, self.LABEL_UNDO_EMPTY),
        (const.ID_REDO, self.LABEL_REDO_EMPTY),
        (),
        (const.ID_VALIDATE_ADDRESSES, 'Validate selected addresses'),
        (const.ID_VALIDATE_ALL_ADDRESSES, 'Validate all addresses'),
    ])
    edit_menu.Bind(wx.EVT_MENU, self.on_menu)
    edit_menu.Bind(wx.EVT_UPDATE_UI, self.on_menu_update)

    main_menu_bar.Insert(1, edit_menu, 'Edit')

This works excellently in OSX, but in Windows, only the wx.EVT_MENU event works when binding the event directly to the edit_menu menu. In other words, the wx.EVT_UPDATE_UI event handler is never triggered. I've also tried binding EVT_MENU_OPEN, EVT_MENU_CLOSE and EVT_MENU_HIGHLIGHT but none of them work. They all work when I bind them to the top level frame, but then there is no way to limit the event handler to only the edit_menu menu.

Is this a bug, or is there something I'm overlooking? Also, can anybody spot a workaround to my issue?


I'm using wxPython 2.9 (dev) on Window 8.

Upvotes: 0

Views: 653

Answers (2)

Chris Barker
Chris Barker

Reputation: 56

Mike's right, avoid explicit IDs -- and you don't need them (see the wxPython Style guide in the wiki for details).

Menu items are not regular Windows, so you can't do the normal event binding thing with them -- too bad.

However, when you bind the event to the frame, you can specify where the event comes from. My usual idiom for binding menu events:

FileMenu = wx.Menu()
item = FileMenu.Append(wx.ID_EXIT, "&Quit")
self.Bind(wx.EVT_MENU, self.OnQuit, item)

In this case, self is a Frame - so you are saying: bind the EVT_MENU to the frame, call the self.OnQuit as a the callback, but only bind events from this particular menu item.

This is uglier when you are doing dynamic binding, but it should work.

Note that in this example I'm using an ID - the exception to that rule is that using standard IDs (like wx.ID-EXIT, wx.ID_HELP) is nice, because then wx can "do the right thing" on various platforms (like more the quite menu to the app menu on OS-X).

Upvotes: 4

Mike Driscoll
Mike Driscoll

Reputation: 33071

I'm pretty sure that you're supposed to bind menu events to the frame, not the menu items themselves. Try that and see if it works.

Upvotes: 1

Related Questions