diff --git a/etg/_propgrid.py b/etg/_propgrid.py index 5e15dddc..706b2cc9 100644 --- a/etg/_propgrid.py +++ b/etg/_propgrid.py @@ -23,7 +23,8 @@ ITEMS = [ ] # The list of other ETG scripts and back-end generator modules that are # included as part of this module. These should all be items that are put in # the wxWidgets "propgrid" library in a multi-lib build. -INCLUDES = [ 'propgriddefs', +INCLUDES = [ 'pgvariant', + 'propgriddefs', 'propgridproperty', 'propgrideditors', 'propgridpagestate', diff --git a/etg/propgrid.py b/etg/propgrid.py index 02ef96b3..70d0eafb 100644 --- a/etg/propgrid.py +++ b/etg/propgrid.py @@ -87,6 +87,13 @@ def run(): """) + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/propgridadvprops.py b/etg/propgridadvprops.py index 940adc9b..c0ff20ae 100644 --- a/etg/propgridadvprops.py +++ b/etg/propgridadvprops.py @@ -52,6 +52,13 @@ def run(): m.find('value').default = 'wxArrayString()' + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/propgrideditors.py b/etg/propgrideditors.py index c96a59f7..ef7dc83d 100644 --- a/etg/propgrideditors.py +++ b/etg/propgrideditors.py @@ -45,6 +45,13 @@ def run(): tools.fixWindowClass(c) + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/propgridiface.py b/etg/propgridiface.py index 3dfb2ecf..07ef3f08 100644 --- a/etg/propgridiface.py +++ b/etg/propgridiface.py @@ -148,6 +148,14 @@ def run(): item.type = 'const wxPGPropArgCls &' + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/propgridpagestate.py b/etg/propgridpagestate.py index ccc75741..baed289c 100644 --- a/etg/propgridpagestate.py +++ b/etg/propgridpagestate.py @@ -52,6 +52,13 @@ def run(): module.find('wxPG_IT_CHILDREN').ignore() + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/propgridproperty.py b/etg/propgridproperty.py index 909136b3..719b1450 100644 --- a/etg/propgridproperty.py +++ b/etg/propgridproperty.py @@ -162,6 +162,13 @@ def run(): """) + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/propgridprops.py b/etg/propgridprops.py index 3826685d..2dfe9b1b 100644 --- a/etg/propgridprops.py +++ b/etg/propgridprops.py @@ -91,6 +91,13 @@ def run(): tools.fixWindowClass(c, hideVirtuals=False, ignoreProtected=False) + # Switch all wxVariant types to wxPGVariant, so the propgrid-specific + # version of the MappedType will be used for converting to/from Python + # objects. + for item in module.allItems(): + if hasattr(item, 'type') and 'wxVariant' in item.type: + item.type = item.type.replace('wxVariant', 'wxPGVariant') + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/src/pgvariant.sip b/src/pgvariant.sip new file mode 100644 index 00000000..5249e3ac --- /dev/null +++ b/src/pgvariant.sip @@ -0,0 +1,186 @@ +//-------------------------------------------------------------------------- +// Name: pgvariant.sip +// Purpose: MappedType for wxPGVariant +// +// Author: Robin Dunn +// +// Created: 24-Feb-2017 +// Copyright: (c) 2017 by Total Control Software +// Licence: wxWindows license +//-------------------------------------------------------------------------- + +%ModuleHeaderCode +// A wxPGVariant is really the same thing as a wxVariant. We just create +// this new type so a different %MappedType can be created for it that +// also supports the variant data types that are in the propgrid APIs +// instead of core. +typedef wxVariant wxPGVariant; +typedef wxVariantList wxPGVariantList; + +wxVariant wxPGVariant_in_helper(PyObject* source); +PyObject* wxPGVariant_out_helper(const wxVariant& value); +%End + + +%ModuleCode +wxVariant wxPGVariant_in_helper(PyObject* obj) +{ + wxVariant value; + + if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxFont"))) { + wxFont* ptr; + wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxFont")); + value << *ptr; + } + + else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxPoint"))) { + wxPoint* ptr; + wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxPoint")); + value << *ptr; + } + + else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxSize"))) { + wxSize* ptr; + wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxSize")); + value << *ptr; + } + + else if (sipCanConvertToType(obj, sipType_wxArrayInt, 0)) { + wxArrayInt* ptr; + int state = 0; + int isErr = 0; + ptr = (wxArrayInt*)sipConvertToType(obj, sipType_wxArrayInt, NULL, 0, &state, &isErr); + value << *ptr; + sipReleaseType(ptr, sipType_wxArrayInt, state); + } + + else + value = wxVariant_in_helper(obj); + return value; +} + + +PyObject* wxPGVariant_out_helper(const wxVariant& value) +{ + PyObject* obj; + + if ( value.IsType("wxFont") ) { + wxFont val; + val << value; + obj = wxPyConstructObject(new wxFont(val), "wxFont", true); + } + + else if ( value.IsType("wxPoint") ) { + const wxPoint& val = wxPointRefFromVariant(value); + obj = wxPyConstructObject(new wxPoint(val), "wxPoint", true); + } + + else if ( value.IsType("wxSize") ) { + const wxSize& val = wxSizeRefFromVariant(value); + obj = wxPyConstructObject(new wxSize(val), "wxSize", true); + } + + else if ( value.IsType("wxArrayInt") ) { + const wxArrayInt& arr = wxArrayIntRefFromVariant(value); + obj = sipConvertFromType((void*)&arr, sipType_wxArrayInt, NULL); + } + + else + obj = wxVariant_out_helper(value); + return obj; +} +%End + + + +%MappedType wxPGVariant /AllowNone/ +{ + %ConvertToTypeCode + // Code to test a PyObject for compatibility. + if (!sipIsErr) { + // Any type should work since we'll just use the PyObject directly + // if the type is not one that is explicitly supported. + return TRUE; + } + + // Code to create a new wxVariant from the PyObject + wxVariant* value = new wxVariant(wxPGVariant_in_helper(sipPy)); + *sipCppPtr = value; + return sipGetState(sipTransferObj); + %End + + + %ConvertFromTypeCode + // Code to convert a wxVariant to a PyObject. + if (sipCpp == NULL) { + return Py_None; + } else { + return wxPGVariant_out_helper(*sipCpp); + } + %End +}; + + + +// Add a typemap for wxVariantList +%MappedType wxPGVariantList +{ + %ConvertToTypeCode + // Code to test a PyObject for compatibility. + if (!sipIsErr) { + // Any type sequence type is okay. + int success = PySequence_Check(sipPy); + if (!success) + PyErr_SetString(PyExc_TypeError, "Sequence type expected."); + return success; + } + + // Code to create a new wxVariantList from the PyObject sequence + wxVariantList* value = new wxVariantList(); + Py_ssize_t len = PySequence_Length(sipPy); + Py_ssize_t idx = 0; + while (idx < len) { + PyObject* item = PySequence_GetItem(sipPy, idx); + value->Append(new wxVariant(wxPGVariant_in_helper(item))); + Py_DECREF(item); + } + *sipCppPtr = value; + return sipGetState(sipTransferObj); + %End + + + %ConvertFromTypeCode + // Code to convert a wxVariantList to a Python list. + if (sipCpp == NULL) { + return Py_None; + } else { + size_t idx = 0; + PyObject* value = PyList_New(0); + for (idx=0; idx < sipCpp->GetCount(); idx++) { + PyObject* item = wxPGVariant_out_helper(sipCpp->Item(idx)); + PyList_Append(value, item); + } + return value; + } + %End +}; + + + + + +// Used just for unit testing the MappedType code, it can be removed later +%ModuleCode +wxPGVariant testPGVariantTypemap(const wxPGVariant& var) +{ + wxVariant local = var; // force a copy + return local; +} + +wxString testPGVariantTypeName(const wxPGVariant& var) +{ + return var.GetType(); +} +%End +wxPGVariant testPGVariantTypemap(const wxPGVariant& var); +wxString testPGVariantTypeName(const wxPGVariant& var); diff --git a/unittests/test_pgvariant.py b/unittests/test_pgvariant.py new file mode 100644 index 00000000..daec0b27 --- /dev/null +++ b/unittests/test_pgvariant.py @@ -0,0 +1,82 @@ +import unittest +from unittests import wtc +import wx +import wx.propgrid as pg + +import six + +#--------------------------------------------------------------------------- + +class pgvariant_Tests(wtc.WidgetTestCase): + + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '') + def test_pgvariant1(self): + d1 = wx.Point(123,456) + d2 = pg.testPGVariantTypemap(d1) + assert isinstance(d2, wx.Point) + assert d1 == d2 + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '') + def test_pgvariant2(self): + d1 = wx.Point(123,456) + assert pg.testPGVariantTypeName(d1) == 'wxPoint' + + + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '') + def test_pgvariant3(self): + d1 = wx.Size(123,456) + d2 = pg.testPGVariantTypemap(d1) + assert isinstance(d2, wx.Size) + assert d1 == d2 + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '') + def test_pgvariant4(self): + d1 = wx.Size(123,456) + assert pg.testPGVariantTypeName(d1) == 'wxSize' + + + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '') + def test_pgvariant5(self): + d1 = wx.Font( wx.FontInfo(10).Bold().Underlined() ) + d2 = pg.testPGVariantTypemap(d1) + assert isinstance(d2, wx.Font) + assert d2.PointSize == 10 + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '') + def test_pgvariant6(self): + d1 = wx.Font( wx.FontInfo(10).Bold().Underlined() ) + assert pg.testPGVariantTypeName(d1) == 'wxFont' + + + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '') + def test_pgvariant7(self): + d1 = [0,1,2,3,4,5,6,7,8,9] + d2 = pg.testPGVariantTypemap(d1) + assert isinstance(d2, list) + assert d1 == d2 + + d1 = (123,456) + d2 = pg.testPGVariantTypemap(d1) + assert isinstance(d2, list) + assert list(d1) == d2 + + @unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '') + def test_pgvariant8(self): + d1 = [0,1,2,3,4,5,6,7,8,9] + assert pg.testPGVariantTypeName(d1) == 'wxArrayInt' + + d1 = (123,456) + assert pg.testPGVariantTypeName(d1) == 'wxArrayInt' + + + + + +#--------------------------------------------------------------------------- + +if __name__ == '__main__': + unittest.main()