From b47abd88ff0f6b051113a483e2d188c6e43d7b2f Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 29 Dec 2010 02:09:50 +0000 Subject: [PATCH] SIP now does the RightThing with __[sg]etattr__ methods, so use them to provide a simpler implementation for wx.PyEvent and wx.PyCommandEvent. Put any attributes set from Python into a separate dictionary object, and copy that dictionary in Clone(). git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@66489 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/pyevent.sip | 248 ++++++++++++++++++++++---------------- unittests/test_pyevent.py | 2 +- 2 files changed, 145 insertions(+), 105 deletions(-) diff --git a/src/pyevent.sip b/src/pyevent.sip index 88968379..5e381498 100644 --- a/src/pyevent.sip +++ b/src/pyevent.sip @@ -1,4 +1,4 @@ -///////////////////////////////////////////////////////////////////////////// +//-------------------------------------------------------------------------- // Name: pyevent.sip // Purpose: A set of event classes that can be derived from in Python // and that preserve their attributes when cloned. @@ -8,145 +8,185 @@ // Created: 18-Dec-2010 // Copyright: (c) 2010 by Total Control Software // Licence: wxWindows license -///////////////////////////////////////////////////////////////////////////// +//-------------------------------------------------------------------------- -// These Event classes can be derived from in Python and passed through the -// event system without loosing anything. They do this by copying any -// attributes added to the Python object for the original event over to the -// new cloned event in the Clone method. +// The wxPyEvent and wxPyCommandEvent classes can be derived from in Python +// and passed through the event system without losing anything. + +%ModuleHeaderCode + // This class holds a Python dictionary object and is used to store any + // attributes that are set from Python code for the wx.PyEvent and + // wx.PyCommandEvent classes. This dictionary is used to make it easy to + // transport those attributes over to the clone when wx needs to make a copy + // of the event instance. + + // NOTE: This class is intentionally not exposed to SIP (see wxPyEvent + // and wxPyCommandEvent) + class wxPyEvtDict + { + public: + wxPyEvtDict() + { + m_dict = PyDict_New(); + } + + wxPyEvtDict(const wxPyEvtDict& other) + { + m_dict = PyDict_Copy(other.m_dict); + } + + ~wxPyEvtDict() + { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + Py_DECREF(m_dict); + m_dict = NULL; + wxPyEndBlockThreads(blocked); + } + + PyObject* _getDict() + { + Py_INCREF(m_dict); + return m_dict; + } + + PyObject* __getattr__(PyObject* name) + { + PyObject* value = NULL; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (PyDict_Contains(m_dict, name)) { + value = PyDict_GetItem(m_dict, name); + Py_INCREF(value); + } + else { + PyErr_SetObject(PyExc_AttributeError, name); + } + wxPyEndBlockThreads(blocked); + return value; + } + + void __setattr__(PyObject* name, PyObject* value) + { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyDict_SetItem(m_dict, name, value); + wxPyEndBlockThreads(blocked); + } + + void __delattr__(PyObject* name) + { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + if (PyDict_Contains(m_dict, name)) + PyDict_DelItem(m_dict, name); + else + PyErr_SetObject(PyExc_AttributeError, name); + wxPyEndBlockThreads(blocked); + } + + protected: + PyObject* m_dict; + }; +%End //--------------------------------------------------------------------------- -// TODO: Use this comment for a docstring +class wxPyEvent : wxEvent +{ +%Docstring +wx.PyEvent can be used as a base class for implementing custom event types +in Python. You should derive from this class instead of `wx.Event` because +this class is Python-aware and is able to transport its Python bits safely +through the wxWidgets event system and have them still be there when the +event handler is invoked. Note that since wx.PyEvent is taking care of +preserving the extra attributes that have been set then you do not need to +implement the Clone method in your derived classes. -// wx.PyEvent can be used as a base class for implementing custom event -// types in Python. You should derive from this class instead of -// `wx.Event` because this class is Python-aware and is able to transport -// its Python bits safely through the wxWidgets event system and have -// them still be there when the event handler is invoked. -// :see: `wx.PyCommandEvent` -class wxPyEvent : wxEvent { +:see: `wx.PyCommandEvent` +%End + // first declare the C++ version of the class %TypeCode - class wxPyEvent : public wxEvent + class wxPyEvent : public wxEvent, public wxPyEvtDict { DECLARE_DYNAMIC_CLASS(wxPyEvent) - public: - wxPyEvent(int winid=0, wxEventType commandType = wxEVT_NULL) - : wxEvent(winid, commandType) {} + public: + wxPyEvent(int id=0, wxEventType eventType = wxEVT_NULL) + : wxEvent(id, eventType) {} - // Create a new instance of the Python object for the cloned event, - // and then copy the attributes from the original Python instance to - // the clone. - virtual wxEvent* Clone() const - { - wxPyEvent* newEvent = new wxPyEvent(*this); - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyObject* thisObj = sipGetPyObject((void*)this, sipType_wxPyEvent); - PyObject* newObj = sipConvertFromType((void*)newEvent, - sipType_wxPyEvent, - NULL); - PyObject* newDict = PyObject_GetAttrString(newObj, "__dict__"); - PyObject* thisDict = PyObject_GetAttrString(thisObj, "__dict__"); - PyDict_Update(newDict, thisDict); - Py_DECREF(newDict); - Py_DECREF(thisDict); - wxPyEndBlockThreads(blocked); - return newEvent; - } + virtual wxEvent* Clone() const { return new wxPyEvent(*this); } }; IMPLEMENT_DYNAMIC_CLASS(wxPyEvent, wxEvent); %End public: - wxPyEvent(int winid=0, wxEventType eventType = wxEVT_NULL ); - - virtual wxEvent* Clone() const /Factory, NoArgParser/; + wxPyEvent(int id=0, wxEventType eventType = wxEVT_NULL ); + virtual wxEvent* Clone() const /Factory/; + + SIP_PYOBJECT __getattr__(SIP_PYOBJECT name); %MethodCode - PyObject *sipParseErr = NULL; - bool sipSelfWasArg = (!sipSelf || sipIsDerived((sipSimpleWrapper *)sipSelf)); - wxPyEvent *sipCpp; - if (sipParseArgs(&sipParseErr, sipArgs, "B", &sipSelf, sipType_wxPyEvent, &sipCpp)) - { - wxEvent *sipRes; - sipRes = (sipSelfWasArg ? sipCpp->wxPyEvent::Clone() : sipCpp->Clone()); - // Note that this MethodCode is nearly identical to what is normally generated, - // except for the following lines. wxPyEvent::Clone already made a PyObject, - // so just fetch it. - PyObject* obj = sipGetPyObject(sipRes, sipType_wxPyEvent); - sipTransferBack(obj); - return obj; - } - sipNoMethod(sipParseErr, sipName_PyEvent, sipName_Clone, NULL); //doc_wxPyEvent_Clone); - return NULL; + sipRes = sipCpp->__getattr__(name); + %End + + void __setattr__(SIP_PYOBJECT name, SIP_PYOBJECT value); + %MethodCode + sipCpp->__setattr__(name, value); + %End + + void __delattr__(SIP_PYOBJECT name); + %MethodCode + sipCpp->__delattr__(name); %End }; -// wx.PyCommandEvent can be used as a base class for implementing custom -// event types in Python, where the event should travel up to parent -// windows looking for a handler. You should derived from this class -// instead of `wx.CommandEvent` because this class is Python-aware and is -// able to transport its Python bits safely through the wxWidgets event -// system and have them still be there when the event handler is invoked. -// :see: `wx.PyEvent` -class wxPyCommandEvent : wxCommandEvent { +class wxPyCommandEvent : wxCommandEvent +{ +%Docstring +wx.PyCommandEvent can be used as a base class for implementing custom event types +in Python. You should derive from this class instead of `wx.CommandEvent` because +this class is Python-aware and is able to transport its Python bits safely +through the wxWidgets event system and have them still be there when the +event handler is invoked. Note that since wx.PyCommandEvent is taking care of +preserving the extra attributes that have been set then you do not need to +implement the Clone method in your derived classes. + +:see: `wx.PyEvent` +%End + // first declare the C++ version of the class %TypeCode - class wxPyCommandEvent : public wxCommandEvent + class wxPyCommandEvent : public wxCommandEvent, public wxPyEvtDict { DECLARE_DYNAMIC_CLASS(wxPyCommandEvent) public: wxPyCommandEvent(wxEventType eventType = wxEVT_NULL, int id=0) : wxCommandEvent(eventType, id) {} - - // Create a new instance of the Python object for the cloned event, - // and then copy the attributes from the original Python instance to - // the clone. - virtual wxEvent* Clone() const - { - wxPyCommandEvent* newEvent = new wxPyCommandEvent(*this); - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyObject* thisObj = sipGetPyObject((void*)this, sipType_wxPyCommandEvent); - PyObject* newObj = sipConvertFromNewType((void*)newEvent, - sipType_wxPyCommandEvent, - NULL); - PyObject* newDict = PyObject_GetAttrString(newObj, "__dict__"); - PyObject* thisDict = PyObject_GetAttrString(thisObj, "__dict__"); - PyDict_Update(newDict, thisDict); - Py_DECREF(newDict); - Py_DECREF(thisDict); - wxPyEndBlockThreads(blocked); - return newEvent; - } + + virtual wxEvent* Clone() const { return new wxPyCommandEvent(*this); } }; IMPLEMENT_DYNAMIC_CLASS(wxPyCommandEvent, wxCommandEvent); %End public: wxPyCommandEvent(wxEventType eventType = wxEVT_NULL, int id=0); - - virtual wxEvent* Clone() const /Factory, NoArgParser/; + virtual wxEvent* Clone() const /Factory/; + SIP_PYOBJECT _getDict(); + + SIP_PYOBJECT __getattr__(SIP_PYOBJECT name); %MethodCode - PyObject *sipParseErr = NULL; - bool sipSelfWasArg = (!sipSelf || sipIsDerived((sipSimpleWrapper *)sipSelf)); - wxPyCommandEvent *sipCpp; - if (sipParseArgs(&sipParseErr, sipArgs, "B", &sipSelf, sipType_wxPyCommandEvent, &sipCpp)) - { - wxEvent *sipRes; - sipRes = (sipSelfWasArg ? sipCpp->wxPyCommandEvent::Clone() : sipCpp->Clone()); - // Note that this MethodCode is nearly identical to what is normally generated, - // except for the following lines. wxPyCommandEvent::Clone already made a PyObject, - // so just fetch it. - PyObject* obj = sipGetPyObject(sipRes, sipType_wxPyCommandEvent); - sipTransferBack(obj); - return obj; - } - sipNoMethod(sipParseErr, sipName_PyCommandEvent, sipName_Clone, NULL); //doc_wxPyEvent_Clone); - return NULL; + sipRes = sipCpp->__getattr__(name); + %End + + void __setattr__(SIP_PYOBJECT name, SIP_PYOBJECT value); + %MethodCode + sipCpp->__setattr__(name, value); + %End + + void __delattr__(SIP_PYOBJECT name); + %MethodCode + sipCpp->__delattr__(name); %End }; - + + // TODO: Temporary testing code, get rid of this later %ModuleCode diff --git a/unittests/test_pyevent.py b/unittests/test_pyevent.py index 070c8e38..a7944662 100644 --- a/unittests/test_pyevent.py +++ b/unittests/test_pyevent.py @@ -73,7 +73,7 @@ class PyEvents(unittest2.TestCase): rc2 = sys.getrefcount(evt2) rc3 = sys.getrefcount(evt1) #print '\n****', rc1, rc2, rc3 - ##self.assertTrue(rc1 == rc2 == rc3) TODO: rc2 has an extra refcount. Why? + self.assertTrue(rc1 == rc2 == rc3) self.assertTrue(evt1.attr == evt2.attr) #def test_AA(self):