2023-08-15 14:20:26 +00:00
|
|
|
# SPDX-FileCopyrightText: 2012-2023 Blender Authors
|
2023-06-15 03:09:04 +00:00
|
|
|
#
|
2022-02-10 22:07:11 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2012-01-01 04:37:08 +00:00
|
|
|
|
|
|
|
import bpy
|
|
|
|
|
|
|
|
|
2014-09-10 01:06:18 +00:00
|
|
|
def build_property_typemap(skip_classes, skip_typemap):
|
2012-01-01 04:37:08 +00:00
|
|
|
|
|
|
|
property_typemap = {}
|
|
|
|
|
|
|
|
for attr in dir(bpy.types):
|
2021-02-24 12:36:15 +00:00
|
|
|
# Skip internal methods.
|
|
|
|
if attr.startswith("_"):
|
|
|
|
continue
|
2012-01-01 04:37:08 +00:00
|
|
|
cls = getattr(bpy.types, attr)
|
2012-01-01 08:09:30 +00:00
|
|
|
if issubclass(cls, skip_classes):
|
2012-01-01 04:37:08 +00:00
|
|
|
continue
|
2021-02-24 12:36:15 +00:00
|
|
|
bl_rna = getattr(cls, "bl_rna", None)
|
|
|
|
# Needed to skip classes added to the modules `__dict__`.
|
|
|
|
if bl_rna is None:
|
|
|
|
continue
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2018-09-03 14:49:08 +00:00
|
|
|
# # to support skip-save we can't get all props
|
2021-02-24 12:36:15 +00:00
|
|
|
# properties = bl_rna.properties.keys()
|
2012-01-11 00:22:21 +00:00
|
|
|
properties = []
|
2021-02-24 12:36:15 +00:00
|
|
|
for prop_id, prop in bl_rna.properties.items():
|
2012-01-11 00:22:21 +00:00
|
|
|
if not prop.is_skip_save:
|
|
|
|
properties.append(prop_id)
|
|
|
|
|
2012-01-01 04:37:08 +00:00
|
|
|
properties.remove("rna_type")
|
|
|
|
property_typemap[attr] = properties
|
|
|
|
|
2014-09-10 01:06:18 +00:00
|
|
|
if skip_typemap:
|
|
|
|
for cls_name, properties_blacklist in skip_typemap.items():
|
|
|
|
properties = property_typemap.get(cls_name)
|
|
|
|
if properties is not None:
|
|
|
|
for prop_id in properties_blacklist:
|
|
|
|
try:
|
|
|
|
properties.remove(prop_id)
|
|
|
|
except:
|
|
|
|
print("skip_typemap unknown prop_id '%s.%s'" % (cls_name, prop_id))
|
|
|
|
else:
|
|
|
|
print("skip_typemap unknown class '%s'" % cls_name)
|
|
|
|
|
2012-01-01 04:37:08 +00:00
|
|
|
return property_typemap
|
|
|
|
|
|
|
|
|
|
|
|
def print_ln(data):
|
|
|
|
print(data, end="")
|
|
|
|
|
|
|
|
|
2018-11-20 02:12:10 +00:00
|
|
|
def rna2xml(
|
|
|
|
fw=print_ln,
|
|
|
|
root_node="",
|
|
|
|
root_rna=None, # must be set
|
|
|
|
root_rna_skip=set(),
|
|
|
|
root_ident="",
|
|
|
|
ident_val=" ",
|
|
|
|
skip_classes=(
|
|
|
|
bpy.types.Operator,
|
|
|
|
bpy.types.Panel,
|
|
|
|
bpy.types.KeyingSet,
|
|
|
|
bpy.types.Header,
|
|
|
|
bpy.types.PropertyGroup,
|
|
|
|
),
|
|
|
|
skip_typemap=None,
|
|
|
|
pretty_format=True,
|
|
|
|
method='DATA',
|
|
|
|
):
|
2012-01-01 04:37:08 +00:00
|
|
|
from xml.sax.saxutils import quoteattr
|
2014-09-10 01:06:18 +00:00
|
|
|
property_typemap = build_property_typemap(skip_classes, skip_typemap)
|
|
|
|
|
|
|
|
# don't follow properties of this type, just reference them by name
|
|
|
|
# they MUST have a unique 'name' property.
|
|
|
|
# 'ID' covers most types
|
|
|
|
referenced_classes = (
|
|
|
|
bpy.types.ID,
|
|
|
|
bpy.types.Bone,
|
|
|
|
bpy.types.ActionGroup,
|
|
|
|
bpy.types.PoseBone,
|
|
|
|
bpy.types.Node,
|
|
|
|
bpy.types.Sequence,
|
2016-07-29 11:22:27 +00:00
|
|
|
)
|
2014-09-10 01:06:18 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
def number_to_str(val, val_type):
|
|
|
|
if val_type == int:
|
|
|
|
return "%d" % val
|
|
|
|
elif val_type == float:
|
|
|
|
return "%.6g" % val
|
|
|
|
elif val_type == bool:
|
|
|
|
return "TRUE" if val else "FALSE"
|
|
|
|
else:
|
2020-01-30 00:50:09 +00:00
|
|
|
raise NotImplementedError("this type is not a number %s" % val_type)
|
2012-01-01 04:37:08 +00:00
|
|
|
|
|
|
|
def rna2xml_node(ident, value, parent):
|
|
|
|
ident_next = ident + ident_val
|
|
|
|
|
|
|
|
# divide into attrs and nodes.
|
|
|
|
node_attrs = []
|
|
|
|
nodes_items = []
|
|
|
|
nodes_lists = []
|
|
|
|
|
|
|
|
value_type = type(value)
|
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
if issubclass(value_type, skip_classes):
|
2012-01-01 04:37:08 +00:00
|
|
|
return
|
|
|
|
|
2022-01-24 04:34:56 +00:00
|
|
|
# XXX, fixme, pointcache has eternal nested pointer to itself.
|
2012-01-01 04:37:08 +00:00
|
|
|
if value == parent:
|
|
|
|
return
|
|
|
|
|
|
|
|
value_type_name = value_type.__name__
|
|
|
|
for prop in property_typemap[value_type_name]:
|
|
|
|
|
|
|
|
subvalue = getattr(value, prop)
|
|
|
|
subvalue_type = type(subvalue)
|
|
|
|
|
2012-08-25 11:54:58 +00:00
|
|
|
if subvalue_type in {int, bool, float}:
|
2012-01-01 08:09:30 +00:00
|
|
|
node_attrs.append("%s=\"%s\"" % (prop, number_to_str(subvalue, subvalue_type)))
|
2012-01-01 04:37:08 +00:00
|
|
|
elif subvalue_type is str:
|
|
|
|
node_attrs.append("%s=%s" % (prop, quoteattr(subvalue)))
|
2012-08-25 11:54:58 +00:00
|
|
|
elif subvalue_type is set:
|
2012-01-01 04:37:08 +00:00
|
|
|
node_attrs.append("%s=%s" % (prop, quoteattr("{" + ",".join(list(subvalue)) + "}")))
|
|
|
|
elif subvalue is None:
|
|
|
|
node_attrs.append("%s=\"NONE\"" % prop)
|
2014-09-10 01:06:18 +00:00
|
|
|
elif issubclass(subvalue_type, referenced_classes):
|
2012-01-01 04:37:08 +00:00
|
|
|
# special case, ID's are always referenced.
|
2012-01-01 08:09:30 +00:00
|
|
|
node_attrs.append("%s=%s" % (prop, quoteattr(subvalue_type.__name__ + "::" + subvalue.name)))
|
2012-01-01 04:37:08 +00:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
subvalue_ls = list(subvalue)
|
|
|
|
except:
|
|
|
|
subvalue_ls = None
|
|
|
|
|
|
|
|
if subvalue_ls is None:
|
|
|
|
nodes_items.append((prop, subvalue, subvalue_type))
|
|
|
|
else:
|
|
|
|
# check if the list contains native types
|
|
|
|
subvalue_rna = value.path_resolve(prop, False)
|
|
|
|
if type(subvalue_rna).__name__ == "bpy_prop_array":
|
2012-01-01 10:05:04 +00:00
|
|
|
# check if this is a 0-1 color (rgb, rgba)
|
2018-09-03 14:49:08 +00:00
|
|
|
# in that case write as a hexadecimal
|
2012-01-01 10:05:04 +00:00
|
|
|
prop_rna = value.bl_rna.properties[prop]
|
|
|
|
if (prop_rna.subtype == 'COLOR_GAMMA' and
|
|
|
|
prop_rna.hard_min == 0.0 and
|
|
|
|
prop_rna.hard_max == 1.0 and
|
|
|
|
prop_rna.array_length in {3, 4}):
|
|
|
|
# -----
|
|
|
|
# color
|
|
|
|
array_value = "#" + "".join(("%.2x" % int(v * 255) for v in subvalue_rna))
|
|
|
|
|
|
|
|
else:
|
|
|
|
# default
|
|
|
|
def str_recursive(s):
|
|
|
|
subsubvalue_type = type(s)
|
2012-08-25 11:54:58 +00:00
|
|
|
if subsubvalue_type in {int, float, bool}:
|
2012-01-01 10:05:04 +00:00
|
|
|
return number_to_str(s, subsubvalue_type)
|
|
|
|
else:
|
|
|
|
return " ".join([str_recursive(si) for si in s])
|
2012-01-17 17:57:20 +00:00
|
|
|
|
2012-01-01 10:05:04 +00:00
|
|
|
array_value = " ".join(str_recursive(v) for v in subvalue_rna)
|
|
|
|
|
|
|
|
node_attrs.append("%s=\"%s\"" % (prop, array_value))
|
2012-01-01 04:37:08 +00:00
|
|
|
else:
|
|
|
|
nodes_lists.append((prop, subvalue_ls, subvalue_type))
|
|
|
|
|
|
|
|
# declare + attributes
|
2012-01-01 08:09:30 +00:00
|
|
|
if pretty_format:
|
2012-02-13 11:14:12 +00:00
|
|
|
if node_attrs:
|
2018-11-20 02:12:10 +00:00
|
|
|
fw("%s<%s\n" % (ident, value_type_name))
|
|
|
|
for node_attr in node_attrs:
|
|
|
|
fw("%s%s\n" % (ident_next, node_attr))
|
|
|
|
fw("%s>\n" % (ident_next,))
|
2012-02-13 11:14:12 +00:00
|
|
|
else:
|
|
|
|
fw("%s<%s>\n" % (ident, value_type_name))
|
2012-01-01 04:37:08 +00:00
|
|
|
else:
|
|
|
|
fw("%s<%s %s>\n" % (ident, value_type_name, " ".join(node_attrs)))
|
|
|
|
|
|
|
|
# unique members
|
|
|
|
for prop, subvalue, subvalue_type in nodes_items:
|
2012-01-01 08:09:30 +00:00
|
|
|
fw("%s<%s>\n" % (ident_next, prop)) # XXX, this is awkward, how best to solve?
|
|
|
|
rna2xml_node(ident_next + ident_val, subvalue, value)
|
|
|
|
fw("%s</%s>\n" % (ident_next, prop)) # XXX, need to check on this.
|
2012-01-01 04:37:08 +00:00
|
|
|
|
|
|
|
# list members
|
|
|
|
for prop, subvalue, subvalue_type in nodes_lists:
|
|
|
|
fw("%s<%s>\n" % (ident_next, prop))
|
|
|
|
for subvalue_item in subvalue:
|
|
|
|
if subvalue_item is not None:
|
|
|
|
rna2xml_node(ident_next + ident_val, subvalue_item, value)
|
|
|
|
fw("%s</%s>\n" % (ident_next, prop))
|
|
|
|
|
|
|
|
fw("%s</%s>\n" % (ident, value_type_name))
|
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
# -------------------------------------------------------------------------
|
2012-11-28 06:43:04 +00:00
|
|
|
# needs re-working to be generic
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
if root_node:
|
2012-01-11 00:22:21 +00:00
|
|
|
fw("%s<%s>\n" % (root_ident, root_node))
|
2012-01-01 08:09:30 +00:00
|
|
|
|
|
|
|
# bpy.data
|
|
|
|
if method == 'DATA':
|
2012-01-11 00:22:21 +00:00
|
|
|
ident = root_ident + ident_val
|
2012-01-01 08:09:30 +00:00
|
|
|
for attr in dir(root_rna):
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
# exceptions
|
|
|
|
if attr.startswith("_"):
|
|
|
|
continue
|
|
|
|
elif attr in root_rna_skip:
|
|
|
|
continue
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
value = getattr(root_rna, attr)
|
|
|
|
try:
|
|
|
|
ls = value[:]
|
|
|
|
except:
|
|
|
|
ls = None
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
if type(ls) == list:
|
2012-01-11 00:22:21 +00:00
|
|
|
fw("%s<%s>\n" % (ident, attr))
|
2012-01-01 08:09:30 +00:00
|
|
|
for blend_id in ls:
|
2012-01-11 00:22:21 +00:00
|
|
|
rna2xml_node(ident + ident_val, blend_id, None)
|
2012-01-01 08:09:30 +00:00
|
|
|
fw("%s</%s>\n" % (ident_val, attr))
|
|
|
|
# any attribute
|
|
|
|
elif method == 'ATTR':
|
2012-01-11 00:22:21 +00:00
|
|
|
rna2xml_node(root_ident, root_rna, None)
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
if root_node:
|
2012-01-11 00:22:21 +00:00
|
|
|
fw("%s</%s>\n" % (root_ident, root_node))
|
2012-01-01 08:09:30 +00:00
|
|
|
|
|
|
|
|
PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.
- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-08 08:03:14 +00:00
|
|
|
def xml2rna(
|
|
|
|
root_xml, *,
|
|
|
|
root_rna=None, # must be set
|
|
|
|
):
|
2012-01-01 08:09:30 +00:00
|
|
|
|
|
|
|
def rna2xml_node(xml_node, value):
|
2014-09-17 08:36:17 +00:00
|
|
|
# print("evaluating:", xml_node.nodeName)
|
2012-01-01 08:09:30 +00:00
|
|
|
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
# Simple attributes
|
|
|
|
|
|
|
|
for attr in xml_node.attributes.keys():
|
2014-09-17 08:36:17 +00:00
|
|
|
# print(" ", attr)
|
2012-01-01 08:09:30 +00:00
|
|
|
subvalue = getattr(value, attr, Ellipsis)
|
|
|
|
|
|
|
|
if subvalue is Ellipsis:
|
|
|
|
print("%s.%s not found" % (type(value).__name__, attr))
|
|
|
|
else:
|
|
|
|
value_xml = xml_node.attributes[attr].value
|
|
|
|
|
|
|
|
subvalue_type = type(subvalue)
|
2022-03-04 00:03:36 +00:00
|
|
|
# tp_name = 'UNKNOWN'
|
2012-01-01 08:09:30 +00:00
|
|
|
if subvalue_type == float:
|
|
|
|
value_xml_coerce = float(value_xml)
|
2022-03-04 00:03:36 +00:00
|
|
|
# tp_name = 'FLOAT'
|
2012-01-01 08:09:30 +00:00
|
|
|
elif subvalue_type == int:
|
|
|
|
value_xml_coerce = int(value_xml)
|
2022-03-04 00:03:36 +00:00
|
|
|
# tp_name = 'INT'
|
2012-01-01 08:09:30 +00:00
|
|
|
elif subvalue_type == bool:
|
|
|
|
value_xml_coerce = {'TRUE': True, 'FALSE': False}[value_xml]
|
2022-03-04 00:03:36 +00:00
|
|
|
# tp_name = 'BOOL'
|
2012-01-01 08:09:30 +00:00
|
|
|
elif subvalue_type == str:
|
|
|
|
value_xml_coerce = value_xml
|
2022-03-04 00:03:36 +00:00
|
|
|
# tp_name = 'STR'
|
2012-01-01 08:09:30 +00:00
|
|
|
elif hasattr(subvalue, "__len__"):
|
2012-01-01 10:05:04 +00:00
|
|
|
if value_xml.startswith("#"):
|
2018-09-03 14:49:08 +00:00
|
|
|
# read hexadecimal value as float array
|
2012-01-01 10:05:04 +00:00
|
|
|
value_xml_split = value_xml[1:]
|
2016-07-29 11:22:27 +00:00
|
|
|
value_xml_coerce = [int(value_xml_split[i:i + 2], 16) /
|
|
|
|
255 for i in range(0, len(value_xml_split), 2)]
|
2012-01-01 10:05:04 +00:00
|
|
|
del value_xml_split
|
|
|
|
else:
|
|
|
|
value_xml_split = value_xml.split()
|
|
|
|
try:
|
|
|
|
value_xml_coerce = [int(v) for v in value_xml_split]
|
|
|
|
except ValueError:
|
2012-09-25 05:17:23 +00:00
|
|
|
try:
|
|
|
|
value_xml_coerce = [float(v) for v in value_xml_split]
|
2012-09-26 21:19:51 +00:00
|
|
|
except ValueError: # bool vector property
|
2012-09-25 05:17:23 +00:00
|
|
|
value_xml_coerce = [{'TRUE': True, 'FALSE': False}[v] for v in value_xml_split]
|
2012-01-01 10:05:04 +00:00
|
|
|
del value_xml_split
|
2022-03-04 00:03:36 +00:00
|
|
|
# tp_name = 'ARRAY'
|
2012-01-01 08:09:30 +00:00
|
|
|
|
2022-02-02 10:05:16 +00:00
|
|
|
# print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type))
|
2012-12-13 09:46:24 +00:00
|
|
|
try:
|
|
|
|
setattr(value, attr, value_xml_coerce)
|
|
|
|
except ValueError:
|
|
|
|
# size mismatch
|
|
|
|
val = getattr(value, attr)
|
|
|
|
if len(val) < len(value_xml_coerce):
|
|
|
|
setattr(value, attr, value_xml_coerce[:len(val)])
|
|
|
|
else:
|
|
|
|
setattr(value, attr, list(value_xml_coerce) + list(val)[len(value_xml_coerce):])
|
2012-01-01 08:09:30 +00:00
|
|
|
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
# Complex attributes
|
|
|
|
for child_xml in xml_node.childNodes:
|
|
|
|
if child_xml.nodeType == child_xml.ELEMENT_NODE:
|
|
|
|
# print()
|
|
|
|
# print(child_xml.nodeName)
|
|
|
|
subvalue = getattr(value, child_xml.nodeName, None)
|
|
|
|
if subvalue is not None:
|
|
|
|
|
|
|
|
elems = []
|
|
|
|
for child_xml_real in child_xml.childNodes:
|
|
|
|
if child_xml_real.nodeType == child_xml_real.ELEMENT_NODE:
|
|
|
|
elems.append(child_xml_real)
|
|
|
|
del child_xml_real
|
|
|
|
|
|
|
|
if hasattr(subvalue, "__len__"):
|
|
|
|
# Collection
|
|
|
|
if len(elems) != len(subvalue):
|
|
|
|
print("Size Mismatch! collection:", child_xml.nodeName)
|
|
|
|
else:
|
|
|
|
for i in range(len(elems)):
|
|
|
|
child_xml_real = elems[i]
|
|
|
|
subsubvalue = subvalue[i]
|
|
|
|
|
|
|
|
if child_xml_real is None or subsubvalue is None:
|
|
|
|
print("None found %s - %d collection:", (child_xml.nodeName, i))
|
|
|
|
else:
|
|
|
|
rna2xml_node(child_xml_real, subsubvalue)
|
|
|
|
|
|
|
|
else:
|
2015-01-29 04:35:06 +00:00
|
|
|
# print(elems)
|
2012-01-01 08:09:30 +00:00
|
|
|
if len(elems) == 1:
|
|
|
|
# sub node named by its type
|
|
|
|
child_xml_real, = elems
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
# print(child_xml_real, subvalue)
|
|
|
|
rna2xml_node(child_xml_real, subvalue)
|
|
|
|
else:
|
|
|
|
# empty is valid too
|
|
|
|
pass
|
2012-01-01 04:37:08 +00:00
|
|
|
|
2012-01-01 08:09:30 +00:00
|
|
|
rna2xml_node(root_xml, root_rna)
|
2012-01-10 15:08:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Utility function used by presets.
|
|
|
|
# The idea is you can run a preset like a script with a few args.
|
|
|
|
#
|
|
|
|
# This roughly matches the operator 'bpy.ops.script.python_file_run'
|
|
|
|
|
2012-01-17 17:57:20 +00:00
|
|
|
|
2012-01-10 16:20:01 +00:00
|
|
|
def _get_context_val(context, path):
|
|
|
|
try:
|
2022-04-06 01:42:46 +00:00
|
|
|
value = context.path_resolve(path)
|
2023-07-30 06:14:13 +00:00
|
|
|
except BaseException as ex:
|
2022-04-06 01:42:46 +00:00
|
|
|
print("Error: %r, path %r not found" % (ex, path))
|
2012-01-10 16:20:01 +00:00
|
|
|
value = Ellipsis
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
2012-01-17 17:57:20 +00:00
|
|
|
|
2012-01-10 15:08:12 +00:00
|
|
|
def xml_file_run(context, filepath, rna_map):
|
|
|
|
import xml.dom.minidom
|
|
|
|
|
|
|
|
xml_nodes = xml.dom.minidom.parse(filepath)
|
|
|
|
bpy_xml = xml_nodes.getElementsByTagName("bpy")[0]
|
|
|
|
|
|
|
|
for rna_path, xml_tag in rna_map:
|
|
|
|
|
|
|
|
# first get xml
|
|
|
|
# TODO, error check
|
|
|
|
xml_node = bpy_xml.getElementsByTagName(xml_tag)[0]
|
|
|
|
|
2012-01-10 16:20:01 +00:00
|
|
|
value = _get_context_val(context, rna_path)
|
2012-01-10 15:08:12 +00:00
|
|
|
|
|
|
|
if value is not Ellipsis and value is not None:
|
2022-02-02 10:05:16 +00:00
|
|
|
# print(" loading XML: %r -> %r" % (filepath, rna_path))
|
2012-01-10 16:20:01 +00:00
|
|
|
xml2rna(xml_node, root_rna=value)
|
|
|
|
|
|
|
|
|
PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.
- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-08 08:03:14 +00:00
|
|
|
def xml_file_write(context, filepath, rna_map, *, skip_typemap=None):
|
2022-02-02 10:05:16 +00:00
|
|
|
with open(filepath, "w", encoding="utf-8") as file:
|
|
|
|
fw = file.write
|
|
|
|
fw("<bpy>\n")
|
|
|
|
|
|
|
|
for rna_path, _xml_tag in rna_map:
|
|
|
|
# xml_tag is ignored, we get this from the rna
|
|
|
|
value = _get_context_val(context, rna_path)
|
|
|
|
rna2xml(
|
|
|
|
fw=fw,
|
2012-01-11 00:22:21 +00:00
|
|
|
root_rna=value,
|
|
|
|
method='ATTR',
|
|
|
|
root_ident=" ",
|
2014-09-10 01:06:18 +00:00
|
|
|
ident_val=" ",
|
|
|
|
skip_typemap=skip_typemap,
|
2022-02-02 10:05:16 +00:00
|
|
|
)
|
2012-01-10 16:20:01 +00:00
|
|
|
|
2022-02-02 10:05:16 +00:00
|
|
|
fw("</bpy>\n")
|