A problem with Event objects

why should I play on me own or, if you ever wondered what cloning is good for :joy:

import wx

class MyEvent(wx.PyEvent):

    def __new__(cls):
        if 'EventType' not in vars(cls):
            cls.EventType = wx.GetApp()._my_evt_type_
        return super().__new__(cls)

    def __init__(self):
        super().__init__(eventType=__class__.EventType)
        print(f'{self} __init__ C++ {wx.siplib.isdeleted(self)}')

    def __del__(self):
        print(f'{self} __del__ C++ {wx.siplib.isdeleted(self)}')

def evt_test(evt):
    '''only clones are send into space..and fade away (after usage)'''
    print(f'{evt} {evt.count=}')

class Gui(wx.Frame):

    def __init__(self, parent):
        print(f'{self.__class__} __init__')
        super().__init__(parent, title='PyEvent Class (cloning)')

        btn = wx.Button(self, label='left click me, please')
        btn.Bind(wx.EVT_BUTTON, self.test)

        self.Centre()
        self.Show()

        self.evt = MyEvent()
        self.Bind(wx.PyEventBinder(self.evt.EventType), evt_test)
        self.evt.count = 0

    def __del__(self):
        self.evt.Destroy()
        print(f'\n{self.__class__} __del__')

    def test(self, _):
        print(f'\n{self.evt} being posted..')
        # self.QueueEvent(self.evt)
        self.AddPendingEvent(self.evt)
        # wx.PostEvent(self, self.evt)
        self.evt.count += 1

if __name__ == '__main__':
    app = wx.App()
    app._my_evt_type_ = wx.NewEventType()
    Gui(None)
    app.MainLoop()
1 Like

Thanks for the nice code!

I noticed two points.

First, I think __del__ in wx.Frame is not necessary, or Destroy should be used instead.
https://wxpython-users.wxwidgets.narkive.com/bqo6qQ0D/del-in-wx-frame

Second, if the last part is replaced with:

    frm = Gui(None) # holds Frame object

it ends with an error in my environment [wx 4.1.2a1.dev5308+2258f215 / Windows 10]

Windows fatal exception: access violation

Current thread 0x00000f74 (most recent call first):
<no Python frame>

(Please launch the program with py -Wd -Xdev -Xtracemalloc <script.py>)
I have no idea why. :worried:

well, all __del__s are not ‘necessary’!!!
I have just used them to show object deletion: the main subject of the snippet (although I do admit that leaving the room as it was at entering has in scripting not the highest priority) and destroying self.evt there came as a convenience hook (some would say ‘quick & dirty’, but adds spice to the talk :stuck_out_tongue_winking_eye:)

what do you want to hold that reference for ? however, try ‘del frm’ after the MainLoop

I’m sure you know well, but I just noted it for others who read your code and may be confused with Destroy and __del__.

I often write as follows:

    frm = Gui(None)
    frm.Show()
    app.MainLoop()

and yes, del frm after MainLoop avoids the exception, but it should not be necessary.
I think that keeping a reference to the event object can cause a potential bug.

I like your pointed stubbornness, but I don’t ‘know’ more than anybody else on here :sneezing_face:
fact is PyEvent likes a proper destroy and with a library underneath (in whatever language) one can only take that into account (practice beats theory :confounded:)
nobody should be confused by __del__ (there is much better stuff for that in python: all knowledge starts in confusion), but I admit my ‘convenience hook’ may push the bug gang

import wx

class MyEvent(wx.PyEvent):

    def __new__(cls):
        if 'EventType' not in vars(cls):
            cls.EventType = wx.GetApp()._my_evt_type_
        return super().__new__(cls)

    def __init__(self):
        super().__init__(eventType=__class__.EventType)
        print(f'{self} __init__ C++ {wx.siplib.isdeleted(self)}')

    def __del__(self):
        print(f'{self} __del__ C++ {wx.siplib.isdeleted(self)}')

def evt_test(evt):
    '''only clones are send into space..and fade away (after usage)'''
    print(f'{evt} {evt.count=}')

class Gui(wx.Frame):

    def __init__(self, parent):
        print(f'{self.__class__} __init__')
        super().__init__(parent, title='PyEvent Class (cloning)')

        btn = wx.Button(self, label='left click me, please')
        btn.Bind(wx.EVT_BUTTON, self.test)

        self.Centre()
        self.Show()

        self.evt = MyEvent()
        self.Bind(wx.EVT_WINDOW_DESTROY,
                            lambda _: self.evt.Destroy(), self)
        self.Bind(wx.PyEventBinder(self.evt.EventType), evt_test)
        self.evt.count = 0

    def __del__(self):
        print(f'\n{self.__class__} __del__')

    def test(self, _):
        print(f'\n{self.evt} being posted..')
        # self.QueueEvent(self.evt)
        self.AddPendingEvent(self.evt)
        # wx.PostEvent(self, self.evt)
        self.evt.count += 1

if __name__ == '__main__':
    app = wx.App()
    app._my_evt_type_ = wx.NewEventType()
    Gui(None)
    app.MainLoop()

so leaves the question who deletes the C++ part of ‘evt’ in wx.CallAfter ? :exploding_head: