Fix our wxEvtHandler::Disconnect replacement

* Fix C++ compare of the handler functor
 * Use PyObject_RichCompare to check for matching python handlers
This commit is contained in:
Robin Dunn
2017-11-21 18:34:59 -08:00
parent d3ac0d6ab6
commit 0ffd220385
2 changed files with 88 additions and 13 deletions

View File

@@ -128,7 +128,7 @@ def run():
body="""\
success = 0
for et in self.evtType:
success += target.Disconnect(id1, id2, et, handler)
success += int(target.Disconnect(id1, id2, et, handler))
return success != 0
"""),
@@ -196,8 +196,7 @@ def run():
body="""\
if (PyCallable_Check(func)) {
self->Connect(id, lastId, eventType,
(wxObjectEventFunction)(wxEventFunction)
&wxPyCallback::EventThunker,
(wxObjectEventFunction)&wxPyCallback::EventThunker,
new wxPyCallback(func));
}
else if (func == Py_None) {
@@ -219,7 +218,7 @@ def run():
body="""\
if (func && func != Py_None) {
// Find the current matching binder that has this function
// pointer and dissconnect that one. Unfortuneatly since we
// pointer and disconnect that one. Unfortunately since we
// wrapped the PyObject function pointer in another object we
// have to do the searching ourselves...
wxList::compatibility_iterator node = self->GetDynamicEventTable()->GetFirst();
@@ -229,12 +228,15 @@ def run():
if ((entry->m_id == id) &&
((entry->m_lastId == lastId) || (lastId == wxID_ANY)) &&
((entry->m_eventType == eventType) || (eventType == wxEVT_NULL)) &&
// FIXME?
//((entry->m_fn->IsMatching((wxObjectEventFunction)(wxEventFunction)&wxPyCallback::EventThunker))) &&
entry->m_fn->IsMatching(wxObjectEventFunctor((wxObjectEventFunction)&wxPyCallback::EventThunker, NULL)) &&
(entry->m_callbackUserData != NULL))
{
wxPyThreadBlocker block;
wxPyCallback *cb = (wxPyCallback*)entry->m_callbackUserData;
if (cb->m_func == func) {
// NOTE: Just comparing PyObject pointers is not enough, as bound
// methods can result in different PyObjects each time obj.Method
// is evaluated. (!!!)
if (PyObject_RichCompareBool(cb->m_func, func, Py_EQ) == 1) {
delete cb;
self->GetDynamicEventTable()->Erase(node);
delete entry;
@@ -247,8 +249,7 @@ def run():
}
else {
return self->Disconnect(id, lastId, eventType,
(wxObjectEventFunction)
&wxPyCallback::EventThunker);
(wxObjectEventFunction)&wxPyCallback::EventThunker);
}
""")

View File

@@ -1,10 +1,11 @@
import unittest
from unittests import wtc
import wx
#---------------------------------------------------------------------------
class Events(unittest.TestCase):
class Events(wtc.WidgetTestCase):
# Test the constructors to make sure the classes are not abstract, except
# for wx.Event
@@ -45,7 +46,6 @@ class Events(unittest.TestCase):
evt = wx.FocusEvent()
def test_HelpEvent_ctor(self):
app = wx.App()
evt = wx.HelpEvent()
def test_IconizeEvent_ctor(self):
@@ -138,14 +138,88 @@ class Events(unittest.TestCase):
def OnSize(self, evt):
self.gotEvent = True
evt.EventObject.Close()
app = wx.App()
frm = Frame(None)
frm.Show()
frm.SetSize((200,100))
#app.MainLoop()
self.myYield()
self.assertTrue(frm.gotEvent)
def test_eventUnbinding1(self):
"Unbind without specifying handler"
self.gotEvent = False
def _onSize(evt):
self.gotEvent = True
self.frame.Bind(wx.EVT_SIZE, _onSize)
val = self.frame.Unbind(wx.EVT_SIZE)
assert val, "Unbind returned false"
self.frame.SetSize((200,200))
self.myYield()
assert not self.gotEvent, "Expected gotEvent to still be False"
def test_eventUnbinding2(self):
"Unbind with specifying handler, simple function object"
self.gotEvent = False
def _onSize(evt):
self.gotEvent = True
self.frame.Bind(wx.EVT_SIZE, _onSize)
val = self.frame.Unbind(wx.EVT_SIZE, handler=_onSize)
assert val, "Unbind returned False"
self.frame.SetSize((200,200))
self.myYield()
assert not self.gotEvent, "Expected gotEvent to still be False"
def test_eventUnbinding3(self):
"Unbind with specifying handler, a bound method"
class Frame(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.gotEvent = False
self.obj = self.OnSize
def doUnBind(self):
#assert id(self.obj) != id(self.OnSize) # Strange, but it's possible
val = self.Unbind(wx.EVT_SIZE, handler=self.OnSize)
assert val, "Unbind returned False"
def OnSize(self, evt):
self.gotEvent = True
evt.EventObject.Close()
frm = Frame(None)
frm.doUnBind()
frm.Show()
frm.SetSize((200,100))
self.myYield()
assert not frm.gotEvent, "Expected gotEvent to still be False"
def test_eventUnbinding4(self):
"Unbind by passing None to Bind()"
self.gotEvent = False
def _onSize(evt):
self.gotEvent = True
self.frame.Bind(wx.EVT_SIZE, _onSize)
self.frame.Bind(wx.EVT_SIZE, None)
self.frame.SetSize((200,200))
self.myYield()
assert not self.gotEvent, "Expected gotEvent to still be False"
def test_DropFilesEvent_tweaks(self):
evt = wx.DropFilesEvent(123, 'one two three four five'.split())
self.assertTrue(evt.NumberOfFiles == 5)