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
This commit is contained in:
Robin Dunn
2010-12-29 02:09:50 +00:00
parent 80898eba5b
commit b47abd88ff
2 changed files with 145 additions and 105 deletions

View File

@@ -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

View File

@@ -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):