[wxPython] implementing close field event in wxPython - Clone memory leak?

The following code serves two purposes.

The first is that I wanted a close field event in PythonCard that is only
fired when the contents of a field change, so the event isn't fired if the
field simply loses focus. The implementation below which defines a new event
and then uses Clone() and ProcessEvent() is probably the proper way to do
this based on Robin's earlier suggestion; Robin, if you have a better idea
for an implementation, I'll be happy to use it. Clone is used because all of
the event info I might need is already in the event sent when the field
loses focus. ProcessEvent is used instead of wxPostEvent so that the close
field event will occur before the next control receives its focus event,
mouse down, etc.

The second is that I think Clone() or at least the way I'm using it is
causing a memory leak. If you run a hybrid version of wxPython, there will
be a wxFocusEvent in the memory dump for each time Clone() was used. Perhaps
I need to do an explicit deletion of the clone event in the close field
handler or call Skip?

Since the code is short, I'm including it below. If the lines get wrapped,
I'll reply with an attachment. The second field (text2) is on the panel just
so we have a way of causing the first field (text1) to lose focus.

ka

···

---

from wxPython import wx

wxEVT_CLOSE_FIELD = wx.wxNewEventType()

def EVT_CLOSE_FIELD(win, id, func):
    win.Connect(id, -1, wxEVT_CLOSE_FIELD, func)

class MyApp(wx.wxApp):

    def OnInit(self):
        frame = wx.wxFrame(wx.NULL, -1, "test clone", size=(400, 200))
        panel = wx.wxPanel(frame, -1)
        frame.Show(1)
        self.SetTopWindow(frame)

        self.text1 = wx.wxTextCtrl(panel, -1, pos=(0, 30))
        self.text2 = wx.wxTextCtrl(panel, -1, pos=(120, 30))

        wx.EVT_SET_FOCUS(self.text1, self.OnGainFocus)
        wx.EVT_KILL_FOCUS(self.text1, self.OnLoseFocus)
        EVT_CLOSE_FIELD(panel, self.text1.GetId(), self.OnCloseField)

        return 1

    def OnGainFocus(self, event):
        print "OnGainFocus"
        event.GetEventObject().DiscardEdits()

    def OnLoseFocus(self, event):
        print "OnLoseFocus"
        obj = event.GetEventObject()
        if obj.IsModified():
            print "cloning"
            clone = event.Clone()
            print "posting close field event"
            clone.SetEventType(wxEVT_CLOSE_FIELD)
            obj.GetParent().GetEventHandler().ProcessEvent(clone)

    def OnCloseField(self, event):
        print "OnCloseField"

app = MyApp(0)
app.MainLoop()

The second is that I think Clone() or at least the way I'm using it is
causing a memory leak. If you run a hybrid version of wxPython, there will
be a wxFocusEvent in the memory dump for each time Clone() was used.

Perhaps

I need to do an explicit deletion of the clone event in the close field
handler or call Skip?

The Clone is only really needed when using wxPostEvent or AddPendingEvent
where it is called by wxWindows before putting the event on the queue. You
shouldn't ever need to call it from Python.

Since you are reusing the focus event then all you really need to to call
its SetEventType without the clone.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

In reply to this post Robin suggested just reusing the event object rather
than using Clone(). After some offlist discussion about why this might not
work with PythonCard the solution turned out to be just creating a new
event. In case anyone is interested, here's the fix for the code below, just
replace the OnLoseFocus method:

    def OnLoseFocus(self, event):
        print "OnLoseFocus"
        obj = event.GetEventObject()
        if obj.IsModified():
            closeFieldEvent = wx.wxWindowCreateEvent()
            closeFieldEvent.SetEventType(wxEVT_CLOSE_FIELD)
            closeFieldEvent.SetEventObject(event.GetEventObject())
            closeFieldEvent.SetId(event.GetId())
            closeFieldEvent.SetTimestamp(event.GetTimestamp())
            print "posting close field event"
            # this will print 1 if ProcessEvent was successful, 0 otherwise
            print obj.GetEventHandler().ProcessEvent(closeFieldEvent)

ka

···

-----Original Message-----
From: wxpython-users-admin@lists.wxwindows.org
[mailto:wxpython-users-admin@lists.wxwindows.org]On Behalf Of Kevin
Altis
Sent: Wednesday, April 10, 2002 11:17 AM
To: Wxpython-Users
Subject: [wxPython] implementing close field event in wxPython - Clone
memory leak?

The following code serves two purposes.

The first is that I wanted a close field event in PythonCard that is only
fired when the contents of a field change, so the event isn't fired if the
field simply loses focus. The implementation below which defines
a new event
and then uses Clone() and ProcessEvent() is probably the proper way to do
this based on Robin's earlier suggestion; Robin, if you have a better idea
for an implementation, I'll be happy to use it. Clone is used
because all of
the event info I might need is already in the event sent when the field
loses focus. ProcessEvent is used instead of wxPostEvent so that the close
field event will occur before the next control receives its focus event,
mouse down, etc.

The second is that I think Clone() or at least the way I'm using it is
causing a memory leak. If you run a hybrid version of wxPython, there will
be a wxFocusEvent in the memory dump for each time Clone() was
used. Perhaps
I need to do an explicit deletion of the clone event in the close field
handler or call Skip?

Since the code is short, I'm including it below. If the lines get wrapped,
I'll reply with an attachment. The second field (text2) is on the
panel just
so we have a way of causing the first field (text1) to lose focus.

ka
---

from wxPython import wx

wxEVT_CLOSE_FIELD = wx.wxNewEventType()

def EVT_CLOSE_FIELD(win, id, func):
    win.Connect(id, -1, wxEVT_CLOSE_FIELD, func)

class MyApp(wx.wxApp):

    def OnInit(self):
        frame = wx.wxFrame(wx.NULL, -1, "test clone", size=(400, 200))
        panel = wx.wxPanel(frame, -1)
        frame.Show(1)
        self.SetTopWindow(frame)

        self.text1 = wx.wxTextCtrl(panel, -1, pos=(0, 30))
        self.text2 = wx.wxTextCtrl(panel, -1, pos=(120, 30))

        wx.EVT_SET_FOCUS(self.text1, self.OnGainFocus)
        wx.EVT_KILL_FOCUS(self.text1, self.OnLoseFocus)
        EVT_CLOSE_FIELD(panel, self.text1.GetId(), self.OnCloseField)

        return 1

    def OnGainFocus(self, event):
        print "OnGainFocus"
        event.GetEventObject().DiscardEdits()

    def OnLoseFocus(self, event):
        print "OnLoseFocus"
        obj = event.GetEventObject()
        if obj.IsModified():
            print "cloning"
            clone = event.Clone()
            print "posting close field event"
            clone.SetEventType(wxEVT_CLOSE_FIELD)
            obj.GetParent().GetEventHandler().ProcessEvent(clone)

    def OnCloseField(self, event):
        print "OnCloseField"

app = MyApp(0)
app.MainLoop()