diff --git a/etg/_core.py b/etg/_core.py index 769551fc..6e7287c5 100644 --- a/etg/_core.py +++ b/etg/_core.py @@ -29,9 +29,9 @@ etgtools.parseDoxyXML(module, ITEMS) # Tweak the parsed meta objects in the module object as needed for customizing # the generated code and docstrings. -import etgtools.tweaker_tools -etgtools.tweaker_tools.ignoreAssignmentOperators(module) -etgtools.tweaker_tools.removeWxPrefixes(module) +import etgtools.tweaker_tools as tools +tools.ignoreAssignmentOperators(module) +tools.removeWxPrefixes(module) # These items are in their own etg scripts for easier maintainability, diff --git a/etg/gdicmn.py b/etg/gdicmn.py index 6fc8968d..a1997074 100644 --- a/etg/gdicmn.py +++ b/etg/gdicmn.py @@ -32,9 +32,9 @@ etgtools.parseDoxyXML(module, ITEMS) # Tweak the parsed meta objects in the module object as needed for customizing # the generated code and docstrings. -import etgtools.tweaker_tools -etgtools.tweaker_tools.ignoreAssignmentOperators(module) -etgtools.tweaker_tools.removeWxPrefixes(module) +import etgtools.tweaker_tools as tools +tools.ignoreAssignmentOperators(module) +tools.removeWxPrefixes(module) module.addHeaderCode('#include ') @@ -73,7 +73,7 @@ c = module.find('wxPoint') # Some operators are documented within the class that shouldn't be, so just # ignore them all. -etgtools.tweaker_tools.ignoreAllOperators(c) +tools.ignoreAllOperators(c) # Undo a few of those ignores for legitimate items that were # documented correctly @@ -100,46 +100,12 @@ module.insertItemAfter(c, wc) # wxPoint typemap -c.convertFromPyObject = """\ - // is it just a typecheck? - if (!sipIsErr) { - if (sipCanConvertToType(sipPy, sipType_wxPoint, SIP_NO_CONVERTORS)) - return 1; +c.convertFromPyObject = tools.convertTwoIntegersTemplate('wxPoint') - if (PySequence_Check(sipPy) && PySequence_Size(sipPy) == 2) { - int rval = 1; - PyObject* o1 = PySequence_ITEM(sipPy, 0); - PyObject* o2 = PySequence_ITEM(sipPy, 1); - if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) - rval = 0; - Py_DECREF(o1); - Py_DECREF(o2); - return rval; - } - return 0; - } - - // otherwise do the conversion - if (PySequence_Check(sipPy)) { - PyObject* o1 = PySequence_ITEM(sipPy, 0); - PyObject* o2 = PySequence_ITEM(sipPy, 1); - *sipCppPtr = new wxPoint(PyInt_AsLong(o1), PyInt_AsLong(o2)); - Py_DECREF(o1); - Py_DECREF(o2); - return sipGetState(sipTransferObj); - } - *sipCppPtr = reinterpret_cast(sipConvertToType( - sipPy, sipType_wxPoint, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr)); - return 0; -""" - - -# Helper to convert a Point to a tuple c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ sipRes = sipBuildResult(&sipIsErr, "(ii)", sipCpp->x, sipCpp->y); """, briefDoc="""\ - Get() -> (x,y) - + Get() -> (x,y)\n Return the x and y properties as a tuple.""") # Add sequence protocol methods and other goodies @@ -168,7 +134,7 @@ c.addProperty("width GetWidth SetWidth") c.addProperty("height GetHeight SetHeight") # take care of the same issues as wxPoint -etgtools.tweaker_tools.ignoreAllOperators(c) +tools.ignoreAllOperators(c) for f in c.find('operator+=').all() + \ c.find('operator-=').all() + \ c.find('operator*=').all() + \ @@ -186,12 +152,38 @@ wxSize operator/(const wxSize& s, int i); module.insertItemAfter(c, wc) +# wxSize typemap +c.convertFromPyObject = tools.convertTwoIntegersTemplate('wxSize') + +c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ + sipRes = sipBuildResult(&sipIsErr, "(ii)", sipCpp->GetWidth(), sipCpp->GetHeight()); +""", briefDoc="""\ + Get() -> (width, height)\n + Return the width and height properties as a tuple.""") + +# Add sequence protocol methods and other goodies +c.addPyMethod('__str__', '(self)', 'return str(self.Get())') +c.addPyMethod('__repr__', '(self)', 'return "wx.Size"+str(self.Get())') +c.addPyMethod('__len__', '(self)', 'return len(self.Get())') +c.addPyMethod('__nonzero__', '(self)', 'return self.Get() != (0,0)') +c.addPyMethod('__reduce__', '(self)', 'return (Size, self.Get())') +c.addPyMethod('__getitem__', '(self, idx)', 'return self.Get()[idx]') +c.addPyMethod('__setitem__', '(self, idx, val)', + """\ + if idx == 0: self.width = val + elif idx == 1: self.height = val + else: raise IndexError + """) +c.addPyCode('Size.__safe_for_unpickling__ = True') + + + #--------------------------------------- # wxRect tweaks c = module.find('wxRect') # take care of the same issues as wxPoint -etgtools.tweaker_tools.ignoreAllOperators(c) +tools.ignoreAllOperators(c) for f in c.find('operator+=').all() + \ c.find('operator*=').all(): f.ignore(False) @@ -216,7 +208,7 @@ module.find('wxRect.Intersect').findOverload(') const').ignore() c = module.find('wxRealPoint') # take care of the same issues as wxPoint -etgtools.tweaker_tools.ignoreAllOperators(c) +tools.ignoreAllOperators(c) for f in c.find('operator+=').all() + \ c.find('operator-=').all(): f.ignore(False) diff --git a/etg/geometry.py b/etg/geometry.py index 0efc65ef..cbdf6576 100644 --- a/etg/geometry.py +++ b/etg/geometry.py @@ -30,9 +30,9 @@ etgtools.parseDoxyXML(module, ITEMS) # Tweak the parsed meta objects in the module object as needed for customizing # the generated code and docstrings. -import etgtools.tweaker_tools -etgtools.tweaker_tools.ignoreAssignmentOperators(module) -etgtools.tweaker_tools.removeWxPrefixes(module) +import etgtools.tweaker_tools as tools +tools.ignoreAssignmentOperators(module) +tools.removeWxPrefixes(module) module.addHeaderCode('#include ') diff --git a/etg/object.py b/etg/object.py index 24bb6b60..b83e97f0 100644 --- a/etg/object.py +++ b/etg/object.py @@ -31,9 +31,9 @@ etgtools.parseDoxyXML(module, ITEMS) # Tweak the parsed meta objects in the module object as needed for customizing # the generated code and docstrings. -import etgtools.tweaker_tools -etgtools.tweaker_tools.ignoreAssignmentOperators(module) -etgtools.tweaker_tools.removeWxPrefixes(module) +import etgtools.tweaker_tools as tools +tools.ignoreAssignmentOperators(module) +tools.removeWxPrefixes(module) module.find('wxObject.operator delete').ignore() diff --git a/etg/template.py b/etg/template.py index a6e404c7..fddfcbb6 100644 --- a/etg/template.py +++ b/etg/template.py @@ -27,9 +27,9 @@ etgtools.parseDoxyXML(module, ITEMS) # Tweak the parsed meta objects in the module object as needed for customizing # the generated code and docstrings. -import etgtools.tweaker_tools -etgtools.tweaker_tools.ignoreAssignmentOperators(module) -etgtools.tweaker_tools.removeWxPrefixes(module) +import etgtools.tweaker_tools as tools +tools.ignoreAssignmentOperators(module) +tools.removeWxPrefixes(module) diff --git a/etgtools/sip_generator.py b/etgtools/sip_generator.py index ebe40d14..2aaf7973 100644 --- a/etgtools/sip_generator.py +++ b/etgtools/sip_generator.py @@ -326,7 +326,7 @@ from %s import * stream.write(')') if prop.briefDoc: stream.write(' // %s' % prop.briefDoc) - stream.write('\n') + stream.write('\n\n') def generateMethod(self, method, stream, indent): diff --git a/etgtools/tweaker_tools.py b/etgtools/tweaker_tools.py index 84f4d1d3..b0f43671 100644 --- a/etgtools/tweaker_tools.py +++ b/etgtools/tweaker_tools.py @@ -54,3 +54,39 @@ def createPyArgsStrings(node): items that can be used as part of the docstring. """ pass + + +def convertTwoIntegersTemplate(CLASS): + return """\ + // is it just a typecheck? + if (!sipIsErr) {{ + if (sipCanConvertToType(sipPy, sipType_{CLASS}, SIP_NO_CONVERTORS)) + return 1; + + if (PySequence_Check(sipPy) && PySequence_Size(sipPy) == 2) {{ + int rval = 1; + PyObject* o1 = PySequence_ITEM(sipPy, 0); + PyObject* o2 = PySequence_ITEM(sipPy, 1); + if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) + rval = 0; + Py_DECREF(o1); + Py_DECREF(o2); + return rval; + }} + return 0; + }} + + // otherwise do the conversion + if (PySequence_Check(sipPy)) {{ + PyObject* o1 = PySequence_ITEM(sipPy, 0); + PyObject* o2 = PySequence_ITEM(sipPy, 1); + *sipCppPtr = new {CLASS}(PyInt_AsLong(o1), PyInt_AsLong(o2)); + Py_DECREF(o1); + Py_DECREF(o2); + return sipGetState(sipTransferObj); + }} + *sipCppPtr = reinterpret_cast<{CLASS}*>(sipConvertToType( + sipPy, sipType_{CLASS}, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr)); + return 0; + """.format(**locals()) + diff --git a/sip/gen/gdicmn.sip b/sip/gen/gdicmn.sip index 3ba0b7bb..b5be21a5 100644 --- a/sip/gen/gdicmn.sip +++ b/sip/gen/gdicmn.sip @@ -271,10 +271,105 @@ public: ); %Property(name=width, get=GetWidth, set=SetWidth) + %Property(name=height, get=GetHeight, set=SetHeight) + + SIP_PYOBJECT Get(); + %MethodCode + sipRes = sipBuildResult(&sipIsErr, "(ii)", sipCpp->GetWidth(), sipCpp->GetHeight()); + %End + + %ConvertToTypeCode + // is it just a typecheck? + if (!sipIsErr) { + if (sipCanConvertToType(sipPy, sipType_wxSize, SIP_NO_CONVERTORS)) + return 1; + + if (PySequence_Check(sipPy) && PySequence_Size(sipPy) == 2) { + int rval = 1; + PyObject* o1 = PySequence_ITEM(sipPy, 0); + PyObject* o2 = PySequence_ITEM(sipPy, 1); + if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) + rval = 0; + Py_DECREF(o1); + Py_DECREF(o2); + return rval; + } + return 0; + } + + // otherwise do the conversion + if (PySequence_Check(sipPy)) { + PyObject* o1 = PySequence_ITEM(sipPy, 0); + PyObject* o2 = PySequence_ITEM(sipPy, 1); + *sipCppPtr = new wxSize(PyInt_AsLong(o1), PyInt_AsLong(o2)); + Py_DECREF(o1); + Py_DECREF(o2); + return sipGetState(sipTransferObj); + } + *sipCppPtr = reinterpret_cast(sipConvertToType( + sipPy, sipType_wxSize, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr)); + return 0; + %End }; // end of class wxSize +%Extract pycode +def _Size___str__(self): + return str(self.Get()) +Size.__str__ = _Size___str__ + +%End + +%Extract pycode +def _Size___repr__(self): + return "wx.Size"+str(self.Get()) +Size.__repr__ = _Size___repr__ + +%End + +%Extract pycode +def _Size___len__(self): + return len(self.Get()) +Size.__len__ = _Size___len__ + +%End + +%Extract pycode +def _Size___nonzero__(self): + return self.Get() != (0,0) +Size.__nonzero__ = _Size___nonzero__ + +%End + +%Extract pycode +def _Size___reduce__(self): + return (Size, self.Get()) +Size.__reduce__ = _Size___reduce__ + +%End + +%Extract pycode +def _Size___getitem__(self, idx): + return self.Get()[idx] +Size.__getitem__ = _Size___getitem__ + +%End + +%Extract pycode +def _Size___setitem__(self, idx, val): + if idx == 0: self.width = val + elif idx == 1: self.height = val + else: raise IndexError +Size.__setitem__ = _Size___setitem__ + +%End + +%Extract pycode +Size.__safe_for_unpickling__ = True + +%End + bool operator==(const wxSize& s1, const wxSize& s2); bool operator!=(const wxSize& s1, const wxSize& s2); wxSize operator*(const wxSize& s, int i); diff --git a/unittests/test_gdicmn.py b/unittests/test_gdicmn.py index ba81ae46..fc5e2eee 100644 --- a/unittests/test_gdicmn.py +++ b/unittests/test_gdicmn.py @@ -1,6 +1,9 @@ import unittest2 import wx + +#--------------------------------------------------------------------------- + class Point(unittest2.TestCase): def test_default_ctor(self): @@ -80,8 +83,132 @@ class Point(unittest2.TestCase): with self.assertRaises(IndexError): p[2] +#--------------------------------------------------------------------------- +class Size(unittest2.TestCase): + + def test_default_ctor(self): + s = wx.Size() + self.assertTrue(s == (0,0)) + + def test_wh_ctor(self): + s = wx.Size(100,200) + + def test_copy_ctor(self): + s1 = wx.Size(100,200) + s2 = wx.Size(s1) + self.assertTrue(s1 is not s2) + self.assertTrue(s1 == s2) + + def test_bogus_ctor(self): + with self.assertRaises(TypeError): + s = wx.Size("aa", "bb") + + def test_DecBy(self): + s = wx.Size(100,100) + s.DecBy(wx.Point(5,5)) + self.assertTrue(s == (95,95)) + s.DecBy(wx.Size(5,5)) + self.assertTrue(s == (90,90)) + s.DecBy(5,5) + self.assertTrue(s == (85,85)) + s.DecBy(5) + self.assertTrue(s == (80,80)) + s.DecBy( (5,5) ) + self.assertTrue(s == (75,75)) + + + def test_IncBy(self): + s = wx.Size(50,50) + s.IncBy(wx.Point(5,5)) + self.assertTrue(s == (55,55)) + s.IncBy(wx.Size(5,5)) + self.assertTrue(s == (60,60)) + s.IncBy(5,5) + self.assertTrue(s == (65,65)) + s.IncBy(5) + self.assertTrue(s == (70,70)) + s.IncBy( (5,5) ) + self.assertTrue(s == (75,75)) + + def test_DecTo(self): + s = wx.Size(5, 15) + s.DecTo( (10,10) ) + self.assertTrue(s == (5,10)) + + def test_IncTo(self): + s = wx.Size(5, 15) + s.IncTo( (10,10) ) + self.assertTrue(s == (10,15)) + + def test_properties(self): + s = wx.Size(23,34) + self.assertTrue(s.width == 23 and s.height == 34) + s.width += 1 + s.height += 1 + self.assertTrue(s == (24,35)) + + def test_operators(self): + s1 = wx.Size(100,200) + s2 = wx.Size(30,40) + s1 += s2 + s1 -= s2 + s1 *= 5 + s1 /= 5 + s1 == s2 + s1 != s2 + s = s1 * 5 + s = 5 * s1 + s = s1 + s2 + s = s1 - s2 + s = s1 / 5 + + def test_GetSet(self): + s = wx.Size(100,200) + t = s.Get() + self.assertTrue(type(t) == tuple) + self.assertTrue(t == (100,200)) + s.Set(5,10) + self.assertTrue(s.Get() == (5,10)) + + def test_SetDefaults(self): + s = wx.Size(50, -1) + s.SetDefaults( (25,25) ) + self.assertTrue(s == (50,25)) + + def test_FullySpecified(self): + self.assertTrue(wx.Size(40,50).IsFullySpecified()) + self.assertTrue(not wx.Size(-1,50).IsFullySpecified()) + self.assertTrue(not wx.Size(40,-1).IsFullySpecified()) + + def test_magic(self): + s = wx.Size(5,6) + self.assertTrue(str(s) == "(5, 6)") + self.assertTrue(repr(s) == "wx.Size(5, 6)") + self.assertTrue(len(s) == 2) + w, h = s + self.assertTrue(5 == 5 and h == 6) + s[0] += 1 # tests both getitem and setitem + s[1] += 2 + self.assertTrue(s == (6,8)) + with self.assertRaises(IndexError): + s[2] + + + + + + + + + + + + + +#--------------------------------------------------------------------------- + if __name__ == '__main__': unittest2.main() \ No newline at end of file