Blender  V2.93
bpy_rna_gizmo.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
23 #include <Python.h>
24 #include <stddef.h>
25 
26 #include "MEM_guardedalloc.h"
27 
28 #include "BLI_alloca.h"
29 #include "BLI_utildefines.h"
30 
31 #include "WM_api.h"
32 #include "WM_types.h"
33 
34 #include "bpy_capi_utils.h"
35 #include "bpy_rna_gizmo.h"
36 
37 #include "../generic/py_capi_utils.h"
38 #include "../generic/python_utildefines.h"
39 
40 #include "RNA_access.h"
41 #include "RNA_enum_types.h"
42 #include "RNA_types.h"
43 
44 #include "bpy_rna.h"
45 
46 /* -------------------------------------------------------------------- */
50 enum {
54 };
55 #define BPY_GIZMO_FN_SLOT_LEN (BPY_GIZMO_FN_SLOT_RANGE_GET + 1)
56 
58 
60 };
61 
63  wmGizmoProperty *gz_prop,
64  void *value_p)
65 {
66  const PyGILState_STATE gilstate = PyGILState_Ensure();
67 
69  PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_GET], NULL);
70  if (ret == NULL) {
71  goto fail;
72  }
73 
74  if (gz_prop->type->data_type == PROP_FLOAT) {
75  float *value = value_p;
76  if (gz_prop->type->array_length == 1) {
77  if ((*value = PyFloat_AsDouble(ret)) == -1.0f && PyErr_Occurred()) {
78  goto fail;
79  }
80  }
81  else {
82  if (PyC_AsArray(value,
83  ret,
84  gz_prop->type->array_length,
85  &PyFloat_Type,
86  false,
87  "Gizmo get callback: ") == -1) {
88  goto fail;
89  }
90  }
91  }
92  else {
93  PyErr_SetString(PyExc_AttributeError, "internal error, unsupported type");
94  goto fail;
95  }
96 
97  Py_DECREF(ret);
98 
99  PyGILState_Release(gilstate);
100  return;
101 
102 fail:
103  PyErr_Print();
104  PyErr_Clear();
105 
106  Py_XDECREF(ret);
107 
108  PyGILState_Release(gilstate);
109 }
110 
112  wmGizmoProperty *gz_prop,
113  const void *value_p)
114 {
115  const PyGILState_STATE gilstate = PyGILState_Ensure();
116 
118 
119  PyObject *args = PyTuple_New(1);
120 
121  if (gz_prop->type->data_type == PROP_FLOAT) {
122  const float *value = value_p;
123  PyObject *py_value;
124  if (gz_prop->type->array_length == 1) {
125  py_value = PyFloat_FromDouble(*value);
126  }
127  else {
128  py_value = PyC_Tuple_PackArray_F32(value, gz_prop->type->array_length);
129  }
130  if (py_value == NULL) {
131  goto fail;
132  }
133  PyTuple_SET_ITEM(args, 0, py_value);
134  }
135  else {
136  PyErr_SetString(PyExc_AttributeError, "internal error, unsupported type");
137  goto fail;
138  }
139 
140  PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_SET], args);
141  if (ret == NULL) {
142  goto fail;
143  }
144  Py_DECREF(args);
145  Py_DECREF(ret);
146 
147  PyGILState_Release(gilstate);
148  return;
149 
150 fail:
151  PyErr_Print();
152  PyErr_Clear();
153 
154  Py_DECREF(args);
155 
156  PyGILState_Release(gilstate);
157 }
158 
160  wmGizmoProperty *gz_prop,
161  void *value_p)
162 {
164 
165  const PyGILState_STATE gilstate = PyGILState_Ensure();
166 
167  PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET], NULL);
168  if (ret == NULL) {
169  goto fail;
170  }
171 
172  if (!PyTuple_Check(ret)) {
173  PyErr_Format(PyExc_TypeError, "Expected a tuple, not %.200s", Py_TYPE(ret)->tp_name);
174  goto fail;
175  }
176 
177  if (PyTuple_GET_SIZE(ret) != 2) {
178  PyErr_Format(PyExc_TypeError, "Expected a tuple of size 2, not %d", PyTuple_GET_SIZE(ret));
179  goto fail;
180  }
181 
182  if (gz_prop->type->data_type == PROP_FLOAT) {
183  float range[2];
184  for (int i = 0; i < 2; i++) {
185  if (((range[i] = PyFloat_AsDouble(PyTuple_GET_ITEM(ret, i))) == -1.0f && PyErr_Occurred()) ==
186  0) {
187  /* pass */
188  }
189  else {
190  goto fail;
191  }
192  }
193  memcpy(value_p, range, sizeof(range));
194  }
195  else {
196  PyErr_SetString(PyExc_AttributeError, "internal error, unsupported type");
197  goto fail;
198  }
199 
200  Py_DECREF(ret);
201  PyGILState_Release(gilstate);
202  return;
203 
204 fail:
205  PyErr_Print();
206  PyErr_Clear();
207 
208  Py_XDECREF(ret);
209 
210  PyGILState_Release(gilstate);
211 }
212 
214 {
216 
217  const PyGILState_STATE gilstate = PyGILState_Ensure();
218  for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
219  Py_XDECREF(data->fn_slots[i]);
220  }
221  PyGILState_Release(gilstate);
222 
223  MEM_freeN(data);
224 }
225 
227  bpy_gizmo_target_set_handler_doc,
228  ".. method:: target_set_handler(target, get, set, range=None):\n"
229  "\n"
230  " Assigns callbacks to a gizmos property.\n"
231  "\n"
232  " :arg get: Function that returns the value for this property (single value or sequence).\n"
233  " :type get: callable\n"
234  " :arg set: Function that takes a single value argument and applies it.\n"
235  " :type set: callable\n"
236  " :arg range: Function that returns a (min, max) tuple for gizmos that use a range.\n"
237  " :type range: callable\n");
238 static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
239 {
240  const PyGILState_STATE gilstate = PyGILState_Ensure();
241 
242  struct {
243  PyObject *self;
244  char *target;
245  PyObject *py_fn_slots[BPY_GIZMO_FN_SLOT_LEN];
246  } params = {
247  .self = NULL,
248  .target = NULL,
249  .py_fn_slots = {NULL},
250  };
251 
252  /* Note: this is a counter-part to functions:
253  * 'Gizmo.target_set_prop & target_set_operator'
254  * (see: rna_wm_gizmo_api.c). conventions should match. */
255  static const char *const _keywords[] = {"self", "target", "get", "set", "range", NULL};
256  static _PyArg_Parser _parser = {"Os|$OOO:target_set_handler", _keywords, 0};
257  if (!_PyArg_ParseTupleAndKeywordsFast(args,
258  kw,
259  &_parser,
260  &params.self,
261  &params.target,
262  &params.py_fn_slots[BPY_GIZMO_FN_SLOT_GET],
263  &params.py_fn_slots[BPY_GIZMO_FN_SLOT_SET],
264  &params.py_fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET])) {
265  goto fail;
266  }
267 
268  wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
269 
271  params.target);
272  if (gz_prop_type == NULL) {
273  PyErr_Format(PyExc_ValueError,
274  "Gizmo target property '%s.%s' not found",
275  gz->type->idname,
276  params.target);
277  goto fail;
278  }
279 
280  {
281  const int slots_required = 2;
282  const int slots_start = 2;
283  for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
284  if (params.py_fn_slots[i] == NULL) {
285  if (i < slots_required) {
286  PyErr_Format(PyExc_ValueError, "Argument '%s' not given", _keywords[slots_start + i]);
287  goto fail;
288  }
289  }
290  else if (!PyCallable_Check(params.py_fn_slots[i])) {
291  PyErr_Format(PyExc_ValueError, "Argument '%s' not callable", _keywords[slots_start + i]);
292  goto fail;
293  }
294  }
295  }
296 
297  struct BPyGizmoHandlerUserData *data = MEM_callocN(sizeof(*data), __func__);
298 
299  for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
300  data->fn_slots[i] = params.py_fn_slots[i];
301  Py_XINCREF(params.py_fn_slots[i]);
302  }
303 
305  gz_prop_type,
306  &(const struct wmGizmoPropertyFnParams){
307  .value_get_fn = py_rna_gizmo_handler_get_cb,
308  .value_set_fn = py_rna_gizmo_handler_set_cb,
309  .range_get_fn = py_rna_gizmo_handler_range_get_cb,
310  .free_fn = py_rna_gizmo_handler_free_cb,
311  .user_data = data,
312  });
313 
314  PyGILState_Release(gilstate);
315 
316  Py_RETURN_NONE;
317 
318 fail:
319  PyGILState_Release(gilstate);
320  return NULL;
321 }
322 
325 /* -------------------------------------------------------------------- */
329 PyDoc_STRVAR(bpy_gizmo_target_get_value_doc,
330  ".. method:: target_get_value(target):\n"
331  "\n"
332  " Get the value of this target property.\n"
333  "\n"
334  " :arg target: Target property name.\n"
335  " :type target: string\n"
336  " :return: The value of the target property.\n"
337  " :rtype: Single value or array based on the target type\n");
338 static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
339 {
340  struct {
341  PyObject *self;
342  char *target;
343  } params = {
344  .self = NULL,
345  .target = NULL,
346  };
347 
348  static const char *const _keywords[] = {"self", "target", NULL};
349  static _PyArg_Parser _parser = {"Os:target_get_value", _keywords, 0};
350  if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &params.self, &params.target)) {
351  goto fail;
352  }
353 
354  wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
355 
357  if (gz_prop == NULL) {
358  PyErr_Format(PyExc_ValueError,
359  "Gizmo target property '%s.%s' not found",
360  gz->type->idname,
361  params.target);
362  goto fail;
363  }
364 
365  const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop);
366  switch (gz_prop->type->data_type) {
367  case PROP_FLOAT: {
368  if (array_len != 0) {
369  float *value = BLI_array_alloca(value, array_len);
370  WM_gizmo_target_property_float_get_array(gz, gz_prop, value);
371  return PyC_Tuple_PackArray_F32(value, array_len);
372  }
373 
374  const float value = WM_gizmo_target_property_float_get(gz, gz_prop);
375  return PyFloat_FromDouble(value);
376 
377  break;
378  }
379  default: {
380  PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
381  goto fail;
382  }
383  }
384 
385 fail:
386  return NULL;
387 }
388 
389 PyDoc_STRVAR(bpy_gizmo_target_set_value_doc,
390  ".. method:: target_set_value(target):\n"
391  "\n"
392  " Set the value of this target property.\n"
393  "\n"
394  " :arg target: Target property name.\n"
395  " :type target: string\n");
396 static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
397 {
398  struct {
399  PyObject *self;
400  char *target;
401  PyObject *value;
402  } params = {
403  .self = NULL,
404  .target = NULL,
405  .value = NULL,
406  };
407 
408  static const char *const _keywords[] = {"self", "target", "value", NULL};
409  static _PyArg_Parser _parser = {"OsO:target_set_value", _keywords, 0};
410  if (!_PyArg_ParseTupleAndKeywordsFast(
411  args, kw, &_parser, &params.self, &params.target, &params.value)) {
412  goto fail;
413  }
414 
415  wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
416 
418  if (gz_prop == NULL) {
419  PyErr_Format(PyExc_ValueError,
420  "Gizmo target property '%s.%s' not found",
421  gz->type->idname,
422  params.target);
423  goto fail;
424  }
425 
426  const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop);
427  switch (gz_prop->type->data_type) {
428  case PROP_FLOAT: {
429  if (array_len != 0) {
430  float *value = BLI_array_alloca(value, array_len);
431  if (PyC_AsArray(value,
432  params.value,
433  gz_prop->type->array_length,
434  &PyFloat_Type,
435  false,
436  "Gizmo target property array") == -1) {
437  goto fail;
438  }
440  }
441  else {
442  float value;
443  if ((value = PyFloat_AsDouble(params.value)) == -1.0f && PyErr_Occurred()) {
444  goto fail;
445  }
447  }
448  Py_RETURN_NONE;
449  }
450  default: {
451  PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
452  goto fail;
453  }
454  }
455 
456 fail:
457  return NULL;
458 }
459 
460 PyDoc_STRVAR(bpy_gizmo_target_get_range_doc,
461  ".. method:: target_get_range(target):\n"
462  "\n"
463  " Get the range for this target property.\n"
464  "\n"
465  " :arg target: Target property name.\n"
466  " :return: The range of this property (min, max).\n"
467  " :rtype: tuple pair.\n");
468 static PyObject *bpy_gizmo_target_get_range(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
469 {
470  struct {
471  PyObject *self;
472  char *target;
473  } params = {
474  .self = NULL,
475  .target = NULL,
476  };
477 
478  static const char *const _keywords[] = {"self", "target", NULL};
479  static _PyArg_Parser _parser = {"Os:target_get_range", _keywords, 0};
480  if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &params.self, &params.target)) {
481  goto fail;
482  }
483 
484  wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
485 
487  if (gz_prop == NULL) {
488  PyErr_Format(PyExc_ValueError,
489  "Gizmo target property '%s.%s' not found",
490  gz->type->idname,
491  params.target);
492  goto fail;
493  }
494 
495  switch (gz_prop->type->data_type) {
496  case PROP_FLOAT: {
497  float range[2];
498  WM_gizmo_target_property_float_range_get(gz, gz_prop, range);
499  return PyC_Tuple_PackArray_F32(range, 2);
500  }
501  default: {
502  PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
503  goto fail;
504  }
505  }
506 
507 fail:
508  return NULL;
509 }
510 
513 bool BPY_rna_gizmo_module(PyObject *mod_par)
514 {
515  static PyMethodDef method_def_array[] = {
516  /* Gizmo Target Property Define API */
517  {"target_set_handler",
518  (PyCFunction)bpy_gizmo_target_set_handler,
519  METH_VARARGS | METH_KEYWORDS,
520  bpy_gizmo_target_set_handler_doc},
521  /* Gizmo Target Property Access API */
522  {"target_get_value",
523  (PyCFunction)bpy_gizmo_target_get_value,
524  METH_VARARGS | METH_KEYWORDS,
525  bpy_gizmo_target_get_value_doc},
526  {"target_set_value",
527  (PyCFunction)bpy_gizmo_target_set_value,
528  METH_VARARGS | METH_KEYWORDS,
529  bpy_gizmo_target_set_value_doc},
530  {"target_get_range",
531  (PyCFunction)bpy_gizmo_target_get_range,
532  METH_VARARGS | METH_KEYWORDS,
533  bpy_gizmo_target_get_range_doc},
534  /* no sentinel needed. */
535  };
536 
537  for (int i = 0; i < ARRAY_SIZE(method_def_array); i++) {
538  PyMethodDef *m = &method_def_array[i];
539  PyObject *func = PyCFunction_New(m, NULL);
540  PyObject *func_inst = PyInstanceMethod_New(func);
541  char name_prefix[128];
542  PyOS_snprintf(name_prefix, sizeof(name_prefix), "_rna_gizmo_%s", m->ml_name);
543  /* TODO, return a type that binds nearly to a method. */
544  PyModule_AddObject(mod_par, name_prefix, func_inst);
545  }
546 
547  return false;
548 }
#define BLI_array_alloca(arr, realsize)
Definition: BLI_alloca.h:36
#define ARRAY_SIZE(arr)
#define UNUSED(x)
Read Guarded memory(de)allocation.
@ PROP_FLOAT
Definition: RNA_types.h:75
struct bContext * BPY_context_get(void)
@ BPY_GIZMO_FN_SLOT_GET
Definition: bpy_rna_gizmo.c:51
@ BPY_GIZMO_FN_SLOT_RANGE_GET
Definition: bpy_rna_gizmo.c:53
@ BPY_GIZMO_FN_SLOT_SET
Definition: bpy_rna_gizmo.c:52
static PyObject * bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
static void py_rna_gizmo_handler_range_get_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, void *value_p)
bool BPY_rna_gizmo_module(PyObject *mod_par)
static void py_rna_gizmo_handler_set_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, const void *value_p)
static PyObject * bpy_gizmo_target_get_range(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
static PyObject * bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
#define BPY_GIZMO_FN_SLOT_LEN
Definition: bpy_rna_gizmo.c:55
static PyObject * bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
static void py_rna_gizmo_handler_get_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, void *value_p)
Definition: bpy_rna_gizmo.c:62
PyDoc_STRVAR(bpy_gizmo_target_set_handler_doc, ".. method:: target_set_handler(target, get, set, range=None):\n" "\n" " Assigns callbacks to a gizmos property.\n" "\n" " :arg get: Function that returns the value for this property (single value or sequence).\n" " :type get: callable\n" " :arg set: Function that takes a single value argument and applies it.\n" " :type set: callable\n" " :arg range: Function that returns a (min, max) tuple for gizmos that use a range.\n" " :type range: callable\n")
static void py_rna_gizmo_handler_free_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
PyObject * PyC_Tuple_PackArray_F32(const float *array, uint len)
int PyC_AsArray(void *array, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, const bool is_double, const char *error_prefix)
return ret
PyObject * fn_slots[BPY_GIZMO_FN_SLOT_LEN]
Definition: bpy_rna_gizmo.c:59
void * data
Definition: RNA_types.h:52
struct wmGizmoProperty::@1149 custom_func
const struct wmGizmoPropertyType * type
const char * idname
struct PointerRNA * ptr
const struct wmGizmoType * type
void WM_gizmo_target_property_float_set(bContext *C, const wmGizmo *gz, wmGizmoProperty *gz_prop, const float value)
wmGizmoProperty * WM_gizmo_target_property_find(wmGizmo *gz, const char *idname)
void WM_gizmo_target_property_float_get_array(const wmGizmo *gz, wmGizmoProperty *gz_prop, float *value)
void WM_gizmo_target_property_def_func_ptr(wmGizmo *gz, const wmGizmoPropertyType *gz_prop_type, const wmGizmoPropertyFnParams *params)
float WM_gizmo_target_property_float_get(const wmGizmo *gz, wmGizmoProperty *gz_prop)
void WM_gizmo_target_property_float_set_array(bContext *C, const wmGizmo *gz, wmGizmoProperty *gz_prop, const float *value)
const wmGizmoPropertyType * WM_gizmotype_target_property_find(const wmGizmoType *gzt, const char *idname)
int WM_gizmo_target_property_array_length(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop)
bool WM_gizmo_target_property_float_range_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, float range[2])