Hi there
I have an application in which Menubar and context menus are created and destroyed many times at run-time. I recently noticed that Unbind(wx.EVT_MENU)
always fails, where many ids are bound to it. Is it possible to unbind all handlers bound to wx.EVT_MENU
all at once?
To demonstrate it,
import wx
from wx.py.shell import Shell
app = wx.App()
frm = wx.Frame(None)
if 1: # Add some local vars to the shell.
frm.self = frm
frm.this = __import__('__main__')
frm.shell = Shell(frm, locals=frm.__dict__)
frm.Show()
app.MainLoop()
Note the shell has a context-menu for copy, paste, etc.
If the id is given explicitly, it works and the handler for the ID_COPY
menu is removed.
>>> self.shell.Unbind(wx.EVT_MENU, id=self.shell.ID_COPY)
True
But,
>>> self.shell.Unbind(wx.EVT_MENU)
False
I’ve not used it, but I wonder if the AppEventHandlerMixin
class in wx.lib.eventStack
might be applicable?
The wxWidgets documentation for wxEvtHandler::Unbind
describes the id
parameter as “The first ID of the identifier range associated with the event handler”. It does not say anything about wx.ID_ANY
being a wildcard.
The feature you are trying to use does not exist.
Hi @RichardT,
Thank you for the information.
Unfortunately, it doesn’t seem to fit my purpose. But, the idea in wx.lib.eventStack
is interesting and it may be useful for another chance.
Hi @AndersMunch,
You are right.
I misunderstood that specifying only the event
argument removes all handlers. Actually, I found that it removes the last bound handler. I think this is one of the “search criteria” described in the document for Unbind
:
wxWidgets: wxEvtHandler Class Reference
…
Unbinds the given function, functor or method dynamically from the event handler, using the specified parameters as search criteria and returning true if a matching function has been found and removed.
Not explicitly stated, but other “criteria” would be “If you bind an event handler using id, you must specify the id to unbind”.
A code snippet to demonstrate it:
import wx
class Frame(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
btn = wx.Button(self, label='test')
## btn.Bind(wx.EVT_BUTTON, self.OnButton1)
## btn.Bind(wx.EVT_BUTTON, self.OnButton2)
## print(btn.Unbind(wx.EVT_BUTTON)) #-> True: Unbind the last handler
self.Bind(wx.EVT_BUTTON, self.OnButton1, btn)
print(self.Unbind(wx.EVT_BUTTON)) #-> False
print(self.Unbind(wx.EVT_BUTTON, btn)) #-> True: `source` or `id` must be specified.
def OnButton1(self, evt):
print("OnButton1:evt.Id =", evt.Id)
evt.Skip()
def OnButton2(self, evt):
print("OnButton2:evt.Id =", evt.Id)
evt.Skip()
if __name__ == "__main__":
app = wx.App()
frm = Frame(None)
frm.Show()
app.MainLoop()
Solved.
def _unbind(menu):
"""Unbind all handlers from the menu."""
for item in menu.MenuItems:
if item.Id != wx.ID_SEPARATOR:
self.Unbind(wx.EVT_MENU, item)
self.Unbind(wx.EVT_UPDATE_UI, item)
self.Unbind(wx.EVT_MENU_HIGHLIGHT, item)
if item.SubMenu:
_unbind(item.SubMenu)
Thanks all!