Manipulator: Add API target_get/set/range wrappers
authorCampbell Barton <ideasman42@gmail.com>
Tue, 22 Aug 2017 08:55:19 +0000 (18:55 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 22 Aug 2017 08:57:38 +0000 (18:57 +1000)
Allows Python manipulators access the values of target properties
needed for Python to make use of the general target property interface.

release/scripts/modules/bpy_types.py
source/blender/makesrna/intern/rna_wm_manipulator_api.c
source/blender/python/intern/bpy_rna_manipulator.c

index 407f0a35fb3a165e55943d7ae5634cc17fb987f5..78c70225a04ba1ad4d07cbd2d464c1ed74e2dae0 100644 (file)
@@ -618,7 +618,12 @@ class Manipulator(StructRNA, metaclass=OrderedMeta):
             return delattr(properties, attr)
         return super().__delattr__(attr)
 
-    target_set_handler = _bpy._rna_manipulator_target_set_handler
+    from _bpy import (
+        _rna_manipulator_target_set_handler as target_set_handler,
+        _rna_manipulator_target_get_value as target_get_value,
+        _rna_manipulator_target_set_value as target_set_value,
+        _rna_manipulator_target_get_range as target_get_range,
+    )
 
     # Convenience wrappers around private `_gawain` module.
     def draw_custom_shape(self, shape, *, matrix=None, select_id=None):
index f40a2208cf957fe7e00400c735740ffd4fbe7414..a0d474968d00a61126d419217aae561e327b3707 100644 (file)
@@ -71,6 +71,10 @@ static void rna_manipulator_draw_preset_facemap(
        ED_manipulator_draw_preset_facemap(C, mpr, scene, ob, facemap, select_id);
 }
 
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Property Define
+ * \{ */
+
 static void rna_manipulator_target_set_prop(
         wmManipulator *mpr, ReportList *reports, const char *target_propname,
         PointerRNA *ptr, const char *propname, int index)
@@ -158,6 +162,27 @@ static PointerRNA rna_manipulator_target_set_operator(
        return mpr->op_data.ptr;
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Property Access
+ * \{ */
+
+static int rna_manipulator_target_is_valid(
+        wmManipulator *mpr, ReportList *reports, const char *target_propname)
+{
+       wmManipulatorProperty *mpr_prop =
+               WM_manipulator_target_property_find(mpr, target_propname);
+       if (mpr_prop == NULL) {
+               BKE_reportf(reports, RPT_ERROR, "Manipulator target property '%s.%s' not found",
+                           mpr->type->idname, target_propname);
+               return false;
+       }
+       return WM_manipulator_target_property_is_valid(mpr_prop);
+}
+
+/** \} */
+
 #else
 
 void RNA_api_manipulator(StructRNA *srna)
@@ -218,6 +243,7 @@ void RNA_api_manipulator(StructRNA *srna)
        /* -------------------------------------------------------------------- */
        /* Property API */
 
+       /* Define Properties */
        /* note, 'target_set_handler' is defined in 'bpy_rna_manipulator.c' */
        func = RNA_def_function(srna, "target_set_prop", "rna_manipulator_target_set_prop");
        RNA_def_function_flag(func, FUNC_USE_REPORTS);
@@ -243,6 +269,16 @@ void RNA_api_manipulator(StructRNA *srna)
        RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR);
        RNA_def_function_return(func, parm);
 
+       /* Access Properties */
+       /* note, 'target_get', 'target_set' is defined in 'bpy_rna_manipulator.c' */
+       func = RNA_def_function(srna, "target_is_valid", "rna_manipulator_target_is_valid");
+       RNA_def_function_flag(func, FUNC_USE_REPORTS);
+       parm = RNA_def_string(func, "property", NULL, 0, "", "Property identifier");
+       RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+       RNA_def_function_ui_description(func, "");
+       parm = RNA_def_boolean(func, "result", 0, "", "");
+       RNA_def_function_return(func, parm);
+
 }
 
 
index 7543f53cf2031db44e5a3ffde0607f96518f4511..01a606e405a2c778b37c5277886918201379035a 100644 (file)
@@ -30,6 +30,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_alloca.h"
 
 #include "BKE_main.h"
 
 
 #include "bpy_rna.h"
 
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Target Property Define API
+ * \{ */
+
 enum {
        BPY_MANIPULATOR_FN_SLOT_GET = 0,
        BPY_MANIPULATOR_FN_SLOT_SET,
@@ -75,7 +81,7 @@ static void py_rna_manipulator_handler_get_cb(
        if (mpr_prop->type->data_type == PROP_FLOAT) {
                float *value = value_p;
                if (mpr_prop->type->array_length == 1) {
-                       if (((*value = PyFloat_AsDouble(ret)) == -1.0f && PyErr_Occurred()) == 0) {
+                       if ((*value = PyFloat_AsDouble(ret)) == -1.0f && PyErr_Occurred()) {
                                goto fail;
                        }
                }
@@ -333,20 +339,275 @@ fail:
        return NULL;
 }
 
-int BPY_rna_manipulator_module(PyObject *mod_par)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Target Property Access API
+ * \{ */
+
+PyDoc_STRVAR(bpy_manipulator_target_get_value_doc,
+".. method:: target_get_value(target):\n"
+"\n"
+"   Get the value of this target property.\n"
+"\n"
+"   :arg target: Target property name.\n"
+"   :type target: string\n"
+"   :return: The value of the target property.\n"
+"   :rtype: Single value or array based on the target type\n"
+);
+static PyObject *bpy_manipulator_target_get_value(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
 {
-       static PyMethodDef method_def = {
-           "target_set_handler", (PyCFunction)bpy_manipulator_target_set_handler, METH_VARARGS | METH_KEYWORDS,
-           bpy_manipulator_target_set_handler_doc};
+       struct {
+               PyObject *self;
+               char *target;
+       } params = {
+               .self = NULL,
+               .target = NULL,
+       };
 
-       PyObject *func = PyCFunction_New(&method_def, NULL);
-       PyObject *func_inst = PyInstanceMethod_New(func);
+       static const char * const _keywords[] = {"self", "target", NULL};
+#define KW_FMT "Os:target_get_value"
+#if PY_VERSION_HEX >= 0x03070000
+       static _PyArg_Parser _parser = {KW_FMT, _keywords, 0};
+       if (!_PyArg_ParseTupleAndKeywordsFast(
+               args, kwds,
+               &_parser,
+               &params.self,
+               &params.target))
+#else
+       if (!PyArg_ParseTupleAndKeywords(
+               args, kwds,
+               KW_FMT, (char **)_keywords,
+               &params.self,
+               &params.target))
+#endif
+       {
+               goto fail;
+       }
+#undef KW_FMT
 
+       wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
 
-       /* TODO, return a type that binds nearly to a method. */
-       PyModule_AddObject(mod_par, "_rna_manipulator_target_set_handler", func_inst);
+       wmManipulatorProperty *mpr_prop =
+               WM_manipulator_target_property_find(mpr, params.target);
+       if (mpr_prop == NULL) {
+               PyErr_Format(PyExc_ValueError,
+                            "Manipulator target property '%s.%s' not found",
+                            mpr->type->idname, params.target);
+               goto fail;
+       }
 
-       return 0;
+       const int array_len = WM_manipulator_target_property_array_length(mpr, mpr_prop);
+       switch (mpr_prop->type->data_type) {
+               case PROP_FLOAT:
+               {
+                       if (array_len != 0) {
+                               float *value = BLI_array_alloca(value, array_len);
+                               WM_manipulator_target_property_value_get_array(mpr, mpr_prop, value);
+                               return PyC_Tuple_PackArray_F32(value, array_len);
+                       }
+                       else {
+                               float value = WM_manipulator_target_property_value_get(mpr, mpr_prop);
+                               return PyFloat_FromDouble(value);
+                       }
+                       break;
+               }
+               default:
+               {
+                       PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+                       goto fail;
+               }
+       }
+
+fail:
+       return NULL;
+}
+
+PyDoc_STRVAR(bpy_manipulator_target_set_value_doc,
+".. method:: target_set_value(target):\n"
+"\n"
+"   Set the value of this target property.\n"
+"\n"
+"   :arg target: Target property name.\n"
+"   :type target: string\n"
+);
+static PyObject *bpy_manipulator_target_set_value(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
+{
+       struct {
+               PyObject *self;
+               char *target;
+               PyObject *value;
+       } params = {
+               .self = NULL,
+               .target = NULL,
+               .value = NULL,
+       };
+
+       static const char * const _keywords[] = {"self", "target", "value", NULL};
+#define KW_FMT "OsO:target_set_value"
+#if PY_VERSION_HEX >= 0x03070000
+       static _PyArg_Parser _parser = {KW_FMT, _keywords, 0};
+       if (!_PyArg_ParseTupleAndKeywordsFast(
+               args, kwds,
+               &_parser,
+               &params.self,
+               &params.target,
+               &params.value))
+#else
+       if (!PyArg_ParseTupleAndKeywords(
+               args, kwds,
+               KW_FMT, (char **)_keywords,
+               &params.self,
+               &params.target,
+               &params.value))
+#endif
+       {
+               goto fail;
+       }
+#undef KW_FMT
+
+       wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
+
+       wmManipulatorProperty *mpr_prop =
+               WM_manipulator_target_property_find(mpr, params.target);
+       if (mpr_prop == NULL) {
+               PyErr_Format(PyExc_ValueError,
+                            "Manipulator target property '%s.%s' not found",
+                            mpr->type->idname, params.target);
+               goto fail;
+       }
+
+       const int array_len = WM_manipulator_target_property_array_length(mpr, mpr_prop);
+       switch (mpr_prop->type->data_type) {
+               case PROP_FLOAT:
+               {
+                       if (array_len != 0) {
+                               float *value = BLI_array_alloca(value, array_len);
+                               if (PyC_AsArray(value, params.value, mpr_prop->type->array_length, &PyFloat_Type, false,
+                                               "Manipulator target property array") == -1)
+                               {
+                                       goto fail;
+                               }
+                               WM_manipulator_target_property_value_set_array(BPy_GetContext(), mpr, mpr_prop, value);
+                       }
+                       else {
+                               float value;
+                               if ((value = PyFloat_AsDouble(params.value)) == -1.0f && PyErr_Occurred()) {
+                                       goto fail;
+                               }
+                               WM_manipulator_target_property_value_set(BPy_GetContext(), mpr, mpr_prop, value);
+                       }
+                       Py_RETURN_NONE;
+               }
+               default:
+               {
+                       PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+                       goto fail;
+               }
+       }
+
+fail:
+       return NULL;
 }
 
 
+PyDoc_STRVAR(bpy_manipulator_target_get_range_doc,
+".. method:: target_get_range(target):\n"
+"\n"
+"   Get the range for this target property.\n"
+"\n"
+"   :arg target: Target property name.\n"
+"   :Get the range for this target property"
+"   :return: The range of this property (min, max).\n"
+"   :rtype: tuple pair.\n"
+);
+static PyObject *bpy_manipulator_target_get_range(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
+{
+       struct {
+               PyObject *self;
+               char *target;
+       } params = {
+               .self = NULL,
+               .target = NULL,
+       };
+
+       static const char * const _keywords[] = {"self", "target", NULL};
+#define KW_FMT "Os:target_get_range"
+#if PY_VERSION_HEX >= 0x03070000
+       static _PyArg_Parser _parser = {KW_FMT, _keywords, 0};
+       if (!_PyArg_ParseTupleAndKeywordsFast(
+               args, kwds,
+               &_parser,
+               &params.self,
+               &params.target))
+#else
+       if (!PyArg_ParseTupleAndKeywords(
+               args, kwds,
+               KW_FMT, (char **)_keywords,
+               &params.self,
+               &params.target))
+#endif
+       {
+               goto fail;
+       }
+#undef KW_FMT
+
+       wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
+
+       wmManipulatorProperty *mpr_prop =
+               WM_manipulator_target_property_find(mpr, params.target);
+       if (mpr_prop == NULL) {
+               PyErr_Format(PyExc_ValueError,
+                            "Manipulator target property '%s.%s' not found",
+                            mpr->type->idname, params.target);
+               goto fail;
+       }
+
+       switch (mpr_prop->type->data_type) {
+               case PROP_FLOAT:
+               {
+                       float range[2];
+                       WM_manipulator_target_property_range_get(mpr, mpr_prop, range);
+                       return PyC_Tuple_PackArray_F32(range, 2);
+               }
+               default:
+               {
+                       PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+                       goto fail;
+               }
+       }
+
+fail:
+       return NULL;
+}
+
+/** \} */
+
+int BPY_rna_manipulator_module(PyObject *mod_par)
+{
+       static PyMethodDef method_def_array[] = {
+               /* Manipulator Target Property Define API */
+               {"target_set_handler", (PyCFunction)bpy_manipulator_target_set_handler,
+                METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_set_handler_doc},
+               /* Manipulator Target Property Access API */
+               {"target_get_value", (PyCFunction)bpy_manipulator_target_get_value,
+                METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_get_value_doc},
+               {"target_set_value", (PyCFunction)bpy_manipulator_target_set_value,
+                METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_set_value_doc},
+               {"target_get_range", (PyCFunction)bpy_manipulator_target_get_range,
+                METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_get_range_doc},
+               /* no sentinel needed. */
+       };
+
+       for (int i = 0; i < ARRAY_SIZE(method_def_array); i++) {
+               PyMethodDef *m = &method_def_array[i];
+               PyObject *func = PyCFunction_New(m, NULL);
+               PyObject *func_inst = PyInstanceMethod_New(func);
+               char name_prefix[128];
+               PyOS_snprintf(name_prefix, sizeof(name_prefix), "_rna_manipulator_%s", m->ml_name);
+               /* TODO, return a type that binds nearly to a method. */
+               PyModule_AddObject(mod_par, name_prefix, func_inst);
+       }
+
+       return 0;
+}