From 8fffd8c53e3765af728a07744fe3d30d6bd0de03 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Mon, 27 Dec 2010 18:53:41 +0000 Subject: [PATCH] Add wxProcess, utils.h and the wx.Py events. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@66461 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- etg/_core.py | 3 + etg/process.py | 45 +++++++++++ etg/utils.py | 76 ++++++++++++++++++ src/pyevent.sip | 161 ++++++++++++++++++++++++++++++++++++++ unittests/test_pyevent.py | 102 ++++++++++++++++++++++++ 5 files changed, 387 insertions(+) create mode 100644 etg/process.py create mode 100644 etg/utils.py create mode 100644 src/pyevent.sip create mode 100644 unittests/test_pyevent.py diff --git a/etg/_core.py b/etg/_core.py index aa6c774e..f3ff394d 100644 --- a/etg/_core.py +++ b/etg/_core.py @@ -53,6 +53,9 @@ INCLUDES = [ 'defs', 'tooltip', 'layout', 'event', + 'pyevent', + 'process', + 'utils', 'evtloop', 'apptrait', diff --git a/etg/process.py b/etg/process.py new file mode 100644 index 00000000..b62d16e5 --- /dev/null +++ b/etg/process.py @@ -0,0 +1,45 @@ +#--------------------------------------------------------------------------- +# Name: etg/process.py +# Author: Robin Dunn +# +# Created: 19-Dec-2010 +# Copyright: (c) 2010 by Total Control Software +# License: wxWindows License +#--------------------------------------------------------------------------- + +import etgtools +import etgtools.tweaker_tools as tools + +PACKAGE = "wx" +MODULE = "_core" +NAME = "process" # Base name of the file to generate to for this script +DOCSTRING = "" + +# The classes and/or the basename of the Doxygen XML files to be processed by +# this script. +ITEMS = [ 'wxProcess' ] + +#--------------------------------------------------------------------------- + +def run(): + # Parse the XML file(s) building a collection of Extractor objects + module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING) + etgtools.parseDoxyXML(module, ITEMS) + + #----------------------------------------------------------------- + # Tweak the parsed meta objects in the module object as needed for + # customizing the generated code and docstrings. + + + + + + #----------------------------------------------------------------- + tools.doCommonTweaks(module) + tools.runGenerators(module) + + +#--------------------------------------------------------------------------- +if __name__ == '__main__': + run() + diff --git a/etg/utils.py b/etg/utils.py new file mode 100644 index 00000000..efda5b8b --- /dev/null +++ b/etg/utils.py @@ -0,0 +1,76 @@ +#--------------------------------------------------------------------------- +# Name: etg/utils.py +# Author: Robin Dunn +# +# Created: 19-Dec-2010 +# Copyright: (c) 2010 by Total Control Software +# License: wxWindows License +#--------------------------------------------------------------------------- + +import etgtools +import etgtools.tweaker_tools as tools + +PACKAGE = "wx" +MODULE = "_core" +NAME = "utils" # Base name of the file to generate to for this script +DOCSTRING = "" + +# The classes and/or the basename of the Doxygen XML files to be processed by +# this script. +ITEMS = [ 'utils_8h.xml', + 'power_8h.xml', + 'wxWindowDisabler', + 'wxBusyCursor', + 'wxVersionInfo', + ] + +#--------------------------------------------------------------------------- + +def run(): + # Parse the XML file(s) building a collection of Extractor objects + module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING) + etgtools.parseDoxyXML(module, ITEMS) + + #----------------------------------------------------------------- + # Tweak the parsed meta objects in the module object as needed for + # customizing the generated code and docstrings. + + module.addHeaderCode('#include ') + module.addHeaderCode('#include ') + + c = module.find('wxWindowDisabler') + assert isinstance(c, etgtools.ClassDef) + c.addPrivateCopyCtor() + + module.find('wxQsort').ignore() + module.find('wxGetEmailAddress').findOverload('buf').ignore() + module.find('wxGetHostName').findOverload('buf').ignore() + module.find('wxGetUserId').findOverload('buf').ignore() + module.find('wxGetUserName').findOverload('buf').ignore() + module.find('CMPFUNCDATA').ignore() + module.find('wxLoadUserResource').ignore() + module.find('wxGetFreeMemory').ignore() + module.find('wxGetLinuxDistributionInfo').ignore() + module.find('wxGetDisplayName').ignore() + module.find('wxSetDisplayName').ignore() + module.find('wxPostDelete').ignore() + + # ignore all the environment related functions + for item in module.allItems(): + if 'Env' in item.name: + item.ignore() + module.find('wxGetenv').ignore() + + # Keep just the first wxExecute overload + f = module.find('wxExecute') + f.overloads = [] + + #----------------------------------------------------------------- + tools.doCommonTweaks(module) + tools.runGenerators(module) + + +#--------------------------------------------------------------------------- +if __name__ == '__main__': + run() + diff --git a/src/pyevent.sip b/src/pyevent.sip new file mode 100644 index 00000000..8e5dffbb --- /dev/null +++ b/src/pyevent.sip @@ -0,0 +1,161 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: pyevent.sip +// Purpose: A set of event classes that can be derived from in Python +// and that preserve their attributes when cloned. +// +// Author: Robin Dunn +// +// 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 keeping a seaparate +// dictionary for storring any extra attributes set from Python code. When the +// event object is cloned then the clone gets a new reference to the same +// dictionary held by the original object. + + +//--------------------------------------------------------------------------- + +// TODO: Use this comment for a 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. +// :see: `wx.PyCommandEvent` +class wxPyEvent : wxEvent { + %TypeCode + class wxPyEvent : public wxEvent + { + DECLARE_DYNAMIC_CLASS(wxPyEvent) + public: + wxPyEvent(int winid=0, wxEventType commandType = wxEVT_NULL) + : wxEvent(winid, commandType) {} + + // 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; + } + }; + IMPLEMENT_DYNAMIC_CLASS(wxPyEvent, wxEvent); + %End + +public: + wxPyEvent(int winid=0, wxEventType eventType = wxEVT_NULL ); + + virtual wxEvent* Clone() const /Factory, NoArgParser/; + %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; + %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 { + %TypeCode + class wxPyCommandEvent : public wxCommandEvent + { + 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; + } + }; + IMPLEMENT_DYNAMIC_CLASS(wxPyCommandEvent, wxCommandEvent); + %End + +public: + wxPyCommandEvent(wxEventType eventType = wxEVT_NULL, int id=0); + + virtual wxEvent* Clone() const /Factory, NoArgParser/; + %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; + %End +}; + + +// TODO: Temporary testing code, get rid of this later +%ModuleCode +wxEvent* testCppClone(wxEvent& evt) +{ + return evt.Clone(); +} +%End +wxEvent* testCppClone(wxEvent& evt); + + diff --git a/unittests/test_pyevent.py b/unittests/test_pyevent.py new file mode 100644 index 00000000..070c8e38 --- /dev/null +++ b/unittests/test_pyevent.py @@ -0,0 +1,102 @@ +import sys +import unittest2 +import wxPhoenix as wx +##import os; print 'PID:', os.getpid(); raw_input('Ready to start, press enter...') + +from wxPhoenix import siplib + +#--------------------------------------------------------------------------- + +class PyEvents(unittest2.TestCase): + + def test_PyEvent(self): + id = wx.NewId() + typ = wx.NewEventType() + evt = wx.PyEvent(id, typ) + evt.newAttr = "hello" + evt2 = evt.Clone() + self.assertTrue(type(evt2) == wx.PyEvent) + self.assertTrue(evt is not evt2) + self.assertTrue(getattr(evt2, 'newAttr')) + self.assertTrue(evt.newAttr == evt2.newAttr) + self.assertTrue(evt.Id == evt2.Id) + self.assertTrue(evt.EventType == evt2.EventType) + + + + def test_PyCommandEvent(self): + id = wx.NewId() + typ = wx.NewEventType() + evt = wx.PyCommandEvent(id, typ) + evt.newAttr = "hello" + evt2 = evt.Clone() + self.assertTrue(type(evt2) == wx.PyCommandEvent) + self.assertTrue(evt is not evt2) + self.assertTrue(getattr(evt2, 'newAttr')) + self.assertTrue(evt.newAttr == evt2.newAttr) + 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) + # still make sense + evt1 = wx.PyEvent() + rc1 = sys.getrefcount(evt1) + evt1.attr = 'Howdy!' + 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) + + + def test_CppClone(self): + # test what happens when Clone is called from C++ + if hasattr(wx, 'testCppClone'): + evt1 = wx.PyEvent() + evt1.attr = 'testCppClone' + evt2 = wx.testCppClone(evt1) + self.assertTrue(evt1.attr == evt2.attr) + + 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) + # still make sense + if hasattr(wx, 'testCppClone'): + evt1 = wx.PyEvent() + rc1 = sys.getrefcount(evt1) + evt1.attr = 'Howdy!' + evt2 = wx.testCppClone(evt1) + 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(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() + +#--------------------------------------------------------------------------- + + +if __name__ == '__main__': + unittest2.main()