Also allow numpy arrays for sequence conversions to value types

This commit is contained in:
Robin Dunn
2017-04-17 17:22:50 -07:00
parent 8e51e93edb
commit efa81882e3
5 changed files with 127 additions and 66 deletions

View File

@@ -198,17 +198,7 @@ def run():
return 1;
if (PyBytes_Check(sipPy) || PyUnicode_Check(sipPy))
return 1;
if (PyTuple_Check(sipPy) || PyList_Check(sipPy)) {
size_t len = PySequence_Size(sipPy);
if (len != 3 && len != 4)
return 0;
// ensure all the items in the sequence are numbers
for (int idx=0; idx<len; idx+=1) {
PyObject* o = PySequence_Fast_GET_ITEM(sipPy, idx);
bool isNum = PyNumber_Check(o);
if (!isNum)
return 0;
}
if (wxPyNumberSequenceCheck(sipPy, 4) || wxPyNumberSequenceCheck(sipPy, 3)) {
return 1;
}
return 0;
@@ -255,20 +245,24 @@ def run():
return sipGetState(sipTransferObj);
}
}
// Is it a 3 or 4 element sequence?
else if (PyTuple_Check(sipPy) || PyList_Check(sipPy)) {
// Is it a sequence? (if so then length was checked above)
else if (wxPyNumberSequenceCheck(sipPy)) {
size_t len = PySequence_Size(sipPy);
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o3 = PySequence_Fast_GET_ITEM(sipPy, 2);
PyObject* o1 = PySequence_ITEM(sipPy, 0);
PyObject* o2 = PySequence_ITEM(sipPy, 1);
PyObject* o3 = PySequence_ITEM(sipPy, 2);
if (len == 3)
*sipCppPtr = new wxColour(wxPyInt_AsLong(o1), wxPyInt_AsLong(o2), wxPyInt_AsLong(o3));
else {
PyObject* o4 = PySequence_Fast_GET_ITEM(sipPy, 3);
PyObject* o4 = PySequence_ITEM(sipPy, 3);
*sipCppPtr = new wxColour(wxPyInt_AsLong(o1), wxPyInt_AsLong(o2), wxPyInt_AsLong(o3),
wxPyInt_AsLong(o4));
Py_DECREF(o4);
}
Py_DECREF(o1);
Py_DECREF(o2);
Py_DECREF(o3);
return sipGetState(sipTransferObj);
}

View File

@@ -653,13 +653,8 @@ def convertTwoIntegersTemplate(CLASS):
if (sipCanConvertToType(sipPy, sipType_{CLASS}, SIP_NO_CONVERTORS))
return 1;
if ((PyTuple_Check(sipPy) || PyList_Check(sipPy)) && PySequence_Size(sipPy) == 2) {{
int rval = 1;
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
if (!PyNumber_Check(o1) || !PyNumber_Check(o2))
rval = 0;
return rval;
if (wxPyNumberSequenceCheck(sipPy, 2)) {{
return 1;
}}
return 0;
}}
@@ -673,9 +668,11 @@ def convertTwoIntegersTemplate(CLASS):
}}
// or create a new instance
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o1 = PySequence_ITEM(sipPy, 0);
PyObject* o2 = PySequence_ITEM(sipPy, 1);
*sipCppPtr = new {CLASS}(wxPyInt_AsLong(o1), wxPyInt_AsLong(o2));
Py_DECREF(o1);
Py_DECREF(o2);
return SIP_TEMPORARY;
""".format(**locals())
@@ -689,15 +686,8 @@ def convertFourIntegersTemplate(CLASS):
if (sipCanConvertToType(sipPy, sipType_{CLASS}, SIP_NO_CONVERTORS))
return 1;
if ((PyTuple_Check(sipPy) || PyList_Check(sipPy)) && PySequence_Size(sipPy) == 4) {{
int rval = 1;
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o3 = PySequence_Fast_GET_ITEM(sipPy, 2);
PyObject* o4 = PySequence_Fast_GET_ITEM(sipPy, 3);
if (!PyNumber_Check(o1) || !PyNumber_Check(o2) || !PyNumber_Check(o3) || !PyNumber_Check(o4))
rval = 0;
return rval;
if (wxPyNumberSequenceCheck(sipPy, 4)) {{
return 1;
}}
return 0;
}}
@@ -710,12 +700,16 @@ def convertFourIntegersTemplate(CLASS):
return 0; // not a new instance
}}
// or create a new instance
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o3 = PySequence_Fast_GET_ITEM(sipPy, 2);
PyObject* o4 = PySequence_Fast_GET_ITEM(sipPy, 3);
PyObject* o1 = PySequence_ITEM(sipPy, 0);
PyObject* o2 = PySequence_ITEM(sipPy, 1);
PyObject* o3 = PySequence_ITEM(sipPy, 2);
PyObject* o4 = PySequence_ITEM(sipPy, 3);
*sipCppPtr = new {CLASS}(wxPyInt_AsLong(o1), wxPyInt_AsLong(o2),
wxPyInt_AsLong(o3), wxPyInt_AsLong(o4));
Py_DECREF(o1);
Py_DECREF(o2);
Py_DECREF(o3);
Py_DECREF(o4);
return SIP_TEMPORARY;
""".format(**locals())
@@ -730,14 +724,9 @@ def convertTwoDoublesTemplate(CLASS):
if (sipCanConvertToType(sipPy, sipType_{CLASS}, SIP_NO_CONVERTORS))
return 1;
if ((PyTuple_Check(sipPy) || PyList_Check(sipPy)) && PySequence_Size(sipPy) == 2) {{
int rval = 1;
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
if (!PyNumber_Check(o1) || !PyNumber_Check(o2))
rval = 0;
return rval;
}}
if (wxPyNumberSequenceCheck(sipPy, 2)) {{
return 1;
}}
return 0;
}}
@@ -750,9 +739,11 @@ def convertTwoDoublesTemplate(CLASS):
}}
// or create a new instance
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o1 = PySequence_ITEM(sipPy, 0);
PyObject* o2 = PySequence_ITEM(sipPy, 1);
*sipCppPtr = new {CLASS}(PyFloat_AsDouble(o1), PyFloat_AsDouble(o2));
Py_DECREF(o1);
Py_DECREF(o2);
return SIP_TEMPORARY;
""".format(**locals())
@@ -766,15 +757,8 @@ def convertFourDoublesTemplate(CLASS):
if (sipCanConvertToType(sipPy, sipType_{CLASS}, SIP_NO_CONVERTORS))
return 1;
if ((PyTuple_Check(sipPy) || PyList_Check(sipPy)) && PySequence_Size(sipPy) == 4) {{
int rval = 1;
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o3 = PySequence_Fast_GET_ITEM(sipPy, 2);
PyObject* o4 = PySequence_Fast_GET_ITEM(sipPy, 3);
if (!PyNumber_Check(o1) || !PyNumber_Check(o2) || !PyNumber_Check(o3) || !PyNumber_Check(o4))
rval = 0;
return rval;
if (wxPyNumberSequenceCheck(sipPy, 4)) {{
return 1;
}}
return 0;
}}
@@ -788,12 +772,16 @@ def convertFourDoublesTemplate(CLASS):
}}
// or create a new instance
PyObject* o1 = PySequence_Fast_GET_ITEM(sipPy, 0);
PyObject* o2 = PySequence_Fast_GET_ITEM(sipPy, 1);
PyObject* o3 = PySequence_Fast_GET_ITEM(sipPy, 2);
PyObject* o4 = PySequence_Fast_GET_ITEM(sipPy, 3);
PyObject* o1 = PySequence_ITEM(sipPy, 0);
PyObject* o2 = PySequence_ITEM(sipPy, 1);
PyObject* o3 = PySequence_ITEM(sipPy, 2);
PyObject* o4 = PySequence_ITEM(sipPy, 3);
*sipCppPtr = new {CLASS}(PyFloat_AsDouble(o1), PyFloat_AsDouble(o2),
PyFloat_AsDouble(o3), PyFloat_AsDouble(o4));
Py_DECREF(o1);
Py_DECREF(o2);
Py_DECREF(o3);
Py_DECREF(o4);
return SIP_TEMPORARY;
""".format(**locals())

View File

@@ -150,6 +150,48 @@ Py_ssize_t wxPyUnicode_AsWideChar(PyObject* unicode, wchar_t* w, Py_ssize_t size
}
inline bool wxPyNumberSequenceCheck(PyObject* obj, int reqLength=-1) {
// Used in the various places where a sequence of numbers can be converted
// to a wx type, like wxPoint, wxSize, wxColour, etc. Returns true if the
// object is a Tuple, List or numpy Array of the proper length.
// tuples or lists are easy
bool isFast = (PyTuple_Check(obj) || PyList_Check(obj));
if (!isFast ) {
// If it's not one of those, then check for an array.
// It's probably not a good idea to do it this way, but this allows us
// to check if the object is a numpy array without requiring that
// numpy be imported even for those applications tha are not using it.
if (strcmp(obj->ob_type->tp_name, "numpy.ndarray") != 0)
return false;
}
// Bail out here if the length isn't given
if (reqLength == -1)
return true;
// Now check that the length matches the expected length
if (PySequence_Length(obj) != reqLength)
return false;
// Check that each item is a number
for (int i=0; i<reqLength; i+=1) {
PyObject* item;
if (isFast)
item = PySequence_Fast_GET_ITEM(obj, i);
else
item = PySequence_ITEM(obj, i);
bool isNum = PyNumber_Check(item);
if (!isFast)
Py_DECREF(item);
if (!isNum)
return false;
}
return true;
}
//--------------------------------------------------------------------------
// These are the API items whose implementation can not or should not be
// inline functions or macros. The implementations will instead be accessed

View File

@@ -30,6 +30,23 @@ class Colour(wtc.WidgetTestCase):
self.assertTrue(c1.Get() == c2.Get())
def test_seq_ctor1(self):
c = wx.Colour( [1,2,3,4] )
self.assertTrue(c.Get() == (1,2,3,4))
def test_seq_ctor2(self):
c = wx.Colour( [1,2,3] )
self.assertTrue(c.Get(False) == (1,2,3))
def test_numpy_ctor(self):
import numpy
a = numpy.array( [1,2,3,4] )
c = wx.Colour(a)
self.assertTrue(c.Get() == (1,2,3,4))
def test_GetPixel(self):
c1 = wx.Colour(1,2,3,4)
p = c1.GetPixel()

View File

@@ -31,6 +31,15 @@ class Point(unittest.TestCase):
with self.assertRaises(TypeError):
p = wx.Point(1,2,3)
def test_seq_ctor(self):
p = wx.Point( [123,456] )
def test_numpy_ctor(self):
import numpy
a = numpy.array([123,456])
p = wx.Point(a)
def test_DefaultPosition(self):
wx.DefaultPosition
self.assertTrue(wx.DefaultPosition == (-1,-1))
@@ -331,20 +340,31 @@ class Rect(unittest.TestCase):
self.assertTrue(r == wx.Rect(pos=(10,10), size=(100,100)))
def test_tlbr_ctor(self):
# TODO: we have to use keyword args here since wx.Point has sequence
# protocol methods then it can also match the typemap for wxSize and
# the other ctor is found first. Check if there is a way to fix or
# work around this.
r = wx.Rect(topLeft=wx.Point(10,10), bottomRight=wx.Point(100,100))
self.assertTrue(r.width == 91 and r.height == 91)
self.assertTrue(r.bottomRight == wx.Point(100,100))
self.assertTrue(r.topLeft == wx.Point(10,10))
def test_tlbr_ctor2(self):
r = wx.Rect(wx.Point(10,10), wx.Point(100,100))
self.assertTrue(r.width == 91 and r.height == 91)
self.assertTrue(r.bottomRight == wx.Point(100,100))
self.assertTrue(r.topLeft == wx.Point(10,10))
def test_size_ctor(self):
r = wx.Rect(wx.Size(50,100))
self.assertTrue(r.width == 50 and r.height == 100)
self.assertTrue(r.x == 0 and r.y == 0)
def test_seq_ctor(self):
r1 = wx.Rect( [1,2,3,4] )
self.assertTrue(r1 == (1,2,3,4))
def test_numpy_ctor(self):
import numpy
r1 = wx.Rect( numpy.array([1,2,3,4]) )
self.assertTrue(r1 == (1,2,3,4))
def test_GetIM(self):
# Test the immutable version returned by GetIM