PropertyGridManager event binding not working properly
When binding an EVT_PG_CHANGED from a PropertyGridManager the event only gets handled when using
PropertyGridManager.Bind()
but not with
Bind(..., MyPropertyGridManager).
The EVT_PG_CHANGED is an instance of wx.CommandEvent, so it should work with both ways.
Windows 11 Pro wx.version(): 4.2.1 msw (phoenix) wxWidgets 3.2.2.1, installed with pip install wxpython Python 3.12.4, stock
Description of the problem:
Code Example (click to expand)
import wx
import wx.propgrid as pg
class MyFrame(wx.Frame):
def __init__(self, *args, **kw):
super(MyFrame, self).__init__(*args, **kw)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.m_pg = pg.PropertyGridManager(panel, style=pg.PGMAN_DEFAULT_STYLE)
self.page = pg.PropertyGridPage()
self.page.Append(pg.IntProperty("an integer", "another_parameter", 123))
self.m_pg.AddPage("some page", wx.BitmapBundle(), self.page)
# THIS DOES NOT WORK
#self.Bind(pg.EVT_PG_CHANGED, self._on_event, self.m_pg)
# THIS DOES
self.m_pg.Bind(pg.EVT_PG_CHANGED, self._on_event)
sizer.Add(self.m_pg, 1, wx.EXPAND | wx.ALL, 10)
panel.SetSizer(sizer)
self.SetSize((400, 300))
self.Centre()
def _on_event(self, event):
print('Property changed')
print(isinstance(event, wx.CommandEvent))
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, title="pg event weirdness")
frame.Show(True)
return True
app = MyApp(False)
app.MainLoop()
This issue also occurs using wxPython 4.2.1 gtk3 (phoenix) wxWidgets 3.2.4 + Python 3.12.3 + Linux Mint 22.
I have noticed that in the case where the EVT_PG_CHANGED event handler does get called in your example, _event.ShouldPropagate() returns False.
That is different to some other event types derived from wx.CommandEvent. For example, when EVT_BUTTON and EVT_TEXT_ENTER are bound in an equivalent manner, their event object's ShouldPropagate() method returns True.
If you derive a new class from PropertyGridPage and override its IsHandlingAllEvents() method to return False:
class MyPropertyGridPage(pg.PropertyGridPage):
def IsHandlingAllEvents(self):
return False
and use it in your example instead of pg.PropertyGridPage() then the self.Bind(pg.EVT_PG_CHANGED, self._on_event, self.m_pg) binding does work.
I think because the base class PropertyGridPage.IsHandlingAllEvents() method returns True, the C++ code calls event.StopPropagation() to stop the event propagating to the parent.
@GodLike3539 or subclass your property
import wx
import wx.propgrid as pg
class MyProperty(pg.IntProperty):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
def StringToValue(self, st, flags):
"""
Convert a string to the correct type for the property.
If failed, return False or (False, None). If success, return tuple
(True, newValue).
"""
print(f'stv {st} {flags} type of st {type(st)}')
try:
val = int(st)
return (True, val)
except (ValueError, TypeError):
pass
except:
raise
return (False, None)
class MyFrame(wx.Frame):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.m_pg = pg.PropertyGridManager(panel, style=pg.PGMAN_DEFAULT_STYLE)
self.page = pg.PropertyGridPage()
self.page.Append(MyProperty("an integer", "another_parameter", 123))
self.m_pg.AddPage("some page", wx.BitmapBundle(), self.page)
# THIS DOES NOT WORK
self.Bind(pg.EVT_PG_CHANGED, self._on_event, self.m_pg)
# THIS DOES
# self.m_pg.Bind(pg.EVT_PG_CHANGED, self._on_event)
sizer.Add(self.m_pg, 1, wx.EXPAND | wx.ALL, 10)
panel.SetSizer(sizer)
self.SetSize((400, 300))
self.Centre()
def _on_event(self, event):
print('Property changed')
print(isinstance(event, wx.CommandEvent))
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, title="pg event weirdness")
frame.Show(True)
return True
app = MyApp(False)
app.MainLoop()```