BPY: Implement get_transform and set_transform for runtime-defined RNA properties.

Improve handling of runtime defined python RNA properties. Mainly:
* Add `get_transform` and `set_transform` new callbacks.
  These allow to edit the value, while still using the default
  (IDProperty-based) storage system.
* Read-only properties should now be defined using a new `options` flag,
  `READ_ONLY`.
* `get`/`set` should only be used when storing data outside of the
  default system now.
  * Having a `get` without a `set` defined forces property to be
    read-only (same behavior as before).
  * Having a `set` without a `get` is now an error.
* Just like with existing `get/set` callbacks, `get_/set_transform`
  callbacks must always generate values matching the constraints defined
  by their `bpy.props` property definition (same type, within required
  range, same dimensions/sizes for the `Vector` properties, etc.).
* To simplify handling of non-statically sized strings, the relevant
  RNA API has been modified, to use `std::string` instead of
  (allocated) char arrays.

Relevant unittests and benchmarking have been added or updated as part
of this project.

Note: From initial benchmarking, 'transform' versions of get/set are
several times faster than 'real' get/set.

Implements #141042.

Pull Request: https://projects.blender.org/blender/blender/pulls/141303
This commit is contained in:
Bastien Montagne 2025-09-02 11:30:09 +02:00 • committed by Bastien Montagne
parent 75c66158aa
commit 469f54f484
19 changed files with 2762 additions and 740 deletions

View file

@ -280,6 +280,8 @@ static void GIZMO_GT_primitive_3d(wmGizmoType *gzt)
RNA_def_property_enum_funcs_runtime(prop,
gizmo_primitive_rna__draw_style_get_fn,
gizmo_primitive_rna__draw_style_set_fn,
nullptr,
nullptr,
nullptr);
prop = RNA_def_float_factor(
@ -287,11 +289,16 @@ static void GIZMO_GT_primitive_3d(wmGizmoType *gzt)
RNA_def_property_float_funcs_runtime(prop,
gizmo_primitive_rna__arc_inner_factor_get_fn,
gizmo_primitive_rna__arc_inner_factor_set_fn,
nullptr,
nullptr,
nullptr);
prop = RNA_def_boolean(gzt->srna, "draw_inner", true, "Draw Inner", "");
RNA_def_property_boolean_funcs_runtime(
prop, gizmo_primitive_rna__draw_inner_get_fn, gizmo_primitive_rna__draw_inner_set_fn);
RNA_def_property_boolean_funcs_runtime(prop,
gizmo_primitive_rna__draw_inner_get_fn,
gizmo_primitive_rna__draw_inner_set_fn,
nullptr,
nullptr);
}
void ED_gizmotypes_primitive_3d()

View file

@ -339,8 +339,12 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
"Point that defines the location of the perpendicular snap",
FLT_MIN,
FLT_MAX);
RNA_def_property_float_array_funcs_runtime(
prop, gizmo_snap_rna_prevpoint_get_fn, gizmo_snap_rna_prevpoint_set_fn, nullptr);
RNA_def_property_float_array_funcs_runtime(prop,
gizmo_snap_rna_prevpoint_get_fn,
gizmo_snap_rna_prevpoint_set_fn,
nullptr,
nullptr,
nullptr);
/* Returns. */
prop = RNA_def_float_translation(gzt->srna,
@ -353,8 +357,12 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
"Snap Point Location",
FLT_MIN,
FLT_MAX);
RNA_def_property_float_array_funcs_runtime(
prop, gizmo_snap_rna_location_get_fn, gizmo_snap_rna_location_set_fn, nullptr);
RNA_def_property_float_array_funcs_runtime(prop,
gizmo_snap_rna_location_get_fn,
gizmo_snap_rna_location_set_fn,
nullptr,
nullptr,
nullptr);
prop = RNA_def_float_vector_xyz(gzt->srna,
"normal",
@ -366,7 +374,8 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
"Snap Point Normal",
FLT_MIN,
FLT_MAX);
RNA_def_property_float_array_funcs_runtime(prop, gizmo_snap_rna_normal_get_fn, nullptr, nullptr);
RNA_def_property_float_array_funcs_runtime(
prop, gizmo_snap_rna_normal_get_fn, nullptr, nullptr, nullptr, nullptr);
prop = RNA_def_int_vector(gzt->srna,
"snap_elem_index",
@ -379,7 +388,7 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
INT_MIN,
INT_MAX);
RNA_def_property_int_array_funcs_runtime(
prop, gizmo_snap_rna_snap_elem_index_get_fn, nullptr, nullptr);
prop, gizmo_snap_rna_snap_elem_index_get_fn, nullptr, nullptr, nullptr, nullptr);
prop = RNA_def_enum(gzt->srna,
"snap_source_type",
@ -390,6 +399,8 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
RNA_def_property_enum_funcs_runtime(prop,
gizmo_snap_rna_snap_srouce_type_get_fn,
gizmo_snap_rna_snap_srouce_type_set_fn,
nullptr,
nullptr,
nullptr);
}

View file

@ -783,7 +783,8 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
DT_LAYERS_ACTIVE_SRC,
"Source Layers Selection",
"Which layers to transfer, in case of multi-layers types");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_layers_select_itemf);
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_itemf, nullptr, nullptr);
prop = RNA_def_enum(ot->srna,
"layers_select_dst",
@ -791,7 +792,8 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
DT_LAYERS_ACTIVE_DST,
"Destination Layers Matching",
"How to match source and destination layers");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_layers_select_itemf);
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_itemf, nullptr, nullptr);
prop = RNA_def_enum(ot->srna,
"mix_mode",
@ -799,7 +801,7 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
CDT_MIX_TRANSFER,
"Mix Mode",
"How to affect destination elements with source values");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_mix_mode_itemf);
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_mix_mode_itemf, nullptr, nullptr);
RNA_def_float(
ot->srna,
"mix_factor",
@ -948,7 +950,8 @@ void OBJECT_OT_datalayout_transfer(wmOperatorType *ot)
DT_LAYERS_ACTIVE_SRC,
"Source Layers Selection",
"Which layers to transfer, in case of multi-layers types");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_layers_select_src_itemf);
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_src_itemf, nullptr, nullptr);
prop = RNA_def_enum(ot->srna,
"layers_select_dst",
@ -956,7 +959,8 @@ void OBJECT_OT_datalayout_transfer(wmOperatorType *ot)
DT_LAYERS_ACTIVE_DST,
"Destination Layers Matching",
"How to match source and destination layers");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_layers_select_dst_itemf);
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_dst_itemf, nullptr, nullptr);
}
} // namespace blender::ed::object

View file

@ -762,7 +762,7 @@ void OUTLINER_OT_id_remap(wmOperatorType *ot)
prop = RNA_def_enum(
ot->srna, "old_id", rna_enum_dummy_NULL_items, 0, "Old ID", "Old ID to replace");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, outliner_id_itemf);
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, outliner_id_itemf, nullptr, nullptr);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE | PROP_HIDDEN);
ot->prop = RNA_def_enum(ot->srna,
@ -771,7 +771,8 @@ void OUTLINER_OT_id_remap(wmOperatorType *ot)
0,
"New ID",
"New ID to remap all selected IDs' users to");
RNA_def_property_enum_funcs_runtime(ot->prop, nullptr, nullptr, outliner_id_itemf);
RNA_def_property_enum_funcs_runtime(
ot->prop, nullptr, nullptr, outliner_id_itemf, nullptr, nullptr);
RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE);
}

View file

@ -541,7 +541,8 @@ std::optional<std::string> RNA_property_string_path_filter(const bContext *C,
PropertyRNA *prop);
/**
* \return the length without `\0` terminator.
* \return The final length without `\0` terminator (might differ from the length of the stored
* string, when a `get_transform` callback is defined).
*/
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_string_get_default(PropertyRNA *prop, char *value, int value_maxncpy);

View file

@ -573,36 +573,64 @@ void RNA_def_property_enum_default_func(PropertyRNA *prop, const char *get_defau
void RNA_def_property_srna(PropertyRNA *prop, const char *type);
void RNA_def_py_data(PropertyRNA *prop, void *py_data);
/* API to define callbacks for runtime-defined properties (mainly for Operators, and from the
* Python `bpy.props` API).
*
* These expect 'extended' versions of the callbacks, with both the StructRNA owner and the
* PropertyRNA as first arguments.
*
* The 'Transform' ones allow to add a transform step (applied after getting, or before setting the
* value), which only modifies the value, but does not handle actual storage. Currently only used
* by `bpy`, more details in the documentation of #BPyPropStore.
*/
void RNA_def_property_boolean_funcs_runtime(PropertyRNA *prop,
BooleanPropertyGetFunc getfunc,
BooleanPropertySetFunc setfunc);
void RNA_def_property_boolean_array_funcs_runtime(PropertyRNA *prop,
BooleanArrayPropertyGetFunc getfunc,
BooleanArrayPropertySetFunc setfunc);
BooleanPropertySetFunc setfunc,
BooleanPropertyGetTransformFunc get_transform_fn,
BooleanPropertySetTransformFunc set_transform_fn);
void RNA_def_property_boolean_array_funcs_runtime(
PropertyRNA *prop,
BooleanArrayPropertyGetFunc getfunc,
BooleanArrayPropertySetFunc setfunc,
BooleanArrayPropertyGetTransformFunc get_transform_fn,
BooleanArrayPropertySetTransformFunc set_transform_fn);
void RNA_def_property_int_funcs_runtime(PropertyRNA *prop,
IntPropertyGetFunc getfunc,
IntPropertySetFunc setfunc,
IntPropertyRangeFunc rangefunc);
IntPropertyRangeFunc rangefunc,
IntPropertyGetTransformFunc get_transform_fn,
IntPropertySetTransformFunc set_transform_fn);
void RNA_def_property_int_array_funcs_runtime(PropertyRNA *prop,
IntArrayPropertyGetFunc getfunc,
IntArrayPropertySetFunc setfunc,
IntPropertyRangeFunc rangefunc);
IntPropertyRangeFunc rangefunc,
IntArrayPropertyGetTransformFunc get_transform_fn,
IntArrayPropertySetTransformFunc set_transform_fn);
void RNA_def_property_float_funcs_runtime(PropertyRNA *prop,
FloatPropertyGetFunc getfunc,
FloatPropertySetFunc setfunc,
FloatPropertyRangeFunc rangefunc);
void RNA_def_property_float_array_funcs_runtime(PropertyRNA *prop,
FloatArrayPropertyGetFunc getfunc,
FloatArrayPropertySetFunc setfunc,
FloatPropertyRangeFunc rangefunc);
FloatPropertyRangeFunc rangefunc,
FloatPropertyGetTransformFunc get_transform_fn,
FloatPropertySetTransformFunc set_transform_fn);
void RNA_def_property_float_array_funcs_runtime(
PropertyRNA *prop,
FloatArrayPropertyGetFunc getfunc,
FloatArrayPropertySetFunc setfunc,
FloatPropertyRangeFunc rangefunc,
FloatArrayPropertyGetTransformFunc get_transform_fn,
FloatArrayPropertySetTransformFunc set_transform_fn);
void RNA_def_property_enum_funcs_runtime(PropertyRNA *prop,
EnumPropertyGetFunc getfunc,
EnumPropertySetFunc setfunc,
EnumPropertyItemFunc itemfunc);
EnumPropertyItemFunc itemfunc,
EnumPropertyGetTransformFunc get_transform_fn,
EnumPropertySetTransformFunc set_transform_fn);
void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
StringPropertyGetFunc getfunc,
StringPropertyLengthFunc lengthfunc,
StringPropertySetFunc setfunc);
StringPropertySetFunc setfunc,
StringPropertyGetTransformFunc get_transform_fn,
StringPropertySetTransformFunc set_transform_fn);
void RNA_def_property_string_search_func_runtime(PropertyRNA *prop,
StringPropertySearchFunc search_fn,
eStringPropertySearchFlag search_flag);

View file

@ -684,30 +684,93 @@ struct EnumPropertyItem {
/** Separator for RNA enum that begins a new column in menus (shown in the UI). */
#define RNA_ENUM_ITEM_SEPR_COLUMN RNA_ENUM_ITEM_HEADING("", NULL)
/* extended versions with PropertyRNA argument */
/* Extended versions with PropertyRNA argument. Used in particular by the bpy code to wrap all the
* py-defined callbacks when defining a property using `bpy.props` module.
*
* The 'Transform' ones allow to add a transform step (applied after getting, or before setting the
* value), which only modifies the value, but does not handle actual storage. Currently only used
* by `bpy`, more details in the documentation of #BPyPropStore.
*/
using BooleanPropertyGetFunc = bool (*)(PointerRNA *ptr, PropertyRNA *prop);
using BooleanPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, bool value);
using BooleanArrayPropertyGetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, bool *values);
using BooleanPropertyGetTransformFunc = bool (*)(PointerRNA *ptr,
PropertyRNA *prop,
bool value,
bool is_set);
using BooleanPropertySetTransformFunc =
bool (*)(PointerRNA *ptr, PropertyRNA *prop, bool new_value, bool curr_value, bool is_set);
using BooleanArrayPropertyGetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, bool *r_values);
using BooleanArrayPropertySetFunc = void (*)(PointerRNA *ptr,
PropertyRNA *prop,
const bool *values);
const bool *r_values);
using BooleanArrayPropertyGetTransformFunc = void (*)(
PointerRNA *ptr, PropertyRNA *prop, const bool *curr_values, bool is_set, bool *r_values);
using BooleanArrayPropertySetTransformFunc = void (*)(PointerRNA *ptr,
PropertyRNA *prop,
const bool *new_values,
const bool *curr_values,
bool is_set,
bool *r_values);
using IntPropertyGetFunc = int (*)(PointerRNA *ptr, PropertyRNA *prop);
using IntPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, int value);
using IntPropertyGetTransformFunc = int (*)(PointerRNA *ptr,
PropertyRNA *prop,
int value,
bool is_set);
using IntPropertySetTransformFunc =
int (*)(PointerRNA *ptr, PropertyRNA *prop, int new_value, int curr_value, bool is_set);
using IntArrayPropertyGetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, int *values);
using IntArrayPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, const int *values);
using IntArrayPropertyGetTransformFunc = void (*)(
PointerRNA *ptr, PropertyRNA *prop, const int *curr_values, bool is_set, int *r_values);
using IntArrayPropertySetTransformFunc = void (*)(PointerRNA *ptr,
PropertyRNA *prop,
const int *new_values,
const int *curr_values,
bool is_set,
int *r_values);
using IntPropertyRangeFunc =
void (*)(PointerRNA *ptr, PropertyRNA *prop, int *min, int *max, int *softmin, int *softmax);
using FloatPropertyGetFunc = float (*)(PointerRNA *ptr, PropertyRNA *prop);
using FloatPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, float value);
using FloatPropertyGetTransformFunc = float (*)(PointerRNA *ptr,
PropertyRNA *prop,
float value,
bool is_set);
using FloatPropertySetTransformFunc =
float (*)(PointerRNA *ptr, PropertyRNA *prop, float new_value, float curr_value, bool is_set);
using FloatArrayPropertyGetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, float *values);
using FloatArrayPropertySetFunc = void (*)(PointerRNA *ptr,
PropertyRNA *prop,
const float *values);
using FloatArrayPropertyGetTransformFunc = void (*)(
PointerRNA *ptr, PropertyRNA *prop, const float *curr_values, bool is_set, float *r_values);
using FloatArrayPropertySetTransformFunc = void (*)(PointerRNA *ptr,
PropertyRNA *prop,
const float *new_values,
const float *curr_values,
bool is_set,
float *r_values);
using FloatPropertyRangeFunc = void (*)(
PointerRNA *ptr, PropertyRNA *prop, float *min, float *max, float *softmin, float *softmax);
using StringPropertyGetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, char *value);
using StringPropertyGetFunc = std::string (*)(PointerRNA *ptr, PropertyRNA *prop);
using StringPropertyLengthFunc = int (*)(PointerRNA *ptr, PropertyRNA *prop);
using StringPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, const char *value);
using StringPropertySetFunc = void (*)(PointerRNA *ptr,
PropertyRNA *prop,
const std::string &value);
using StringPropertyGetTransformFunc = std::string (*)(PointerRNA *ptr,
PropertyRNA *prop,
const std::string &value,
bool is_set);
using StringPropertySetTransformFunc = std::string (*)(PointerRNA *ptr,
PropertyRNA *prop,
const std::string &new_value,
const std::string &curr_value,
bool is_set);
struct StringPropertySearchVisitParams {
/** Text being searched for. */
@ -763,6 +826,12 @@ using StringPropertyPathFilterFunc = std::optional<std::string> (*)(const bConte
using EnumPropertyGetFunc = int (*)(PointerRNA *ptr, PropertyRNA *prop);
using EnumPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, int value);
using EnumPropertyGetTransformFunc = int (*)(PointerRNA *ptr,
PropertyRNA *prop,
int value,
bool is_set);
using EnumPropertySetTransformFunc =
int (*)(PointerRNA *ptr, PropertyRNA *prop, int new_value, int curr_value, bool is_set);
/* same as PropEnumItemFunc */
using EnumPropertyItemFunc = const EnumPropertyItem *(*)(bContext *C,
PointerRNA *ptr,

View file

@ -2050,7 +2050,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
if (!(prop->flag & PROP_EDITABLE) &&
(bprop->set || bprop->set_ex || bprop->setarray || bprop->setarray_ex))
(bprop->set || bprop->set_ex || bprop->set_transform || bprop->setarray ||
bprop->setarray_ex || bprop->setarray_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, is read-only but has defines a \"set\" callback.",
@ -2060,7 +2061,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
}
if (!prop->arraydimension &&
(bprop->getarray || bprop->getarray_ex || bprop->setarray || bprop->setarray_ex))
(bprop->getarray || bprop->getarray_ex || bprop->getarray_transform || bprop->setarray ||
bprop->setarray_ex || bprop->setarray_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, is not an array but defines an array callback.",
@ -2091,7 +2093,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
if (!(prop->flag & PROP_EDITABLE) &&
(iprop->set || iprop->set_ex || iprop->setarray || iprop->setarray_ex))
(iprop->set || iprop->set_ex || iprop->set_transform || iprop->setarray ||
iprop->setarray_ex || iprop->setarray_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, is read-only but has defines a \"set\" callback.",
@ -2101,7 +2104,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
}
if (!prop->arraydimension &&
(iprop->getarray || iprop->getarray_ex || iprop->setarray || iprop->setarray_ex))
(iprop->getarray || iprop->getarray_ex || iprop->getarray_transform || iprop->setarray ||
iprop->setarray_ex || iprop->setarray_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, is not an array but defines an array callback.",
@ -2136,7 +2140,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
if (!(prop->flag & PROP_EDITABLE) &&
(fprop->set || fprop->set_ex || fprop->setarray || fprop->setarray_ex))
(fprop->set || fprop->set_ex || fprop->set_transform || fprop->setarray ||
fprop->setarray_ex || fprop->setarray_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, is read-only but has defines a \"set\" callback.",
@ -2146,7 +2151,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
}
if (!prop->arraydimension &&
(fprop->getarray || fprop->getarray_ex || fprop->setarray || fprop->setarray_ex))
(fprop->getarray || fprop->getarray_ex || fprop->getarray_transform || fprop->setarray ||
fprop->setarray_ex || fprop->setarray_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, is not an array but defines an array callback.",
@ -2181,7 +2187,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
if (dp->enumbitflags && eprop->item_fn &&
!(eprop->item != rna_enum_dummy_NULL_items || eprop->set || eprop->set_ex))
!(eprop->item != rna_enum_dummy_NULL_items || eprop->set || eprop->set_ex ||
eprop->set_transform))
{
CLOG_ERROR(&LOG,
"%s.%s, bitflag enum should not define an `item` callback function, unless "
@ -2191,7 +2198,7 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
DefRNA.error = true;
}
if (!(prop->flag & PROP_EDITABLE) && (eprop->set || eprop->set_ex)) {
if (!(prop->flag & PROP_EDITABLE) && (eprop->set || eprop->set_ex || eprop->set_transform)) {
CLOG_ERROR(&LOG,
"%s.%s, is read-only but has defines a \"set\" callback.",
srna->identifier,
@ -2212,7 +2219,7 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
case PROP_STRING: {
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
if (!(prop->flag & PROP_EDITABLE) && (sprop->set || sprop->set_ex)) {
if (!(prop->flag & PROP_EDITABLE) && (sprop->set || sprop->set_ex || sprop->set_transform)) {
CLOG_ERROR(&LOG,
"%s.%s, is read-only but has defines a \"set\" callback.",
srna->identifier,
@ -4453,7 +4460,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_BOOLEAN: {
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
fprintf(f,
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, ",
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, ",
rna_function_string(bprop->get),
rna_function_string(bprop->set),
rna_function_string(bprop->getarray),
@ -4462,6 +4469,10 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(bprop->set_ex),
rna_function_string(bprop->getarray_ex),
rna_function_string(bprop->setarray_ex),
rna_function_string(bprop->get_transform),
rna_function_string(bprop->set_transform),
rna_function_string(bprop->getarray_transform),
rna_function_string(bprop->setarray_transform),
rna_function_string(bprop->get_default),
rna_function_string(bprop->get_default_array),
bprop->defaultvalue);
@ -4476,7 +4487,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_INT: {
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
fprintf(f,
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,\n\t",
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,\n\t",
rna_function_string(iprop->get),
rna_function_string(iprop->set),
rna_function_string(iprop->getarray),
@ -4486,7 +4497,11 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(iprop->set_ex),
rna_function_string(iprop->getarray_ex),
rna_function_string(iprop->setarray_ex),
rna_function_string(iprop->range_ex));
rna_function_string(iprop->range_ex),
rna_function_string(iprop->get_transform),
rna_function_string(iprop->set_transform),
rna_function_string(iprop->getarray_transform),
rna_function_string(iprop->setarray_transform));
fprintf(f, "%s", rna_ui_scale_type_string(iprop->ui_scale_type));
fprintf(f, ", ");
rna_int_print(f, iprop->softmin);
@ -4517,7 +4532,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_FLOAT: {
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
fprintf(f,
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, ",
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, ",
rna_function_string(fprop->get),
rna_function_string(fprop->set),
rna_function_string(fprop->getarray),
@ -4527,7 +4542,11 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(fprop->set_ex),
rna_function_string(fprop->getarray_ex),
rna_function_string(fprop->setarray_ex),
rna_function_string(fprop->range_ex));
rna_function_string(fprop->range_ex),
rna_function_string(fprop->get_transform),
rna_function_string(fprop->set_transform),
rna_function_string(fprop->getarray_transform),
rna_function_string(fprop->setarray_transform));
fprintf(f, "%s, ", rna_ui_scale_type_string(fprop->ui_scale_type));
rna_float_print(f, fprop->softmin);
fprintf(f, ", ");
@ -4559,13 +4578,15 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_STRING: {
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
fprintf(f,
"\t%s, %s, %s, %s, %s, %s, %s, eStringPropertySearchFlag(%d), %s, %d, ",
"\t%s, %s, %s, %s, %s, %s, %s, %s, %s, eStringPropertySearchFlag(%d), %s, %d, ",
rna_function_string(sprop->get),
rna_function_string(sprop->length),
rna_function_string(sprop->set),
rna_function_string(sprop->get_ex),
rna_function_string(sprop->length_ex),
rna_function_string(sprop->set_ex),
rna_function_string(sprop->get_transform),
rna_function_string(sprop->set_transform),
rna_function_string(sprop->search),
int(sprop->search_flag),
rna_function_string(sprop->path_filter),
@ -4577,12 +4598,14 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_ENUM: {
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
fprintf(f,
"\t%s, %s, %s, %s, %s, %s, ",
"\t%s, %s, %s, %s, %s, %s, %s, %s, ",
rna_function_string(eprop->get),
rna_function_string(eprop->set),
rna_function_string(eprop->item_fn),
rna_function_string(eprop->get_ex),
rna_function_string(eprop->set_ex),
rna_function_string(eprop->get_transform),
rna_function_string(eprop->set_transform),
rna_function_string(eprop->get_default));
if (eprop->item) {
const char *item_global_id = rna_enum_id_from_pointer(eprop->item);

File diff suppressed because it is too large Load diff

View file

@ -3312,7 +3312,9 @@ void RNA_def_property_boolean_funcs(PropertyRNA *prop, const char *get, const ch
void RNA_def_property_boolean_funcs_runtime(PropertyRNA *prop,
BooleanPropertyGetFunc getfunc,
BooleanPropertySetFunc setfunc)
BooleanPropertySetFunc setfunc,
BooleanPropertyGetTransformFunc get_transform_fn,
BooleanPropertySetTransformFunc set_transform_fn)
{
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
@ -3331,11 +3333,21 @@ void RNA_def_property_boolean_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
bprop->get_transform = get_transform_fn;
}
if (set_transform_fn) {
bprop->set_transform = set_transform_fn;
}
}
void RNA_def_property_boolean_array_funcs_runtime(PropertyRNA *prop,
BooleanArrayPropertyGetFunc getfunc,
BooleanArrayPropertySetFunc setfunc)
void RNA_def_property_boolean_array_funcs_runtime(
PropertyRNA *prop,
BooleanArrayPropertyGetFunc getfunc,
BooleanArrayPropertySetFunc setfunc,
BooleanArrayPropertyGetTransformFunc get_transform_fn,
BooleanArrayPropertySetTransformFunc set_transform_fn)
{
BoolPropertyRNA *bprop = (BoolPropertyRNA *)prop;
@ -3354,6 +3366,13 @@ void RNA_def_property_boolean_array_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
bprop->getarray_transform = get_transform_fn;
}
if (set_transform_fn) {
bprop->setarray_transform = set_transform_fn;
}
}
void RNA_def_property_int_funcs(PropertyRNA *prop,
@ -3403,7 +3422,9 @@ void RNA_def_property_int_funcs(PropertyRNA *prop,
void RNA_def_property_int_funcs_runtime(PropertyRNA *prop,
IntPropertyGetFunc getfunc,
IntPropertySetFunc setfunc,
IntPropertyRangeFunc rangefunc)
IntPropertyRangeFunc rangefunc,
IntPropertyGetTransformFunc get_transform_fn,
IntPropertySetTransformFunc set_transform_fn)
{
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
@ -3425,12 +3446,21 @@ void RNA_def_property_int_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
iprop->get_transform = get_transform_fn;
}
if (set_transform_fn) {
iprop->set_transform = set_transform_fn;
}
}
void RNA_def_property_int_array_funcs_runtime(PropertyRNA *prop,
IntArrayPropertyGetFunc getfunc,
IntArrayPropertySetFunc setfunc,
IntPropertyRangeFunc rangefunc)
IntPropertyRangeFunc rangefunc,
IntArrayPropertyGetTransformFunc get_transform_fn,
IntArrayPropertySetTransformFunc set_transform_fn)
{
IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
@ -3452,6 +3482,13 @@ void RNA_def_property_int_array_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
iprop->getarray_transform = get_transform_fn;
}
if (set_transform_fn) {
iprop->setarray_transform = set_transform_fn;
}
}
void RNA_def_property_float_funcs(PropertyRNA *prop,
@ -3501,7 +3538,9 @@ void RNA_def_property_float_funcs(PropertyRNA *prop,
void RNA_def_property_float_funcs_runtime(PropertyRNA *prop,
FloatPropertyGetFunc getfunc,
FloatPropertySetFunc setfunc,
FloatPropertyRangeFunc rangefunc)
FloatPropertyRangeFunc rangefunc,
FloatPropertyGetTransformFunc get_transform_fn,
FloatPropertySetTransformFunc set_transform_fn)
{
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
@ -3523,12 +3562,22 @@ void RNA_def_property_float_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
fprop->get_transform = get_transform_fn;
}
if (set_transform_fn) {
fprop->set_transform = set_transform_fn;
}
}
void RNA_def_property_float_array_funcs_runtime(PropertyRNA *prop,
FloatArrayPropertyGetFunc getfunc,
FloatArrayPropertySetFunc setfunc,
FloatPropertyRangeFunc rangefunc)
void RNA_def_property_float_array_funcs_runtime(
PropertyRNA *prop,
FloatArrayPropertyGetFunc getfunc,
FloatArrayPropertySetFunc setfunc,
FloatPropertyRangeFunc rangefunc,
FloatArrayPropertyGetTransformFunc get_transform_fn,
FloatArrayPropertySetTransformFunc set_transform_fn)
{
FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
@ -3550,6 +3599,13 @@ void RNA_def_property_float_array_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
fprop->getarray_transform = get_transform_fn;
}
if (set_transform_fn) {
fprop->setarray_transform = set_transform_fn;
}
}
void RNA_def_property_enum_funcs(PropertyRNA *prop,
@ -3589,7 +3645,9 @@ void RNA_def_property_enum_funcs(PropertyRNA *prop,
void RNA_def_property_enum_funcs_runtime(PropertyRNA *prop,
EnumPropertyGetFunc getfunc,
EnumPropertySetFunc setfunc,
EnumPropertyItemFunc itemfunc)
EnumPropertyItemFunc itemfunc,
EnumPropertyGetTransformFunc get_transform_fn,
EnumPropertySetTransformFunc set_transform_fn)
{
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
@ -3611,6 +3669,13 @@ void RNA_def_property_enum_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
eprop->get_transform = get_transform_fn;
}
if (set_transform_fn) {
eprop->set_transform = set_transform_fn;
}
}
void RNA_def_property_string_funcs(PropertyRNA *prop,
@ -3699,7 +3764,9 @@ void RNA_def_property_string_filepath_filter_func(PropertyRNA *prop, const char
void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
StringPropertyGetFunc getfunc,
StringPropertyLengthFunc lengthfunc,
StringPropertySetFunc setfunc)
StringPropertySetFunc setfunc,
StringPropertyGetTransformFunc get_transform_fn,
StringPropertySetTransformFunc set_transform_fn)
{
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
@ -3721,6 +3788,13 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
}
if (get_transform_fn) {
sprop->get_transform = get_transform_fn;
}
if (set_transform_fn) {
sprop->set_transform = set_transform_fn;
}
}
void RNA_def_property_string_search_func_runtime(PropertyRNA *prop,

View file

@ -103,6 +103,9 @@ using PropCollectionAssignIntFunc = bool (*)(PointerRNA *ptr,
const PointerRNA *assign_ptr);
/* Extended versions with #PropertyRNA argument. */
/* NOTE: All extended get/set callbacks will always get a 'real' PropertyRNA `prop` pointer, never
* an 'IDProperty as PropertyRNA' one (i.e. when called, the given `prop` is the RNA result of a
* call to `rna_property_rna_or_id_get` or one of its wrappers). */
using PropBooleanGetFuncEx = bool (*)(PointerRNA *ptr, PropertyRNA *prop);
using PropBooleanSetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, bool value);
@ -120,12 +123,35 @@ using PropFloatArrayGetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, flo
using PropFloatArraySetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, const float *values);
using PropFloatRangeFuncEx = void (*)(
PointerRNA *ptr, PropertyRNA *prop, float *min, float *max, float *softmin, float *softmax);
using PropStringGetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, char *value);
using PropStringGetFuncEx = std::string (*)(PointerRNA *ptr, PropertyRNA *prop);
using PropStringLengthFuncEx = int (*)(PointerRNA *ptr, PropertyRNA *prop);
using PropStringSetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, const char *value);
using PropStringSetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, const std::string &value);
using PropEnumGetFuncEx = int (*)(PointerRNA *ptr, PropertyRNA *prop);
using PropEnumSetFuncEx = void (*)(PointerRNA *ptr, PropertyRNA *prop, int value);
/* Transform step (applied after getting, or before setting the value). Currently only used by
* `bpy`, more details in the documentation of #BPyPropStore. */
/* NOTE: All transform get/set callbacks will always get a 'real' PropertyRNA `prop` pointer, never
* an 'IDProperty as PropertyRNA' one (i.e. when called, the given `prop` is the RNA result of a
* call to `rna_property_rna_or_id_get` or one of its wrappers). */
using PropBooleanGetTransformFunc = BooleanPropertyGetTransformFunc;
using PropBooleanSetTransformFunc = BooleanPropertySetTransformFunc;
using PropBooleanArrayGetTransformFunc = BooleanArrayPropertyGetTransformFunc;
using PropBooleanArraySetTransformFunc = BooleanArrayPropertySetTransformFunc;
using PropIntGetTransformFunc = IntPropertyGetTransformFunc;
using PropIntSetTransformFunc = IntPropertySetTransformFunc;
using PropIntArrayGetTransformFunc = IntArrayPropertyGetTransformFunc;
using PropIntArraySetTransformFunc = IntArrayPropertySetTransformFunc;
using PropFloatGetTransformFunc = FloatPropertyGetTransformFunc;
using PropFloatSetTransformFunc = FloatPropertySetTransformFunc;
using PropFloatArrayGetTransformFunc = FloatArrayPropertyGetTransformFunc;
using PropFloatArraySetTransformFunc = FloatArrayPropertySetTransformFunc;
using PropStringGetTransformFunc = StringPropertyGetTransformFunc;
using PropStringSetTransformFunc = StringPropertySetTransformFunc;
using PropEnumGetTransformFunc = EnumPropertyGetTransformFunc;
using PropEnumSetTransformFunc = EnumPropertySetTransformFunc;
/* Handling override operations, and also comparison. */
/** Structure storing all needed data to process all three kinds of RNA properties. */
@ -463,6 +489,11 @@ struct BoolPropertyRNA {
PropBooleanArrayGetFuncEx getarray_ex;
PropBooleanArraySetFuncEx setarray_ex;
PropBooleanGetTransformFunc get_transform;
PropBooleanSetTransformFunc set_transform;
PropBooleanArrayGetTransformFunc getarray_transform;
PropBooleanArraySetTransformFunc setarray_transform;
PropBooleanGetFuncEx get_default;
PropBooleanArrayGetFuncEx get_default_array;
bool defaultvalue;
@ -484,6 +515,11 @@ struct IntPropertyRNA {
PropIntArraySetFuncEx setarray_ex;
PropIntRangeFuncEx range_ex;
PropIntGetTransformFunc get_transform;
PropIntSetTransformFunc set_transform;
PropIntArrayGetTransformFunc getarray_transform;
PropIntArraySetTransformFunc setarray_transform;
PropertyScaleType ui_scale_type;
int softmin, softmax;
int hardmin, hardmax;
@ -510,6 +546,11 @@ struct FloatPropertyRNA {
PropFloatArraySetFuncEx setarray_ex;
PropFloatRangeFuncEx range_ex;
PropFloatGetTransformFunc get_transform;
PropFloatSetTransformFunc set_transform;
PropFloatArrayGetTransformFunc getarray_transform;
PropFloatArraySetTransformFunc setarray_transform;
PropertyScaleType ui_scale_type;
float softmin, softmax;
float hardmin, hardmax;
@ -531,9 +572,14 @@ struct StringPropertyRNA {
PropStringSetFunc set;
PropStringGetFuncEx get_ex;
/* This callback only returns the 'storage' length (i.e. length of string returned by `get_ex`),
* _not_ the final length (potentially modified by the `get_transform` callback). */
PropStringLengthFuncEx length_ex;
PropStringSetFuncEx set_ex;
PropStringGetTransformFunc get_transform;
PropStringSetTransformFunc set_transform;
/**
* Optional callback to list candidates for a string.
* This is only for use as suggestions in UI, other values may be assigned.
@ -565,6 +611,9 @@ struct EnumPropertyRNA {
PropEnumGetFuncEx get_ex;
PropEnumSetFuncEx set_ex;
PropEnumGetTransformFunc get_transform;
PropEnumSetTransformFunc set_transform;
PropEnumGetFuncEx get_default;
const EnumPropertyItem *item;

View file

@ -159,6 +159,7 @@ const EnumPropertyItem rna_enum_property_unit_items[] = {
};
/* Descriptions for rna_enum_property_flag_items and rna_enum_property_flag_enum_items. */
static constexpr auto PROP_READ_ONLY_DESCR = "When set, the property cannot be edited";
static constexpr auto PROP_HIDDEN_DESCR =
"For operators: hide from places in the user interface where Blender would add the property "
"automatically, like Adjust Last Operation. Also this property is not written to presets.";
@ -183,6 +184,19 @@ static constexpr auto PROP_PATH_SUPPORTS_TEMPLATES_DESCR =
static constexpr auto PROP_ENUM_FLAG_DESCR = "";
const EnumPropertyItem rna_enum_property_flag_items[] = {
/* NOTE: This is used only in the `bpy.props` module to define runtime RNA properties.
* The value of this 'READ_ONLY' enum item is logically inverted compared to the
* `PROP_EDITABLE` used everywhere else in RNA-related code.
*
* This inversion logic is handled by the bpy property definition code (see
* #bpy_prop_assign_flag), and does not affect any other code area.
*
* This special handling is done to allow python property definitions to create by default
* editable properties, without having to specify the `options={'EDITABLE', ...}` parameter all
* the time.
*/
{PROP_EDITABLE, "READ_ONLY", 0, "Read Only", PROP_READ_ONLY_DESCR},
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", PROP_HIDDEN_DESCR},
{PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", PROP_SKIP_SAVE_DESCR},
{PROP_SKIP_PRESET, "SKIP_PRESET", 0, "Skip Preset", PROP_SKIP_PRESET_DESCR},
@ -214,6 +228,19 @@ const EnumPropertyItem rna_enum_property_flag_items[] = {
/** Only for enum type properties. */
const EnumPropertyItem rna_enum_property_flag_enum_items[] = {
/* NOTE: This is used only in the `bpy.props` module to define runtime RNA properties.
* The value of this 'READ_ONLY' enum item is logically inverted compared to the
* `PROP_EDITABLE` used everywhere else in RNA-related code.
*
* This inversion logic is handled by the bpy property definition code (see
* #bpy_prop_assign_flag), and does not affect any other code area.
*
* This special handling is done to allow python property definitions to create by default
* editable properties, without having to specify the `options={'EDITABLE', ...}` parameter all
* the time.
*/
{PROP_EDITABLE, "READ_ONLY", 0, "Read Only", PROP_READ_ONLY_DESCR},
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", PROP_HIDDEN_DESCR},
{PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", PROP_SKIP_SAVE_DESCR},
{PROP_ANIMATABLE, "ANIMATABLE", 0, "Animatable", PROP_ANIMATABLE_DESCR},

View file

@ -141,8 +141,12 @@ static void node_rna(StructRNA *srna)
PropertyRNA *prop;
prop = RNA_def_property(srna, "ui_shortcut", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs_runtime(
prop, rna_Node_Viewer_shortcut_node_get, rna_Node_Viewer_shortcut_node_set, nullptr);
RNA_def_property_int_funcs_runtime(prop,
rna_Node_Viewer_shortcut_node_get,
rna_Node_Viewer_shortcut_node_set,
nullptr,
nullptr,
nullptr);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_int_default(prop, NODE_VIEWER_SHORTCUT_NONE);

View file

@ -33,7 +33,8 @@ PropertyRNA *RNA_def_node_enum(StructRNA *srna,
const bool allow_animation)
{
PropertyRNA *prop = RNA_def_property(srna, identifier, PROP_ENUM, PROP_NONE);
RNA_def_property_enum_funcs_runtime(prop, accessors.getter, accessors.setter, item_func);
RNA_def_property_enum_funcs_runtime(
prop, accessors.getter, accessors.setter, item_func, nullptr, nullptr);
RNA_def_property_enum_items(prop, static_items);
if (default_value.has_value()) {
RNA_def_property_enum_default(prop, *default_value);
@ -59,7 +60,8 @@ PropertyRNA *RNA_def_node_boolean(StructRNA *srna,
bool allow_animation)
{
PropertyRNA *prop = RNA_def_property(srna, identifier, PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs_runtime(prop, accessors.getter, accessors.setter);
RNA_def_property_boolean_funcs_runtime(
prop, accessors.getter, accessors.setter, nullptr, nullptr);
if (default_value.has_value()) {
RNA_def_property_boolean_default(prop, *default_value);
}

File diff suppressed because it is too large Load diff

View file

@ -69,18 +69,28 @@ def _run_runtime_group_register_access(args):
do_register = args.get("do_register", False)
do_access = args.get("do_access", False)
do_get_set = args.get("do_get_set", False)
do_transform = args.get("do_transform", False)
property_type = args.get("property_type", 'IntProperty')
property_definition_cb = getattr(bpy.props, property_type)
assert (not (do_get_set and do_transform))
# Define basic 'transform' callbacks to test setting value,
# default to just setting untransformed value for 'unknown'/undefined property types.
property_transform_cb = {
property_transform_set_cb = {
'BoolProperty': lambda v: not v,
'IntProperty': lambda v: v + 1,
'FloatVectorProperty': lambda v: [v[2] + 1.0, v[0], v[1]],
'StringProperty': lambda v: ("B" if (v and v[0] == "A") else "A") + v[1:],
}.get(property_type, lambda v: v)
property_transform_get_cb = {
'BoolProperty': lambda v: not v,
'IntProperty': lambda v: v - 1,
'FloatVectorProperty': lambda v: [v[0], v[1], v[2]],
'StringProperty': lambda v: ("B" if (v and v[0] == "A") else "A") + v[1:],
}.get(property_type, lambda v: v)
if do_get_set:
class DummyGroup(bpy.types.PropertyGroup):
dummy_prop: property_definition_cb(
@ -95,6 +105,12 @@ def _run_runtime_group_register_access(args):
"dummy_prop",
val),
)
elif do_transform:
class DummyGroup(bpy.types.PropertyGroup):
dummy_prop: property_definition_cb(
get_transform=lambda self, curr_v, is_set: property_transform_get_cb(curr_v),
set_transform=lambda self, curr_v, new_v, is_set: property_transform_set_cb(curr_v),
)
else:
class DummyGroup(bpy.types.PropertyGroup):
dummy_prop: property_definition_cb()
@ -115,9 +131,14 @@ def _run_runtime_group_register_access(args):
bpy.utils.register_class(DummyGroup)
bpy.types.Scene.dummy_group = bpy.props.PointerProperty(type=DummyGroup)
for i in range(iterations):
v = sce.dummy_group.dummy_prop
sce.dummy_group.dummy_prop = property_transform_cb(v)
if do_transform:
for i in range(iterations):
v = sce.dummy_group.dummy_prop
sce.dummy_group.dummy_prop = v
else:
for i in range(iterations):
v = sce.dummy_group.dummy_prop
sce.dummy_group.dummy_prop = property_transform_set_cb(v)
del bpy.types.Scene.dummy_group
bpy.utils.unregister_class(DummyGroup)
@ -165,4 +186,10 @@ def generate(env):
{"do_access": True, "do_get_set": True, "property_type": 'FloatVectorProperty'}),
BPYRNATest("Py-Defined StringProperty Custom Get/Set Access", _run_runtime_group_register_access, 10 * 1000,
{"do_access": True, "do_get_set": True, "property_type": 'StringProperty'}),
BPYRNATest("Py-Defined BoolProperty Custom Transform Access", _run_runtime_group_register_access, 1000 * 1000,
{"do_access": True, "do_transform": True, "property_type": 'BoolProperty'}),
BPYRNATest("Py-Defined FloatVectorProperty Custom Transform Access", _run_runtime_group_register_access, 1000 * 1000,
{"do_access": True, "do_transform": True, "property_type": 'FloatVectorProperty'}),
BPYRNATest("Py-Defined StringProperty Custom Transform Access", _run_runtime_group_register_access, 1000 * 1000,
{"do_access": True, "do_transform": True, "property_type": 'StringProperty'}),
]

View file

@ -7,6 +7,8 @@ import bpy
import rna_prop_ui
import idprop
import io
import sys
import unittest
from array import array
@ -266,6 +268,12 @@ class TestIdPropertyDynamicRNA(TestHelper, unittest.TestCase):
string_prop: bpy.props.StringProperty()
int_prop: bpy.props.IntProperty()
float_array_prop: bpy.props.FloatVectorProperty(size=[3])
# Basic get/set transform.
string_prop_transform: bpy.props.StringProperty(
default="test",
maxlen=10,
get_transform=lambda self, storage_val, is_set: storage_val + "!!",
set_transform=lambda self, new_val, storage_val, is_set: storage_val + "!!" + new_val)
def setUp(self):
super().setUp()
@ -348,6 +356,40 @@ class TestIdPropertyDynamicRNA(TestHelper, unittest.TestCase):
with self.assertRaises(TypeError):
self.id.bl_system_properties_get()['dynrna_prop']['float_array_prop'] = [1.0, 10.0, 100.0, 0.1]
def test_get_set_transform(self):
self.assertEqual(len(self.id.dynrna_prop.string_prop_transform), 6)
self.assertEqual(self.id.dynrna_prop.string_prop_transform, "test!!")
# Default value only, was not yet set.
self.assertFalse('string_prop_transform' in self.id.bl_system_properties_get()['dynrna_prop'])
self.id.dynrna_prop.string_prop_transform = "-"
self.assertEqual(self.id.dynrna_prop.string_prop_transform, "test!!-!!")
self.assertEqual(self.id.bl_system_properties_get()['dynrna_prop']['string_prop_transform'], "test!!-")
# Raw-set exactly maxlen - 1 char.
self.id.bl_system_properties_get()['dynrna_prop']['string_prop_transform'] = "test!!tes"
# get_transform will produce an 11-char results, which should trigger an error.
# These asserts are not raised currently, but only print in `stderr`...
# But the returned string is the 'storage' one, not the result from get_transform.
stderr, sys.stderr = sys.stderr, io.StringIO()
self.assertEqual(self.id.dynrna_prop.string_prop_transform, "test!!tes")
self.assertTrue("ValueError" in sys.stderr.getvalue() and
"10" in sys.stderr.getvalue() and "11" in sys.stderr.getvalue())
sys.stderr.close()
sys.stderr = stderr
# Raw-set back to default value.
self.id.bl_system_properties_get()['dynrna_prop']['string_prop_transform'] = "test"
# Now set_transform will return 12-char string, wich is also invalid and discarded.
stderr, sys.stderr = sys.stderr, io.StringIO()
self.id.dynrna_prop.string_prop_transform = "test!!"
self.assertTrue("ValueError" in sys.stderr.getvalue() and
"10" in sys.stderr.getvalue() and "12" in sys.stderr.getvalue())
sys.stderr.close()
sys.stderr = stderr
self.assertEqual(self.id.bl_system_properties_get()['dynrna_prop']['string_prop_transform'], "test")
self.assertEqual(self.id.dynrna_prop.string_prop_transform, "test!!")
class TestIdPropertyGroupView(TestHelper, unittest.TestCase):

View file

@ -45,33 +45,70 @@ class TestPropNumerical(unittest.TestCase):
id_type.test_float = FloatProperty(default=float(self.default_value))
self.test_bool_storage = bool(self.custom_value)
self.test_int_storage = int(self.custom_value)
self.test_float_storage = float(self.custom_value)
def set_(s, v):
def bool_set_(s, v):
self.test_bool_storage = v
def int_set_(s, v):
self.test_int_storage = v
def float_set_(s, v):
self.test_float_storage = v
id_type.test_bool_getset = BoolProperty(
default=bool(self.default_value),
get=lambda s: self.test_bool_storage,
set=set_,
set=bool_set_,
)
self.test_int_storage = int(self.custom_value)
def set_(s, v):
self.test_int_storage = v
id_type.test_int_getset = IntProperty(
default=int(self.default_value),
get=lambda s: self.test_int_storage,
set=set_,
set=int_set_,
)
self.test_float_storage = float(self.custom_value)
def set_(s, v):
self.test_float_storage = v
id_type.test_float_getset = FloatProperty(
default=float(self.default_value),
get=lambda s: self.test_float_storage,
set=set_,
set=float_set_,
)
id_type.test_bool_transform = BoolProperty(
default=bool(self.default_value),
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_int_transform = IntProperty(
default=int(self.default_value),
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_float_transform = FloatProperty(
default=float(self.default_value),
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_bool_getset_transform = BoolProperty(
default=bool(self.default_value),
get=lambda s: self.test_bool_storage,
set=bool_set_,
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_int_getset_transform = IntProperty(
default=int(self.default_value),
get=lambda s: self.test_int_storage,
set=int_set_,
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_float_getset_transform = FloatProperty(
default=float(self.default_value),
get=lambda s: self.test_float_storage,
set=float_set_,
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
def tearDown(self):
@ -83,6 +120,14 @@ class TestPropNumerical(unittest.TestCase):
del id_type.test_int_getset
del id_type.test_bool_getset
del id_type.test_float_transform
del id_type.test_int_transform
del id_type.test_bool_transform
del id_type.test_float_getset_transform
del id_type.test_int_getset_transform
del id_type.test_bool_getset_transform
def do_test_access(self, prop_name, py_type, expected_value):
v = getattr(id_inst, prop_name)
self.assertIsInstance(v, py_type)
@ -110,6 +155,24 @@ class TestPropNumerical(unittest.TestCase):
def test_access_float_getset(self):
self.do_test_access("test_float_getset", float, float(self.custom_value))
def test_access_bool_transform(self):
self.do_test_access("test_bool_transform", bool, bool(self.default_value))
def test_access_int_transform(self):
self.do_test_access("test_int_transform", int, int(self.default_value))
def test_access_float_transform(self):
self.do_test_access("test_float_transform", float, float(self.default_value))
def test_access_bool_getset_transform(self):
self.do_test_access("test_bool_getset_transform", bool, bool(self.custom_value))
def test_access_int_getset_transform(self):
self.do_test_access("test_int_getset_transform", int, int(self.custom_value))
def test_access_float_getset_transform(self):
self.do_test_access("test_float_getset_transform", float, float(self.custom_value))
# TODO: Add expected failure cases (e.g. handling of out-of range values).
@ -171,28 +234,63 @@ class TestPropEnum(unittest.TestCase):
)
self.test_enum_storage = self.enum_expected_values[self.custom_value]
def set_(s, v):
self.test_enum_storage = v
id_type.test_enum_getset = EnumProperty(
items=self.enum_items,
default=self.default_value,
get=lambda s: self.test_enum_storage,
set=set_,
)
self.test_enum_bitflag_storage = functools.reduce(
lambda a, b: a | b,
(self.enum_expected_bitflag_values[bf] for bf in self.custom_bitflag_value))
def set_(s, v):
def enum_set_(s, v):
self.test_enum_storage = v
def bitflag_set_(s, v):
self.test_enum_bitflag_storage = v
id_type.test_enum_getset = EnumProperty(
items=self.enum_items,
default=self.default_value,
get=lambda s: self.test_enum_storage,
set=enum_set_,
)
id_type.test_enum_bitflag_getset = EnumProperty(
items=self.enum_items,
default=self.default_bitflag_value,
options={"ENUM_FLAG"},
get=lambda s: self.test_enum_bitflag_storage,
set=set_,
set=bitflag_set_,
)
id_type.test_enum_transform = EnumProperty(
items=self.enum_items,
default=self.default_value,
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_enum_bitflag_transform = EnumProperty(
items=self.enum_items,
default=self.default_bitflag_value,
options={"ENUM_FLAG"},
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_enum_getset_transform = EnumProperty(
items=self.enum_items,
default=self.default_value,
get=lambda s: self.test_enum_storage,
set=enum_set_,
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_enum_bitflag_getset_transform = EnumProperty(
items=self.enum_items,
default=self.default_bitflag_value,
options={"ENUM_FLAG"},
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
get=lambda s: self.test_enum_bitflag_storage,
set=bitflag_set_,
)
def tearDown(self):
@ -200,6 +298,10 @@ class TestPropEnum(unittest.TestCase):
del id_type.test_enum_bitflag
del id_type.test_enum_getset
del id_type.test_enum_bitflag_getset
del id_type.test_enum_transform
del id_type.test_enum_bitflag_transform
del id_type.test_enum_getset_transform
del id_type.test_enum_bitflag_getset_transform
# Test expected generated values for enum items.
def do_test_enum_values(self, prop_name, expected_item_values):
@ -215,6 +317,24 @@ class TestPropEnum(unittest.TestCase):
def test_enum_bitflag_item_values(self):
self.do_test_enum_values("test_enum_bitflag", self.enum_expected_bitflag_values)
def test_enum_getset_item_values(self):
self.do_test_enum_values("test_enum_getset", self.enum_expected_values)
def test_enum_bitflag_getset_item_values(self):
self.do_test_enum_values("test_enum_bitflag_getset", self.enum_expected_bitflag_values)
def test_enum_transform_item_values(self):
self.do_test_enum_values("test_enum_transform", self.enum_expected_values)
def test_enum_bitflag_transform_item_values(self):
self.do_test_enum_values("test_enum_bitflag_transform", self.enum_expected_bitflag_values)
def test_enum_getset_transform_item_values(self):
self.do_test_enum_values("test_enum_getset_transform", self.enum_expected_values)
def test_enum_bitflag_getset_transform_item_values(self):
self.do_test_enum_values("test_enum_bitflag_getset_transform", self.enum_expected_bitflag_values)
# Test basic access to enum values.
def do_test_access(self, prop_name, py_type, expected_value):
v = getattr(id_inst, prop_name)
@ -234,6 +354,18 @@ class TestPropEnum(unittest.TestCase):
def test_access_enum_bitflag_getset(self):
self.do_test_access("test_enum_bitflag_getset", set, self.custom_bitflag_value)
def test_access_enum_transform(self):
self.do_test_access("test_enum_transform", str, self.default_value)
def test_access_enum_bitflag_transform(self):
self.do_test_access("test_enum_bitflag_transform", set, self.default_bitflag_value)
def test_access_enum_getset_transform(self):
self.do_test_access("test_enum_getset_transform", str, self.custom_value)
def test_access_enum_bitflag_getset_transform(self):
self.do_test_access("test_enum_bitflag_getset_transform", set, self.custom_bitflag_value)
# TODO: Add expected failure cases (e.g. handling of invalid items identifiers).

View file

@ -147,32 +147,71 @@ class TestPropArrayIndex(unittest.TestCase):
self.test_array_b_2d_storage = [[bool(v) for v in range(self.size_2d[1])] for i in range(self.size_2d[0])]
def set_(s, v):
def bool_set_(s, v):
self.test_array_b_2d_storage = v
id_type.test_array_b_2d_getset = BoolVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_b_2d_storage,
set=set_,
)
self.test_array_i_2d_storage = [[int(v) for v in range(self.size_2d[1])] for i in range(self.size_2d[0])]
def set_(s, v):
def int_set_(s, v):
self.test_array_i_2d_storage = v
id_type.test_array_i_2d_getset = IntVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_i_2d_storage,
set=set_,
)
self.test_array_f_2d_storage = [[float(v) for v in range(self.size_2d[1])] for i in range(self.size_2d[0])]
def set_(s, v):
def float_set_(s, v):
self.test_array_f_2d_storage = v
id_type.test_array_b_2d_getset = BoolVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_b_2d_storage,
set=bool_set_,
)
id_type.test_array_i_2d_getset = IntVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_i_2d_storage,
set=int_set_,
)
id_type.test_array_f_2d_getset = FloatVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_f_2d_storage,
set=set_,
set=float_set_,
)
id_type.test_array_b_3d_transform = BoolVectorProperty(
size=self.size_3d,
get_transform=lambda s, c_v, isset: seq_items_xform(c_v, lambda v: not v),
set_transform=lambda s, n_v, c_v, isset: seq_items_xform(n_v, lambda v: not v),
)
id_type.test_array_i_3d_transform = IntVectorProperty(
size=self.size_3d,
get_transform=lambda s, c_v, isset: seq_items_xform(c_v, lambda v: v + 1),
set_transform=lambda s, n_v, c_v, isset: seq_items_xform(n_v, lambda v: v - 1),
)
id_type.test_array_f_3d_transform = FloatVectorProperty(
size=self.size_3d,
get_transform=lambda s, c_v, isset: seq_items_xform(c_v, lambda v: v * 2.0),
set_transform=lambda s, n_v, c_v, isset: seq_items_xform(n_v, lambda v: v / 2.0),
)
id_type.test_array_b_2d_getset_transform = BoolVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_b_2d_storage,
set=bool_set_,
get_transform=lambda s, c_v, isset: seq_items_xform(c_v, lambda v: not v),
set_transform=lambda s, n_v, c_v, isset: seq_items_xform(n_v, lambda v: not v),
)
id_type.test_array_i_2d_getset_transform = IntVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_i_2d_storage,
set=int_set_,
get_transform=lambda s, c_v, isset: seq_items_xform(c_v, lambda v: v + 1),
set_transform=lambda s, n_v, c_v, isset: seq_items_xform(n_v, lambda v: v - 1),
)
id_type.test_array_f_2d_getset_transform = FloatVectorProperty(
size=self.size_2d,
get=lambda s: self.test_array_f_2d_storage,
set=float_set_,
get_transform=lambda s, c_v, isset: seq_items_xform(c_v, lambda v: v * 2.0),
set_transform=lambda s, n_v, c_v, isset: seq_items_xform(n_v, lambda v: v / 2.0),
)
def tearDown(self):
@ -190,6 +229,14 @@ class TestPropArrayIndex(unittest.TestCase):
del id_type.test_array_i_2d_getset
del id_type.test_array_b_2d_getset
del id_type.test_array_f_3d_transform
del id_type.test_array_i_3d_transform
del id_type.test_array_b_3d_transform
del id_type.test_array_f_2d_getset_transform
del id_type.test_array_i_2d_getset_transform
del id_type.test_array_b_2d_getset_transform
@staticmethod
def compute_slice_len(s):
if not isinstance(s, slice):
@ -297,6 +344,36 @@ class TestPropArrayIndex(unittest.TestCase):
id_inst.test_array_f_2d_getset, self.size_2d, self.valid_indices_2d, self.invalid_indices_2d
)
def test_indices_access_b_3d_transform(self):
self.do_test_indices_access(
id_inst.test_array_b_3d_transform, self.size_3d, self.valid_indices_3d, self.invalid_indices_3d
)
def test_indices_access_i_3d_transform(self):
self.do_test_indices_access(
id_inst.test_array_i_3d_transform, self.size_3d, self.valid_indices_3d, self.invalid_indices_3d
)
def test_indices_access_f_3d_transform(self):
self.do_test_indices_access(
id_inst.test_array_f_3d_transform, self.size_3d, self.valid_indices_3d, self.invalid_indices_3d
)
def test_indices_access_b_2d_getset_transform(self):
self.do_test_indices_access(
id_inst.test_array_b_2d_getset_transform, self.size_2d, self.valid_indices_2d, self.invalid_indices_2d
)
def test_indices_access_i_2d_getset_transform(self):
self.do_test_indices_access(
id_inst.test_array_i_2d_getset_transform, self.size_2d, self.valid_indices_2d, self.invalid_indices_2d
)
def test_indices_access_f_2d_getset_transform(self):
self.do_test_indices_access(
id_inst.test_array_f_2d_getset_transform, self.size_2d, self.valid_indices_2d, self.invalid_indices_2d
)
class TestPropArrayForeach(unittest.TestCase):
# Test foreach_get/_set access of Int and Float vector properties (bool ones do not support this).
@ -499,12 +576,37 @@ class TestPropArrayMultiDimensional(unittest.TestCase):
def set_fn(id_arg, value):
local_data["array"] = value
def get_tx_fn(id_arg, curr_value, is_set):
return seq_items_xform(curr_value, lambda v: v + 1.0)
def set_tx_fn(id_arg, new_value, curr_value, is_set):
return seq_items_xform(new_value, lambda v: v - 1.0)
id_type.temp = FloatVectorProperty(size=(dim_x, dim_y), subtype='MATRIX', get=get_fn, set=set_fn)
id_inst.temp = data_native
data_as_tuple = seq_items_as_tuple(id_inst.temp)
self.assertEqual(data_as_tuple, data_native)
del id_type.temp
id_type.temp = FloatVectorProperty(
size=(dim_x, dim_y), subtype='MATRIX', get_transform=get_tx_fn, set_transform=set_tx_fn)
id_inst.temp = data_native
data_as_tuple = seq_items_as_tuple(id_inst.temp)
self.assertEqual(data_as_tuple, data_native)
del id_type.temp
id_type.temp = FloatVectorProperty(
size=(dim_x, dim_y),
subtype='MATRIX',
get=get_fn,
set=set_fn,
get_transform=get_tx_fn,
set_transform=set_tx_fn)
id_inst.temp = data_native
data_as_tuple = seq_items_as_tuple(id_inst.temp)
self.assertEqual(data_as_tuple, data_native)
del id_type.temp
def test_matrix_3x3(self):
self._test_matrix(3, 3)