Node panels: Enable new node group interfaces #1

Closed
Lukas Tönne wants to merge 14 commits from node-panels-final into node-panels-rna

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
3 changed files with 81 additions and 5 deletions
Showing only changes of commit b3c7f3c8a9 - Show all commits

View File

@ -80,6 +80,7 @@ def rna_idprop_ui_create(
description=None, description=None,
overridable=False, overridable=False,
subtype=None, subtype=None,
id_type='OBJECT',
): ):
"""Create and initialize a custom property with limits, defaults and other settings.""" """Create and initialize a custom property with limits, defaults and other settings."""
@ -91,11 +92,16 @@ def rna_idprop_ui_create(
proptype, _ = rna_idprop_value_item_type(default) proptype, _ = rna_idprop_value_item_type(default)
if (proptype is bool) or (proptype is str): if (proptype is bool) or (proptype is str):
ui_data = item.id_properties_ui(prop)
ui_data.update( ui_data.update(
description=description, description=description,
default=default, default=default,
) )
elif proptype is type(None) or issubclass(proptype, bpy.types.ID):
ui_data.update(
description=description,
default=default,
id_type=id_type,
)
else: else:
if soft_min is None: if soft_min is None:
soft_min = min soft_min = min
@ -156,6 +162,7 @@ def draw(layout, context, context_member, property_type, *, use_edit=True):
to_dict = getattr(value, "to_dict", None) to_dict = getattr(value, "to_dict", None)
to_list = getattr(value, "to_list", None) to_list = getattr(value, "to_list", None)
is_datablock = value is None or isinstance(value, bpy.types.ID)
if to_dict: if to_dict:
value = to_dict() value = to_dict()
@ -178,6 +185,8 @@ def draw(layout, context, context_member, property_type, *, use_edit=True):
props = value_column.operator("wm.properties_edit_value", text="Edit Value") props = value_column.operator("wm.properties_edit_value", text="Edit Value")
props.data_path = context_member props.data_path = context_member
props.property_name = key props.property_name = key
elif is_datablock:
value_column.template_ID(rna_item, '["%s"]' % escape_identifier(key), text="")
else: else:
value_column.prop(rna_item, '["%s"]' % escape_identifier(key), text="") value_column.prop(rna_item, '["%s"]' % escape_identifier(key), text="")

View File

@ -1387,6 +1387,7 @@ rna_custom_property_type_items = (
('BOOL', "Boolean", "A true or false value"), ('BOOL', "Boolean", "A true or false value"),
('BOOL_ARRAY', "Boolean Array", "An array of true or false values"), ('BOOL_ARRAY', "Boolean Array", "An array of true or false values"),
('STRING', "String", "A string value"), ('STRING', "String", "A string value"),
('DATA_BLOCK', "Data-Block", "A data-block value"),
('PYTHON', "Python", "Edit a Python value directly, for unsupported property types"), ('PYTHON', "Python", "Edit a Python value directly, for unsupported property types"),
) )
@ -1412,6 +1413,8 @@ rna_custom_property_subtype_vector_items = (
('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"), ('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"),
) )
rna_id_type_items = tuple((item.identifier, item.name, item.description, item.icon, item.value)
for item in bpy.types.Action.bl_rna.properties['id_root'].enum_items)
class WM_OT_properties_edit(Operator): class WM_OT_properties_edit(Operator):
"""Change a custom property's type, or adjust how it is displayed in the interface""" """Change a custom property's type, or adjust how it is displayed in the interface"""
@ -1554,6 +1557,14 @@ class WM_OT_properties_edit(Operator):
maxlen=1024, maxlen=1024,
) )
# Data-block properties.
id_type: EnumProperty(
name="ID Type",
items=rna_id_type_items,
default='OBJECT',
)
# Store the value converted to a string as a fallback for otherwise unsupported types. # Store the value converted to a string as a fallback for otherwise unsupported types.
eval_string: StringProperty( eval_string: StringProperty(
name="Value", name="Value",
@ -1623,9 +1634,21 @@ class WM_OT_properties_edit(Operator):
if is_array: if is_array:
return 'PYTHON' return 'PYTHON'
return 'STRING' return 'STRING'
elif prop_type == type(None) or issubclass(prop_type, bpy.types.ID):
if is_array:
return 'PYTHON'
return 'DATA_BLOCK'
return 'PYTHON' return 'PYTHON'
# For `DATA_BLOCK` types, return the `id_type` or an empty string for non data-block types.
@staticmethod
def get_property_id_type(item, property_name):
ui_data = item.id_properties_ui(property_name)
rna_data = ui_data.as_dict()
# For non `DATA_BLOCK` types, the `id_type` wont exist.
return rna_data.get("id_type", "")
def _init_subtype(self, subtype): def _init_subtype(self, subtype):
self.subtype = subtype or 'NONE' self.subtype = subtype or 'NONE'
@ -1664,6 +1687,8 @@ class WM_OT_properties_edit(Operator):
self.default_string = rna_data["default"] self.default_string = rna_data["default"]
elif self.property_type in {'BOOL', 'BOOL_ARRAY'}: elif self.property_type in {'BOOL', 'BOOL_ARRAY'}:
self.default_bool = self._convert_new_value_array(rna_data["default"], bool, 32) self.default_bool = self._convert_new_value_array(rna_data["default"], bool, 32)
elif self.property_type == 'DATA_BLOCK':
self.id_type = rna_data["id_type"]
if self.property_type in {'FLOAT_ARRAY', 'INT_ARRAY', 'BOOL_ARRAY'}: if self.property_type in {'FLOAT_ARRAY', 'INT_ARRAY', 'BOOL_ARRAY'}:
self.array_length = len(item[name]) self.array_length = len(item[name])
@ -1677,7 +1702,7 @@ class WM_OT_properties_edit(Operator):
# When the operator chooses a different type than the original property, # When the operator chooses a different type than the original property,
# attempt to convert the old value to the new type for continuity and speed. # attempt to convert the old value to the new type for continuity and speed.
def _get_converted_value(self, item, name_old, prop_type_new): def _get_converted_value(self, item, name_old, prop_type_new, id_type_old, id_type_new):
if prop_type_new == 'INT': if prop_type_new == 'INT':
return self._convert_new_value_single(item[name_old], int) return self._convert_new_value_single(item[name_old], int)
elif prop_type_new == 'FLOAT': elif prop_type_new == 'FLOAT':
@ -1700,6 +1725,14 @@ class WM_OT_properties_edit(Operator):
return [False] * self.array_length return [False] * self.array_length
elif prop_type_new == 'STRING': elif prop_type_new == 'STRING':
return self.convert_custom_property_to_string(item, name_old) return self.convert_custom_property_to_string(item, name_old)
elif prop_type_new == 'DATA_BLOCK':
if id_type_old != id_type_new:
return None
old_value = item[name_old]
if not isinstance(old_value, bpy.types.ID):
return None
return old_value
# If all else fails, create an empty string property. That should avoid errors later on anyway. # If all else fails, create an empty string property. That should avoid errors later on anyway.
return "" return ""
@ -1761,6 +1794,12 @@ class WM_OT_properties_edit(Operator):
default=self.default_string, default=self.default_string,
description=self.description, description=self.description,
) )
elif prop_type_new == 'DATA_BLOCK':
ui_data = item.id_properties_ui(name)
ui_data.update(
description=self.description,
id_type=self.id_type,
)
escaped_name = bpy.utils.escape_identifier(name) escaped_name = bpy.utils.escape_identifier(name)
item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library) item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library)
@ -1824,6 +1863,9 @@ class WM_OT_properties_edit(Operator):
prop_type_new = self.property_type prop_type_new = self.property_type
self._old_prop_name[:] = [name] self._old_prop_name[:] = [name]
id_type_old = self.get_property_id_type(item, name_old)
id_type_new = self.id_type
if prop_type_new == 'PYTHON': if prop_type_new == 'PYTHON':
try: try:
new_value = eval(self.eval_string) new_value = eval(self.eval_string)
@ -1838,7 +1880,7 @@ class WM_OT_properties_edit(Operator):
if name_old != name: if name_old != name:
del item[name_old] del item[name_old]
else: else:
new_value = self._get_converted_value(item, name_old, prop_type_new) new_value = self._get_converted_value(item, name_old, prop_type_new, id_type_old, id_type_new)
del item[name_old] del item[name_old]
item[name] = new_value item[name] = new_value
@ -1991,6 +2033,8 @@ class WM_OT_properties_edit(Operator):
layout.prop(self, "default_bool", index=0) layout.prop(self, "default_bool", index=0)
elif self.property_type == 'STRING': elif self.property_type == 'STRING':
layout.prop(self, "default_string") layout.prop(self, "default_string")
elif self.property_type == 'DATA_BLOCK':
layout.prop(self, "id_type")
if self.property_type == 'PYTHON': if self.property_type == 'PYTHON':
layout.prop(self, "eval_string") layout.prop(self, "eval_string")

View File

@ -439,9 +439,10 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec
{ {
const char *rna_subtype = nullptr; const char *rna_subtype = nullptr;
const char *description = nullptr; const char *description = nullptr;
const char *kwlist[] = {"subtype", "description", nullptr}; const char *id_type = nullptr;
const char *kwlist[] = {"subtype", "description", "id_type", nullptr};
if (!PyArg_ParseTupleAndKeywords( if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "|$zz:update", (char **)kwlist, &rna_subtype, &description)) args, kwargs, "|$zzz:update", (char **)kwlist, &rna_subtype, &description, &id_type))
{ {
return false; return false;
} }
@ -455,6 +456,15 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec
return false; return false;
} }
int id_type_tmp;
if (pyrna_enum_value_from_id(
rna_enum_id_type_items, id_type, &id_type_tmp, "IDPropertyUIManager.update") == -1)
{
return false;
}
ui_data.id_type = short(id_type_tmp);
/* Write back to the property's UI data. */ /* Write back to the property's UI data. */
IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base);
*ui_data_orig = ui_data; *ui_data_orig = ui_data;
@ -471,6 +481,7 @@ PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc,
"precision=None, " "precision=None, "
"step=None, " "step=None, "
"default=None, " "default=None, "
"id_type=None, "
"description=None)\n" "description=None)\n"
"\n" "\n"
" Update the RNA information of the IDProperty used for interaction and\n" " Update the RNA information of the IDProperty used for interaction and\n"
@ -619,6 +630,17 @@ static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict)
Py_DECREF(item); Py_DECREF(item);
} }
static void idprop_ui_data_to_dict_id(IDProperty *property, PyObject *dict)
{
IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)property->ui_data;
const char *id_type = nullptr;
RNA_enum_identifier(rna_enum_id_type_items, ui_data->id_type, &id_type);
PyObject *item = PyUnicode_FromString(id_type);
PyDict_SetItemString(dict, "id_type", item);
Py_DECREF(item);
}
PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc, PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc,
".. method:: as_dict()\n" ".. method:: as_dict()\n"
"\n" "\n"
@ -655,6 +677,7 @@ static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self
idprop_ui_data_to_dict_string(property, dict); idprop_ui_data_to_dict_string(property, dict);
break; break;
case IDP_UI_DATA_TYPE_ID: case IDP_UI_DATA_TYPE_ID:
idprop_ui_data_to_dict_id(property, dict);
break; break;
case IDP_UI_DATA_TYPE_INT: case IDP_UI_DATA_TYPE_INT:
idprop_ui_data_to_dict_int(property, dict); idprop_ui_data_to_dict_int(property, dict);