Phoenix icon indicating copy to clipboard operation
Phoenix copied to clipboard

PropertyGridManager event binding not working properly

Open GodLike3539 opened this issue 1 year ago • 3 comments

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()

GodLike3539 avatar Sep 04 '24 11:09 GodLike3539

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.

ghost avatar Sep 08 '24 12:09 ghost

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.

ghost avatar Sep 08 '24 14:09 ghost

@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()```

da-dada avatar Mar 07 '25 22:03 da-dada