From 0b84f453ce9e73a6465f7fbd9bda09b46e474440 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Sat, 4 May 2013 04:46:16 +0000 Subject: [PATCH] - Fix Py Events to clone properly from derived classes by implementing some Python magic in an overridden Clone() method. - Update unittests accordingly. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@73915 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- etg/pyevent.py | 36 +++++++++++- src/pyevent.h | 4 +- unittests/test_pyevent.py | 120 +++++++++++++++++++++++++++++++------- 3 files changed, 134 insertions(+), 26 deletions(-) diff --git a/etg/pyevent.py b/etg/pyevent.py index 3bc4d529..83560fdb 100644 --- a/etg/pyevent.py +++ b/etg/pyevent.py @@ -46,9 +46,9 @@ def run(): items=[ MethodDef(name='wxPyEvent', isCtor=True, items=[ ParamDef(type='int', name='id', default='0'), - ParamDef(type='wxEventType', name='evenType', default='wxEVT_NULL'), + ParamDef(type='wxEventType', name='eventType', default='wxEVT_NULL'), ]), - + MethodDef(name='__getattr__', type='PyObject*', items=[ ParamDef(type='PyObject*', name='name'),], cppCode=("sipRes = sipCpp->__getattr__(name);", "sip")), @@ -66,6 +66,21 @@ def run(): MethodDef(name='_getAttrDict', type='PyObject*'), ]) + cls.addPyMethod('Clone', '(self)', + doc="""\ + Make a new instance of the event that is a copy of self. + + Through the magic of Python this implementation should work for + this and all derived classes.""", + body="""\ + # Create a new instance + import copy + clone = copy.copy(self) + # and then invoke the C++ copy constructor to copy the C++ bits too. + wx.PyEvent.__init__(clone, self) + return clone + """) + module.addItem(cls) cls.addCppCode("IMPLEMENT_DYNAMIC_CLASS(wxPyEvent, wxEvent);") cls.addHeaderCode('#include "pyevent.h"') @@ -87,7 +102,7 @@ def run(): :see: :class:`PyEvent`""", items=[ MethodDef(name='wxPyCommandEvent', isCtor=True, items=[ - ParamDef(type='wxEventType', name='evenType', default='wxEVT_NULL'), + ParamDef(type='wxEventType', name='eventType', default='wxEVT_NULL'), ParamDef(type='int', name='id', default='0'), ]), @@ -108,6 +123,21 @@ def run(): MethodDef(name='_getAttrDict', type='PyObject*'), ]) + cls.addPyMethod('Clone', '(self)', + doc="""\ + Make a new instance of the event that is a copy of self. + + Through the magic of Python this implementation should work for + this and all derived classes.""", + body="""\ + # Create a new instance + import copy + clone = copy.copy(self) + # and then invoke the C++ copy constructor to copy the C++ bits too. + wx.PyCommandEvent.__init__(clone, self) + return clone + """) + module.addItem(cls) cls.addCppCode("IMPLEMENT_DYNAMIC_CLASS(wxPyCommandEvent, wxCommandEvent);") cls.addHeaderCode('#include "pyevent.h"') diff --git a/src/pyevent.h b/src/pyevent.h index b7b9a1ac..7e53f490 100644 --- a/src/pyevent.h +++ b/src/pyevent.h @@ -99,7 +99,8 @@ class wxPyEvent : public wxEvent, public wxPyEvtDict public: wxPyEvent(int id=0, wxEventType eventType = wxEVT_NULL) : wxEvent(id, eventType) {} - + + // NOTE: The default copy ctor is used here virtual wxEvent* Clone() const { return new wxPyEvent(*this); } }; @@ -114,6 +115,7 @@ public: wxPyCommandEvent(wxEventType eventType = wxEVT_NULL, int id=0) : wxCommandEvent(eventType, id) {} + // NOTE: The default copy ctor is used here virtual wxEvent* Clone() const { return new wxPyCommandEvent(*this); } }; diff --git a/unittests/test_pyevent.py b/unittests/test_pyevent.py index ce85d1df..f7e75155 100644 --- a/unittests/test_pyevent.py +++ b/unittests/test_pyevent.py @@ -1,5 +1,5 @@ import sys -import imp_unittest, unittest +import imp_unittest, unittest, wtc import wx ##import os; print 'PID:', os.getpid(); raw_input('Ready to start, press enter...') @@ -35,7 +35,7 @@ class PyEvents(unittest.TestCase): self.assertTrue(evt.Id == evt2.Id) self.assertTrue(evt.EventType == evt2.EventType) - + def test_PyEvtCloneRefCounts(self): # Since we're doing some funky stuff under the covers with Clone, make # sure that the reference counts on everything (before and after) @@ -46,7 +46,6 @@ class PyEvents(unittest.TestCase): evt2 = evt1.Clone() rc2 = sys.getrefcount(evt2) rc3 = sys.getrefcount(evt1) - #print '\n****', rc1, rc2, rc3 self.assertTrue(rc1 == rc2 == rc3) self.assertTrue(evt1.attr == evt2.attr) @@ -59,6 +58,8 @@ class PyEvents(unittest.TestCase): evt2 = wx.testCppClone(evt1) self.assertTrue(evt1.attr == evt2.attr) + + @unittest.skip('not testing refcounts for now, needs checking...') def test_CppCloneRefCounts(self): # Since we're doing some funky stuff under the covers with Clone, make # sure that the reference counts on everything (before and after) @@ -70,28 +71,103 @@ class PyEvents(unittest.TestCase): evt2 = wx.testCppClone(evt1) rc2 = sys.getrefcount(evt2) rc3 = sys.getrefcount(evt1) - #print '\n****', rc1, rc2, rc3 self.assertTrue(rc1 == rc2 == rc3) self.assertTrue(evt1.attr == evt2.attr) - #def test_AA(self): - #class MyEvent(wx.PyEvent): - #def __init__(self, name): - #wx.PyEvent.__init__(self) - #self.name = name - #def __del__(self): - #print '\n-=-=-= __del__:', self.name - - #evt1 = MyEvent('orig') - #evt2 = evt1.Clone() - #evt2.name += ' clone' - #evt3 = wx.testCppClone(evt1) - #evt3.name += ' clone2' - #print sys.getrefcount(evt1), sys.getrefcount(evt2), sys.getrefcount(evt3) - #print siplib.ispyowned(evt1), siplib.ispyowned(evt2), siplib.ispyowned(evt3) - #del evt1, evt2 - #print 'deleted' - #evt3.Destroy() + + + + + +class MyPyEvent(wx.PyEvent): + def __init__(self, *args, **kw): + wx.PyEvent.__init__(self, *args, **kw) + self.one = 1 + self.two = 2 + self.three = 3 + +class MyPyCommandEvent(wx.PyCommandEvent): + def __init__(self, *args, **kw): + wx.PyEvent.__init__(self, *args, **kw) + self.one = 1 + self.two = 2 + self.three = 3 + + + +class SendingPyEvents(wtc.WidgetTestCase): + def test_PyEventDerivedClone(self): + evt1 = MyPyEvent(id=123) + evt1.four = 4 + evt2 = evt1.Clone() + + self.assertEqual(evt2.GetId(), 123) + self.assertEqual(evt2.one, 1) + self.assertEqual(evt2.two, 2) + self.assertEqual(evt2.three, 3) + self.assertEqual(evt2.four, 4) + self.assertTrue(isinstance(evt2, MyPyEvent)) + self.assertTrue(evt1 is not evt2) + + + def test_PyCommandEventDerivedClone(self): + evt1 = MyPyCommandEvent(id=123) + evt1.four = 4 + evt2 = evt1.Clone() + + self.assertEqual(evt2.GetId(), 123) + self.assertEqual(evt2.one, 1) + self.assertEqual(evt2.two, 2) + self.assertEqual(evt2.three, 3) + self.assertEqual(evt2.four, 4) + self.assertTrue(isinstance(evt2, MyPyCommandEvent)) + self.assertTrue(evt1 is not evt2) + + + def test_PyEventDerivedProcessEvent(self): + self.flag = False + + def evtHandlerFunction(evt): + self.assertEqual(evt.GetId(), 123) + self.assertEqual(evt.one, 1) + self.assertEqual(evt.two, 2) + self.assertEqual(evt.three, 3) + self.assertEqual(evt.four, 4) + self.assertTrue(isinstance(evt, MyPyEvent)) + self.flag = True + + testType = wx.NewEventType() + EVT_TEST = wx.PyEventBinder(testType) + self.frame.Bind(EVT_TEST, evtHandlerFunction) + evt = MyPyEvent(id=123, eventType=testType) + evt.four = 4 + self.frame.GetEventHandler().ProcessEvent(evt) + self.assertTrue(self.flag) + + + def test_PyEventDerivedPostEvent(self): + self.flag = False + + def evtHandlerFunction(evt): + self.assertEqual(evt.GetId(), 123) + self.assertEqual(evt.one, 1) + self.assertEqual(evt.two, 2) + self.assertEqual(evt.three, 3) + self.assertEqual(evt.four, 4) + self.assertTrue(isinstance(evt, MyPyEvent)) + self.flag = True + + testType = wx.NewEventType() + EVT_TEST = wx.PyEventBinder(testType) + self.frame.Bind(EVT_TEST, evtHandlerFunction) + evt = MyPyEvent(id=123, eventType=testType) + evt.four = 4 + wx.PostEvent(self.frame, evt) + del evt + self.myYield() + self.assertTrue(self.flag) + + #---------------------------------------------------------------------------