Blender V4.3
idprop_py_ui_api.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <Python.h>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_string.h"
14#include "BLI_utildefines.h"
15
16#include "idprop_py_ui_api.hh"
17
18#include "BKE_idprop.hh"
19
20#include "DNA_ID.h"
21
22#include "RNA_access.hh"
23#include "RNA_enum_types.hh"
24
25#define USE_STRING_COERCE
26
27#ifdef USE_STRING_COERCE
28# include "py_capi_utils.hh"
29#endif
30#include "py_capi_rna.hh"
31
32#include "python_utildefines.hh"
33
34/* -------------------------------------------------------------------- */
38static bool args_contain_key(PyObject *kwargs, const char *name)
39{
40 /* When a function gets called without any kwargs, */
41 /* Python just passes nullptr instead. #PyDict_GetItemString() is not null-safe, though. */
42 return kwargs && PyDict_GetItemString(kwargs, name) != nullptr;
43}
44
49 const char *rna_subtype,
50 const char *description)
51{
52 if (rna_subtype != nullptr) {
54 rna_subtype,
55 &ui_data->rna_subtype,
56 "IDPropertyUIManager.update") == -1)
57 {
58 return false;
59 }
60 }
61
62 if (description != nullptr) {
63 ui_data->description = BLI_strdup(description);
64 }
65
66 return true;
67}
68
69/* Utility function for parsing ints in an if statement. */
70static bool py_long_as_int(PyObject *py_long, int *r_int)
71{
72 if (PyLong_CheckExact(py_long)) {
73 *r_int = int(PyLong_AS_LONG(py_long));
74 return true;
75 }
76 return false;
77}
78
84static bool try_parse_enum_item(PyObject *py_item, const int index, IDPropertyUIDataEnumItem &item)
85{
86 if (!PyTuple_CheckExact(py_item)) {
87 return false;
88 }
89 Py_ssize_t item_size = PyTuple_GET_SIZE(py_item);
90 if (item_size < 3 || item_size > 5) {
91 return false;
92 }
93
94 Py_ssize_t identifier_len;
95 Py_ssize_t name_len;
96 Py_ssize_t description_len;
97 const char *identifier = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(py_item, 0), &identifier_len);
98 const char *name = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(py_item, 1), &name_len);
99 const char *description = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(py_item, 2),
100 &description_len);
101 if (!identifier || !name || !description) {
102 return false;
103 }
104
105 const char *icon_name = nullptr;
106 if (item_size <= 3) {
107 item.value = index;
108 }
109 else if (item_size == 4) {
110 if (!py_long_as_int(PyTuple_GET_ITEM(py_item, 3), &item.value)) {
111 return false;
112 }
113 }
114 else if (item_size == 5) {
115 /* Must have icon value or name. */
116 if (!py_long_as_int(PyTuple_GET_ITEM(py_item, 3), &item.icon) &&
117 !(icon_name = PyUnicode_AsUTF8(PyTuple_GET_ITEM(py_item, 3))))
118 {
119 return false;
120 }
121 if (!py_long_as_int(PyTuple_GET_ITEM(py_item, 4), &item.value)) {
122 return false;
123 }
124 }
125
126 item.identifier = BLI_strdup(identifier);
127 item.name = BLI_strdup(name);
128 item.description = BLI_strdup_null(description);
129 if (icon_name) {
131 }
132 return true;
133}
134
135static IDPropertyUIDataEnumItem *idprop_enum_items_from_py(PyObject *seq_fast, int &r_items_num)
136{
138
139 const Py_ssize_t seq_len = PySequence_Fast_GET_SIZE(seq_fast);
140 PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
141 int i;
142
143 items = MEM_cnew_array<IDPropertyUIDataEnumItem>(seq_len, __func__);
144 r_items_num = seq_len;
145
146 for (i = 0; i < seq_len; i++) {
147 IDPropertyUIDataEnumItem item = {nullptr, nullptr, nullptr, 0, 0};
148 PyObject *py_item = seq_fast_items[i];
149 if (try_parse_enum_item(py_item, i, item)) {
150 items[i] = item;
151 }
152 else if (py_item == Py_None) {
153 items[i].identifier = nullptr;
154 }
155 else {
156 MEM_freeN(items);
157 PyErr_SetString(PyExc_TypeError,
158 "expected a tuple containing "
159 "(identifier, name, description) and optionally an "
160 "icon name and unique number");
161 return nullptr;
162 }
163 }
164
165 return items;
166}
167
173 IDPropertyUIDataInt *ui_data,
174 PyObject *default_value)
175{
176 if (PySequence_Check(default_value)) {
177 if (idprop->type != IDP_ARRAY) {
178 PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values");
179 return false;
180 }
181
182 Py_ssize_t len = PySequence_Size(default_value);
183 int *new_default_array = (int *)MEM_malloc_arrayN(len, sizeof(int), __func__);
184 if (PyC_AsArray(
185 new_default_array, sizeof(int), default_value, len, &PyLong_Type, "ui_data_update") ==
186 -1)
187 {
188 MEM_freeN(new_default_array);
189 return false;
190 }
191
192 ui_data->default_array_len = len;
193 ui_data->default_array = new_default_array;
194 }
195 else {
196 const int value = PyC_Long_AsI32(default_value);
197 if ((value == -1) && PyErr_Occurred()) {
198 PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to integer");
199 return false;
200 }
201
202 /* Use the non-array default, even for arrays, also prevent dangling pointer, see #127952. */
203 ui_data->default_array = nullptr;
204 ui_data->default_array_len = 0;
205
206 ui_data->default_value = value;
207 }
208
209 return true;
210}
211
215static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObject *kwargs)
216{
217 const char *rna_subtype = nullptr;
218 const char *description = nullptr;
219 int min, max, soft_min, soft_max, step;
220 PyObject *default_value = nullptr;
221 PyObject *items = nullptr;
222 const char *kwlist[] = {
223 "min",
224 "max",
225 "soft_min",
226 "soft_max",
227 "step",
228 "default",
229 "items",
230 "subtype",
231 "description",
232 nullptr,
233 };
234 if (!PyArg_ParseTupleAndKeywords(args,
235 kwargs,
236 "|$iiiiiOOzz:update",
237 (char **)kwlist,
238 &min,
239 &max,
240 &soft_min,
241 &soft_max,
242 &step,
243 &default_value,
244 &items,
245 &rna_subtype,
246 &description))
247 {
248 return false;
249 }
250
251 /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
252 IDPropertyUIDataInt *ui_data_orig = (IDPropertyUIDataInt *)idprop->ui_data;
253 IDPropertyUIDataInt ui_data = *ui_data_orig;
254
255 if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
256 IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
257 return false;
258 }
259
260 if (args_contain_key(kwargs, "min")) {
261 ui_data.min = min;
262 ui_data.soft_min = std::max(ui_data.soft_min, ui_data.min);
263 ui_data.max = std::max(ui_data.min, ui_data.max);
264 }
265 if (args_contain_key(kwargs, "max")) {
266 ui_data.max = max;
267 ui_data.soft_max = std::min(ui_data.soft_max, ui_data.max);
268 ui_data.min = std::min(ui_data.min, ui_data.max);
269 }
270 if (args_contain_key(kwargs, "soft_min")) {
271 ui_data.soft_min = soft_min;
272 ui_data.soft_min = std::max(ui_data.soft_min, ui_data.min);
273 ui_data.soft_max = std::max(ui_data.soft_min, ui_data.soft_max);
274 }
275 if (args_contain_key(kwargs, "soft_max")) {
276 ui_data.soft_max = soft_max;
277 ui_data.soft_max = std::min(ui_data.soft_max, ui_data.max);
278 ui_data.soft_min = std::min(ui_data.soft_min, ui_data.soft_max);
279 }
280 if (args_contain_key(kwargs, "step")) {
281 ui_data.step = step;
282 }
283
284 if (!ELEM(default_value, nullptr, Py_None)) {
285 if (!idprop_ui_data_update_int_default(idprop, &ui_data, default_value)) {
287 &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
288 return false;
289 }
290 }
291
292 if (!ELEM(items, nullptr, Py_None)) {
293 PyObject *items_fast;
294 if (!(items_fast = PySequence_Fast(items, "expected a sequence of tuples for the enum items")))
295 {
296 return false;
297 }
298
299 int idprop_items_num = 0;
300 IDPropertyUIDataEnumItem *idprop_items = idprop_enum_items_from_py(items_fast,
301 idprop_items_num);
302 if (!idprop_items) {
303 Py_DECREF(items_fast);
304 return false;
305 }
306 if (!IDP_EnumItemsValidate(idprop_items, idprop_items_num, [](const char *msg) {
307 PyErr_SetString(PyExc_ValueError, msg);
308 }))
309 {
310 Py_DECREF(items_fast);
311 return false;
312 }
313 Py_DECREF(items_fast);
314 ui_data.enum_items = idprop_items;
315 ui_data.enum_items_num = idprop_items_num;
316 }
317 else {
318 ui_data.enum_items = nullptr;
319 ui_data.enum_items_num = 0;
320 }
321
322 /* Write back to the property's UI data. */
323 IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
324 *ui_data_orig = ui_data;
325 return true;
326}
327
333 IDPropertyUIDataBool *ui_data,
334 PyObject *default_value)
335{
336 if (PySequence_Check(default_value)) {
337 if (idprop->type != IDP_ARRAY) {
338 PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values");
339 return false;
340 }
341
342 Py_ssize_t len = PySequence_Size(default_value);
343 int8_t *new_default_array = (int8_t *)MEM_malloc_arrayN(len, sizeof(int8_t), __func__);
344 if (PyC_AsArray(new_default_array,
345 sizeof(int8_t),
346 default_value,
347 len,
348 &PyBool_Type,
349 "ui_data_update") == -1)
350 {
351 MEM_freeN(new_default_array);
352 return false;
353 }
354
355 ui_data->default_array_len = len;
356 ui_data->default_array = new_default_array;
357 }
358 else {
359 const int value = PyC_Long_AsBool(default_value);
360 if ((value == -1) && PyErr_Occurred()) {
361 PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to integer");
362 return false;
363 }
364
365 /* Use the non-array default, even for arrays, also prevent dangling pointer, see #127952. */
366 ui_data->default_array_len = 0;
367 ui_data->default_array = nullptr;
368
369 ui_data->default_value = (value != 0);
370 }
371
372 return true;
373}
374
378static bool idprop_ui_data_update_bool(IDProperty *idprop, PyObject *args, PyObject *kwargs)
379{
380 const char *rna_subtype = nullptr;
381 const char *description = nullptr;
382 PyObject *default_value = nullptr;
383 const char *kwlist[] = {"default", "subtype", "description", nullptr};
384 if (!PyArg_ParseTupleAndKeywords(args,
385 kwargs,
386 "|$Ozz:update",
387 (char **)kwlist,
388 &default_value,
389 &rna_subtype,
390 &description))
391 {
392 return false;
393 }
394
395 /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
396 IDPropertyUIDataBool *ui_data_orig = (IDPropertyUIDataBool *)idprop->ui_data;
397 IDPropertyUIDataBool ui_data = *ui_data_orig;
398
399 if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
400 IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
401 return false;
402 }
403
404 if (!ELEM(default_value, nullptr, Py_None)) {
405 if (!idprop_ui_data_update_bool_default(idprop, &ui_data, default_value)) {
407 &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
408 return false;
409 }
410 }
411
412 /* Write back to the property's UI data. */
413 IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
414 *ui_data_orig = ui_data;
415 return true;
416}
417
423 IDPropertyUIDataFloat *ui_data,
424 PyObject *default_value)
425{
426 if (PySequence_Check(default_value)) {
427 if (idprop->type != IDP_ARRAY) {
428 PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values");
429 return false;
430 }
431
432 Py_ssize_t len = PySequence_Size(default_value);
433 double *new_default_array = (double *)MEM_malloc_arrayN(len, sizeof(double), __func__);
434 if (PyC_AsArray(new_default_array,
435 sizeof(double),
436 default_value,
437 len,
438 &PyFloat_Type,
439 "ui_data_update") == -1)
440 {
441 MEM_freeN(new_default_array);
442 return false;
443 }
444
445 ui_data->default_array_len = len;
446 ui_data->default_array = new_default_array;
447 }
448 else {
449 const double value = PyFloat_AsDouble(default_value);
450 if ((value == -1.0) && PyErr_Occurred()) {
451 PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to double");
452 return false;
453 }
454
455 /* Use the non-array default, even for arrays, also prevent dangling pointer, see #127952. */
456 ui_data->default_array_len = 0;
457 ui_data->default_array = nullptr;
458
459 ui_data->default_value = value;
460 }
461
462 return true;
463}
464
468static bool idprop_ui_data_update_float(IDProperty *idprop, PyObject *args, PyObject *kwargs)
469{
470 const char *rna_subtype = nullptr;
471 const char *description = nullptr;
472 int precision;
473 double min, max, soft_min, soft_max, step;
474 PyObject *default_value = nullptr;
475 const char *kwlist[] = {"min",
476 "max",
477 "soft_min",
478 "soft_max",
479 "step",
480 "precision",
481 "default",
482 "subtype",
483 "description",
484 nullptr};
485 if (!PyArg_ParseTupleAndKeywords(args,
486 kwargs,
487 "|$dddddiOzz:update",
488 (char **)kwlist,
489 &min,
490 &max,
491 &soft_min,
492 &soft_max,
493 &step,
494 &precision,
495 &default_value,
496 &rna_subtype,
497 &description))
498 {
499 return false;
500 }
501
502 /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
503 IDPropertyUIDataFloat *ui_data_orig = (IDPropertyUIDataFloat *)idprop->ui_data;
504 IDPropertyUIDataFloat ui_data = *ui_data_orig;
505
506 if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
507 IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
508 return false;
509 }
510
511 if (args_contain_key(kwargs, "min")) {
512 ui_data.min = min;
513 ui_data.soft_min = std::max(ui_data.soft_min, ui_data.min);
514 ui_data.max = std::max(ui_data.min, ui_data.max);
515 }
516 if (args_contain_key(kwargs, "max")) {
517 ui_data.max = max;
518 ui_data.soft_max = std::min(ui_data.soft_max, ui_data.max);
519 ui_data.min = std::min(ui_data.min, ui_data.max);
520 }
521 if (args_contain_key(kwargs, "soft_min")) {
522 ui_data.soft_min = soft_min;
523 ui_data.soft_min = std::max(ui_data.soft_min, ui_data.min);
524 ui_data.soft_max = std::max(ui_data.soft_min, ui_data.soft_max);
525 }
526 if (args_contain_key(kwargs, "soft_max")) {
527 ui_data.soft_max = soft_max;
528 ui_data.soft_max = std::min(ui_data.soft_max, ui_data.max);
529 ui_data.soft_min = std::min(ui_data.soft_min, ui_data.soft_max);
530 }
531 if (args_contain_key(kwargs, "step")) {
532 ui_data.step = float(step);
533 }
534 if (args_contain_key(kwargs, "precision")) {
535 ui_data.precision = precision;
536 }
537
538 if (!ELEM(default_value, nullptr, Py_None)) {
539 if (!idprop_ui_data_update_float_default(idprop, &ui_data, default_value)) {
541 &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
542 return false;
543 }
544 }
545
546 /* Write back to the property's UI data. */
547 IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
548 *ui_data_orig = ui_data;
549 return true;
550}
551
555static bool idprop_ui_data_update_string(IDProperty *idprop, PyObject *args, PyObject *kwargs)
556{
557 const char *rna_subtype = nullptr;
558 const char *description = nullptr;
559 const char *default_value = nullptr;
560 const char *kwlist[] = {"default", "subtype", "description", nullptr};
561 if (!PyArg_ParseTupleAndKeywords(args,
562 kwargs,
563 "|$zzz:update",
564 (char **)kwlist,
565 &default_value,
566 &rna_subtype,
567 &description))
568 {
569 return false;
570 }
571
572 /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
573 IDPropertyUIDataString *ui_data_orig = (IDPropertyUIDataString *)idprop->ui_data;
574 IDPropertyUIDataString ui_data = *ui_data_orig;
575
576 if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
577 IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
578 return false;
579 }
580
581 if (default_value != nullptr) {
582 ui_data.default_value = BLI_strdup(default_value);
583 }
584
585 /* Write back to the property's UI data. */
586 IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
587 *ui_data_orig = ui_data;
588 return true;
589}
590
594static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObject *kwargs)
595{
596 const char *rna_subtype = nullptr;
597 const char *description = nullptr;
598 const char *id_type = nullptr;
599 const char *kwlist[] = {"subtype", "description", "id_type", nullptr};
600 if (!PyArg_ParseTupleAndKeywords(
601 args, kwargs, "|$zzz:update", (char **)kwlist, &rna_subtype, &description, &id_type))
602 {
603 return false;
604 }
605
606 /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
607 IDPropertyUIDataID *ui_data_orig = (IDPropertyUIDataID *)idprop->ui_data;
608 IDPropertyUIDataID ui_data = *ui_data_orig;
609
610 if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) {
611 IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base);
612 return false;
613 }
614
615 if (id_type != nullptr) {
616 int id_type_tmp;
618 rna_enum_id_type_items, id_type, &id_type_tmp, "IDPropertyUIManager.update") == -1)
619 {
620 return false;
621 }
622
623 ui_data.id_type = short(id_type_tmp);
624 }
625
626 /* Write back to the property's UI data. */
627 IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
628 *ui_data_orig = ui_data;
629 return true;
630}
631
633 /* Wrap. */
634 BPy_IDPropertyUIManager_update_doc,
635 ".. method:: update( "
636 "subtype=None, "
637 "min=None, "
638 "max=None, "
639 "soft_min=None, "
640 "soft_max=None, "
641 "precision=None, "
642 "step=None, "
643 "default=None, "
644 "id_type=None, "
645 "items=None, "
646 "description=None)\n"
647 "\n"
648 " Update the RNA information of the IDProperty used for interaction and\n"
649 " display in the user interface. The required types for many of the keyword\n"
650 " arguments depend on the type of the property.\n ");
652 PyObject *args,
653 PyObject *kwargs)
654{
655 IDProperty *property = self->property;
657
658 switch (IDP_ui_data_type(property)) {
660 IDP_ui_data_ensure(property);
661 if (!idprop_ui_data_update_int(property, args, kwargs)) {
662 return nullptr;
663 }
664 Py_RETURN_NONE;
666 IDP_ui_data_ensure(property);
667 if (!idprop_ui_data_update_bool(property, args, kwargs)) {
668 return nullptr;
669 }
670 Py_RETURN_NONE;
672 IDP_ui_data_ensure(property);
673 if (!idprop_ui_data_update_float(property, args, kwargs)) {
674 return nullptr;
675 }
676 Py_RETURN_NONE;
678 IDP_ui_data_ensure(property);
679 if (!idprop_ui_data_update_string(property, args, kwargs)) {
680 return nullptr;
681 }
682 Py_RETURN_NONE;
684 IDP_ui_data_ensure(property);
685 if (!idprop_ui_data_update_id(property, args, kwargs)) {
686 return nullptr;
687 }
688 Py_RETURN_NONE;
690 PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support RNA data", property->name);
691 return nullptr;
692 }
693
695 Py_RETURN_NONE;
696}
697
700/* -------------------------------------------------------------------- */
704static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict)
705{
706 IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)property->ui_data;
707 PyObject *item;
708
709 PyDict_SetItemString(dict, "min", item = PyLong_FromLong(ui_data->min));
710 Py_DECREF(item);
711 PyDict_SetItemString(dict, "max", item = PyLong_FromLong(ui_data->max));
712 Py_DECREF(item);
713 PyDict_SetItemString(dict, "soft_min", item = PyLong_FromLong(ui_data->soft_min));
714 Py_DECREF(item);
715 PyDict_SetItemString(dict, "soft_max", item = PyLong_FromLong(ui_data->soft_max));
716 Py_DECREF(item);
717 PyDict_SetItemString(dict, "step", item = PyLong_FromLong(ui_data->step));
718 Py_DECREF(item);
719 if ((property->type == IDP_ARRAY) && ui_data->default_array) {
720 PyObject *list = PyList_New(ui_data->default_array_len);
721 for (int i = 0; i < ui_data->default_array_len; i++) {
722 PyList_SET_ITEM(list, i, PyLong_FromLong(ui_data->default_array[i]));
723 }
724 PyDict_SetItemString(dict, "default", list);
725 Py_DECREF(list);
726 }
727 else {
728 PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->default_value));
729 Py_DECREF(item);
730 }
731
732 if (ui_data->enum_items_num > 0) {
733 PyObject *items_list = PyList_New(ui_data->enum_items_num);
734 for (int i = 0; i < ui_data->enum_items_num; ++i) {
735 const IDPropertyUIDataEnumItem &item = ui_data->enum_items[i];
736 BLI_assert(item.identifier != nullptr);
737 BLI_assert(item.name != nullptr);
738
739 PyObject *item_tuple = PyTuple_New(5);
740 PyTuple_SET_ITEM(item_tuple, 0, PyUnicode_FromString(item.identifier));
741 PyTuple_SET_ITEM(item_tuple, 1, PyUnicode_FromString(item.name));
742 PyTuple_SET_ITEM(
743 item_tuple, 2, PyUnicode_FromString(item.description ? item.description : ""));
744 PyTuple_SET_ITEM(item_tuple, 3, PyLong_FromLong(item.icon));
745 PyTuple_SET_ITEM(item_tuple, 4, PyLong_FromLong(item.value));
746
747 PyList_SET_ITEM(items_list, i, item_tuple);
748 }
749 PyDict_SetItemString(dict, "items", items_list);
750 Py_DECREF(items_list);
751 }
752}
753
754static void idprop_ui_data_to_dict_bool(IDProperty *property, PyObject *dict)
755{
756 IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)property->ui_data;
757 PyObject *item;
758
759 if ((property->type == IDP_ARRAY) && ui_data->default_array) {
760 PyObject *list = PyList_New(ui_data->default_array_len);
761 for (int i = 0; i < ui_data->default_array_len; i++) {
762 PyList_SET_ITEM(list, i, PyBool_FromLong(ui_data->default_array[i]));
763 }
764 PyDict_SetItemString(dict, "default", list);
765 Py_DECREF(list);
766 }
767 else {
768 PyDict_SetItemString(dict, "default", item = PyBool_FromLong(ui_data->default_value));
769 Py_DECREF(item);
770 }
771}
772
773static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict)
774{
776 PyObject *item;
777
778 PyDict_SetItemString(dict, "min", item = PyFloat_FromDouble(ui_data->min));
779 Py_DECREF(item);
780 PyDict_SetItemString(dict, "max", item = PyFloat_FromDouble(ui_data->max));
781 Py_DECREF(item);
782 PyDict_SetItemString(dict, "soft_min", item = PyFloat_FromDouble(ui_data->soft_min));
783 Py_DECREF(item);
784 PyDict_SetItemString(dict, "soft_max", item = PyFloat_FromDouble(ui_data->soft_max));
785 Py_DECREF(item);
786 PyDict_SetItemString(dict, "step", item = PyFloat_FromDouble(double(ui_data->step)));
787 Py_DECREF(item);
788 PyDict_SetItemString(dict, "precision", item = PyLong_FromDouble(double(ui_data->precision)));
789 Py_DECREF(item);
790 if ((property->type == IDP_ARRAY) && ui_data->default_array) {
791 PyObject *list = PyList_New(ui_data->default_array_len);
792 for (int i = 0; i < ui_data->default_array_len; i++) {
793 PyList_SET_ITEM(list, i, PyFloat_FromDouble(ui_data->default_array[i]));
794 }
795 PyDict_SetItemString(dict, "default", list);
796 Py_DECREF(list);
797 }
798 else {
799 PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->default_value));
800 Py_DECREF(item);
801 }
802}
803
804static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict)
805{
807 PyObject *item;
808
809 const char *default_value = (ui_data->default_value == nullptr) ? "" : ui_data->default_value;
810
811 PyDict_SetItemString(dict, "default", item = PyUnicode_FromString(default_value));
812 Py_DECREF(item);
813}
814
815static void idprop_ui_data_to_dict_id(IDProperty *property, PyObject *dict)
816{
817 IDPropertyUIDataID *ui_data = reinterpret_cast<IDPropertyUIDataID *>(property->ui_data);
818
819 short id_type_value = ui_data->id_type;
820 if (id_type_value == 0) {
821 /* While UI exposed custom properties do not allow the 'all ID types' `0` value, in
822 * py-defined IDProperties it is accepted. So force defining a valid id_type value when this
823 * function is called. */
824 ID *id = IDP_Id(property);
825 id_type_value = id ? GS(id->name) : ID_OB;
826 }
827
828 const char *id_type = nullptr;
829 if (!RNA_enum_identifier(rna_enum_id_type_items, id_type_value, &id_type)) {
830 /* Same fall-back as above, in case it is an unknown ID type (from a future version of
831 * Blender e.g.). */
833 }
834 PyObject *item = PyUnicode_FromString(id_type);
835 PyDict_SetItemString(dict, "id_type", item);
836 Py_DECREF(item);
837}
838
840 /* Wrap. */
841 BPy_IDPropertyUIManager_as_dict_doc,
842 ".. method:: as_dict()\n"
843 "\n"
844 " Return a dictionary of the property's RNA UI data. The fields in the\n"
845 " returned dictionary and their types will depend on the property's type.\n");
847{
848 IDProperty *property = self->property;
850
851 IDPropertyUIData *ui_data = IDP_ui_data_ensure(property);
852
853 PyObject *dict = PyDict_New();
854
855 /* RNA subtype. */
856 {
857 const char *subtype_id = nullptr;
859 PyObject *item = PyUnicode_FromString(subtype_id);
860 PyDict_SetItemString(dict, "subtype", item);
861 Py_DECREF(item);
862 }
863
864 /* Description. */
865 if (ui_data->description != nullptr) {
866 PyObject *item = PyUnicode_FromString(ui_data->description);
867 PyDict_SetItemString(dict, "description", item);
868 Py_DECREF(item);
869 }
870
871 /* Type specific data. */
872 switch (IDP_ui_data_type(property)) {
874 idprop_ui_data_to_dict_string(property, dict);
875 break;
877 idprop_ui_data_to_dict_id(property, dict);
878 break;
880 idprop_ui_data_to_dict_int(property, dict);
881 break;
883 idprop_ui_data_to_dict_bool(property, dict);
884 break;
886 idprop_ui_data_to_dict_float(property, dict);
887 break;
890 break;
891 }
892
893 return dict;
894}
895
898/* -------------------------------------------------------------------- */
903 /* Wrap. */
904 BPy_IDPropertyUIManager_clear_doc,
905 ".. method:: clear()\n"
906 "\n"
907 " Remove the RNA UI data from this IDProperty.\n");
909{
910 IDProperty *property = self->property;
912
913 if (property == nullptr) {
914 PyErr_SetString(PyExc_RuntimeError, "IDPropertyUIManager missing property");
916 return nullptr;
917 }
918
919 if (property->ui_data != nullptr) {
920 IDP_ui_data_free(property);
921 }
922
923 Py_RETURN_NONE;
924}
925
928/* -------------------------------------------------------------------- */
933 /* Wrap. */
934 BPy_IDPropertyUIManager_update_from_doc,
935 ".. method:: update_from(ui_manager_source)\n"
936 "\n"
937 " Copy UI data from an IDProperty in the source group to a property in this group.\n "
938 " If the source property has no UI data, the target UI data will be reset if it exists.\n"
939 "\n"
940 " :raises TypeError: If the types of the two properties don't match.\n");
942{
943 IDProperty *property = self->property;
945
946 BPy_IDPropertyUIManager *ui_manager_src;
947 if (!PyArg_ParseTuple(args, "O!:update_from", &BPy_IDPropertyUIManager_Type, &ui_manager_src)) {
948 return nullptr;
949 }
950
951 if (property->ui_data != nullptr) {
952 IDP_ui_data_free(property);
953 }
954
955 if (ui_manager_src->property && ui_manager_src->property->ui_data) {
956 property->ui_data = IDP_ui_data_copy(ui_manager_src->property);
957 }
958
959 Py_RETURN_NONE;
960}
961
964/* -------------------------------------------------------------------- */
968#if (defined(__GNUC__) && !defined(__clang__))
969# pragma GCC diagnostic push
970# pragma GCC diagnostic ignored "-Wcast-function-type"
971#endif
972
973static PyMethodDef BPy_IDPropertyUIManager_methods[] = {
974 {"update",
976 METH_VARARGS | METH_KEYWORDS,
977 BPy_IDPropertyUIManager_update_doc},
978 {"as_dict",
980 METH_NOARGS,
981 BPy_IDPropertyUIManager_as_dict_doc},
982 {"clear",
984 METH_NOARGS,
985 BPy_IDPropertyUIManager_clear_doc},
986 {"update_from",
988 METH_VARARGS,
989 BPy_IDPropertyUIManager_update_from_doc},
990 {nullptr, nullptr, 0, nullptr},
991};
992
993#if (defined(__GNUC__) && !defined(__clang__))
994# pragma GCC diagnostic pop
995#endif
996
998{
999 return PyUnicode_FromFormat(
1000 "<bpy id prop ui manager: name=\"%s\", address=%p>", self->property->name, self->property);
1001}
1002
1004{
1005 return _Py_HashPointer(self->property);
1006}
1007
1009 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
1010 /* For printing, in format `<module>.<name>`. */
1011 /*tp_name*/ "IDPropertyUIManager",
1012 /*tp_basicsize*/ sizeof(BPy_IDPropertyUIManager),
1013 /*tp_itemsize*/ 0,
1014 /*tp_dealloc*/ nullptr,
1015 /*tp_vectorcall_offset*/ 0,
1016 /*tp_getattr*/ nullptr,
1017 /*tp_setattr*/ nullptr,
1018 /*tp_as_async*/ nullptr,
1019 /*tp_repr*/ (reprfunc)BPy_IDPropertyUIManager_repr,
1020 /*tp_as_number*/ nullptr,
1021 /*tp_as_sequence*/ nullptr,
1022 /*tp_as_mapping*/ nullptr,
1023 /*tp_hash*/ (hashfunc)BPy_IDPropertyUIManager_hash,
1024 /*tp_call*/ nullptr,
1025 /*tp_str*/ nullptr,
1026 /*tp_getattro*/ nullptr,
1027 /*tp_setattro*/ nullptr,
1028 /*tp_as_buffer*/ nullptr,
1029 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
1030 /*tp_doc*/ nullptr,
1031 /*tp_traverse*/ nullptr,
1032 /*tp_clear*/ nullptr,
1033 /*tp_richcompare*/ nullptr,
1034 /*tp_weaklistoffset*/ 0,
1035 /*tp_iter*/ nullptr,
1036 /*tp_iternext*/ nullptr,
1037 /*tp_methods*/ BPy_IDPropertyUIManager_methods,
1038 /*tp_members*/ nullptr,
1039 /*tp_getset*/ nullptr,
1040 /*tp_base*/ nullptr,
1041 /*tp_dict*/ nullptr,
1042 /*tp_descr_get*/ nullptr,
1043 /*tp_descr_set*/ nullptr,
1044 /*tp_dictoffset*/ 0,
1045 /*tp_init*/ nullptr,
1046 /*tp_alloc*/ nullptr,
1047 /*tp_new*/ nullptr,
1048 /*tp_free*/ nullptr,
1049 /*tp_is_gc*/ nullptr,
1050 /*tp_bases*/ nullptr,
1051 /*tp_mro*/ nullptr,
1052 /*tp_cache*/ nullptr,
1053 /*tp_subclasses*/ nullptr,
1054 /*tp_weaklist*/ nullptr,
1055 /*tp_del*/ nullptr,
1056 /*tp_version_tag*/ 0,
1057 /*tp_finalize*/ nullptr,
1058 /*tp_vectorcall*/ nullptr,
1059};
1060
1062{
1063 PyType_Ready(&BPy_IDPropertyUIManager_Type);
1064}
1065
void IDP_ui_data_free(IDProperty *prop)
Definition idprop.cc:1183
bool IDP_EnumItemsValidate(const IDPropertyUIDataEnumItem *items, int items_num, void(*error_fn)(const char *))
Definition idprop.cc:480
#define IDP_Id(prop)
eIDPropertyUIDataType IDP_ui_data_type(const IDProperty *prop)
Definition idprop.cc:1665
bool IDP_ui_data_supported(const IDProperty *prop)
Definition idprop.cc:1687
IDPropertyUIData * IDP_ui_data_copy(const IDProperty *prop)
Definition idprop.cc:260
@ IDP_UI_DATA_TYPE_ID
@ IDP_UI_DATA_TYPE_BOOLEAN
@ IDP_UI_DATA_TYPE_UNSUPPORTED
@ IDP_UI_DATA_TYPE_INT
@ IDP_UI_DATA_TYPE_FLOAT
@ IDP_UI_DATA_TYPE_STRING
void IDP_ui_data_free_unique_contents(IDPropertyUIData *ui_data, eIDPropertyUIDataType type, const IDPropertyUIData *other)
Definition idprop.cc:1093
IDPropertyUIData * IDP_ui_data_ensure(IDProperty *prop)
Definition idprop.cc:1739
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.c:45
#define ELEM(...)
ID and Library types, which are fundamental for SDNA.
@ ID_OB
@ IDP_ARRAY
Read Guarded memory(de)allocation.
PyObject * self
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void IDPropertyUIData_Init_Types()
static bool idprop_ui_data_update_bool(IDProperty *idprop, PyObject *args, PyObject *kwargs)
static PyObject * BPy_IDPropertyUIManager_update(BPy_IDPropertyUIManager *self, PyObject *args, PyObject *kwargs)
static bool idprop_ui_data_update_float(IDProperty *idprop, PyObject *args, PyObject *kwargs)
static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObject *kwargs)
static PyObject * BPy_IDPropertyUIManager_clear(BPy_IDPropertyUIManager *self)
static bool py_long_as_int(PyObject *py_long, int *r_int)
static PyObject * BPy_IDPropertyUIManager_update_from(BPy_IDPropertyUIManager *self, PyObject *args)
static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObject *kwargs)
static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict)
static PyObject * BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self)
static PyMethodDef BPy_IDPropertyUIManager_methods[]
static bool idprop_ui_data_update_bool_default(IDProperty *idprop, IDPropertyUIDataBool *ui_data, PyObject *default_value)
static IDPropertyUIDataEnumItem * idprop_enum_items_from_py(PyObject *seq_fast, int &r_items_num)
static bool idprop_ui_data_update_base(IDPropertyUIData *ui_data, const char *rna_subtype, const char *description)
static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict)
static bool args_contain_key(PyObject *kwargs, const char *name)
static PyObject * BPy_IDPropertyUIManager_repr(BPy_IDPropertyUIManager *self)
static bool idprop_ui_data_update_float_default(IDProperty *idprop, IDPropertyUIDataFloat *ui_data, PyObject *default_value)
static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict)
static Py_hash_t BPy_IDPropertyUIManager_hash(BPy_IDPropertyUIManager *self)
static bool idprop_ui_data_update_string(IDProperty *idprop, PyObject *args, PyObject *kwargs)
static bool try_parse_enum_item(PyObject *py_item, const int index, IDPropertyUIDataEnumItem &item)
static void idprop_ui_data_to_dict_id(IDProperty *property, PyObject *dict)
PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc, ".. method:: update( " "subtype=None, " "min=None, " "max=None, " "soft_min=None, " "soft_max=None, " "precision=None, " "step=None, " "default=None, " "id_type=None, " "items=None, " "description=None)\n" "\n" " Update the RNA information of the IDProperty used for interaction and\n" " display in the user interface. The required types for many of the keyword\n" " arguments depend on the type of the property.\n ")
PyTypeObject BPy_IDPropertyUIManager_Type
static bool idprop_ui_data_update_int_default(IDProperty *idprop, IDPropertyUIDataInt *ui_data, PyObject *default_value)
static void idprop_ui_data_to_dict_bool(IDProperty *property, PyObject *dict)
#define GS(x)
Definition iris.cc:202
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
int pyrna_enum_value_from_id(const EnumPropertyItem *item, const char *identifier, int *r_value, const char *error_prefix)
int PyC_Long_AsBool(PyObject *value)
int PyC_AsArray(void *array, const size_t array_item_size, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, const char *error_prefix)
header-only utilities
const EnumPropertyItem rna_enum_id_type_items[]
Definition rna_ID.cc:35
bool RNA_enum_value_from_identifier(const EnumPropertyItem *item, const char *identifier, int *r_value)
bool RNA_enum_identifier(const EnumPropertyItem *item, const int value, const char **r_identifier)
const EnumPropertyItem rna_enum_property_subtype_items[]
Definition rna_rna.cc:127
const EnumPropertyItem rna_enum_icon_items[]
Definition rna_ui_api.cc:31
#define min(a, b)
Definition sort.c:32
signed char int8_t
Definition stdint.h:75
PyObject_VAR_HEAD IDProperty * property
int8_t * default_array
Definition DNA_ID.h:100
IDPropertyUIData base
Definition DNA_ID.h:99
double * default_array
Definition DNA_ID.h:110
IDPropertyUIData base
Definition DNA_ID.h:109
IDPropertyUIData base
Definition DNA_ID.h:132
IDPropertyUIData base
Definition DNA_ID.h:82
IDPropertyUIDataEnumItem * enum_items
Definition DNA_ID.h:94
int * default_array
Definition DNA_ID.h:83
IDPropertyUIData base
Definition DNA_ID.h:126
char * description
Definition DNA_ID.h:59
IDPropertyUIData * ui_data
Definition DNA_ID.h:182
char type
Definition DNA_ID.h:154
Definition DNA_ID.h:413
float max