Files
test2/source/blender/python/intern/bpy_props.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

5773 lines
181 KiB
C++
Raw Permalink Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup pythonintern
*
* This file defines 'bpy.props' module used so scripts can define their own
* rna properties for use with python operators or adding new properties to
* existing blender types.
2011-02-27 20:10:08 +00:00
*/
/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */
#define PY_SSIZE_T_CLEAN
#include <algorithm>
#include <string>
#include <Python.h>
#include "RNA_types.hh"
#include "BLI_array.hh"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "bpy_capi_utils.hh"
#include "bpy_props.hh"
#include "bpy_rna.hh"
#include "RNA_access.hh"
#include "RNA_define.hh" /* for defining our own rna */
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "MEM_guardedalloc.h"
#include "DNA_ID.h" /* MAX_IDPROP_NAME */
#include "../generic/py_capi_rna.hh"
#include "../generic/py_capi_utils.hh"
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
#include "../generic/python_utildefines.hh"
using blender::Array;
/* Disabled duplicating strings because the array can still be freed and
* the strings from it referenced, for now we can't support dynamically
* created strings from Python. */
// #define USE_ENUM_COPY_STRINGS
/* -------------------------------------------------------------------- */
/** \name Shared Enums & Doc-Strings
* \{ */
#define BPY_PROPDEF_OPTIONS_DOC \
" :arg options: Enumerator in :ref:`rna_enum_property_flag_items`.\n" \
" :type options: set[str]\n"
#define BPY_PROPDEF_OPTIONS_ENUM_DOC \
" :arg options: Enumerator in :ref:`rna_enum_property_flag_enum_items`.\n" \
" :type options: set[str]\n"
#define BPY_PROPDEF_OPTIONS_OVERRIDE_DOC \
" :arg override: Enumerator in :ref:`rna_enum_property_override_flag_items`.\n" \
" :type override: set[str]\n"
#define BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC \
" :arg override: Enumerator in :ref:`rna_enum_property_override_flag_collection_items`.\n" \
" :type override: set[str]\n"
#define BPY_PROPDEF_SUBTYPE_STRING_DOC \
" :arg subtype: Enumerator in :ref:`rna_enum_property_subtype_string_items`.\n" \
" :type subtype: str\n"
#define BPY_PROPDEF_SUBTYPE_NUMBER_DOC \
" :arg subtype: Enumerator in :ref:`rna_enum_property_subtype_number_items`.\n" \
" :type subtype: str\n"
#define BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC \
" :arg subtype: Enumerator in :ref:`rna_enum_property_subtype_number_array_items`.\n" \
" :type subtype: str\n"
/** \} */
/* -------------------------------------------------------------------- */
/** \name Python Property Storage API
*
* Functionality needed to use Python native callbacks from generic C RNA callbacks.
* \{ */
/**
* Store #PyObject data for a dynamically defined property.
* Currently this is only used to store call-back functions.
* Properties that don't use custom callbacks won't allocate this struct.
*
* Memory/Reference Management
* ---------------------------
*
* This struct adds/removes the user-count of each #PyObject it references,
* it's needed in case the function is removed from the class (unlikely but possible),
* also when an annotation evaluates to a `lambda` with Python 3.10 and newer e.g: #86332.
*
* Pointers to this struct are held in:
*
* - #PropertyRNA.py_data (owns the memory).
* Freed when the RNA property is freed.
*
* - #g_bpy_prop_store_list (borrows the memory)
* Having a global list means the users can be visited by the GC and cleared on exit.
*
* This list can't be used for freeing as #BPyPropStore doesn't hold a #PropertyRNA back-pointer,
* (while it could be supported it would only complicate things).
*
* All RNA properties are freed after Python has been shut-down.
* At that point Python user counts can't be touched and must have already been dealt with.
*
* Decrementing users is handled by:
*
* - #bpy_prop_py_data_remove manages decrementing at run-time (when a property is removed),
*
* - #BPY_rna_props_clear_all does this on exit for all dynamic properties.
*/
struct BPyPropStore {
BPyPropStore *next, *prev;
/**
* Only store #PyObject types, so this member can be cast to an array and iterated over.
* nullptr members are skipped.
*/
struct {
/**
* Wrap: `RNA_def_property_*_funcs` (depending on type).
*
* - `get`/`set` are used to provide access to a non-standard storage for the value (i.e. not
* in the default 'system-defined' IDProperties storage).
* - Their fallback implementation uses the system IDProperties storage system.
* - If `get` is specified, but not `set`, the property is considered read-only.
* - If `set` is specified, but not `get`, this is an error.
* - `get_transform`/`set_transform` are used to perform some additional transformation of the
* data, after `get` is called / before `set` is called (or their matching default get/set
* implementations).
* - Their fallback implementation is 'pass-through'.
*
* Conceptually, the flow of callings is:
* - getter:
* `return get_transform(self, get(self), is_property_set(self, "prop"))`
* - setter:
* `set(self, set_transform(self, new_value, get(self), is_property_set(self, "prop")))`
*/
PyObject *get_fn;
PyObject *set_fn;
PyObject *get_transform_fn;
PyObject *set_transform_fn;
/** Wrap: #RNA_def_property_update_runtime */
PyObject *update_fn;
/** Arguments by type. */
union {
/** #PROP_ENUM type. */
struct {
/** Wrap: #RNA_def_property_enum_funcs_runtime */
PyObject *itemf_fn;
} enum_data;
/** #PROP_POINTER type. */
struct {
/** Wrap: #RNA_def_property_poll_runtime */
PyObject *poll_fn;
} pointer_data;
/** #PROP_STRING type. */
struct {
/** Wrap: #RNA_def_property_string_search_func_runtime */
PyObject *search_fn;
} string_data;
};
} py_data;
};
#define BPY_PROP_STORE_PY_DATA_SIZE (sizeof(BPyPropStore::py_data) / sizeof(PyObject *))
#define ASSIGN_PYOBJECT_INCREF(a, b) \
{ \
BLI_assert((a) == nullptr); \
Py_INCREF(b); \
a = b; \
} \
((void)0)
/**
* Maintain a list of Python defined properties, so the GC can visit them,
* and so they can be cleared on exit.
*/
static ListBase g_bpy_prop_store_list = {nullptr, nullptr};
static BPyPropStore *bpy_prop_py_data_ensure(PropertyRNA *prop)
{
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
if (prop_store == nullptr) {
prop_store = MEM_callocN<BPyPropStore>(__func__);
RNA_def_py_data(prop, prop_store);
BLI_addtail(&g_bpy_prop_store_list, prop_store);
}
return prop_store;
}
/**
* Perform all removal actions except for freeing, which is handled by RNA.
*/
static void bpy_prop_py_data_remove(PropertyRNA *prop)
{
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
if (prop_store == nullptr) {
return;
}
PyObject **py_data = (PyObject **)&prop_store->py_data;
for (int i = 0; i < BPY_PROP_STORE_PY_DATA_SIZE; i++) {
Py_XDECREF(py_data[i]);
}
BLI_remlink(&g_bpy_prop_store_list, prop_store);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred Property Type
*
* Operators and classes use this so it can store the arguments given but defer
* running it until the operator runs where these values are used to setup
* the default arguments for that operator instance.
* \{ */
static void bpy_prop_deferred_dealloc(BPy_PropDeferred *self)
{
PyObject_GC_UnTrack(self);
Py_CLEAR(self->kw);
PyObject_GC_Del(self);
}
static int bpy_prop_deferred_traverse(BPy_PropDeferred *self, visitproc visit, void *arg)
{
Py_VISIT(self->kw);
return 0;
}
static int bpy_prop_deferred_clear(BPy_PropDeferred *self)
{
Py_CLEAR(self->kw);
return 0;
}
static PyObject *bpy_prop_deferred_repr(BPy_PropDeferred *self)
{
return PyUnicode_FromFormat("<%.200s, %R, %R>", Py_TYPE(self)->tp_name, self->fn, self->kw);
}
/**
* HACK: needed by `typing.get_type_hints`
* with `from __future__ import annotations` enabled or when using Python 3.10 or newer.
*
* When callable this object type passes the test for being an acceptable annotation.
*/
static PyObject *bpy_prop_deferred_call(BPy_PropDeferred * /*self*/,
PyObject * /*args*/,
PyObject * /*kw*/)
{
/* Dummy value. */
Py_RETURN_NONE;
}
/* Get/Set Items. */
/**
* Expose the function in case scripts need to introspect this information
2021-11-07 23:20:29 -06:00
* (not currently used by Blender itself).
*/
static PyObject *bpy_prop_deferred_function_get(BPy_PropDeferred *self, void * /*closure*/)
{
PyObject *ret = static_cast<PyObject *>(self->fn);
Py_IncRef(ret);
return ret;
}
/**
* Expose keywords in case scripts need to introspect this information
2021-11-07 23:20:29 -06:00
* (not currently used by Blender itself).
*/
static PyObject *bpy_prop_deferred_keywords_get(BPy_PropDeferred *self, void * /*closure*/)
{
PyObject *ret = self->kw;
Py_IncRef(ret);
return ret;
}
static PyGetSetDef bpy_prop_deferred_getset[] = {
{"function", (getter)bpy_prop_deferred_function_get, (setter) nullptr, nullptr, nullptr},
{"keywords", (getter)bpy_prop_deferred_keywords_get, (setter) nullptr, nullptr, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
};
PyDoc_STRVAR(
/* Wrap. */
bpy_prop_deferred_doc,
"Intermediate storage for properties before registration.\n"
"\n"
".. note::\n"
"\n"
" This is not part of the stable API and may change between releases.\n");
PyTypeObject bpy_prop_deferred_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "_PropertyDeferred",
/*tp_basicsize*/ sizeof(BPy_PropDeferred),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)bpy_prop_deferred_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ (reprfunc)bpy_prop_deferred_repr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ (ternaryfunc)bpy_prop_deferred_call,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
/*tp_doc*/ bpy_prop_deferred_doc,
/*tp_traverse*/ (traverseproc)bpy_prop_deferred_traverse,
/*tp_clear*/ (inquiry)bpy_prop_deferred_clear,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ nullptr,
/*tp_members*/ nullptr,
/*tp_getset*/ bpy_prop_deferred_getset,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_free*/ nullptr,
/*tp_is_gc*/ nullptr,
/*tp_bases*/ nullptr,
/*tp_mro*/ nullptr,
/*tp_cache*/ nullptr,
/*tp_subclasses*/ nullptr,
/*tp_weaklist*/ nullptr,
/*tp_del*/ nullptr,
/*tp_version_tag*/ 0,
/*tp_finalize*/ nullptr,
/*tp_vectorcall*/ nullptr,
};
static PyObject *bpy_prop_deferred_data_CreatePyObject(PyObject *fn, PyObject *kw)
{
BPy_PropDeferred *self = PyObject_GC_New(BPy_PropDeferred, &bpy_prop_deferred_Type);
self->fn = fn;
if (kw == nullptr) {
kw = PyDict_New();
}
else {
Py_INCREF(kw);
}
self->kw = kw;
BLI_assert(!PyObject_GC_IsTracked((PyObject *)self));
PyObject_GC_Track(self);
return (PyObject *)self;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shared Property Utilities
* \{ */
/* PyObject's */
static PyObject *pymeth_BoolProperty = nullptr;
static PyObject *pymeth_BoolVectorProperty = nullptr;
static PyObject *pymeth_IntProperty = nullptr;
static PyObject *pymeth_IntVectorProperty = nullptr;
static PyObject *pymeth_FloatProperty = nullptr;
static PyObject *pymeth_FloatVectorProperty = nullptr;
static PyObject *pymeth_StringProperty = nullptr;
static PyObject *pymeth_EnumProperty = nullptr;
static PyObject *pymeth_PointerProperty = nullptr;
static PyObject *pymeth_CollectionProperty = nullptr;
static PyObject *pymeth_RemoveProperty = nullptr;
static PyObject *pyrna_struct_as_instance(PointerRNA *ptr)
{
PyObject *self = nullptr;
/* first get self */
/* operators can store their own instance for later use */
2011-10-13 01:29:08 +00:00
if (ptr->data) {
void **instance = RNA_struct_instance(ptr);
2011-10-13 01:29:08 +00:00
if (instance) {
if (*instance) {
self = static_cast<PyObject *>(*instance);
Py_INCREF(self);
}
}
}
/* in most cases this will run */
if (self == nullptr) {
self = pyrna_struct_CreatePyObject(ptr);
}
return self;
}
static void bpy_prop_assign_flag(PropertyRNA *prop, int flag)
{
/* Map `READ_ONLY` to `EDITABLE`. */
flag ^= PROP_EDITABLE;
/* The default is editable. */
const int flag_mask_set = (flag & ~PROP_EDITABLE);
const int flag_mask_clear = ((PROP_ANIMATABLE | PROP_EDITABLE) & ~flag);
if (flag_mask_set) {
RNA_def_property_flag(prop, PropertyFlag(flag_mask_set));
}
if (flag_mask_clear) {
RNA_def_property_clear_flag(prop, PropertyFlag(flag_mask_clear));
}
}
static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_override)
{
RNA_def_property_override_flag(prop, PropertyOverrideFlag(flag_override));
}
/* These utility functions de-duplicate boiler plate code used by most property callbacks.
* It's important they're at the beginning & end of the callbacks and both are always called. */
struct BPyPropGIL_RNAWritable_State {
PyGILState_STATE gilstate;
bool is_write_ok;
};
static BPyPropGIL_RNAWritable_State bpy_prop_gil_rna_writable_begin()
{
/* It's important to acquire the lock before reading other state information, see: #127767. */
const PyGILState_STATE gilstate = PyGILState_Ensure();
const bool is_write_ok = pyrna_write_check();
return {
/*gilstate*/ gilstate,
/*is_write_ok*/ is_write_ok,
};
}
static void bpy_prop_gil_rna_writable_end(const BPyPropGIL_RNAWritable_State &prop_state)
{
if (!prop_state.is_write_ok) {
pyrna_write_set(false);
}
PyGILState_Release(prop_state.gilstate);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Multi-Dimensional Property Utilities
* \{ */
struct BPyPropArrayLength {
int len_total = 0;
/** Ignore `dims` when `dims_len == 0`. */
int dims[RNA_MAX_ARRAY_DIMENSION] = {};
int dims_len = 0;
BPyPropArrayLength() = default;
BPyPropArrayLength(PointerRNA *ptr, PropertyRNA *prop)
{
this->len_total = RNA_property_array_length(ptr, prop);
this->dims_len = RNA_property_array_dimension(ptr, prop, this->dims);
}
bool operator==(const BPyPropArrayLength &other) const
{
if ((this->len_total != other.len_total) || (this->dims_len != other.dims_len)) {
return false;
}
for (int i = 0; i < this->dims_len; i++) {
if (this->dims[i] != other.dims[i]) {
return false;
}
}
return true;
}
};
/**
* Use with #PyArg_ParseTuple's `O&` formatting.
*/
static int bpy_prop_array_length_parse(PyObject *o, void *p)
{
BPyPropArrayLength *array_len_info = static_cast<BPyPropArrayLength *>(p);
if (PyLong_CheckExact(o)) {
int size;
if ((size = PyLong_AsLong(o)) == -1) {
PyErr_Format(
PyExc_ValueError, "expected number or sequence of numbers, got %s", Py_TYPE(o)->tp_name);
return 0;
}
if (size < 1 || size > PYRNA_STACK_ARRAY) {
PyErr_Format(
PyExc_TypeError, "(size=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY), size);
return 0;
}
array_len_info->len_total = size;
/* Don't use this value. */
array_len_info->dims_len = 0;
}
else {
PyObject *seq_fast;
if (!(seq_fast = PySequence_Fast(o, "size must be a number of a sequence of numbers"))) {
return 0;
}
const int seq_len = PySequence_Fast_GET_SIZE(seq_fast);
if (seq_len < 1 || seq_len > RNA_MAX_ARRAY_DIMENSION) {
PyErr_Format(
PyExc_TypeError,
"(len(size)=%d) length must be between 1 and " STRINGIFY(RNA_MAX_ARRAY_DIMENSION),
seq_len);
Py_DECREF(seq_fast);
return 0;
}
PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
array_len_info->len_total = 1;
for (int i = 0; i < seq_len; i++) {
int size;
if ((size = PyLong_AsLong(seq_items[i])) == -1) {
Py_DECREF(seq_fast);
PyErr_Format(PyExc_ValueError,
"expected number in sequence, got %s at index %d",
Py_TYPE(o)->tp_name,
i);
return 0;
}
if (size < 1 || size > PYRNA_STACK_ARRAY) {
Py_DECREF(seq_fast);
PyErr_Format(PyExc_TypeError,
"(size[%d]=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY),
i,
size);
return 0;
}
array_len_info->dims[i] = size;
array_len_info->dims_len = seq_len;
array_len_info->len_total *= size;
}
}
return 1;
}
/**
* Return -1 on error.
*/
static int bpy_prop_array_from_py_with_dims(void *values,
size_t values_elem_size,
PyObject *py_values,
const BPyPropArrayLength *array_len_info,
const PyTypeObject *type,
const char *error_str)
{
if (array_len_info->dims_len == 0) {
return PyC_AsArray(
values, values_elem_size, py_values, array_len_info->len_total, type, error_str);
}
const int *dims = array_len_info->dims;
const int dims_len = array_len_info->dims_len;
return PyC_AsArray_Multi(values, values_elem_size, py_values, dims, dims_len, type, error_str);
}
/* NOTE: Always increases refcount of the returned value. */
static PyObject *bpy_py_object_from_prop_array_with_dims(const void *values,
const BPyPropArrayLength &array_len_info,
const PyTypeObject &type)
{
PyObject *py_values = nullptr;
if (&type == &PyBool_Type) {
if (array_len_info.dims_len == 0) {
py_values = PyC_Tuple_PackArray_Bool(static_cast<const bool *>(values),
uint(array_len_info.len_total));
}
else {
py_values = PyC_Tuple_PackArray_Multi_Bool(
static_cast<const bool *>(values), array_len_info.dims, array_len_info.dims_len);
}
}
else if (&type == &PyLong_Type) {
if (array_len_info.dims_len == 0) {
py_values = PyC_Tuple_PackArray_I32(static_cast<const int *>(values),
uint(array_len_info.len_total));
}
else {
py_values = PyC_Tuple_PackArray_Multi_I32(
static_cast<const int *>(values), array_len_info.dims, array_len_info.dims_len);
}
}
else if (&type == &PyFloat_Type) {
if (array_len_info.dims_len == 0) {
py_values = PyC_Tuple_PackArray_F32(static_cast<const float *>(values),
uint(array_len_info.len_total));
}
else {
/* No need for matrix column/row swapping here unless the matrix data is read directly. */
py_values = PyC_Tuple_PackArray_Multi_F32(
static_cast<const float *>(values), array_len_info.dims, array_len_info.dims_len);
}
}
else {
BLI_assert_unreachable();
}
return py_values;
}
static bool bpy_prop_array_is_matrix_compatible_ex(int subtype,
const BPyPropArrayLength *array_len_info)
{
return ((subtype == PROP_MATRIX) && (array_len_info->dims_len == 2) &&
((array_len_info->dims[0] >= 2) && (array_len_info->dims[0] <= 4)) &&
((array_len_info->dims[1] >= 2) && (array_len_info->dims[1] <= 4)));
}
static bool bpy_prop_array_is_matrix_compatible(PropertyRNA *prop,
const BPyPropArrayLength *array_len_info)
{
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
return bpy_prop_array_is_matrix_compatible_ex(RNA_property_subtype(prop), array_len_info);
}
/**
* Needed since the internal storage of matrices swaps row/column.
*/
static void bpy_prop_array_matrix_swap_row_column_vn_vn(float *values_dst,
const float *values_src,
const BPyPropArrayLength *array_len_info)
{
BLI_assert(values_dst != values_src);
const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1];
BLI_assert(dim0 <= 4 && dim1 <= 4);
for (int i = 0; i < dim0; i++) {
for (int j = 0; j < dim1; j++) {
values_dst[(j * dim0) + i] = values_src[(i * dim1) + j];
}
}
}
static void bpy_prop_array_matrix_swap_row_column_vn(float *values,
const BPyPropArrayLength *array_len_info)
{
const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1];
BLI_assert(dim0 <= 4 && dim1 <= 4);
float values_orig[4 * 4];
memcpy(values_orig, values, sizeof(float) * (dim0 * dim1));
bpy_prop_array_matrix_swap_row_column_vn_vn(values, values_orig, array_len_info);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shared Property Callbacks
*
* Unique data is accessed via #RNA_property_py_data_get
* \{ */
/* callbacks */
static void bpy_prop_update_fn(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
{
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
const bool is_write_ok = pyrna_write_check();
if (!is_write_ok) {
pyrna_write_set(true);
}
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.update_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, reinterpret_cast<PyObject *>(bpy_context_module));
Py_INCREF(bpy_context_module);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
2011-10-13 01:29:08 +00:00
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
2011-10-13 01:29:08 +00:00
if (!is_write_ok) {
pyrna_write_set(false);
}
bpy_context_clear(C, &gilstate);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Boolean Property Callbacks
* \{ */
static bool bpy_prop_boolean_get_fn(PointerRNA *ptr, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
bool value;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
value = false;
}
else {
const int value_i = PyC_Long_AsBool(ret);
if (value_i == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
value = false;
}
else {
value = bool(value_i);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return value;
}
static bool bpy_prop_boolean_get_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
bool curr_value,
bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyBool_FromLong(curr_value), PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bool ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
const int value_i = PyC_Long_AsBool(ret);
if (value_i == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = bool(value_i);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_boolean_set_fn(PointerRNA *ptr, PropertyRNA *prop, bool value)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyBool_FromLong(value));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static bool bpy_prop_boolean_set_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, bool new_value, bool curr_value, bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args,
self,
PyBool_FromLong(new_value),
PyBool_FromLong(curr_value),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bool ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
const int value_i = PyC_Long_AsBool(ret);
if (value_i == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = bool(value_i);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_boolean_array_from_callback_or_error(PyObject *bool_array_obj,
const BPyPropArrayLength &array_len_info,
PyObject *py_func,
bool *r_values)
{
bool is_values_set = false;
if (bool_array_obj != nullptr) {
if (bpy_prop_array_from_py_with_dims(r_values,
sizeof(*r_values),
bool_array_obj,
&array_len_info,
&PyBool_Type,
"BoolVectorProperty get callback") == -1)
{
PyC_Err_PrintWithFunc(py_func);
}
else {
is_values_set = true;
}
}
if (is_values_set == false) {
/* This is the flattened length for multi-dimensional arrays. */
for (int i = 0; i < array_len_info.len_total; i++) {
r_values[i] = false;
}
}
Py_XDECREF(bool_array_obj);
}
static void bpy_prop_boolean_array_get_fn(PointerRNA *ptr, PropertyRNA *prop, bool *values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
const BPyPropArrayLength array_len_info{ptr, prop};
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bpy_prop_boolean_array_from_callback_or_error(ret, array_len_info, py_func, values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_boolean_array_get_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, const bool *curr_values, bool is_set, bool *r_values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
const BPyPropArrayLength array_len_info{ptr, prop};
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
bpy_py_object_from_prop_array_with_dims(curr_values, array_len_info, PyBool_Type),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bpy_prop_boolean_array_from_callback_or_error(ret, array_len_info, py_func, r_values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_boolean_array_set_fn(PointerRNA *ptr, PropertyRNA *prop, const bool *values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
const BPyPropArrayLength array_len_info{ptr, prop};
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args, self, bpy_py_object_from_prop_array_with_dims(values, array_len_info, PyBool_Type));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_boolean_array_set_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
const bool *new_values,
const bool *curr_values,
bool is_set,
bool *r_final_values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
const BPyPropArrayLength array_len_info{ptr, prop};
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
bpy_py_object_from_prop_array_with_dims(new_values, array_len_info, PyBool_Type),
bpy_py_object_from_prop_array_with_dims(curr_values, array_len_info, PyBool_Type),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bpy_prop_boolean_array_from_callback_or_error(ret, array_len_info, py_func, r_final_values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Int Property Callbacks
* \{ */
static int bpy_prop_int_get_fn(PointerRNA *ptr, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
int value;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
value = 0;
}
else {
value = PyC_Long_AsI32(ret);
if (value == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
value = 0;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return value;
}
static int bpy_prop_int_get_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
int curr_value,
bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyLong_FromLong(curr_value), PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
int ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = PyC_Long_AsI32(ret);
if (ret_value == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
ret_value = curr_value;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_int_set_fn(PointerRNA *ptr, PropertyRNA *prop, int value)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyLong_FromLong(value));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static int bpy_prop_int_set_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, int new_value, int curr_value, bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args,
self,
PyLong_FromLong(new_value),
PyLong_FromLong(curr_value),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
int ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = PyC_Long_AsI32(ret);
if (ret_value == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
ret_value = curr_value;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_int_array_from_callback_or_error(PyObject *int_array_obj,
const BPyPropArrayLength &array_len_info,
PyObject *py_func,
int *r_values)
{
bool is_values_set = false;
if (int_array_obj != nullptr) {
if (bpy_prop_array_from_py_with_dims(r_values,
sizeof(*r_values),
int_array_obj,
&array_len_info,
&PyLong_Type,
"IntVectorProperty get callback") == -1)
{
PyC_Err_PrintWithFunc(py_func);
}
else {
is_values_set = true;
}
}
if (is_values_set == false) {
/* This is the flattened length for multi-dimensional arrays. */
for (int i = 0; i < array_len_info.len_total; i++) {
r_values[i] = 0;
}
}
Py_XDECREF(int_array_obj);
}
static void bpy_prop_int_array_get_fn(PointerRNA *ptr, PropertyRNA *prop, int *values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
const BPyPropArrayLength array_len_info{ptr, prop};
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bpy_prop_int_array_from_callback_or_error(ret, array_len_info, py_func, values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_int_array_get_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, const int *curr_values, bool is_set, int *r_values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
const BPyPropArrayLength array_len_info{ptr, prop};
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
bpy_py_object_from_prop_array_with_dims(curr_values, array_len_info, PyLong_Type),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bpy_prop_int_array_from_callback_or_error(ret, array_len_info, py_func, r_values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_int_array_set_fn(PointerRNA *ptr, PropertyRNA *prop, const int *values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
const BPyPropArrayLength array_len_info{ptr, prop};
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args, self, bpy_py_object_from_prop_array_with_dims(values, array_len_info, PyLong_Type));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_int_array_set_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
const int *new_values,
const int *curr_values,
bool is_set,
int *r_final_values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
const BPyPropArrayLength array_len_info{ptr, prop};
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
bpy_py_object_from_prop_array_with_dims(new_values, array_len_info, PyLong_Type),
bpy_py_object_from_prop_array_with_dims(curr_values, array_len_info, PyLong_Type),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
bpy_prop_int_array_from_callback_or_error(ret, array_len_info, py_func, r_final_values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Float Property Callbacks
* \{ */
static float bpy_prop_float_get_fn(PointerRNA *ptr, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
float value;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
value = 0.0f;
}
else {
value = PyFloat_AsDouble(ret);
if (value == -1.0f && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
value = 0.0f;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return value;
}
static float bpy_prop_float_get_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
float curr_value,
bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyFloat_FromDouble(curr_value), PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
float ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = PyFloat_AsDouble(ret);
if (ret_value == -1.0f && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
ret_value = curr_value;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_float_set_fn(PointerRNA *ptr, PropertyRNA *prop, float value)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyFloat_FromDouble(value));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static float bpy_prop_float_set_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, float new_value, float curr_value, bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args,
self,
PyFloat_FromDouble(new_value),
PyFloat_FromDouble(curr_value),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
float ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = PyFloat_AsDouble(ret);
if (ret_value == -1.0f && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
ret_value = curr_value;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_float_array_from_callback_or_error(PropertyRNA *prop,
PyObject *float_array_obj,
const BPyPropArrayLength &array_len_info,
PyObject *py_func,
const bool do_matrix_row_col_swap,
float *r_values)
{
bool is_values_set = false;
if (float_array_obj != nullptr) {
if (bpy_prop_array_from_py_with_dims(r_values,
sizeof(*r_values),
float_array_obj,
&array_len_info,
&PyFloat_Type,
"FloatVectorProperty get callback") == -1)
{
PyC_Err_PrintWithFunc(py_func);
}
else {
/* Only for float types. */
/* TODO: Clear and comnplete explanations about this matrix swap? */
if (do_matrix_row_col_swap && bpy_prop_array_is_matrix_compatible(prop, &array_len_info)) {
bpy_prop_array_matrix_swap_row_column_vn(r_values, &array_len_info);
}
is_values_set = true;
}
}
if (is_values_set == false) {
/* This is the flattened length for multi-dimensional arrays. */
for (int i = 0; i < array_len_info.len_total; i++) {
r_values[i] = 0.0f;
}
}
Py_XDECREF(float_array_obj);
}
static void bpy_prop_float_array_get_fn(PointerRNA *ptr, PropertyRNA *prop, float *values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
const BPyPropArrayLength array_len_info{ptr, prop};
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
/* Custom getter always needs to perform the matrix row/col swap. */
bpy_prop_float_array_from_callback_or_error(prop, ret, array_len_info, py_func, true, values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_float_array_get_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, const float *curr_values, bool is_set, float *r_values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
const BPyPropArrayLength array_len_info{ptr, prop};
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
bpy_py_object_from_prop_array_with_dims(curr_values, array_len_info, PyFloat_Type),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
/* If there is a custom py-defined 'get' callback, the row/col matrix swap has already been
* performed, otherwise it needs to be done here. */
const bool do_matrix_row_col_swap = prop_store->py_data.get_fn == nullptr;
bpy_prop_float_array_from_callback_or_error(
prop, ret, array_len_info, py_func, do_matrix_row_col_swap, r_values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_float_array_set_fn(PointerRNA *ptr, PropertyRNA *prop, const float *values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
const BPyPropArrayLength array_len_info{ptr, prop};
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args, self, bpy_py_object_from_prop_array_with_dims(values, array_len_info, PyFloat_Type));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static void bpy_prop_float_array_set_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
const float *new_values,
const float *curr_values,
bool is_set,
float *r_final_values)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
const BPyPropArrayLength array_len_info{ptr, prop};
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
bpy_py_object_from_prop_array_with_dims(new_values, array_len_info, PyFloat_Type),
bpy_py_object_from_prop_array_with_dims(curr_values, array_len_info, PyFloat_Type),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
/* No need for matrix column/row swapping here unless the matrix data is read directly. */
bpy_prop_float_array_from_callback_or_error(
prop, ret, array_len_info, py_func, false, r_final_values);
bpy_prop_gil_rna_writable_end(bpy_state);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Property Callbacks
* \{ */
static std::optional<std::string> bpy_prop_string_from_callback_or_error(PyObject *str_obj,
const size_t max_length,
PyObject *py_func)
{
std::optional<std::string> ret_value{};
/* TODO: handle bytes strings. */
if (str_obj == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else if (!PyUnicode_Check(str_obj)) {
PyErr_Format(
PyExc_TypeError, "return value must be a string, not %.200s", Py_TYPE(str_obj)->tp_name);
PyC_Err_PrintWithFunc(py_func);
}
else {
/* NOTE: Python returns the length _without_ the `\0` terminator. */
Py_ssize_t length;
const char *ret_cstr = PyUnicode_AsUTF8AndSize(str_obj, &length);
if (max_length && size_t(length) + 1 > max_length) {
PyErr_Format(PyExc_ValueError,
"return string must be of max length %zu, not %d",
max_length - 1,
length);
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = {ret_cstr, size_t(length)};
}
}
Py_XDECREF(str_obj);
return ret_value;
}
static std::string bpy_prop_string_get_locked_fn(PointerRNA *ptr, PropertyRNA *prop)
{
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
return bpy_prop_string_from_callback_or_error(ret, RNA_property_string_maxlength(prop), py_func)
.value_or("");
}
static std::string bpy_prop_string_get_fn(PointerRNA *ptr, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
std::string ret_value = bpy_prop_string_get_locked_fn(ptr, prop);
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static std::string bpy_prop_string_get_transform_locked_fn(PointerRNA *ptr,
PropertyRNA *prop,
const std::string &curr_value,
bool is_set)
{
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
/* TODO: handle bytes strings. */
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
PyUnicode_FromStringAndSize(curr_value.c_str(), Py_ssize_t(curr_value.size())),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
return bpy_prop_string_from_callback_or_error(ret, RNA_property_string_maxlength(prop), py_func)
.value_or(curr_value);
}
static std::string bpy_prop_string_get_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
const std::string &curr_value,
bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
std::string ret_value = bpy_prop_string_get_transform_locked_fn(ptr, prop, curr_value, is_set);
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static int bpy_prop_string_length_fn(PointerRNA *ptr, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
/* This bpyprops-specific length callback is only called when there is a custom `get` function.
*/
std::string ret = bpy_prop_string_get_locked_fn(ptr, prop);
const int length = int(ret.size());
bpy_prop_gil_rna_writable_end(bpy_state);
return length;
}
static void bpy_prop_string_set_fn(PointerRNA *ptr, PropertyRNA *prop, const std::string &value)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
/* TODO: handle bytes strings. */
const size_t max_length = RNA_property_string_maxlength(prop);
if (max_length && value.size() >= max_length) {
PyErr_Format(PyExc_ValueError,
"the given string must be of max length %zu, not %zu",
max_length - 1,
value.size());
PyC_Err_PrintWithFunc(py_func);
}
PyObject *py_value = PyUnicode_FromStringAndSize(value.c_str(), value.size());
if (!py_value) {
PyErr_SetString(PyExc_ValueError,
"the given string value cannot be converted into a python string");
PyC_Err_PrintWithFunc(py_func);
py_value = Py_None;
Py_INCREF(py_value);
}
PyTuple_SET_ITEMS(args, self, py_value);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static std::string bpy_prop_string_set_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
const std::string &new_value,
const std::string &curr_value,
bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(
args,
self,
PyUnicode_FromStringAndSize(new_value.c_str(), Py_ssize_t(new_value.size())),
PyUnicode_FromStringAndSize(curr_value.c_str(), Py_ssize_t(curr_value.size())),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
std::string ret_value = bpy_prop_string_from_callback_or_error(
ret, RNA_property_string_maxlength(prop), py_func)
.value_or(curr_value);
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static bool bpy_prop_string_visit_fn_call(
PyObject *py_func,
PyObject *item,
blender::FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
{
const char *text;
const char *info = nullptr;
if (PyTuple_CheckExact(item)) {
/* Positional only. */
static const char *_keywords[] = {
"",
"",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"s" /* `text` */
"s" /* `info` */
":search",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(item, nullptr, &_parser, &text, &info)) {
PyC_Err_PrintWithFunc(py_func);
return false;
}
}
else {
text = PyUnicode_AsUTF8(item);
if (UNLIKELY(text == nullptr)) {
PyErr_Clear();
PyErr_Format(PyExc_TypeError,
"expected sequence of strings or tuple pairs of strings, not %.200s",
Py_TYPE(item)->tp_name);
PyC_Err_PrintWithFunc(py_func);
return false;
}
}
StringPropertySearchVisitParams visit_params{};
visit_params.text = text;
visit_params.info = info ? info : "";
visit_fn(visit_params);
return true;
}
static void bpy_prop_string_visit_for_search_fn(
const bContext *C,
PointerRNA *ptr,
PropertyRNA *prop,
const char *edit_text,
blender::FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
{
PyGILState_STATE gilstate;
if (C) {
bpy_context_set((bContext *)C, &gilstate);
}
else {
gilstate = PyGILState_Ensure();
}
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.string_data.search_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyObject *py_context = reinterpret_cast<PyObject *>(bpy_context_module);
PyObject *py_edit_text = PyUnicode_FromString(edit_text);
Py_INCREF(py_context);
PyTuple_SET_ITEMS(args, self, py_context, py_edit_text);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (PyIter_Check(ret)) {
/* Iterators / generator types. */
PyObject *it;
PyObject *(*iternext)(PyObject *);
it = PyObject_GetIter(ret);
if (it == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
iternext = *Py_TYPE(it)->tp_iternext;
for (;;) {
PyObject *py_text = iternext(it);
if (py_text == nullptr) {
break;
}
const bool ok = bpy_prop_string_visit_fn_call(py_func, py_text, visit_fn);
Py_DECREF(py_text);
if (!ok) {
break;
}
}
Py_DECREF(it);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
PyErr_Clear();
}
else {
PyC_Err_PrintWithFunc(py_func);
}
}
}
}
else {
/* Sequence (typically list/tuple). */
PyObject *ret_fast = PySequence_Fast(
ret,
"StringProperty(...): "
"return value from search callback was not a sequence, iterator or generator");
if (ret_fast == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
const Py_ssize_t ret_num = PySequence_Fast_GET_SIZE(ret_fast);
PyObject **ret_fast_items = PySequence_Fast_ITEMS(ret_fast);
for (Py_ssize_t i = 0; i < ret_num; i++) {
const bool ok = bpy_prop_string_visit_fn_call(py_func, ret_fast_items[i], visit_fn);
if (!ok) {
break;
}
}
Py_DECREF(ret_fast);
}
}
Py_DECREF(ret);
}
if (C) {
bpy_context_clear((bContext *)C, &gilstate);
}
else {
PyGILState_Release(gilstate);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pointer Property Callbacks
* \{ */
static bool bpy_prop_pointer_poll_fn(PointerRNA *self, PointerRNA candidate, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
bool result;
BLI_assert(self != nullptr);
py_func = prop_store->py_data.pointer_data.poll_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *py_self = pyrna_struct_as_instance(self);
PyObject *py_candidate = pyrna_struct_as_instance(&candidate);
PyTuple_SET_ITEMS(args, py_self, py_candidate);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
result = false;
}
else {
result = PyObject_IsTrue(ret);
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Enum Property Callbacks
* \{ */
static int bpy_prop_enum_get_fn(PointerRNA *ptr, PropertyRNA *prop)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
int value;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_fn;
{
PyObject *args = PyTuple_New(1);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self);
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
value = RNA_property_enum_get_default(ptr, prop);
}
else {
value = PyC_Long_AsI32(ret);
if (value == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
value = RNA_property_enum_get_default(ptr, prop);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return value;
}
static int bpy_prop_enum_get_transform_fn(PointerRNA *ptr,
PropertyRNA *prop,
int curr_value,
bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.get_transform_fn;
{
PyObject *args = PyTuple_New(3);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyLong_FromLong(curr_value), PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
int ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = PyC_Long_AsI32(ret);
if (ret_value == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
ret_value = curr_value;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
static void bpy_prop_enum_set_fn(PointerRNA *ptr, PropertyRNA *prop, int value)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_fn;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args, self, PyLong_FromLong(value));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
if (ret != Py_None) {
PyErr_SetString(PyExc_ValueError, "the return value must be None");
PyC_Err_PrintWithFunc(py_func);
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
}
static int bpy_prop_enum_set_transform_fn(
PointerRNA *ptr, PropertyRNA *prop, int new_value, int curr_value, bool is_set)
{
const BPyPropGIL_RNAWritable_State bpy_state = bpy_prop_gil_rna_writable_begin();
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func;
PyObject *ret;
BLI_assert(prop_store != nullptr);
py_func = prop_store->py_data.set_transform_fn;
{
PyObject *args = PyTuple_New(4);
PyObject *self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEMS(args,
self,
PyLong_FromLong(new_value),
PyLong_FromLong(curr_value),
PyBool_FromLong(is_set));
ret = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
int ret_value = curr_value;
if (ret == nullptr) {
PyC_Err_PrintWithFunc(py_func);
}
else {
ret_value = PyC_Long_AsI32(ret);
if (ret_value == -1 && PyErr_Occurred()) {
PyC_Err_PrintWithFunc(py_func);
ret_value = curr_value;
}
Py_DECREF(ret);
}
bpy_prop_gil_rna_writable_end(bpy_state);
return ret_value;
}
/* utility function we need for parsing int's in an if statement */
static bool py_long_as_int(PyObject *py_long, int *r_int)
{
if (PyLong_CheckExact(py_long)) {
*r_int = int(PyLong_AS_LONG(py_long));
return true;
}
return false;
}
#ifdef USE_ENUM_COPY_STRINGS
/* copies orig to buf, then sets orig to buf, returns copy length */
static size_t strswapbufcpy(char *buf, const char **orig)
{
const char *src = *orig;
char *dst = buf;
size_t i = 0;
*orig = buf;
while ((*dst = *src)) {
dst++;
src++;
i++;
}
return i + 1; /* include '\0' */
}
#endif
static int icon_id_from_name(const char *name)
{
const EnumPropertyItem *item;
int id;
if (name[0]) {
for (item = rna_enum_icon_items, id = 0; item->identifier; item++, id++) {
if (STREQ(item->name, name)) {
return item->value;
}
}
}
return 0;
}
static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast,
const bool is_enum_flag,
PyObject *default_py,
int *r_default_value)
{
EnumPropertyItem *items;
PyObject *item;
const Py_ssize_t seq_len = PySequence_Fast_GET_SIZE(seq_fast);
PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
int i;
#ifdef USE_ENUM_COPY_STRINGS
Py_ssize_t totbuf = 0;
#endif
short default_used = 0;
const char *default_str_cmp = nullptr;
int default_int_cmp = 0;
if (is_enum_flag) {
if (seq_len > RNA_ENUM_BITFLAG_SIZE) {
PyErr_SetString(PyExc_TypeError,
"EnumProperty(...): maximum " STRINGIFY(
RNA_ENUM_BITFLAG_SIZE) " members for a ENUM_FLAG type property");
return nullptr;
}
if (default_py && !PySet_Check(default_py)) {
PyErr_Format(PyExc_TypeError,
"EnumProperty(...): default option must be a 'set' "
"type when ENUM_FLAG is enabled, not a '%.200s'",
Py_TYPE(default_py)->tp_name);
return nullptr;
}
}
else {
if (default_py) {
if (!py_long_as_int(default_py, &default_int_cmp)) {
default_str_cmp = PyUnicode_AsUTF8(default_py);
if (default_str_cmp == nullptr) {
PyErr_Format(PyExc_TypeError,
"EnumProperty(...): default option must be a 'str' or 'int' "
"type when ENUM_FLAG is disabled, not a '%.200s'",
Py_TYPE(default_py)->tp_name);
return nullptr;
}
}
}
}
/* blank value */
*r_default_value = 0;
items = MEM_calloc_arrayN<EnumPropertyItem>(size_t(seq_len) + 1, "enum_items_from_py1");
for (i = 0; i < seq_len; i++) {
EnumPropertyItem tmp = {0, "", 0, "", ""};
const char *tmp_icon = nullptr;
Py_ssize_t item_size;
Py_ssize_t id_str_len;
Py_ssize_t name_str_len;
Py_ssize_t desc_str_len;
item = seq_fast_items[i];
2021-07-16 11:45:52 +10:00
if (PyTuple_CheckExact(item) && (item_size = PyTuple_GET_SIZE(item)) &&
(item_size >= 3 && item_size <= 5) &&
(tmp.identifier = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 0), &id_str_len)) &&
(tmp.name = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 1), &name_str_len)) &&
(tmp.description = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 2), &desc_str_len)) &&
/* TODO: number isn't ensured to be unique from the script author. */
(item_size != 4 || py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.value)) &&
(item_size != 5 || ((py_long_as_int(PyTuple_GET_ITEM(item, 3), &tmp.icon) ||
(tmp_icon = PyUnicode_AsUTF8(PyTuple_GET_ITEM(item, 3)))) &&
py_long_as_int(PyTuple_GET_ITEM(item, 4), &tmp.value))))
{
if (is_enum_flag) {
if (item_size < 4) {
tmp.value = 1 << i;
}
if (default_py && PySet_Contains(default_py, PyTuple_GET_ITEM(item, 0))) {
*r_default_value |= tmp.value;
default_used++;
}
}
else {
if (item_size < 4) {
tmp.value = i;
}
if (default_py && default_used == 0) {
if ((default_str_cmp != nullptr && STREQ(default_str_cmp, tmp.identifier)) ||
(default_str_cmp == nullptr && default_int_cmp == tmp.value))
{
*r_default_value = tmp.value;
default_used++; /* only ever 1 */
}
}
}
if (tmp_icon) {
tmp.icon = icon_id_from_name(tmp_icon);
}
items[i] = tmp;
#ifdef USE_ENUM_COPY_STRINGS
/* Calculate combine string length. */
totbuf += id_str_len + name_str_len + desc_str_len + 3; /* 3 is for '\0's */
#endif
}
else if (item == Py_None) {
/* Only set since the rest is cleared. */
items[i].identifier = "";
}
else {
MEM_freeN(items);
PyErr_SetString(PyExc_TypeError,
"EnumProperty(...): expected a tuple containing "
"(identifier, name, description) and optionally an "
"icon name and unique number");
return nullptr;
}
}
if (is_enum_flag) {
/* strict check that all set members were used */
if (default_py && default_used != PySet_GET_SIZE(default_py)) {
MEM_freeN(items);
PyErr_Format(PyExc_TypeError,
"EnumProperty(..., default={...}): set has %d unused member(s)",
PySet_GET_SIZE(default_py) - default_used);
return nullptr;
}
}
else {
if (default_py && default_used == 0) {
MEM_freeN(items);
if (default_str_cmp) {
PyErr_Format(PyExc_TypeError,
"EnumProperty(..., default=\'%s\'): not found in enum members",
default_str_cmp);
}
else {
PyErr_Format(PyExc_TypeError,
"EnumProperty(..., default=%d): not found in enum members",
default_int_cmp);
}
return nullptr;
}
}
#ifdef USE_ENUM_COPY_STRINGS
/* This would all work perfectly _but_ the python strings may be freed immediately after use,
* so we need to duplicate them, ugh. annoying because it works most of the time without this. */
{
EnumPropertyItem *items_dup = MEM_mallocN((sizeof(EnumPropertyItem) * (seq_len + 1)) +
(sizeof(char) * totbuf),
"enum_items_from_py2");
EnumPropertyItem *items_ptr = items_dup;
char *buf = ((char *)items_dup) + (sizeof(EnumPropertyItem) * (seq_len + 1));
memcpy(items_dup, items, sizeof(EnumPropertyItem) * (seq_len + 1));
for (i = 0; i < seq_len; i++, items_ptr++) {
buf += strswapbufcpy(buf, &items_ptr->identifier);
buf += strswapbufcpy(buf, &items_ptr->name);
buf += strswapbufcpy(buf, &items_ptr->description);
}
MEM_freeN(items);
items = items_dup;
}
/* end string duplication */
#endif
return items;
}
static const EnumPropertyItem *bpy_prop_enum_itemf_fn(bContext *C,
PointerRNA *ptr,
PropertyRNA *prop,
bool *r_free)
{
PyGILState_STATE gilstate;
if (C) {
bpy_context_set(C, &gilstate);
}
else {
gilstate = PyGILState_Ensure();
}
BPyPropStore *prop_store = static_cast<BPyPropStore *>(RNA_property_py_data_get(prop));
PyObject *py_func = prop_store->py_data.enum_data.itemf_fn;
PyObject *items; /* returned from the function call */
const EnumPropertyItem *eitems = nullptr;
int err = 0;
{
PyObject *args = PyTuple_New(2);
PyObject *self = pyrna_struct_as_instance(ptr);
PyObject *py_context = C ? reinterpret_cast<PyObject *>(bpy_context_module) : Py_None;
Py_INCREF(py_context);
PyTuple_SET_ITEMS(args, self, py_context);
items = PyObject_CallObject(py_func, args);
Py_DECREF(args);
}
if (items == nullptr) {
err = -1;
}
else {
PyObject *items_fast;
int default_value_dummy = 0;
if (!(items_fast = PySequence_Fast(items,
"EnumProperty(...): "
"return value from the callback was not a sequence")))
{
err = -1;
}
else {
eitems = enum_items_from_py(items_fast,
(RNA_property_flag(prop) & PROP_ENUM_FLAG) != 0,
nullptr,
&default_value_dummy);
Py_DECREF(items_fast);
if (!eitems) {
err = -1;
}
}
Py_DECREF(items);
}
if (err != -1) { /* worked */
*r_free = true;
}
else {
PyC_Err_PrintWithFunc(py_func);
eitems = rna_enum_dummy_NULL_items;
}
if (C) {
bpy_context_clear(C, &gilstate);
}
else {
PyGILState_Release(gilstate);
}
return eitems;
}
static int bpy_prop_callback_check(PyObject *py_func, const char *keyword, int argcount)
{
if (py_func && py_func != Py_None) {
2011-10-13 01:29:08 +00:00
if (!PyFunction_Check(py_func)) {
PyErr_Format(PyExc_TypeError,
"%s keyword: expected a function type, not a %.200s",
keyword,
Py_TYPE(py_func)->tp_name);
return -1;
}
PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(py_func);
if (f_code->co_argcount != argcount) {
PyErr_Format(PyExc_TypeError,
"%s keyword: expected a function taking %d arguments, not %d",
keyword,
argcount,
f_code->co_argcount);
return -1;
}
}
return 0;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shared Callback Assignment
* \{ */
static void bpy_prop_callback_assign_update(PropertyRNA *prop, PyObject *update_fn)
{
/* assume this is already checked for type and arg length */
2021-03-16 17:22:34 +11:00
if (update_fn && update_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
RNA_def_property_update_runtime_with_context_and_property(prop, bpy_prop_update_fn);
2021-03-16 17:22:34 +11:00
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.update_fn, update_fn);
}
}
static void bpy_prop_callback_assign_pointer(PropertyRNA *prop, PyObject *poll_fn)
{
2021-03-16 17:22:34 +11:00
if (poll_fn && poll_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
RNA_def_property_poll_runtime(prop, reinterpret_cast<const void *>(bpy_prop_pointer_poll_fn));
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.pointer_data.poll_fn, poll_fn);
}
}
static bool bpy_prop_callback_assign_boolean(PropertyRNA *prop,
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
BooleanPropertyGetFunc rna_get_fn = nullptr;
BooleanPropertySetFunc rna_set_fn = nullptr;
BooleanPropertyGetTransformFunc rna_get_transform_fn = nullptr;
BooleanPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_boolean_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
return false;
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_boolean_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_boolean_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_boolean_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_boolean_funcs_runtime(
prop, rna_get_fn, rna_set_fn, rna_get_transform_fn, rna_set_transform_fn);
return true;
}
static bool bpy_prop_callback_assign_boolean_array(PropertyRNA *prop,
2021-03-16 17:22:34 +11:00
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
BooleanArrayPropertyGetFunc rna_get_fn = nullptr;
BooleanArrayPropertySetFunc rna_set_fn = nullptr;
BooleanArrayPropertyGetTransformFunc rna_get_transform_fn = nullptr;
BooleanArrayPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_boolean_array_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
return false;
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_boolean_array_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_boolean_array_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_boolean_array_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_boolean_array_funcs_runtime(
prop, rna_get_fn, rna_set_fn, rna_get_transform_fn, rna_set_transform_fn);
return true;
}
static bool bpy_prop_callback_assign_int(PropertyRNA *prop,
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
IntPropertyGetFunc rna_get_fn = nullptr;
IntPropertySetFunc rna_set_fn = nullptr;
IntPropertyGetTransformFunc rna_get_transform_fn = nullptr;
IntPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_int_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
return false;
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_int_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_int_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_int_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_int_funcs_runtime(
prop, rna_get_fn, rna_set_fn, nullptr, rna_get_transform_fn, rna_set_transform_fn);
return true;
}
static void bpy_prop_callback_assign_int_array(PropertyRNA *prop,
2021-03-16 17:22:34 +11:00
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
IntArrayPropertyGetFunc rna_get_fn = nullptr;
IntArrayPropertySetFunc rna_set_fn = nullptr;
IntArrayPropertyGetTransformFunc rna_get_transform_fn = nullptr;
IntArrayPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_int_array_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_int_array_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_int_array_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_int_array_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_int_array_funcs_runtime(
prop, rna_get_fn, rna_set_fn, nullptr, rna_get_transform_fn, rna_set_transform_fn);
}
static bool bpy_prop_callback_assign_float(PropertyRNA *prop,
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
FloatPropertyGetFunc rna_get_fn = nullptr;
FloatPropertySetFunc rna_set_fn = nullptr;
FloatPropertyGetTransformFunc rna_get_transform_fn = nullptr;
FloatPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_float_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
return false;
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_float_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_float_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_float_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_float_funcs_runtime(
prop, rna_get_fn, rna_set_fn, nullptr, rna_get_transform_fn, rna_set_transform_fn);
return true;
}
static void bpy_prop_callback_assign_float_array(PropertyRNA *prop,
2021-03-16 17:22:34 +11:00
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
FloatArrayPropertyGetFunc rna_get_fn = nullptr;
FloatArrayPropertySetFunc rna_set_fn = nullptr;
FloatArrayPropertyGetTransformFunc rna_get_transform_fn = nullptr;
FloatArrayPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_float_array_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_float_array_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_float_array_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_float_array_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_float_array_funcs_runtime(
prop, rna_get_fn, rna_set_fn, nullptr, rna_get_transform_fn, rna_set_transform_fn);
}
static bool bpy_prop_callback_assign_string(PropertyRNA *prop,
2021-03-16 17:22:34 +11:00
PyObject *get_fn,
PyObject *set_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn,
PyObject *search_fn,
const eStringPropertySearchFlag search_flag)
{
StringPropertyGetFunc rna_get_fn = nullptr;
StringPropertyLengthFunc rna_length_fn = nullptr;
StringPropertySetFunc rna_set_fn = nullptr;
StringPropertySearchFunc rna_search_fn = nullptr;
StringPropertyGetTransformFunc rna_get_transform_fn = nullptr;
StringPropertySetTransformFunc rna_set_transform_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_string_get_fn;
rna_length_fn = bpy_prop_string_length_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
return false;
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_string_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_string_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_string_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
if (search_fn) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_search_fn = bpy_prop_string_visit_for_search_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.string_data.search_fn, search_fn);
}
RNA_def_property_string_funcs_runtime(
prop, rna_get_fn, rna_length_fn, rna_set_fn, rna_get_transform_fn, rna_set_transform_fn);
if (rna_search_fn) {
RNA_def_property_string_search_func_runtime(prop, rna_search_fn, search_flag);
}
return true;
}
static bool bpy_prop_callback_assign_enum(PropertyRNA *prop,
2021-03-16 17:22:34 +11:00
PyObject *get_fn,
PyObject *set_fn,
PyObject *itemf_fn,
PyObject *get_transform_fn,
PyObject *set_transform_fn)
{
EnumPropertyGetFunc rna_get_fn = nullptr;
EnumPropertySetFunc rna_set_fn = nullptr;
EnumPropertyGetTransformFunc rna_get_transform_fn = nullptr;
EnumPropertySetTransformFunc rna_set_transform_fn = nullptr;
EnumPropertyItemFunc rna_itemf_fn = nullptr;
2021-03-16 17:22:34 +11:00
if (get_fn && get_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_get_fn = bpy_prop_enum_get_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_fn, get_fn);
}
2021-03-16 17:22:34 +11:00
if (set_fn && set_fn != Py_None) {
if (!rna_get_fn) {
PyErr_SetString(PyExc_ValueError,
"The `set` callback is defined without a matching `get` function, this is "
"not supported. `set_transform` should probably be used instead?");
return false;
}
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_set_fn = bpy_prop_enum_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
2021-03-16 17:22:34 +11:00
if (itemf_fn && itemf_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
2021-03-16 17:22:34 +11:00
rna_itemf_fn = bpy_prop_enum_itemf_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.enum_data.itemf_fn, itemf_fn);
}
if (get_transform_fn && get_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_get_transform_fn = bpy_prop_enum_get_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.get_transform_fn, get_transform_fn);
}
if (set_transform_fn && set_transform_fn != Py_None) {
BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
rna_set_transform_fn = bpy_prop_enum_set_transform_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_transform_fn, set_transform_fn);
}
RNA_def_property_enum_funcs_runtime(
prop, rna_get_fn, rna_set_fn, rna_itemf_fn, rna_get_transform_fn, rna_set_transform_fn);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shared Method Utilities
* \{ */
/**
* This define runs at the start of each function and deals with
* returning a deferred property #BPy_PropDeferred (to be registered later).
*
* \param self: The self argument from the caller.
* \param args: The positional arguments of the caller.
* \param kw: The keyword arguments of the caller.
* \param method_object: The method of the caller (unfortunately this can't be deduced).
* \param r_deferred_result: The deferred result (or nullptr in the case of an error).
* The caller must return this value unless a valid `srna` is returned.
*
* \returns When not null, the caller is expected to perform the registration.
*/
static StructRNA *bpy_prop_deferred_data_or_srna(PyObject *self,
PyObject *args,
PyObject *kw,
PyObject *method_object,
PyObject **r_deferred_result)
{
/* This must be the methods of one of the main property types defined in this file. */
BLI_assert(PyCFunction_CheckExact(method_object));
const int args_len = PyTuple_GET_SIZE(args);
PyMethodDef *method_def = ((PyCFunctionObject *)method_object)->m_ml;
/* Call this function with the first argument set to `self`. */
if (args_len == 1) {
self = PyTuple_GET_ITEM(args, 0);
args = PyTuple_New(0);
/* This will be #BPy_BoolProperty or one of the functions that define a type. */
PyCFunctionWithKeywords method_fn = (PyCFunctionWithKeywords)(void *)method_def->ml_meth;
*r_deferred_result = method_fn(self, args, kw);
Py_DECREF(args);
/* May be an error (depending on `r_deferred_result`). */
return nullptr;
}
const char *error_prefix = method_def->ml_name;
if (args_len > 1) {
PyErr_Format(PyExc_ValueError, "%s: all args must be keywords", error_prefix);
*r_deferred_result = nullptr;
/* An error. */
return nullptr;
}
StructRNA *srna = srna_from_self(self, error_prefix);
if (srna == nullptr) {
*r_deferred_result = PyErr_Occurred() ?
nullptr :
bpy_prop_deferred_data_CreatePyObject(method_object, kw);
/* May be an error (depending on `r_deferred_result`). */
return nullptr;
}
/* Crash if this is ever used by accident! */
#ifndef NDEBUG
2023-12-07 11:42:12 +11:00
*r_deferred_result = (PyObject *)intptr_t(1);
#endif
/* No error or deferred result, perform registration immediately. */
return srna;
}
struct BPy_PropIDParse {
const char *value;
StructRNA *srna;
/**
* In the case registering this properly replaces an existing dynamic property.
* Store a handle to the property for removal.
* This is needed so the property removal is deferred until all other arguments
* have been validated, otherwise failure elsewhere could leave the property un-registered.
*/
void *prop_free_handle;
};
/**
* Use with #PyArg_ParseTuple's `O&` formatting.
*/
static int bpy_prop_arg_parse_id(PyObject *o, void *p)
{
BPy_PropIDParse *parse_data = static_cast<BPy_PropIDParse *>(p);
StructRNA *srna = parse_data->srna;
if (!PyUnicode_Check(o)) {
PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name);
return 0;
}
Py_ssize_t id_len;
const char *id;
id = PyUnicode_AsUTF8AndSize(o, &id_len);
if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) {
PyErr_Format(PyExc_TypeError, "'%.200s' too long, max length is %d", id, MAX_IDPROP_NAME - 1);
return 0;
}
parse_data->prop_free_handle = nullptr;
if (UNLIKELY(RNA_def_property_free_identifier_deferred_prepare(
srna, id, &parse_data->prop_free_handle) == -1))
{
PyErr_Format(PyExc_TypeError,
"'%s' is defined as a non-dynamic type for '%s'",
id,
RNA_struct_identifier(srna));
return 0;
}
parse_data->value = id;
return 1;
}
/**
* Needed so #RNA_struct_property_tag_defines can be called on the `srna`.
*/
struct BPy_EnumProperty_Parse_WithSRNA {
BPy_EnumProperty_Parse base;
StructRNA *srna;
};
/**
* Wrapper for #pyrna_enum_bitfield_parse_set
* that looks up tags from the `srna`.
*/
static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p)
{
BPy_EnumProperty_Parse_WithSRNA *parse_data = static_cast<BPy_EnumProperty_Parse_WithSRNA *>(p);
parse_data->base.items = RNA_struct_property_tag_defines(parse_data->srna);
if (parse_data->base.items == nullptr) {
PyErr_Format(PyExc_TypeError,
"property-tags not available for '%s'",
RNA_struct_identifier(parse_data->srna));
return 0;
}
return pyrna_enum_bitfield_parse_set(o, &parse_data->base);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shared Method Doc-Strings
* \{ */
2011-02-15 11:43:35 +00:00
#define BPY_PROPDEF_NAME_DOC \
" :arg name: Name used in the user interface.\n" \
" :type name: str\n"
2011-02-15 11:43:35 +00:00
#define BPY_PROPDEF_DESC_DOC \
" :arg description: Text used for the tooltip and api documentation.\n" \
" :type description: str\n"
2011-02-15 11:43:35 +00:00
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
#define BPY_PROPDEF_CTXT_DOC \
" :arg translation_context: Text used as context to disambiguate translations.\n" \
" :type translation_context: str\n"
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
2011-08-02 10:56:09 +00:00
#define BPY_PROPDEF_UNIT_DOC \
" :arg unit: Enumerator in :ref:`rna_enum_property_unit_items`.\n" \
" :type unit: str\n"
2011-08-02 10:56:09 +00:00
#define BPY_PROPDEF_NUM_MIN_DOC_(ty) \
" :arg min: Hard minimum, trying to assign a value below will silently assign this minimum " \
"instead.\n" \
" :type min: " ty "\n"
#define BPY_PROPDEF_NUM_MAX_DOC_(ty) \
" :arg max: Hard maximum, trying to assign a value above will silently assign this maximum " \
"instead.\n" \
" :type max: " ty "\n"
#define BPY_PROPDEF_NUM_MINMAX_DOC(ty) BPY_PROPDEF_NUM_MIN_DOC_(ty) BPY_PROPDEF_NUM_MAX_DOC_(ty)
#define BPY_PROPDEF_NUM_SOFT_MIN_DOC_(ty) \
" :arg soft_min: Soft minimum (>= *min*), " \
"user won't be able to drag the widget below this value in the UI.\n" \
" :type soft_min: " ty "\n"
#define BPY_PROPDEF_NUM_SOFT_MAX_DOC_(ty) \
" :arg soft_max: Soft maximum (<= *max*), " \
"user won't be able to drag the widget above this value in the UI.\n" \
" :type soft_max: " ty "\n"
#define BPY_PROPDEF_NUM_SOFT_MINMAX_DOC(ty) \
BPY_PROPDEF_NUM_SOFT_MIN_DOC_(ty) BPY_PROPDEF_NUM_SOFT_MAX_DOC_(ty)
#define BPY_PROPDEF_VECSIZE_DOC \
" :arg size: Vector dimensions in [1, " STRINGIFY(PYRNA_STACK_ARRAY) "]. " \
"An int sequence can be used to define multi-dimension arrays.\n" \
" :type size: int | Sequence[int]\n"
#define BPY_PROPDEF_INT_STEP_DOC \
" :arg step: Step of increment/decrement in UI, in [1, 100], defaults to 1 (WARNING: unused " \
"currently!).\n" \
" :type step: int\n"
#define BPY_PROPDEF_FLOAT_STEP_DOC \
" :arg step: Step of increment/decrement in UI, in [1, 100], defaults to 3 (WARNING: actual " \
"value is /100).\n" \
" :type step: int\n"
#define BPY_PROPDEF_FLOAT_PREC_DOC \
" :arg precision: Maximum number of decimal digits to display, in [0, 6]. Fraction is " \
"automatically hidden for exact integer values of fields with unit 'NONE' or 'TIME' (frame " \
"count) and step divisible by 100.\n" \
" :type precision: int\n"
2011-08-02 10:56:09 +00:00
#define BPY_PROPDEF_UPDATE_DOC \
" :arg update: Function to be called when this value is modified,\n" \
" This function must take 2 values (self, context) and return None.\n" \
" *Warning* there are no safety checks to avoid infinite recursion.\n" \
" :type update: Callable[[:class:`bpy.types.bpy_struct`, :class:`bpy.types.Context`], " \
"None]\n"
#define BPY_PROPDEF_POLL_DOC \
" :arg poll: Function that determines whether an item is valid for this property.\n" \
" The function must take 2 values (self, object) and return a boolean.\n" \
"\n" \
" .. note:: The return value will be checked only when assigning an item from the UI, " \
"but it is still possible to assign an \"invalid\" item to the property directly.\n" \
"\n" \
" :type poll: Callable[[:class:`bpy.types.bpy_struct`, :class:`bpy.types.ID`], " \
"bool]\n"
#define BPY_PROPDEF_GET_DOC(ty) \
" :arg get: Function to be called when this value is 'read', and the default,\n" \
" system-defined storage is not used for this property.\n" \
" This function must take 1 value (self) and return the value of the property.\n" \
"\n" \
" .. note:: Defining this callback without a matching ``set`` one will make " \
"the property read-only (even if ``READ_ONLY`` option is not set)." \
"\n" \
" :type get: Callable[[:class:`bpy.types.bpy_struct`], " ty "]\n"
#define BPY_PROPDEF_SET_DOC(ty) \
" :arg set: Function to be called when this value is 'written', and the default,\n" \
" system-defined storage is not used for this property.\n" \
" This function must take 2 values (self, value) and return None.\n" \
"\n" \
" .. note:: Defining this callback without a matching ``get`` one is invalid." \
"\n" \
" :type set: Callable[[:class:`bpy.types.bpy_struct`, " ty "], None]\n"
#define BPY_PROPDEF_GET_TRANSFORM_DOC(ty) \
" :arg get_transform: Function to be called when this value is 'read',\n" \
" if some additional processing must be performed on the stored value.\n" \
" This function must take three arguments (self, the stored value,\n" \
" and a boolean indicating if the property is currently set),\n" \
" and return the final, transformed value of the property.\n" \
"\n" \
" .. note:: The callback is responsible to ensure that value limits of the property " \
"(min/max, length...) are respected. Otherwise a ValueError exception is raised.\n" \
"\n" \
" :type get_transform: Callable[[:class:`bpy.types.bpy_struct`, " ty ", bool], " ty "]\n"
#define BPY_PROPDEF_SET_TRANSFORM_DOC(ty) \
" :arg set_transform: Function to be called when this value is 'written',\n" \
" if some additional processing must be performed on the given value before storing it.\n" \
" This function must take four arguments (self, the given value to store,\n" \
" the currently stored value ('raw' value, without any ``get_transform`` applied to " \
"it),\n" \
" and a boolean indicating if the property is currently set),\n" \
" and return the final, transformed value of the property.\n" \
"\n" \
" .. note:: The callback is responsible to ensure that value limits (min/max, " \
"length...) are respected. Otherwise a ValueError exception is raised.\n" \
"\n" \
" :type set_transform: " \
"Callable[[:class:`bpy.types.bpy_struct`, " ty ", " ty ", bool], " ty "]\n"
#define BPY_PROPDEF_SEARCH_DOC \
" :arg search: Function to be called to show candidates for this string (shown in the UI).\n" \
" This function must take 3 values (self, context, edit_text)\n" \
" and return a sequence, iterator or generator where each item must be:\n" \
"\n" \
" - A single string (representing a candidate to display).\n" \
" - A tuple-pair of strings, where the first is a candidate and the second\n" \
" is additional information about the candidate.\n" \
" :type search: Callable[[:class:`bpy.types.bpy_struct`, :class:`bpy.types.Context`, str], " \
"Iterable[str | tuple[str, str]]" \
"]\n" \
" :arg search_options: Set of strings in:\n" \
"\n" \
" - 'SORT' sorts the resulting items.\n" \
" - 'SUGGESTION' lets the user enter values not found in search candidates.\n" \
" **WARNING** disabling this flag causes the search callback to run on redraw,\n" \
" so only disable this flag if it's not likely to cause performance issues.\n" \
"\n" \
" :type search_options: set[str]\n"
#define BPY_PROPDEF_POINTER_TYPE_DOC \
" :arg type: A subclass of a property group or ID types.\n" \
" :type type: type[:class:`bpy.types.PropertyGroup` | :class:`bpy.types.ID`]\n"
#define BPY_PROPDEF_COLLECTION_TYPE_DOC \
" :arg type: A subclass of a property group.\n" \
" :type type: type[:class:`bpy.types.PropertyGroup`]\n"
#define BPY_PROPDEF_TAGS_DOC \
" :arg tags: Enumerator of tags that are defined by parent class.\n" \
" :type tags: set[str]\n"
#if 0
static int bpy_struct_id_used(StructRNA *srna, char *identifier)
{
PointerRNA ptr = RNA_pointer_create_discrete(nullptr, srna, nullptr);
return (RNA_struct_find_property(&ptr, identifier) != nullptr);
}
#endif
/** \} */
/* -------------------------------------------------------------------- */
/** \name Module Methods
2021-07-30 16:16:38 +10:00
*
* Functions that register RNA.
*
* \note The `self` argument is nullptr when called from Python,
2021-07-30 16:16:38 +10:00
* but being abused from C so we can pass the `srna` along.
* This isn't incorrect since its a Python object - but be careful.
*
* \{ */
PyDoc_STRVAR(
/* Wrap. */
BPy_BoolProperty_doc,
".. function:: BoolProperty("
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"default=False, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None)\n"
"\n"
" Returns a new boolean property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC BPY_PROPDEF_OPTIONS_DOC
BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC
BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC("bool") BPY_PROPDEF_SET_DOC("bool")
BPY_PROPDEF_GET_TRANSFORM_DOC("bool") BPY_PROPDEF_SET_TRANSFORM_DOC("bool"));
static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_BoolProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
bool default_value = false;
PropertyRNA *prop;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_number_items;
subtype_enum.value = PROP_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr",
"name",
"description",
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context",
"default",
"options",
"override",
"tags",
"subtype",
"update",
"get",
"set",
"get_transform",
"set_transform",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O&" /* `default` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":BoolProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
PyC_ParseBool,
&default_value,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_BOOLEAN, subtype_enum.value);
RNA_def_property_boolean_default(prop, default_value);
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
if (!bpy_prop_callback_assign_boolean(prop, get_fn, set_fn, get_transform_fn, set_transform_fn))
{
return nullptr;
}
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_BoolVectorProperty_doc,
".. function:: BoolVectorProperty("
"*, "
"name=\"\", "
"description=\"\", "
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context=\"*\", "
"default=(False, False, False), "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"size=3, "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None)\n"
"\n"
" Returns a new vector boolean property definition.\n"
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC
" :arg default: sequence of booleans the length of *size*.\n"
" :type default: Sequence[bool]\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC
BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC("Sequence[bool]")
BPY_PROPDEF_SET_DOC("tuple[bool, ...]")
BPY_PROPDEF_GET_TRANSFORM_DOC("Sequence[bool]")
BPY_PROPDEF_SET_TRANSFORM_DOC("Sequence[bool]"));
static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(
self, args, kw, pymeth_BoolVectorProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
Array<bool, RNA_STACK_ARRAY> default_value;
BPyPropArrayLength array_len_info{};
array_len_info.len_total = 3;
PropertyRNA *prop;
PyObject *default_py = nullptr;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_number_array_items;
subtype_enum.value = PROP_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr",
"name",
"description",
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context",
"default",
"options",
"override",
"tags",
"subtype",
"size",
"update",
"get",
"set",
"get_transform",
"set_transform",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O" /* `default` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O&" /* `size` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":BoolVectorProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_py,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
bpy_prop_array_length_parse,
&array_len_info,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (default_py != nullptr) {
default_value.reinitialize(array_len_info.len_total);
if (bpy_prop_array_from_py_with_dims(default_value.data(),
sizeof(*default_value.data()),
default_py,
&array_len_info,
&PyBool_Type,
"BoolVectorProperty(default=sequence)") == -1)
{
return nullptr;
}
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_BOOLEAN, subtype_enum.value);
if (array_len_info.dims_len == 0) {
RNA_def_property_array(prop, array_len_info.len_total);
if (default_py != nullptr) {
RNA_def_property_boolean_array_default(prop, default_value.data());
}
}
else {
RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims);
if (default_py != nullptr) {
RNA_def_property_boolean_array_default(prop, default_value.data());
}
}
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_boolean_array(prop, get_fn, set_fn, get_transform_fn, set_transform_fn);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_IntProperty_doc,
".. function:: IntProperty("
"*, "
"name=\"\", "
"description=\"\", "
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context=\"*\", "
"default=0, "
"min=-2**31, max=2**31-1, "
"soft_min=-2**31, soft_max=2**31-1, "
"step=1, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None)\n"
"\n"
" Returns a new int property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC
BPY_PROPDEF_NUM_MINMAX_DOC("int") BPY_PROPDEF_NUM_SOFT_MINMAX_DOC("int")
BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC
BPY_PROPDEF_GET_DOC("int") BPY_PROPDEF_SET_DOC("int")
BPY_PROPDEF_GET_TRANSFORM_DOC("int") BPY_PROPDEF_SET_TRANSFORM_DOC("int"));
static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_IntProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX;
int step = 1;
int default_value = 0;
PropertyRNA *prop;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_number_items;
subtype_enum.value = PROP_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr", "name", "description", "translation_context",
"default", "min", "max", "soft_min",
"soft_max", "step", "options", "override",
"tags", "subtype", "update", "get",
"set", "get_transform", "set_transform", nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"i" /* `default` */
"i" /* `min` */
"i" /* `max` */
"i" /* `soft_min` */
"i" /* `soft_max` */
"i" /* `step` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":IntProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_value,
&min,
&max,
&soft_min,
&soft_max,
&step,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_INT, subtype_enum.value);
RNA_def_property_int_default(prop, default_value);
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
RNA_def_property_range(prop, min, max);
RNA_def_property_ui_range(prop, std::max(soft_min, min), std::min(soft_max, max), step, 3);
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_int(prop, get_fn, set_fn, get_transform_fn, set_transform_fn);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_IntVectorProperty_doc,
".. function:: IntVectorProperty("
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"default=(0, 0, 0), min=-2**31, max=2**31-1, "
"soft_min=-2**31, "
"soft_max=2**31-1, "
"step=1, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"size=3, "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None)\n"
"\n"
" Returns a new vector int property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC
" :arg default: sequence of ints the length of *size*.\n"
" :type default: Sequence[int]\n" BPY_PROPDEF_NUM_MINMAX_DOC("int")
BPY_PROPDEF_NUM_SOFT_MINMAX_DOC("int")
BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC
BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC("Sequence[int]")
BPY_PROPDEF_SET_DOC("tuple[int, ...]")
BPY_PROPDEF_GET_TRANSFORM_DOC("Sequence[int]")
BPY_PROPDEF_SET_TRANSFORM_DOC("Sequence[int]"));
static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(
self, args, kw, pymeth_IntVectorProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX;
int step = 1;
Array<int, RNA_STACK_ARRAY> default_value;
BPyPropArrayLength array_len_info{};
array_len_info.len_total = 3;
PropertyRNA *prop;
PyObject *default_py = nullptr;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_number_array_items;
subtype_enum.value = PROP_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr", "name", "description", "translation_context",
"default", "min", "max", "soft_min",
"soft_max", "step", "options", "override",
"tags", "subtype", "size", "update",
"get", "set", "get_transform", "set_transform",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O" /* `default` */
"i" /* `min` */
"i" /* `max` */
"i" /* `soft_min` */
"i" /* `soft_max` */
"i" /* `step` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O&" /* `size` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":IntVectorProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_py,
&min,
&max,
&soft_min,
&soft_max,
&step,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
bpy_prop_array_length_parse,
&array_len_info,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (default_py != nullptr) {
default_value.reinitialize(array_len_info.len_total);
if (bpy_prop_array_from_py_with_dims(default_value.data(),
sizeof(*default_value.data()),
default_py,
&array_len_info,
&PyLong_Type,
"IntVectorProperty(default=sequence)") == -1)
{
return nullptr;
}
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_INT, subtype_enum.value);
if (array_len_info.dims_len == 0) {
RNA_def_property_array(prop, array_len_info.len_total);
if (default_py != nullptr) {
RNA_def_property_int_array_default(prop, default_value.data());
}
}
else {
RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims);
if (default_py != nullptr) {
RNA_def_property_int_array_default(prop, default_value.data());
}
}
RNA_def_property_range(prop, min, max);
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
RNA_def_property_ui_range(prop, std::max(soft_min, min), std::min(soft_max, max), step, 3);
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_int_array(prop, get_fn, set_fn, get_transform_fn, set_transform_fn);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_FloatProperty_doc,
".. function:: FloatProperty("
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"default=0.0, "
"min=-3.402823e+38, max=3.402823e+38, "
"soft_min=-3.402823e+38, soft_max=3.402823e+38, "
"step=3, "
"precision=2, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"unit='NONE', "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None)\n"
"\n"
" Returns a new float (single precision) property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC BPY_PROPDEF_NUM_MINMAX_DOC(
"float") BPY_PROPDEF_NUM_SOFT_MINMAX_DOC("float")
BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC BPY_PROPDEF_OPTIONS_DOC
BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC
BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC("float")
BPY_PROPDEF_SET_DOC("float") BPY_PROPDEF_GET_TRANSFORM_DOC("float")
BPY_PROPDEF_SET_TRANSFORM_DOC("float"));
static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_FloatProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX;
float step = 3;
float default_value = 0.0f;
int precision = 2;
PropertyRNA *prop;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_number_items;
subtype_enum.value = PROP_NONE;
BPy_EnumProperty_Parse unit_enum{};
unit_enum.items = rna_enum_property_unit_items;
unit_enum.value = PROP_UNIT_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr", "name", "description", "translation_context",
"default", "min", "max", "soft_min",
"soft_max", "step", "precision", "options",
"override", "tags", "subtype", "unit",
"update", "get", "set", "get_transform",
"set_transform", nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"f" /* `default` */
"f" /* `min` */
"f" /* `max` */
"f" /* `soft_min` */
"f" /* `soft_max` */
"f" /* `step` */
"i" /* `precision` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O&" /* `unit` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":FloatProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_value,
&min,
&max,
&soft_min,
&soft_max,
&step,
&precision,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
pyrna_enum_value_parse_string,
&unit_enum,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_FLOAT, subtype_enum.value | unit_enum.value);
RNA_def_property_float_default(prop, default_value);
RNA_def_property_range(prop, min, max);
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
RNA_def_property_ui_range(
prop, std::max(soft_min, min), std::min(soft_max, max), step, precision);
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_float(prop, get_fn, set_fn, get_transform_fn, set_transform_fn);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_FloatVectorProperty_doc,
".. function:: FloatVectorProperty("
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"default=(0.0, 0.0, 0.0), "
"min=sys.float_info.min, max=sys.float_info.max, "
"soft_min=sys.float_info.min, soft_max=sys.float_info.max, "
"step=3, "
"precision=2, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"unit='NONE', "
"size=3, "
"update=None, "
"get=None, "
"set=None)\n"
"\n"
" Returns a new vector float property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC
" :arg default: Sequence of floats the length of *size*.\n"
" :type default: Sequence[float]\n" BPY_PROPDEF_NUM_MINMAX_DOC(
"float") BPY_PROPDEF_NUM_SOFT_MINMAX_DOC("float")
BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC
BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC
BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_VECSIZE_DOC
BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC("Sequence[float]")
BPY_PROPDEF_SET_DOC("tuple[float, ...]")
BPY_PROPDEF_GET_TRANSFORM_DOC("Sequence[float]")
BPY_PROPDEF_SET_TRANSFORM_DOC("Sequence[float]"));
static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(
self, args, kw, pymeth_FloatVectorProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX;
float step = 3;
Array<float, RNA_STACK_ARRAY> default_value;
int precision = 2;
BPyPropArrayLength array_len_info{};
array_len_info.len_total = 3;
PropertyRNA *prop;
PyObject *default_py = nullptr;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_number_array_items;
subtype_enum.value = PROP_NONE;
BPy_EnumProperty_Parse unit_enum{};
unit_enum.items = rna_enum_property_unit_items;
unit_enum.value = PROP_UNIT_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr", "name", "description", "translation_context",
"default", "min", "max", "soft_min",
"soft_max", "step", "precision", "options",
"override", "tags", "subtype", "unit",
"size", "update", "get", "set",
"get_transform", "set_transform", nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O" /* `default` */
"f" /* `min` */
"f" /* `max` */
"f" /* `soft_min` */
"f" /* `soft_max` */
"f" /* `step` */
"i" /* `precision` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O&" /* `unit` */
"O&" /* `size` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":FloatVectorProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_py,
&min,
&max,
&soft_min,
&soft_max,
&step,
&precision,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
pyrna_enum_value_parse_string,
&unit_enum,
bpy_prop_array_length_parse,
&array_len_info,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (default_py != nullptr) {
default_value.reinitialize(array_len_info.len_total);
if (bpy_prop_array_from_py_with_dims(default_value.data(),
sizeof(*default_value.data()),
default_py,
&array_len_info,
&PyFloat_Type,
"FloatVectorProperty(default=sequence)") == -1)
{
return nullptr;
}
if (bpy_prop_array_is_matrix_compatible_ex(subtype_enum.value, &array_len_info)) {
bpy_prop_array_matrix_swap_row_column_vn(default_value.data(), &array_len_info);
}
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_FLOAT, subtype_enum.value | unit_enum.value);
if (array_len_info.dims_len == 0) {
RNA_def_property_array(prop, array_len_info.len_total);
if (default_py != nullptr) {
RNA_def_property_float_array_default(prop, default_value.data());
}
}
else {
RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims);
if (default_py != nullptr) {
RNA_def_property_float_array_default(prop, default_value.data());
}
}
RNA_def_property_range(prop, min, max);
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
RNA_def_property_ui_range(
prop, std::max(soft_min, min), std::min(soft_max, max), step, precision);
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_float_array(prop, get_fn, set_fn, get_transform_fn, set_transform_fn);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_StringProperty_doc,
".. function:: StringProperty("
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"default=\"\", "
"maxlen=0, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"subtype='NONE', "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None, "
"search=None, "
"search_options={'SUGGESTION'})\n"
"\n"
" Returns a new string property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC
" :arg default: initializer string.\n"
" :type default: str\n"
" :arg maxlen: maximum length of the string.\n"
" :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC
BPY_PROPDEF_GET_DOC("str") BPY_PROPDEF_SET_DOC("str")
BPY_PROPDEF_GET_TRANSFORM_DOC("str") BPY_PROPDEF_SET_TRANSFORM_DOC("str")
BPY_PROPDEF_SEARCH_DOC);
static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_StringProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr, *default_value = "";
int maxlen = 0;
PropertyRNA *prop;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
BPy_EnumProperty_Parse subtype_enum{};
subtype_enum.items = rna_enum_property_subtype_string_items;
subtype_enum.value = PROP_NONE;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
PyObject *search_fn = nullptr;
BPy_EnumProperty_Parse search_options_enum{};
search_options_enum.items = rna_enum_property_string_search_flag_items;
search_options_enum.value = PROP_STRING_SEARCH_SUGGESTION;
static const char *_keywords[] = {
"attr",
"name",
"description",
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context",
"default",
"maxlen",
"options",
"override",
"tags",
"subtype",
"update",
"get",
"set",
"get_transform",
"set_transform",
"search",
"search_options",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"s" /* `default` */
"i" /* `maxlen` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O&" /* `subtype` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
"O" /* `search` */
"O&" /* `search_options` */
":StringProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_value,
&maxlen,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
pyrna_enum_value_parse_string,
&subtype_enum,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn,
&search_fn,
pyrna_enum_bitfield_parse_set,
&search_options_enum))
{
return nullptr;
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(search_fn, "search", 3) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_property(srna, id_data.value, PROP_STRING, subtype_enum.value);
if (maxlen != 0) {
/* +1 since it includes null terminator. */
RNA_def_property_string_maxlength(prop, maxlen + 1);
}
if (default_value && default_value[0]) {
RNA_def_property_string_default(prop, default_value);
}
RNA_def_property_ui_text(prop, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_string(prop,
get_fn,
set_fn,
get_transform_fn,
set_transform_fn,
search_fn,
eStringPropertySearchFlag(search_options_enum.value));
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_EnumProperty_doc,
".. function:: EnumProperty("
"items, "
"*, "
"name=\"\", "
"description=\"\", "
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context=\"*\", "
"default=None, "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"update=None, "
"get=None, "
"set=None, "
"get_transform=None, "
"set_transform=None)\n"
"\n"
" Returns a new enumerator property definition.\n"
"\n"
" :arg items: sequence of enum items formatted:\n"
" ``[(identifier, name, description, icon, number), ...]``.\n"
"\n"
" The first three elements of the tuples are mandatory.\n"
"\n"
" :identifier: The identifier is used for Python access.\n"
" An empty identifier means that the item is a separator\n"
" :name: Name for the interface.\n"
" :description: Used for documentation and tooltips.\n"
" :icon: An icon string identifier or integer icon value\n"
" (e.g. returned by :class:`bpy.types.UILayout.icon`)\n"
" :number: Unique value used as the identifier for this item (stored in file data).\n"
" Use when the identifier may need to change. If the *ENUM_FLAG* option is used,\n"
2021-10-03 12:06:09 +11:00
" the values are bit-masks and should be powers of two.\n"
"\n"
" When an item only contains 4 items they define ``(identifier, name, description, "
"number)``.\n"
"\n"
" Separators may be added using either None (nameless separator),\n"
" or a regular item tuple with an empty identifier string, in which case the name,\n"
" if non-empty, will be displayed in the UI above the separator line."
"\n"
" For dynamic values a callback can be passed which returns a list in\n"
" the same format as the static list.\n"
" This function must take 2 arguments ``(self, context)``, **context may be None**.\n"
"\n"
" .. warning::\n"
"\n"
" There is a known bug with using a callback,\n"
" Python must keep a reference to the strings returned by the callback or Blender\n"
" will misbehave or even crash."
"\n"
" :type items: Iterable["
"tuple[str, str, str] | "
"tuple[str, str, str, int] | "
"tuple[str, str, str, int, int] | "
"None] | "
"Callable[[:class:`bpy.types.bpy_struct`, :class:`bpy.types.Context` | None], "
/* NOTE(@ideasman42): a type alias would be useful here (same as above). */
"Iterable["
"tuple[str, str, str] | "
"tuple[str, str, str, int] | "
"tuple[str, str, str, int, int] | "
"None]"
"]\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_CTXT_DOC
" :arg default: The default value for this enum, a string from the identifiers used in "
"*items*, or integer matching an item number.\n"
" If the *ENUM_FLAG* option is used this must be a set of such string identifiers "
"instead.\n"
" WARNING: Strings cannot be specified for dynamic enums\n"
" (i.e. if a callback function is given as *items* parameter).\n"
" :type default: str | int | set[str]\n" BPY_PROPDEF_OPTIONS_ENUM_DOC
BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_UPDATE_DOC
BPY_PROPDEF_GET_DOC("int") BPY_PROPDEF_SET_DOC("int")
BPY_PROPDEF_GET_TRANSFORM_DOC("int") BPY_PROPDEF_SET_TRANSFORM_DOC("int"));
static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_EnumProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
PyObject *default_py = nullptr;
int default_value = 0;
PyObject *items, *items_fast;
const EnumPropertyItem *eitems;
PropertyRNA *prop;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_enum_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
bool is_itemf = false;
PyObject *update_fn = nullptr;
PyObject *get_fn = nullptr;
PyObject *set_fn = nullptr;
PyObject *get_transform_fn = nullptr;
PyObject *set_transform_fn = nullptr;
static const char *_keywords[] = {
"attr",
"items",
"name",
"description",
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context",
"default",
"options",
"override",
"tags",
"update",
"get",
"set",
"get_transform",
"set_transform",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"O" /* `items` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O" /* `default` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
"O" /* `get_transform` */
"O" /* `set_transform` */
":EnumProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&items,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
&default_py,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
&update_fn,
&get_fn,
&set_fn,
&get_transform_fn,
&set_transform_fn))
{
return nullptr;
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_fn, "get", 1) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(get_transform_fn, "get_transform", 3) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(set_transform_fn, "set_transform", 4) == -1) {
return nullptr;
}
if (default_py == Py_None) {
/* This allows to get same behavior when explicitly passing None as default value,
* and not defining a default value at all! */
default_py = nullptr;
}
/* Items can be a list or a callable.
* NOTE: Don't use #PyCallable_Check because we need the function code for errors. */
if (PyFunction_Check(items)) {
PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(items);
if (f_code->co_argcount != 2) {
PyErr_Format(PyExc_ValueError,
"EnumProperty(...): expected 'items' function to take 2 arguments, not %d",
f_code->co_argcount);
return nullptr;
}
if (default_py) {
/* Only support getting integer default values here. */
if (!py_long_as_int(default_py, &default_value)) {
/* NOTE: using type error here is odd but python does this for invalid arguments. */
PyErr_SetString(
PyExc_TypeError,
"EnumProperty(...): 'default' can only be an integer when 'items' is a function");
return nullptr;
}
}
is_itemf = true;
eitems = rna_enum_dummy_NULL_items;
}
else {
if (!(items_fast = PySequence_Fast(
items,
"EnumProperty(...): "
"expected a sequence of tuples for the enum items or a function")))
{
return nullptr;
}
eitems = enum_items_from_py(
items_fast, (options_enum.value & PROP_ENUM_FLAG) != 0, default_py, &default_value);
if (!eitems) {
Py_DECREF(items_fast);
return nullptr;
}
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
if (options_enum.value & PROP_ENUM_FLAG) {
prop = RNA_def_enum_flag(
srna, id_data.value, eitems, default_value, name ? name : id_data.value, description);
}
else {
prop = RNA_def_enum(
srna, id_data.value, eitems, default_value, name ? name : id_data.value, description);
}
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_enum(
prop, get_fn, set_fn, (is_itemf ? items : nullptr), get_transform_fn, set_transform_fn);
RNA_def_property_duplicate_pointers(srna, prop);
if (is_itemf == false) {
/* NOTE: this must be postponed until after #RNA_def_property_duplicate_pointers
* otherwise if this is a generator it may free the strings before we copy them */
Py_DECREF(items_fast);
MEM_freeN(eitems);
}
Py_RETURN_NONE;
}
StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
{
StructRNA *srna;
srna = srna_from_self(value, "");
2011-10-13 01:29:08 +00:00
if (!srna) {
if (PyErr_Occurred()) {
PyObject *msg = PyC_ExceptionBuffer();
const char *msg_char = PyUnicode_AsUTF8(msg);
PyErr_Clear();
PyErr_Format(
PyExc_TypeError, "%.200s expected an RNA type, failed with: %s", error_prefix, msg_char);
Py_DECREF(msg);
}
else {
PyErr_Format(PyExc_TypeError,
"%.200s expected an RNA type, failed with type '%s'",
error_prefix,
Py_TYPE(value)->tp_name);
}
return nullptr;
}
return srna;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_PointerProperty_doc,
".. function:: PointerProperty("
"type, "
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set(), "
"poll=None, "
"update=None)\n"
"\n"
" Returns a new pointer property definition.\n"
"\n" BPY_PROPDEF_POINTER_TYPE_DOC BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_CTXT_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_POLL_DOC BPY_PROPDEF_UPDATE_DOC
"\n"
".. note:: Pointer properties do not support storing references to embedded IDs "
"(e.g. :class:`bpy.types.Scene.collection`, :class:`bpy.types.Material.node_tree`).\n"
" These should exclusively be referenced and accessed through their owner ID "
"(e.g. the scene or material).\n");
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(
self, args, kw, pymeth_PointerProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
PropertyRNA *prop;
StructRNA *ptype;
PyObject *type = Py_None;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
PyObject *update_fn = nullptr, *poll_fn = nullptr;
static const char *_keywords[] = {
"attr",
"type",
"name",
"description",
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context",
"options",
"override",
"tags",
"poll",
"update",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"O" /* `type` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
"O" /* `poll` */
"O" /* `update` */
":PointerProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&type,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum,
&poll_fn,
&update_fn))
{
return nullptr;
}
ptype = pointer_type_from_py(type, "PointerProperty(...)");
if (!ptype) {
return nullptr;
}
if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) {
PyErr_Format(PyExc_TypeError,
"PointerProperty(...) expected an RNA type derived from %.200s or %.200s",
RNA_struct_ui_name(&RNA_ID),
RNA_struct_ui_name(&RNA_PropertyGroup));
return nullptr;
}
if (bpy_prop_callback_check(update_fn, "update", 2) == -1) {
return nullptr;
}
if (bpy_prop_callback_check(poll_fn, "poll", 2) == -1) {
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_pointer_runtime(
srna, id_data.value, ptype, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
if (RNA_struct_idprops_contains_datablock(ptype)) {
if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
}
}
bpy_prop_callback_assign_update(prop, update_fn);
bpy_prop_callback_assign_pointer(prop, poll_fn);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_CollectionProperty_doc,
".. function:: CollectionProperty("
"type, "
"*, "
"name=\"\", "
"description=\"\", "
"translation_context=\"*\", "
"options={'ANIMATABLE'}, "
"override=set(), "
"tags=set())\n"
"\n"
" Returns a new collection property definition.\n"
"\n" BPY_PROPDEF_COLLECTION_TYPE_DOC BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_CTXT_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC
BPY_PROPDEF_TAGS_DOC);
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
{ /* Keep this block first. */
PyObject *deferred_result;
srna = bpy_prop_deferred_data_or_srna(
self, args, kw, pymeth_CollectionProperty, &deferred_result);
if (srna == nullptr) {
return deferred_result;
}
}
BPy_PropIDParse id_data{};
id_data.srna = srna;
const char *name = nullptr, *description = "";
const char *translation_context = nullptr;
PropertyRNA *prop;
StructRNA *ptype;
PyObject *type = Py_None;
BPy_EnumProperty_Parse options_enum{};
options_enum.items = rna_enum_property_flag_items;
options_enum.value = 0;
BPy_EnumProperty_Parse override_enum{};
override_enum.items = rna_enum_property_override_flag_collection_items;
override_enum.value = 0;
BPy_EnumProperty_Parse_WithSRNA tags_enum{};
tags_enum.srna = srna;
static const char *_keywords[] = {
"attr",
"type",
"name",
"description",
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"translation_context",
"options",
"override",
"tags",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `attr` */
"O" /* `type` */
"|$" /* Optional, keyword only arguments. */
"s" /* `name` */
"s" /* `description` */
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
"s" /* `translation_context` */
"O&" /* `options` */
"O&" /* `override` */
"O&" /* `tags` */
":CollectionProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
bpy_prop_arg_parse_id,
&id_data,
&type,
&name,
&description,
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
&translation_context,
pyrna_enum_bitfield_parse_set,
&options_enum,
pyrna_enum_bitfield_parse_set,
&override_enum,
bpy_prop_arg_parse_tag_defines,
&tags_enum))
{
return nullptr;
}
ptype = pointer_type_from_py(type, "CollectionProperty(...):");
if (!ptype) {
return nullptr;
}
if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
PyErr_Format(PyExc_TypeError,
"CollectionProperty(...) expected an RNA type derived from %.200s",
RNA_struct_ui_name(&RNA_PropertyGroup));
return nullptr;
}
if (id_data.prop_free_handle != nullptr) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
}
prop = RNA_def_collection_runtime(
srna, id_data.value, ptype, name ? name : id_data.value, description);
I18n: Add translation contexts to properties declared from Python Some property labels need a context to disambiguate them from others which have the same name. The only way to show the proper text currently for such properties is to override it in the UI code with a translation context, like: ```python layout.prop(obj, "area", text="Area", context=i18n_contexts.amount) ``` Python properties already store a translation context though, but this context cannot be chosen from a Python script. For instance, typing: ```python bpy.types.Scene.test_area = bpy.props.BoolProperty(name="Area") print(bpy.context.scene.bl_rna.properties['test_area'].translation_context) ``` will print `*`, the default context for Python props. This commit allows specifying a context in this manner: ```python from bpy.app.translations import contexts as i18n_contexts bpy.types.Scene.test_number_area = bpy.props.BoolProperty( name="Area", translation_context=i18n_contexts.amount ) print(bpy.context.scene.bl_rna.properties['test_number_area'].translation_context) ``` will now print `Amount` and can be translated differently from other labels. In this instance, the word for a surface area measurement, instead of a UI area. ----- This is what translated properties look like using the existing ("Area", "") and ("Area", "Amount") messages: ![python_prop_contexts_test.png](/attachments/b0d9737e-4b31-4c91-a08e-b347db31225f) The panel can be generated with this script: [python_prop_contexts_test.py](/attachments/ab613cdc-8eba-46bc-8f3c-ad0a97e7a6e5) Pull Request: https://projects.blender.org/blender/blender/pulls/107150
2023-05-22 11:38:51 +02:00
if (translation_context) {
RNA_def_property_translation_context(prop, translation_context);
}
if (tags_enum.base.is_set) {
RNA_def_property_tags(prop, tags_enum.base.value);
}
if (options_enum.is_set) {
bpy_prop_assign_flag(prop, options_enum.value);
}
if (override_enum.is_set) {
bpy_prop_assign_flag_override(prop, override_enum.value);
}
if (RNA_struct_idprops_contains_datablock(ptype)) {
if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
}
}
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
BPy_RemoveProperty_doc,
".. function:: RemoveProperty(cls, attr)\n"
"\n"
" Removes a dynamically defined property.\n"
"\n"
" :arg cls: The class containing the property (must be a positional argument).\n"
" :type cls: type\n"
" :arg attr: Property name (must be passed as a keyword).\n"
" :type attr: str\n"
"\n"
".. note:: Typically this function doesn't need to be accessed directly.\n"
" Instead use ``del cls.attr``\n");
static PyObject *BPy_RemoveProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
2011-10-13 01:29:08 +00:00
if (PyTuple_GET_SIZE(args) == 1) {
PyObject *ret;
self = PyTuple_GET_ITEM(args, 0);
args = PyTuple_New(0);
ret = BPy_RemoveProperty(self, args, kw);
Py_DECREF(args);
return ret;
}
if (PyTuple_GET_SIZE(args) > 1) {
PyErr_SetString(PyExc_ValueError, "expected one positional arg, one keyword arg");
return nullptr;
}
srna = srna_from_self(self, "RemoveProperty(...):");
if (srna == nullptr && PyErr_Occurred()) {
return nullptr; /* self's type was compatible but error getting the srna */
}
if (srna == nullptr) {
PyErr_SetString(PyExc_TypeError, "RemoveProperty(): struct rna not available for this type");
return nullptr;
}
const char *id = nullptr;
static const char *_keywords[] = {
"attr",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"s" /* `attr` */
":RemoveProperty",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &id)) {
return nullptr;
}
if (RNA_def_property_free_identifier(srna, id) != 1) {
PyErr_Format(PyExc_TypeError, "RemoveProperty(): '%s' not a defined dynamic property", id);
return nullptr;
}
Py_RETURN_NONE;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Main Module `bpy.props`
* \{ */
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef props_methods[] = {
2012-03-26 20:41:54 +00:00
{"BoolProperty",
(PyCFunction)BPy_BoolProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_BoolProperty_doc},
{"BoolVectorProperty",
(PyCFunction)BPy_BoolVectorProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_BoolVectorProperty_doc},
{"IntProperty",
(PyCFunction)BPy_IntProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_IntProperty_doc},
{"IntVectorProperty",
(PyCFunction)BPy_IntVectorProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_IntVectorProperty_doc},
{"FloatProperty",
(PyCFunction)BPy_FloatProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_FloatProperty_doc},
{"FloatVectorProperty",
(PyCFunction)BPy_FloatVectorProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_FloatVectorProperty_doc},
{"StringProperty",
(PyCFunction)BPy_StringProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_StringProperty_doc},
{"EnumProperty",
(PyCFunction)BPy_EnumProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_EnumProperty_doc},
{"PointerProperty",
(PyCFunction)BPy_PointerProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_PointerProperty_doc},
{"CollectionProperty",
(PyCFunction)BPy_CollectionProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_CollectionProperty_doc},
2012-03-26 20:41:54 +00:00
{"RemoveProperty",
(PyCFunction)BPy_RemoveProperty,
METH_VARARGS | METH_KEYWORDS,
BPy_RemoveProperty_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
static int props_visit(PyObject * /*self*/, visitproc visit, void *arg)
{
LISTBASE_FOREACH (BPyPropStore *, prop_store, &g_bpy_prop_store_list) {
PyObject **py_data = (PyObject **)&prop_store->py_data;
for (int i = 0; i < BPY_PROP_STORE_PY_DATA_SIZE; i++) {
Py_VISIT(py_data[i]);
}
}
return 0;
}
static int props_clear(PyObject * /*self*/)
{
LISTBASE_FOREACH (BPyPropStore *, prop_store, &g_bpy_prop_store_list) {
PyObject **py_data = (PyObject **)&prop_store->py_data;
for (int i = 0; i < BPY_PROP_STORE_PY_DATA_SIZE; i++) {
Py_CLEAR(py_data[i]);
}
}
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
props_module_doc,
2015-04-24 14:08:31 +01:00
"This module defines properties to extend Blender's internal data. The result of these "
"functions"
" is used to assign properties to classes registered with Blender and can't be used "
"directly.\n"
"\n"
".. note:: All parameters to these functions must be passed as keywords.\n");
static PyModuleDef props_module = {
/*m_base*/ PyModuleDef_HEAD_INIT,
/*m_name*/ "bpy.props",
/*m_doc*/ props_module_doc,
/*m_size*/ -1, /* multiple "initialization" just copies the module dict. */
/*m_methods*/ props_methods,
/*m_slots*/ nullptr,
/*m_traverse*/ props_visit,
/*m_clear*/ props_clear,
/*m_free*/ nullptr,
};
PyObject *BPY_rna_props()
{
PyObject *submodule;
PyObject *submodule_dict;
submodule = PyModule_Create(&props_module);
PyDict_SetItemString(PyImport_GetModuleDict(), props_module.m_name, submodule);
/* API needs the PyObjects internally. */
submodule_dict = PyModule_GetDict(submodule);
#define ASSIGN_STATIC(_name) pymeth_##_name = PyDict_GetItemString(submodule_dict, #_name)
ASSIGN_STATIC(BoolProperty);
ASSIGN_STATIC(BoolVectorProperty);
ASSIGN_STATIC(IntProperty);
ASSIGN_STATIC(IntVectorProperty);
ASSIGN_STATIC(FloatProperty);
ASSIGN_STATIC(FloatVectorProperty);
ASSIGN_STATIC(StringProperty);
ASSIGN_STATIC(EnumProperty);
ASSIGN_STATIC(PointerProperty);
ASSIGN_STATIC(CollectionProperty);
ASSIGN_STATIC(RemoveProperty);
if (PyType_Ready(&bpy_prop_deferred_Type) < 0) {
return nullptr;
}
PyModule_AddType(submodule, &bpy_prop_deferred_Type);
/* Run this when properties are freed. */
RNA_def_property_free_pointers_set_py_data_callback(bpy_prop_py_data_remove);
return submodule;
}
void BPY_rna_props_clear_all()
{
/* Remove all user counts, so this isn't considered a leak from Python's perspective. */
props_clear(nullptr);
/* Running is harmless, but redundant. */
RNA_def_property_free_pointers_set_py_data_callback(nullptr);
/* Include as it's correct, in practice this should never be used again. */
BLI_listbase_clear(&g_bpy_prop_store_list);
}
/** \} */