2023-08-16 00:20:26 +10:00
|
|
|
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
2023-06-15 13:09:04 +10:00
|
|
|
#
|
2022-02-11 09:07:11 +11:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
# classes for extracting info from blenders internal classes
|
|
|
|
|
|
2025-01-04 20:33:53 +11:00
|
|
|
__all__ = (
|
|
|
|
|
"BuildRNAInfo",
|
|
|
|
|
"InfoFunctionRNA",
|
|
|
|
|
"InfoOperatorRNA",
|
|
|
|
|
"InfoPropertyRNA",
|
|
|
|
|
"InfoStructRNA",
|
|
|
|
|
"rna_id_ignore",
|
|
|
|
|
)
|
|
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
import bpy
|
|
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
# use to strip python paths
|
|
|
|
|
script_paths = bpy.utils.script_paths()
|
|
|
|
|
|
2010-04-10 18:35:50 +00:00
|
|
|
_FAKE_STRUCT_SUBCLASS = True
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2010-04-17 19:05:53 +00:00
|
|
|
|
2010-04-09 20:04:37 +00:00
|
|
|
def _get_direct_attr(rna_type, attr):
|
|
|
|
|
props = getattr(rna_type, attr)
|
|
|
|
|
base = rna_type.base
|
|
|
|
|
|
|
|
|
|
if not base:
|
2010-04-09 20:43:58 +00:00
|
|
|
return [prop for prop in props]
|
2010-04-09 20:04:37 +00:00
|
|
|
else:
|
|
|
|
|
props_base = getattr(base, attr).values()
|
2010-04-09 20:43:58 +00:00
|
|
|
return [prop for prop in props if prop not in props_base]
|
2010-04-09 20:04:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_direct_properties(rna_type):
|
|
|
|
|
return _get_direct_attr(rna_type, "properties")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_direct_functions(rna_type):
|
|
|
|
|
return _get_direct_attr(rna_type, "functions")
|
|
|
|
|
|
2010-04-17 19:05:53 +00:00
|
|
|
|
2010-04-10 18:35:50 +00:00
|
|
|
def rna_id_ignore(rna_id):
|
|
|
|
|
if rna_id == "rna_type":
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
if "_OT_" in rna_id:
|
|
|
|
|
return True
|
|
|
|
|
if "_MT_" in rna_id:
|
|
|
|
|
return True
|
|
|
|
|
if "_PT_" in rna_id:
|
|
|
|
|
return True
|
|
|
|
|
if "_HT_" in rna_id:
|
|
|
|
|
return True
|
2010-04-11 12:05:27 +00:00
|
|
|
if "_KSI_" in rna_id:
|
|
|
|
|
return True
|
2010-04-10 18:35:50 +00:00
|
|
|
return False
|
2010-04-09 20:04:37 +00:00
|
|
|
|
2010-04-17 19:05:53 +00:00
|
|
|
|
2009-12-21 23:14:16 +00:00
|
|
|
def range_str(val):
|
2009-12-26 09:36:50 +00:00
|
|
|
if val < -10000000:
|
2012-06-19 22:17:19 +00:00
|
|
|
return "-inf"
|
2009-12-26 09:36:50 +00:00
|
|
|
elif val > 10000000:
|
2012-06-19 22:17:19 +00:00
|
|
|
return "inf"
|
2009-12-26 09:36:50 +00:00
|
|
|
elif type(val) == float:
|
2024-04-27 16:06:51 +10:00
|
|
|
return "{:g}".format(val)
|
2009-12-21 23:14:16 +00:00
|
|
|
else:
|
|
|
|
|
return str(val)
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
def float_as_string(f):
|
2024-04-27 16:06:51 +10:00
|
|
|
val_str = "{:g}".format(f)
|
2022-05-23 12:05:42 +10:00
|
|
|
# Ensure a `.0` suffix for whole numbers, excluding scientific notation such as `1e-05` or `1e+5`.
|
|
|
|
|
if '.' not in val_str and 'e' not in val_str:
|
2009-12-25 14:42:00 +00:00
|
|
|
val_str += '.0'
|
|
|
|
|
return val_str
|
|
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2017-09-04 16:39:13 +10:00
|
|
|
def get_py_class_from_rna(rna_type):
|
2018-09-03 16:49:08 +02:00
|
|
|
""" Gets the Python type for a class which isn't necessarily added to ``bpy.types``.
|
2017-09-04 16:39:13 +10:00
|
|
|
"""
|
|
|
|
|
identifier = rna_type.identifier
|
|
|
|
|
py_class = getattr(bpy.types, identifier, None)
|
|
|
|
|
if py_class is not None:
|
|
|
|
|
return py_class
|
|
|
|
|
|
|
|
|
|
def subclasses_recurse(cls):
|
|
|
|
|
for c in cls.__subclasses__():
|
|
|
|
|
# is_registered
|
2023-05-25 05:10:23 +01:00
|
|
|
if "bl_rna" in c.__dict__:
|
2017-09-04 16:39:13 +10:00
|
|
|
yield c
|
|
|
|
|
yield from subclasses_recurse(c)
|
|
|
|
|
|
2023-05-25 05:10:23 +01:00
|
|
|
base = rna_type.base
|
|
|
|
|
while base is not None:
|
2017-09-04 16:39:13 +10:00
|
|
|
py_class_base = getattr(bpy.types, base.identifier, None)
|
|
|
|
|
if py_class_base is not None:
|
|
|
|
|
for cls in subclasses_recurse(py_class_base):
|
|
|
|
|
if cls.bl_rna.identifier == identifier:
|
|
|
|
|
return cls
|
2023-05-25 05:10:23 +01:00
|
|
|
base = base.base
|
|
|
|
|
raise Exception("can't find type")
|
2017-09-04 16:39:13 +10:00
|
|
|
|
|
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
class InfoStructRNA:
|
2013-09-18 04:32:29 +00:00
|
|
|
__slots__ = (
|
|
|
|
|
"bl_rna",
|
|
|
|
|
"identifier",
|
|
|
|
|
"name",
|
|
|
|
|
"description",
|
|
|
|
|
"base",
|
|
|
|
|
"nested",
|
|
|
|
|
"full_path",
|
|
|
|
|
"functions",
|
|
|
|
|
"children",
|
|
|
|
|
"references",
|
|
|
|
|
"properties",
|
2019-11-16 02:06:20 +11:00
|
|
|
"py_class",
|
|
|
|
|
"module_name",
|
2016-07-29 21:22:27 +10:00
|
|
|
)
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2013-09-18 04:32:29 +00:00
|
|
|
global_lookup = {}
|
2013-11-20 03:38:18 +11:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def __init__(self, rna_type):
|
|
|
|
|
self.bl_rna = rna_type
|
|
|
|
|
|
|
|
|
|
self.identifier = rna_type.identifier
|
|
|
|
|
self.name = rna_type.name
|
|
|
|
|
self.description = rna_type.description.strip()
|
|
|
|
|
|
|
|
|
|
# set later
|
|
|
|
|
self.base = None
|
|
|
|
|
self.nested = None
|
|
|
|
|
self.full_path = ""
|
|
|
|
|
|
|
|
|
|
self.functions = []
|
|
|
|
|
self.children = []
|
|
|
|
|
self.references = []
|
|
|
|
|
self.properties = []
|
|
|
|
|
|
2019-11-16 02:06:20 +11:00
|
|
|
self.py_class = get_py_class_from_rna(self.bl_rna)
|
|
|
|
|
self.module_name = (
|
|
|
|
|
self.py_class.__module__
|
|
|
|
|
if (self.py_class and not hasattr(bpy.types, self.identifier)) else
|
|
|
|
|
"bpy.types"
|
|
|
|
|
)
|
|
|
|
|
if self.module_name == "bpy_types":
|
|
|
|
|
self.module_name = "bpy.types"
|
|
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def build(self):
|
|
|
|
|
rna_type = self.bl_rna
|
|
|
|
|
parent_id = self.identifier
|
2016-07-29 21:22:27 +10:00
|
|
|
self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id)
|
|
|
|
|
for rna_prop in get_direct_properties(rna_type) if rna_prop.identifier != "rna_type"]
|
|
|
|
|
self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id)
|
|
|
|
|
for rna_prop in get_direct_functions(rna_type)]
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2009-12-25 09:01:23 +00:00
|
|
|
def get_bases(self):
|
|
|
|
|
bases = []
|
|
|
|
|
item = self
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 09:01:23 +00:00
|
|
|
while item:
|
|
|
|
|
item = item.base
|
|
|
|
|
if item:
|
|
|
|
|
bases.append(item)
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 09:01:23 +00:00
|
|
|
return bases
|
|
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
def get_nested_properties(self, ls=None):
|
2009-12-19 13:48:50 +00:00
|
|
|
if not ls:
|
|
|
|
|
ls = self.properties[:]
|
|
|
|
|
|
|
|
|
|
if self.nested:
|
2009-12-25 09:01:23 +00:00
|
|
|
self.nested.get_nested_properties(ls)
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
return ls
|
|
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
def _get_py_visible_attrs(self):
|
|
|
|
|
attrs = []
|
2019-11-16 02:06:20 +11:00
|
|
|
py_class = self.py_class
|
2017-09-04 16:39:13 +10:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
for attr_str in dir(py_class):
|
|
|
|
|
if attr_str.startswith("_"):
|
2009-12-26 09:36:50 +00:00
|
|
|
continue
|
2009-12-25 14:42:00 +00:00
|
|
|
attrs.append((attr_str, getattr(py_class, attr_str)))
|
|
|
|
|
return attrs
|
|
|
|
|
|
|
|
|
|
def get_py_properties(self):
|
|
|
|
|
properties = []
|
|
|
|
|
for identifier, attr in self._get_py_visible_attrs():
|
|
|
|
|
if type(attr) is property:
|
|
|
|
|
properties.append((identifier, attr))
|
|
|
|
|
return properties
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
def get_py_functions(self):
|
|
|
|
|
import types
|
|
|
|
|
functions = []
|
|
|
|
|
for identifier, attr in self._get_py_visible_attrs():
|
2025-03-26 17:11:39 +11:00
|
|
|
# Methods may be Python wrappers to C-API functions.
|
2024-06-01 16:08:56 +10:00
|
|
|
ok = False
|
|
|
|
|
if (attr_func := getattr(attr, "__func__", None)) is not None:
|
|
|
|
|
if type(attr_func) == types.FunctionType:
|
|
|
|
|
ok = True
|
|
|
|
|
else:
|
|
|
|
|
if type(attr) in {types.FunctionType, types.MethodType}:
|
|
|
|
|
ok = True
|
|
|
|
|
if ok:
|
2009-12-25 14:42:00 +00:00
|
|
|
functions.append((identifier, attr))
|
|
|
|
|
return functions
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2011-03-14 10:31:50 +00:00
|
|
|
def get_py_c_functions(self):
|
|
|
|
|
import types
|
|
|
|
|
functions = []
|
|
|
|
|
for identifier, attr in self._get_py_visible_attrs():
|
2025-03-26 17:11:39 +11:00
|
|
|
# Methods may be Python wrappers to C-API functions.
|
2024-06-01 16:08:56 +10:00
|
|
|
ok = False
|
|
|
|
|
if (attr_func := getattr(attr, "__func__", None)) is not None:
|
|
|
|
|
if type(attr_func) == types.BuiltinFunctionType:
|
|
|
|
|
ok = True
|
|
|
|
|
else:
|
|
|
|
|
if type(attr) == types.BuiltinMethodType:
|
|
|
|
|
ok = True
|
|
|
|
|
elif type(attr) == types.MethodDescriptorType:
|
2022-04-19 13:04:35 +10:00
|
|
|
# Without the `objclass` check, many inherited methods are included.
|
2024-06-01 16:08:56 +10:00
|
|
|
if attr.__objclass__ == self.py_class:
|
|
|
|
|
ok = True
|
|
|
|
|
if ok:
|
2011-03-14 10:31:50 +00:00
|
|
|
functions.append((identifier, attr))
|
|
|
|
|
return functions
|
|
|
|
|
|
2022-05-18 21:43:38 +10:00
|
|
|
def get_py_c_properties_getset(self):
|
|
|
|
|
import types
|
|
|
|
|
properties_getset = []
|
|
|
|
|
for identifier, descr in self.py_class.__dict__.items():
|
|
|
|
|
if type(descr) == types.GetSetDescriptorType:
|
|
|
|
|
properties_getset.append((identifier, descr))
|
|
|
|
|
return properties_getset
|
|
|
|
|
|
2010-10-15 13:09:38 +00:00
|
|
|
def __str__(self):
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-04-10 18:35:50 +00:00
|
|
|
txt = ""
|
2009-12-19 13:48:50 +00:00
|
|
|
txt += self.identifier
|
|
|
|
|
if self.base:
|
2024-04-27 16:06:51 +10:00
|
|
|
txt += "({:s})".format(self.base.identifier)
|
2010-04-10 18:35:50 +00:00
|
|
|
txt += ": " + self.description + "\n"
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
for prop in self.properties:
|
2010-04-10 18:35:50 +00:00
|
|
|
txt += prop.__repr__() + "\n"
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
for func in self.functions:
|
2010-04-10 18:35:50 +00:00
|
|
|
txt += func.__repr__() + "\n"
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
return txt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InfoPropertyRNA:
|
2013-09-18 04:32:29 +00:00
|
|
|
__slots__ = (
|
|
|
|
|
"bl_prop",
|
|
|
|
|
"srna",
|
|
|
|
|
"identifier",
|
|
|
|
|
"name",
|
|
|
|
|
"description",
|
|
|
|
|
"default_str",
|
|
|
|
|
"default",
|
|
|
|
|
"enum_items",
|
2022-05-31 14:07:14 +10:00
|
|
|
"enum_pointer",
|
2013-09-18 04:32:29 +00:00
|
|
|
"min",
|
|
|
|
|
"max",
|
|
|
|
|
"array_length",
|
2017-09-20 16:39:13 +02:00
|
|
|
"array_dimensions",
|
2013-09-18 04:32:29 +00:00
|
|
|
"collection_type",
|
|
|
|
|
"type",
|
|
|
|
|
"fixed_type",
|
2022-11-27 00:15:12 +02:00
|
|
|
"subtype",
|
2013-09-18 04:32:29 +00:00
|
|
|
"is_argument_optional",
|
|
|
|
|
"is_enum_flag",
|
|
|
|
|
"is_required",
|
|
|
|
|
"is_readonly",
|
|
|
|
|
"is_never_none",
|
2025-04-10 23:27:21 +00:00
|
|
|
"is_path_supports_blend_relative",
|
Templates for render output paths
This adds basic templating support to render output paths. By putting
"{variable_name}" in the path string, it will be replaced by the named
variable's value when generating the actual output path. This is similar
to how "//" is already substituted with the path to the blend file's
current directory.
This templating system is implemented for both the primary render output
path as well as the File Output node in the compositing nodes. Support
for using templates in other places can be implemented in future PRs.
In addition to the "{variable_name}" syntax, some additional syntax is
also supported:
- Since "{" and "}" now have special meaning, "{{" and "}}" are now
escape sequences for literal "{" and "}".
- "{variable_name:format_specifier}", where "format_specifier" is a
special syntax using "#", which allows the user to specify how numeric
variables should be formatted:
- "{variable_name:###}" will format the number as an integer with at
least 3 characters (padding with zeros as needed).
- "{variable_name:.##}" will format the number as a float with
precisely 2 fractional digits.
- "{variable_name:###.##}" will format the number as a float with at
least 3 characters for the integer part and precisely 2 for the
fractional part.
For the primary render output path: if there is a template syntax error,
a variable doesn't exist, or a format specifier isn't valid (e.g. trying
to format a string with "##"), the render that needs to write to the
output path fails with a descriptive error message.
For both the primary and File Output node paths: if there are template
syntax errors the field is highlighted in red in the UI, and a tooltip
describes the offending syntax errors. Note that these do *not* yet
reflect errors due to missing variables. That will be for a follow-up
PR.
In addition to the general system, this PR also implements a limited set
of variables for use in templates, but more can be implemented in future
PRs. The variables added in this PR are:
- `blend_name`: the name of the current blend file without the file
extension.
- `fps`: the frames per second of the current scene.
- `resolution_x` and `resolution_y`: the render output resolution.
Pull Request: https://projects.blender.org/blender/blender/pulls/134860
2025-05-08 15:37:28 +02:00
|
|
|
"is_path_supports_templates",
|
2016-07-29 21:22:27 +10:00
|
|
|
)
|
2009-12-19 13:48:50 +00:00
|
|
|
global_lookup = {}
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def __init__(self, rna_prop):
|
|
|
|
|
self.bl_prop = rna_prop
|
|
|
|
|
self.identifier = rna_prop.identifier
|
|
|
|
|
self.name = rna_prop.name
|
|
|
|
|
self.description = rna_prop.description.strip()
|
2009-12-25 09:01:23 +00:00
|
|
|
self.default_str = "<UNKNOWN>"
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
def build(self):
|
|
|
|
|
rna_prop = self.bl_prop
|
|
|
|
|
|
|
|
|
|
self.enum_items = []
|
2009-12-21 23:14:16 +00:00
|
|
|
self.min = getattr(rna_prop, "hard_min", -1)
|
|
|
|
|
self.max = getattr(rna_prop, "hard_max", -1)
|
2009-12-19 13:48:50 +00:00
|
|
|
self.array_length = getattr(rna_prop, "array_length", 0)
|
2017-09-20 16:39:13 +02:00
|
|
|
self.array_dimensions = getattr(rna_prop, "array_dimensions", ())[:]
|
2009-12-25 14:42:00 +00:00
|
|
|
self.collection_type = GetInfoStructRNA(rna_prop.srna)
|
2022-11-27 00:15:12 +02:00
|
|
|
self.subtype = getattr(rna_prop, "subtype", "")
|
2009-12-25 14:42:00 +00:00
|
|
|
self.is_required = rna_prop.is_required
|
|
|
|
|
self.is_readonly = rna_prop.is_readonly
|
|
|
|
|
self.is_never_none = rna_prop.is_never_none
|
2013-09-18 04:32:29 +00:00
|
|
|
self.is_argument_optional = rna_prop.is_argument_optional
|
2025-04-10 23:27:21 +00:00
|
|
|
self.is_path_supports_blend_relative = rna_prop.is_path_supports_blend_relative
|
Templates for render output paths
This adds basic templating support to render output paths. By putting
"{variable_name}" in the path string, it will be replaced by the named
variable's value when generating the actual output path. This is similar
to how "//" is already substituted with the path to the blend file's
current directory.
This templating system is implemented for both the primary render output
path as well as the File Output node in the compositing nodes. Support
for using templates in other places can be implemented in future PRs.
In addition to the "{variable_name}" syntax, some additional syntax is
also supported:
- Since "{" and "}" now have special meaning, "{{" and "}}" are now
escape sequences for literal "{" and "}".
- "{variable_name:format_specifier}", where "format_specifier" is a
special syntax using "#", which allows the user to specify how numeric
variables should be formatted:
- "{variable_name:###}" will format the number as an integer with at
least 3 characters (padding with zeros as needed).
- "{variable_name:.##}" will format the number as a float with
precisely 2 fractional digits.
- "{variable_name:###.##}" will format the number as a float with at
least 3 characters for the integer part and precisely 2 for the
fractional part.
For the primary render output path: if there is a template syntax error,
a variable doesn't exist, or a format specifier isn't valid (e.g. trying
to format a string with "##"), the render that needs to write to the
output path fails with a descriptive error message.
For both the primary and File Output node paths: if there are template
syntax errors the field is highlighted in red in the UI, and a tooltip
describes the offending syntax errors. Note that these do *not* yet
reflect errors due to missing variables. That will be for a follow-up
PR.
In addition to the general system, this PR also implements a limited set
of variables for use in templates, but more can be implemented in future
PRs. The variables added in this PR are:
- `blend_name`: the name of the current blend file without the file
extension.
- `fps`: the frames per second of the current scene.
- `resolution_x` and `resolution_y`: the render output resolution.
Pull Request: https://projects.blender.org/blender/blender/pulls/134860
2025-05-08 15:37:28 +02:00
|
|
|
self.is_path_supports_templates = rna_prop.is_path_supports_templates
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
self.type = rna_prop.type.lower()
|
2009-12-21 23:14:16 +00:00
|
|
|
fixed_type = getattr(rna_prop, "fixed_type", "")
|
|
|
|
|
if fixed_type:
|
2010-09-07 15:17:42 +00:00
|
|
|
self.fixed_type = GetInfoStructRNA(fixed_type) # valid for pointer/collections
|
2009-12-21 23:14:16 +00:00
|
|
|
else:
|
|
|
|
|
self.fixed_type = None
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2022-05-31 14:07:14 +10:00
|
|
|
self.enum_pointer = 0
|
2009-12-21 23:14:16 +00:00
|
|
|
if self.type == "enum":
|
2022-11-08 16:43:16 +11:00
|
|
|
# WARNING: don't convert to a tuple as this causes dynamically allocated enums to access freed memory
|
|
|
|
|
# since freeing the iterator may free the memory used to store the internal `EnumPropertyItem` array.
|
|
|
|
|
# To support this properly RNA would have to support owning the dynamically allocated memory.
|
|
|
|
|
items = rna_prop.enum_items
|
2022-05-31 14:07:14 +10:00
|
|
|
items_static = tuple(rna_prop.enum_items_static)
|
|
|
|
|
self.enum_items[:] = [(item.identifier, item.name, item.description) for item in items]
|
2011-02-16 10:19:14 +00:00
|
|
|
self.is_enum_flag = rna_prop.is_enum_flag
|
2022-05-31 14:07:14 +10:00
|
|
|
# Prioritize static items as this is never going to be allocated data and is therefor
|
|
|
|
|
# will be a stable match to compare against.
|
|
|
|
|
item = (items_static or items)
|
|
|
|
|
if item:
|
|
|
|
|
self.enum_pointer = item[0].as_pointer()
|
2022-11-08 16:43:16 +11:00
|
|
|
del items, items_static, item
|
2011-02-16 10:19:14 +00:00
|
|
|
else:
|
|
|
|
|
self.is_enum_flag = False
|
2010-05-16 12:15:04 +00:00
|
|
|
|
2017-09-20 16:39:13 +02:00
|
|
|
self.default_str = "" # fallback
|
|
|
|
|
|
2009-12-25 09:01:23 +00:00
|
|
|
if self.array_length:
|
2009-12-25 14:42:00 +00:00
|
|
|
self.default = tuple(getattr(rna_prop, "default_array", ()))
|
2017-09-20 16:39:13 +02:00
|
|
|
if self.array_dimensions[1] != 0: # Multi-dimensional array, convert default flat one accordingly.
|
|
|
|
|
self.default_str = tuple(float_as_string(v) if self.type == "float" else str(v) for v in self.default)
|
|
|
|
|
for dim in self.array_dimensions[::-1]:
|
|
|
|
|
if dim != 0:
|
|
|
|
|
self.default = tuple(zip(*((iter(self.default),) * dim)))
|
2020-05-29 12:45:20 +10:00
|
|
|
self.default_str = tuple(
|
2024-04-27 16:06:51 +10:00
|
|
|
"({:s})".format(", ".join(s for s in b)) for b in zip(*((iter(self.default_str),) * dim))
|
2020-05-29 12:45:20 +10:00
|
|
|
)
|
2017-09-20 16:39:13 +02:00
|
|
|
self.default_str = self.default_str[0]
|
2011-02-16 10:19:14 +00:00
|
|
|
elif self.type == "enum" and self.is_enum_flag:
|
|
|
|
|
self.default = getattr(rna_prop, "default_flag", set())
|
2010-05-04 06:08:00 +00:00
|
|
|
else:
|
|
|
|
|
self.default = getattr(rna_prop, "default", None)
|
|
|
|
|
|
|
|
|
|
if self.type == "pointer":
|
|
|
|
|
# pointer has no default, just set as None
|
|
|
|
|
self.default = None
|
|
|
|
|
self.default_str = "None"
|
|
|
|
|
elif self.type == "string":
|
2024-04-27 16:06:51 +10:00
|
|
|
self.default_str = "\"{:s}\"".format(self.default)
|
2010-05-04 06:08:00 +00:00
|
|
|
elif self.type == "enum":
|
2011-02-16 10:19:14 +00:00
|
|
|
if self.is_enum_flag:
|
2024-04-12 10:04:07 +10:00
|
|
|
# self.default_str = repr(self.default) # repr or set()
|
2024-04-27 16:06:51 +10:00
|
|
|
self.default_str = "{{{:s}}}".format(repr(list(sorted(self.default)))[1:-1])
|
2011-02-16 10:19:14 +00:00
|
|
|
else:
|
2024-04-27 16:06:51 +10:00
|
|
|
self.default_str = "'{:s}'".format(self.default)
|
2010-05-04 06:08:00 +00:00
|
|
|
elif self.array_length:
|
2017-09-20 16:39:13 +02:00
|
|
|
if self.array_dimensions[1] == 0: # single dimension array, we already took care of multi-dimensions ones.
|
|
|
|
|
# special case for floats
|
|
|
|
|
if self.type == "float" and len(self.default) > 0:
|
2024-04-27 16:06:51 +10:00
|
|
|
self.default_str = "({:s})".format(", ".join(float_as_string(f) for f in self.default))
|
2017-09-20 16:39:13 +02:00
|
|
|
else:
|
|
|
|
|
self.default_str = str(self.default)
|
2009-12-25 09:01:23 +00:00
|
|
|
else:
|
2010-05-04 06:08:00 +00:00
|
|
|
if self.type == "float":
|
2009-12-25 14:42:00 +00:00
|
|
|
self.default_str = float_as_string(self.default)
|
|
|
|
|
else:
|
|
|
|
|
self.default_str = str(self.default)
|
2009-12-25 09:01:23 +00:00
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
self.srna = GetInfoStructRNA(rna_prop.srna) # valid for pointer/collections
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
def get_arg_default(self, force=True):
|
2010-05-04 06:08:00 +00:00
|
|
|
default = self.default_str
|
2012-10-08 08:28:05 +00:00
|
|
|
if default and (force or self.is_required is False):
|
2024-04-27 16:06:51 +10:00
|
|
|
return "{:s}={:s}".format(self.identifier, default)
|
2009-12-25 14:42:00 +00:00
|
|
|
return self.identifier
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2022-05-31 14:07:14 +10:00
|
|
|
def get_type_description(
|
|
|
|
|
self, *,
|
|
|
|
|
as_ret=False,
|
|
|
|
|
as_arg=False,
|
2024-04-27 16:06:51 +10:00
|
|
|
class_fmt="{:s}",
|
|
|
|
|
mathutils_fmt="{:s}",
|
2022-05-31 14:07:14 +10:00
|
|
|
collection_id="Collection",
|
|
|
|
|
enum_descr_override=None,
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
:arg enum_descr_override: Optionally override items for enum.
|
|
|
|
|
Otherwise expand the literal items.
|
2024-11-03 15:42:19 +11:00
|
|
|
:type enum_descr_override: str | None
|
2022-05-31 14:07:14 +10:00
|
|
|
"""
|
2009-12-26 16:47:25 +00:00
|
|
|
type_str = ""
|
|
|
|
|
if self.fixed_type is None:
|
|
|
|
|
type_str += self.type
|
2025-02-25 14:02:49 -05:00
|
|
|
if self.type == "string" and self.subtype == "BYTE_STRING":
|
|
|
|
|
type_str = "byte string"
|
2009-12-26 16:47:25 +00:00
|
|
|
if self.array_length:
|
2017-09-20 16:39:13 +02:00
|
|
|
if self.array_dimensions[1] != 0:
|
2024-04-27 16:06:51 +10:00
|
|
|
dimension_str = " of {:s} items".format(
|
2020-05-29 12:45:20 +10:00
|
|
|
" * ".join(str(d) for d in self.array_dimensions if d != 0)
|
|
|
|
|
)
|
2022-11-27 00:15:12 +02:00
|
|
|
type_str += " multi-dimensional array" + dimension_str
|
2017-09-20 16:39:13 +02:00
|
|
|
else:
|
2024-04-27 16:06:51 +10:00
|
|
|
dimension_str = " of {:d} items".format(self.array_length)
|
2022-11-27 00:15:12 +02:00
|
|
|
type_str += " array" + dimension_str
|
|
|
|
|
|
|
|
|
|
# Describe mathutils types; logic mirrors pyrna_math_object_from_array
|
|
|
|
|
if self.type == "float":
|
|
|
|
|
if self.subtype == "MATRIX":
|
|
|
|
|
if self.array_length in {9, 16}:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str = (mathutils_fmt.format("Matrix")) + dimension_str
|
2022-11-27 00:15:12 +02:00
|
|
|
elif self.subtype in {"COLOR", "COLOR_GAMMA"}:
|
|
|
|
|
if self.array_length == 3:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str = (mathutils_fmt.format("Color")) + dimension_str
|
2022-11-27 00:15:12 +02:00
|
|
|
elif self.subtype in {"EULER", "QUATERNION"}:
|
|
|
|
|
if self.array_length == 3:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str = (mathutils_fmt.format("Euler")) + " rotation" + dimension_str
|
2022-11-27 00:15:12 +02:00
|
|
|
elif self.array_length == 4:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str = (mathutils_fmt.format("Quaternion")) + " rotation" + dimension_str
|
2023-09-29 14:32:54 +10:00
|
|
|
elif self.subtype in {
|
|
|
|
|
'COORDINATES', 'TRANSLATION', 'DIRECTION', 'VELOCITY',
|
|
|
|
|
'ACCELERATION', 'XYZ', 'XYZ_LENGTH',
|
|
|
|
|
}:
|
2022-11-27 00:15:12 +02:00
|
|
|
if 2 <= self.array_length <= 4:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str = (mathutils_fmt.format("Vector")) + dimension_str
|
2010-05-16 12:15:04 +00:00
|
|
|
|
2011-08-08 05:21:37 +00:00
|
|
|
if self.type in {"float", "int"}:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str += " in [{:s}, {:s}]".format(range_str(self.min), range_str(self.max))
|
2009-12-26 16:47:25 +00:00
|
|
|
elif self.type == "enum":
|
2022-05-31 14:07:14 +10:00
|
|
|
enum_descr = enum_descr_override
|
|
|
|
|
if not enum_descr:
|
|
|
|
|
if self.is_enum_flag:
|
2024-04-27 16:06:51 +10:00
|
|
|
enum_descr = "{{{:s}}}".format(", ".join(("'{:s}'".format(s[0])) for s in self.enum_items))
|
2022-05-31 14:07:14 +10:00
|
|
|
else:
|
2024-04-27 16:06:51 +10:00
|
|
|
enum_descr = "[{:s}]".format(", ".join(("'{:s}'".format(s[0])) for s in self.enum_items))
|
2011-02-16 10:19:14 +00:00
|
|
|
if self.is_enum_flag:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str += " set in {:s}".format(enum_descr)
|
2011-02-16 10:19:14 +00:00
|
|
|
else:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str += " in {:s}".format(enum_descr)
|
2022-05-31 14:07:14 +10:00
|
|
|
del enum_descr
|
2010-05-03 15:52:15 +00:00
|
|
|
|
|
|
|
|
if not (as_arg or as_ret):
|
|
|
|
|
# write default property, ignore function args for this
|
2010-05-04 06:08:00 +00:00
|
|
|
if self.type != "pointer":
|
|
|
|
|
if self.default_str:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str += ", default {:s}".format(self.default_str)
|
2010-05-03 15:52:15 +00:00
|
|
|
|
2009-12-26 16:47:25 +00:00
|
|
|
else:
|
|
|
|
|
if self.type == "collection":
|
|
|
|
|
if self.collection_type:
|
2024-04-27 16:06:51 +10:00
|
|
|
collection_str = (
|
|
|
|
|
class_fmt.format(self.collection_type.identifier) +
|
|
|
|
|
" {:s} of ".format(collection_id)
|
|
|
|
|
)
|
2009-12-26 16:47:25 +00:00
|
|
|
else:
|
2024-04-27 16:06:51 +10:00
|
|
|
collection_str = "{:s} of ".format(collection_id)
|
2009-12-26 16:47:25 +00:00
|
|
|
else:
|
|
|
|
|
collection_str = ""
|
|
|
|
|
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str += collection_str + (class_fmt.format(self.fixed_type.identifier))
|
2009-12-26 16:47:25 +00:00
|
|
|
|
2010-05-03 15:52:15 +00:00
|
|
|
# setup qualifiers for this value.
|
|
|
|
|
type_info = []
|
2010-01-17 20:59:35 +00:00
|
|
|
if as_ret:
|
|
|
|
|
pass
|
|
|
|
|
elif as_arg:
|
2009-12-26 16:47:25 +00:00
|
|
|
if not self.is_required:
|
2010-05-03 15:52:15 +00:00
|
|
|
type_info.append("optional")
|
2013-08-26 21:39:06 +00:00
|
|
|
if self.is_argument_optional:
|
2025-02-11 16:24:39 +11:00
|
|
|
type_info.append("optional for registration")
|
2011-10-17 06:58:07 +00:00
|
|
|
else: # readonly is only useful for self's, not args
|
2009-12-26 16:47:25 +00:00
|
|
|
if self.is_readonly:
|
2010-05-03 15:52:15 +00:00
|
|
|
type_info.append("readonly")
|
2009-12-26 16:47:25 +00:00
|
|
|
|
|
|
|
|
if self.is_never_none:
|
2010-05-03 15:52:15 +00:00
|
|
|
type_info.append("never None")
|
|
|
|
|
|
2025-04-10 23:27:21 +00:00
|
|
|
if self.is_path_supports_blend_relative:
|
|
|
|
|
type_info.append("blend relative ``//`` prefix supported")
|
|
|
|
|
|
Templates for render output paths
This adds basic templating support to render output paths. By putting
"{variable_name}" in the path string, it will be replaced by the named
variable's value when generating the actual output path. This is similar
to how "//" is already substituted with the path to the blend file's
current directory.
This templating system is implemented for both the primary render output
path as well as the File Output node in the compositing nodes. Support
for using templates in other places can be implemented in future PRs.
In addition to the "{variable_name}" syntax, some additional syntax is
also supported:
- Since "{" and "}" now have special meaning, "{{" and "}}" are now
escape sequences for literal "{" and "}".
- "{variable_name:format_specifier}", where "format_specifier" is a
special syntax using "#", which allows the user to specify how numeric
variables should be formatted:
- "{variable_name:###}" will format the number as an integer with at
least 3 characters (padding with zeros as needed).
- "{variable_name:.##}" will format the number as a float with
precisely 2 fractional digits.
- "{variable_name:###.##}" will format the number as a float with at
least 3 characters for the integer part and precisely 2 for the
fractional part.
For the primary render output path: if there is a template syntax error,
a variable doesn't exist, or a format specifier isn't valid (e.g. trying
to format a string with "##"), the render that needs to write to the
output path fails with a descriptive error message.
For both the primary and File Output node paths: if there are template
syntax errors the field is highlighted in red in the UI, and a tooltip
describes the offending syntax errors. Note that these do *not* yet
reflect errors due to missing variables. That will be for a follow-up
PR.
In addition to the general system, this PR also implements a limited set
of variables for use in templates, but more can be implemented in future
PRs. The variables added in this PR are:
- `blend_name`: the name of the current blend file without the file
extension.
- `fps`: the frames per second of the current scene.
- `resolution_x` and `resolution_y`: the render output resolution.
Pull Request: https://projects.blender.org/blender/blender/pulls/134860
2025-05-08 15:37:28 +02:00
|
|
|
if self.is_path_supports_templates:
|
2025-05-22 09:58:25 +02:00
|
|
|
type_info.append(
|
2025-05-23 03:58:31 +00:00
|
|
|
"Supports `template expressions "
|
|
|
|
|
"<https://docs.blender.org/manual/en/{:d}.{:d}/files/file_paths.html#path-templates>`_".format(
|
|
|
|
|
*bpy.app.version[:2],
|
|
|
|
|
),
|
|
|
|
|
)
|
Templates for render output paths
This adds basic templating support to render output paths. By putting
"{variable_name}" in the path string, it will be replaced by the named
variable's value when generating the actual output path. This is similar
to how "//" is already substituted with the path to the blend file's
current directory.
This templating system is implemented for both the primary render output
path as well as the File Output node in the compositing nodes. Support
for using templates in other places can be implemented in future PRs.
In addition to the "{variable_name}" syntax, some additional syntax is
also supported:
- Since "{" and "}" now have special meaning, "{{" and "}}" are now
escape sequences for literal "{" and "}".
- "{variable_name:format_specifier}", where "format_specifier" is a
special syntax using "#", which allows the user to specify how numeric
variables should be formatted:
- "{variable_name:###}" will format the number as an integer with at
least 3 characters (padding with zeros as needed).
- "{variable_name:.##}" will format the number as a float with
precisely 2 fractional digits.
- "{variable_name:###.##}" will format the number as a float with at
least 3 characters for the integer part and precisely 2 for the
fractional part.
For the primary render output path: if there is a template syntax error,
a variable doesn't exist, or a format specifier isn't valid (e.g. trying
to format a string with "##"), the render that needs to write to the
output path fails with a descriptive error message.
For both the primary and File Output node paths: if there are template
syntax errors the field is highlighted in red in the UI, and a tooltip
describes the offending syntax errors. Note that these do *not* yet
reflect errors due to missing variables. That will be for a follow-up
PR.
In addition to the general system, this PR also implements a limited set
of variables for use in templates, but more can be implemented in future
PRs. The variables added in this PR are:
- `blend_name`: the name of the current blend file without the file
extension.
- `fps`: the frames per second of the current scene.
- `resolution_x` and `resolution_y`: the render output resolution.
Pull Request: https://projects.blender.org/blender/blender/pulls/134860
2025-05-08 15:37:28 +02:00
|
|
|
|
2010-05-03 15:52:15 +00:00
|
|
|
if type_info:
|
2024-04-27 16:06:51 +10:00
|
|
|
type_str += ", ({:s})".format(", ".join(type_info))
|
2009-12-27 11:14:06 +00:00
|
|
|
|
2009-12-26 16:47:25 +00:00
|
|
|
return type_str
|
|
|
|
|
|
2010-10-15 13:09:38 +00:00
|
|
|
def __str__(self):
|
2012-06-19 22:17:19 +00:00
|
|
|
txt = ""
|
|
|
|
|
txt += " * " + self.identifier + ": " + self.description
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
return txt
|
|
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
class InfoFunctionRNA:
|
2013-09-18 04:32:29 +00:00
|
|
|
__slots__ = (
|
|
|
|
|
"bl_func",
|
|
|
|
|
"identifier",
|
|
|
|
|
"description",
|
|
|
|
|
"args",
|
|
|
|
|
"return_values",
|
|
|
|
|
"is_classmethod",
|
2016-07-29 21:22:27 +10:00
|
|
|
)
|
2009-12-19 13:48:50 +00:00
|
|
|
global_lookup = {}
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def __init__(self, rna_func):
|
|
|
|
|
self.bl_func = rna_func
|
|
|
|
|
self.identifier = rna_func.identifier
|
|
|
|
|
# self.name = rna_func.name # functions have no name!
|
|
|
|
|
self.description = rna_func.description.strip()
|
2010-08-18 07:14:10 +00:00
|
|
|
self.is_classmethod = not rna_func.use_self
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2009-12-24 19:50:43 +00:00
|
|
|
self.args = []
|
2010-01-02 18:55:07 +00:00
|
|
|
self.return_values = ()
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
def build(self):
|
2009-12-25 09:01:23 +00:00
|
|
|
rna_func = self.bl_func
|
|
|
|
|
parent_id = rna_func
|
2010-01-02 18:55:07 +00:00
|
|
|
self.return_values = []
|
2009-12-26 09:36:50 +00:00
|
|
|
|
|
|
|
|
for rna_prop in rna_func.parameters.values():
|
2009-12-24 19:50:43 +00:00
|
|
|
prop = GetInfoPropertyRNA(rna_prop, parent_id)
|
2010-08-18 08:26:18 +00:00
|
|
|
if rna_prop.is_output:
|
2010-01-02 18:55:07 +00:00
|
|
|
self.return_values.append(prop)
|
2009-12-24 19:50:43 +00:00
|
|
|
else:
|
|
|
|
|
self.args.append(prop)
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-01-02 18:55:07 +00:00
|
|
|
self.return_values = tuple(self.return_values)
|
|
|
|
|
|
2010-10-15 13:09:38 +00:00
|
|
|
def __str__(self):
|
2009-12-19 13:48:50 +00:00
|
|
|
txt = ''
|
|
|
|
|
txt += ' * ' + self.identifier + '('
|
|
|
|
|
|
|
|
|
|
for arg in self.args:
|
|
|
|
|
txt += arg.identifier + ', '
|
|
|
|
|
txt += '): ' + self.description
|
|
|
|
|
return txt
|
|
|
|
|
|
|
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
class InfoOperatorRNA:
|
2013-09-18 04:32:29 +00:00
|
|
|
__slots__ = (
|
|
|
|
|
"bl_op",
|
|
|
|
|
"identifier",
|
|
|
|
|
"name",
|
|
|
|
|
"module_name",
|
|
|
|
|
"func_name",
|
|
|
|
|
"description",
|
|
|
|
|
"args",
|
2016-07-29 21:22:27 +10:00
|
|
|
)
|
2009-12-25 14:42:00 +00:00
|
|
|
global_lookup = {}
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
def __init__(self, rna_op):
|
|
|
|
|
self.bl_op = rna_op
|
|
|
|
|
self.identifier = rna_op.identifier
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
mod, name = self.identifier.split("_OT_", 1)
|
|
|
|
|
self.module_name = mod.lower()
|
|
|
|
|
self.func_name = name
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
# self.name = rna_func.name # functions have no name!
|
|
|
|
|
self.description = rna_op.description.strip()
|
|
|
|
|
|
|
|
|
|
self.args = []
|
|
|
|
|
|
|
|
|
|
def build(self):
|
|
|
|
|
rna_op = self.bl_op
|
|
|
|
|
parent_id = self.identifier
|
|
|
|
|
for rna_id, rna_prop in rna_op.properties.items():
|
|
|
|
|
if rna_id == "rna_type":
|
|
|
|
|
continue
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
prop = GetInfoPropertyRNA(rna_prop, parent_id)
|
|
|
|
|
self.args.append(prop)
|
|
|
|
|
|
|
|
|
|
def get_location(self):
|
2017-09-04 16:39:13 +10:00
|
|
|
try:
|
|
|
|
|
op_class = getattr(bpy.types, self.identifier)
|
|
|
|
|
except AttributeError:
|
|
|
|
|
# defined in C.
|
|
|
|
|
return None, None
|
2009-12-25 14:42:00 +00:00
|
|
|
op_func = getattr(op_class, "execute", None)
|
|
|
|
|
if op_func is None:
|
|
|
|
|
op_func = getattr(op_class, "invoke", None)
|
|
|
|
|
if op_func is None:
|
|
|
|
|
op_func = getattr(op_class, "poll", None)
|
|
|
|
|
|
|
|
|
|
if op_func:
|
|
|
|
|
op_code = op_func.__code__
|
|
|
|
|
source_path = op_code.co_filename
|
|
|
|
|
|
|
|
|
|
# clear the prefix
|
|
|
|
|
for p in script_paths:
|
|
|
|
|
source_path = source_path.split(p)[-1]
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
if source_path[0] in "/\\":
|
2009-12-26 09:36:50 +00:00
|
|
|
source_path = source_path[1:]
|
|
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
return source_path, op_code.co_firstlineno
|
|
|
|
|
else:
|
|
|
|
|
return None, None
|
|
|
|
|
|
|
|
|
|
|
2012-06-19 22:17:19 +00:00
|
|
|
def _GetInfoRNA(bl_rna, cls, parent_id=""):
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-09-18 10:43:32 +00:00
|
|
|
if bl_rna is None:
|
2009-12-19 13:48:50 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
key = parent_id, bl_rna.identifier
|
|
|
|
|
try:
|
|
|
|
|
return cls.global_lookup[key]
|
2010-03-06 01:40:29 +00:00
|
|
|
except KeyError:
|
2009-12-19 13:48:50 +00:00
|
|
|
instance = cls.global_lookup[key] = cls(bl_rna)
|
|
|
|
|
return instance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def GetInfoStructRNA(bl_rna):
|
|
|
|
|
return _GetInfoRNA(bl_rna, InfoStructRNA)
|
|
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def GetInfoPropertyRNA(bl_rna, parent_id):
|
|
|
|
|
return _GetInfoRNA(bl_rna, InfoPropertyRNA, parent_id)
|
|
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def GetInfoFunctionRNA(bl_rna, parent_id):
|
|
|
|
|
return _GetInfoRNA(bl_rna, InfoFunctionRNA, parent_id)
|
|
|
|
|
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
def GetInfoOperatorRNA(bl_rna):
|
|
|
|
|
return _GetInfoRNA(bl_rna, InfoOperatorRNA)
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-04-17 19:05:53 +00:00
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
def BuildRNAInfo():
|
2014-12-18 14:29:24 +01:00
|
|
|
|
|
|
|
|
# needed on successive calls to prevent stale data access
|
|
|
|
|
for cls in (InfoStructRNA, InfoFunctionRNA, InfoOperatorRNA, InfoPropertyRNA):
|
|
|
|
|
cls.global_lookup.clear()
|
|
|
|
|
del cls
|
|
|
|
|
|
2009-12-19 13:48:50 +00:00
|
|
|
# Use for faster lookups
|
|
|
|
|
# use rna_struct.identifier as the key for each dict
|
2009-12-26 09:36:50 +00:00
|
|
|
rna_struct_dict = {} # store identifier:rna lookups
|
2019-09-10 06:11:52 +10:00
|
|
|
rna_full_path_dict = {} # store the result of full_rna_struct_path(rna_struct)
|
2010-09-07 15:17:42 +00:00
|
|
|
rna_children_dict = {} # store all rna_structs nested from here
|
|
|
|
|
rna_references_dict = {} # store a list of rna path strings that reference this type
|
|
|
|
|
# rna_functions_dict = {} # store all functions directly in this type (not inherited)
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
def full_rna_struct_path(rna_struct):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""
|
2009-12-19 13:48:50 +00:00
|
|
|
Needed when referencing one struct from another
|
2012-07-03 09:02:41 +00:00
|
|
|
"""
|
2009-12-19 13:48:50 +00:00
|
|
|
nested = rna_struct.nested
|
|
|
|
|
if nested:
|
2024-04-27 16:06:51 +10:00
|
|
|
return "{:s}.{:s}".format(full_rna_struct_path(nested), rna_struct.identifier)
|
2009-12-19 13:48:50 +00:00
|
|
|
else:
|
|
|
|
|
return rna_struct.identifier
|
|
|
|
|
|
|
|
|
|
# def write_func(rna_func, ident):
|
|
|
|
|
def base_id(rna_struct):
|
2009-12-26 09:36:50 +00:00
|
|
|
try:
|
|
|
|
|
return rna_struct.base.identifier
|
2024-10-08 09:41:53 +11:00
|
|
|
except AttributeError:
|
2010-09-07 15:17:42 +00:00
|
|
|
return "" # invalid id
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2023-03-01 22:12:18 +11:00
|
|
|
# structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()]
|
2009-12-19 13:48:50 +00:00
|
|
|
'''
|
|
|
|
|
structs = []
|
|
|
|
|
for rna_struct in bpy.doc.structs.values():
|
|
|
|
|
structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) )
|
|
|
|
|
'''
|
|
|
|
|
structs = []
|
|
|
|
|
|
2019-11-16 02:06:20 +11:00
|
|
|
def _bpy_types_iterator():
|
2022-05-23 11:43:47 +10:00
|
|
|
# Don't report when these types are ignored.
|
|
|
|
|
suppress_warning = {
|
|
|
|
|
"bpy_func",
|
|
|
|
|
"bpy_prop",
|
|
|
|
|
"bpy_prop_array",
|
|
|
|
|
"bpy_prop_collection",
|
|
|
|
|
"bpy_struct",
|
|
|
|
|
"bpy_struct_meta_idprop",
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-16 02:06:20 +11:00
|
|
|
names_unique = set()
|
|
|
|
|
rna_type_list = []
|
|
|
|
|
for rna_type_name in dir(bpy.types):
|
|
|
|
|
names_unique.add(rna_type_name)
|
|
|
|
|
rna_type = getattr(bpy.types, rna_type_name)
|
|
|
|
|
rna_struct = getattr(rna_type, "bl_rna", None)
|
|
|
|
|
if rna_struct is not None:
|
|
|
|
|
rna_type_list.append(rna_type)
|
|
|
|
|
yield (rna_type_name, rna_struct)
|
2022-05-23 11:43:47 +10:00
|
|
|
elif rna_type_name.startswith("_"):
|
|
|
|
|
# Ignore "__dir__", "__getattr__" .. etc.
|
|
|
|
|
pass
|
|
|
|
|
elif rna_type_name in suppress_warning:
|
|
|
|
|
pass
|
2019-11-16 02:06:20 +11:00
|
|
|
else:
|
2022-05-23 11:43:47 +10:00
|
|
|
print("rna_info.BuildRNAInfo(..): ignoring type", repr(rna_type_name))
|
2019-11-16 02:06:20 +11:00
|
|
|
|
|
|
|
|
# Now, there are some sub-classes in add-ons we also want to include.
|
|
|
|
|
# Cycles for e.g. these are referenced from the Scene, but not part of
|
|
|
|
|
# bpy.types module.
|
|
|
|
|
# Include all sub-classes we didn't already get from 'bpy.types'.
|
|
|
|
|
i = 0
|
|
|
|
|
while i < len(rna_type_list):
|
|
|
|
|
rna_type = rna_type_list[i]
|
|
|
|
|
for rna_sub_type in rna_type.__subclasses__():
|
|
|
|
|
rna_sub_struct = getattr(rna_sub_type, "bl_rna", None)
|
|
|
|
|
if rna_sub_struct is not None:
|
|
|
|
|
rna_sub_type_name = rna_sub_struct.identifier
|
|
|
|
|
if rna_sub_type_name not in names_unique:
|
|
|
|
|
names_unique.add(rna_sub_type_name)
|
|
|
|
|
rna_type_list.append(rna_sub_type)
|
|
|
|
|
# The bl_idname may not match the class name in the file.
|
|
|
|
|
# Always use the 'bl_idname' because using the Python
|
|
|
|
|
# class name causes confusion - having two names for the same thing.
|
|
|
|
|
# Since having two names for the same thing is trickier to support
|
|
|
|
|
# without a significant benefit.
|
|
|
|
|
yield (rna_sub_type_name, rna_sub_struct)
|
|
|
|
|
i += 1
|
|
|
|
|
|
2021-03-06 18:16:39 +11:00
|
|
|
for (_rna_type_name, rna_struct) in _bpy_types_iterator():
|
|
|
|
|
# if not _rna_type_name.startswith('__'):
|
2019-11-16 02:06:20 +11:00
|
|
|
|
|
|
|
|
identifier = rna_struct.identifier
|
|
|
|
|
|
|
|
|
|
if not rna_id_ignore(identifier):
|
|
|
|
|
structs.append((base_id(rna_struct), identifier, rna_struct))
|
|
|
|
|
|
|
|
|
|
# Simple lookup
|
|
|
|
|
rna_struct_dict[identifier] = rna_struct
|
|
|
|
|
|
|
|
|
|
# Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings'
|
|
|
|
|
rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct)
|
|
|
|
|
|
|
|
|
|
# Store a list of functions, remove inherited later
|
|
|
|
|
# NOT USED YET
|
2023-03-01 22:12:18 +11:00
|
|
|
# rna_functions_dict[identifier] = get_direct_functions(rna_struct)
|
2019-11-16 02:06:20 +11:00
|
|
|
|
|
|
|
|
# fill in these later
|
|
|
|
|
rna_children_dict[identifier] = []
|
|
|
|
|
rna_references_dict[identifier] = []
|
|
|
|
|
|
|
|
|
|
del _bpy_types_iterator
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
structs.sort() # not needed but speeds up sort below, setting items without an inheritance first
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
# Arrange so classes are always defined in the correct order
|
|
|
|
|
deps_ok = False
|
2012-10-08 08:28:05 +00:00
|
|
|
while deps_ok is False:
|
2009-12-19 13:48:50 +00:00
|
|
|
deps_ok = True
|
|
|
|
|
rna_done = set()
|
|
|
|
|
|
|
|
|
|
for i, (rna_base, identifier, rna_struct) in enumerate(structs):
|
|
|
|
|
|
|
|
|
|
rna_done.add(identifier)
|
|
|
|
|
|
|
|
|
|
if rna_base and rna_base not in rna_done:
|
|
|
|
|
deps_ok = False
|
|
|
|
|
data = structs.pop(i)
|
|
|
|
|
ok = False
|
|
|
|
|
while i < len(structs):
|
2009-12-26 09:36:50 +00:00
|
|
|
if structs[i][1] == rna_base:
|
2010-09-07 15:17:42 +00:00
|
|
|
structs.insert(i + 1, data) # insert after the item we depend on.
|
2009-12-19 13:48:50 +00:00
|
|
|
ok = True
|
|
|
|
|
break
|
2009-12-26 09:36:50 +00:00
|
|
|
i += 1
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
if not ok:
|
2024-04-27 16:06:51 +10:00
|
|
|
print("Dependency \"{:s}\" could not be found for \"{:s}\"".format(identifier, rna_base))
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# Done ordering structs
|
|
|
|
|
|
2011-10-17 06:58:07 +00:00
|
|
|
# precalculate vars to avoid a lot of looping
|
2009-12-19 13:48:50 +00:00
|
|
|
for (rna_base, identifier, rna_struct) in structs:
|
|
|
|
|
|
|
|
|
|
# rna_struct_path = full_rna_struct_path(rna_struct)
|
|
|
|
|
rna_struct_path = rna_full_path_dict[identifier]
|
|
|
|
|
|
2010-04-09 20:43:58 +00:00
|
|
|
for rna_prop in get_direct_properties(rna_struct):
|
|
|
|
|
rna_prop_identifier = rna_prop.identifier
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-04-09 20:04:37 +00:00
|
|
|
if rna_prop_identifier == 'RNA' or rna_id_ignore(rna_prop_identifier):
|
2009-12-26 09:36:50 +00:00
|
|
|
continue
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", None)):
|
|
|
|
|
# Does this property point to me?
|
2017-09-20 16:35:23 +02:00
|
|
|
if rna_prop_ptr and rna_prop_ptr.identifier in rna_references_dict:
|
2016-07-29 21:22:27 +10:00
|
|
|
rna_references_dict[rna_prop_ptr.identifier].append(
|
2024-04-27 16:06:51 +10:00
|
|
|
"{:s}.{:s}".format(rna_struct_path, rna_prop_identifier))
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-04-09 20:43:58 +00:00
|
|
|
for rna_func in get_direct_functions(rna_struct):
|
2009-12-19 13:48:50 +00:00
|
|
|
for rna_prop_identifier, rna_prop in rna_func.parameters.items():
|
|
|
|
|
|
2010-04-09 20:04:37 +00:00
|
|
|
if rna_prop_identifier == 'RNA' or rna_id_ignore(rna_prop_identifier):
|
2009-12-26 09:36:50 +00:00
|
|
|
continue
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2010-04-09 20:04:37 +00:00
|
|
|
rna_prop_ptr = getattr(rna_prop, "fixed_type", None)
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
# Does this property point to me?
|
2017-09-20 16:35:23 +02:00
|
|
|
if rna_prop_ptr and rna_prop_ptr.identifier in rna_references_dict:
|
2016-07-29 21:22:27 +10:00
|
|
|
rna_references_dict[rna_prop_ptr.identifier].append(
|
2024-04-27 16:06:51 +10:00
|
|
|
"{:s}.{:s}".format(rna_struct_path, rna_func.identifier))
|
2009-12-19 13:48:50 +00:00
|
|
|
|
|
|
|
|
# Store nested children
|
|
|
|
|
nested = rna_struct.nested
|
|
|
|
|
if nested:
|
|
|
|
|
rna_children_dict[nested.identifier].append(rna_struct)
|
|
|
|
|
|
|
|
|
|
# Sort the refs, just reads nicer
|
|
|
|
|
for rna_refs in rna_references_dict.values():
|
|
|
|
|
rna_refs.sort()
|
|
|
|
|
|
|
|
|
|
info_structs = []
|
|
|
|
|
for (rna_base, identifier, rna_struct) in structs:
|
2016-07-29 21:22:27 +10:00
|
|
|
# if rna_struct.nested:
|
|
|
|
|
# continue
|
2009-12-19 13:48:50 +00:00
|
|
|
|
2023-03-01 22:12:18 +11:00
|
|
|
# write_struct(rna_struct, '')
|
2009-12-26 09:36:50 +00:00
|
|
|
info_struct = GetInfoStructRNA(rna_struct)
|
2009-12-19 13:48:50 +00:00
|
|
|
if rna_base:
|
|
|
|
|
info_struct.base = GetInfoStructRNA(rna_struct_dict[rna_base])
|
|
|
|
|
info_struct.nested = GetInfoStructRNA(rna_struct.nested)
|
|
|
|
|
info_struct.children[:] = rna_children_dict[identifier]
|
|
|
|
|
info_struct.references[:] = rna_references_dict[identifier]
|
|
|
|
|
info_struct.full_path = rna_full_path_dict[identifier]
|
|
|
|
|
|
|
|
|
|
info_structs.append(info_struct)
|
|
|
|
|
|
|
|
|
|
for rna_info_prop in InfoPropertyRNA.global_lookup.values():
|
|
|
|
|
rna_info_prop.build()
|
|
|
|
|
|
|
|
|
|
for rna_info_prop in InfoFunctionRNA.global_lookup.values():
|
|
|
|
|
rna_info_prop.build()
|
|
|
|
|
|
2017-09-20 16:35:23 +02:00
|
|
|
done_keys = set()
|
|
|
|
|
new_keys = set(InfoStructRNA.global_lookup.keys())
|
|
|
|
|
while new_keys:
|
|
|
|
|
for rna_key in new_keys:
|
|
|
|
|
rna_info = InfoStructRNA.global_lookup[rna_key]
|
|
|
|
|
rna_info.build()
|
|
|
|
|
for prop in rna_info.properties:
|
2010-01-02 18:55:07 +00:00
|
|
|
prop.build()
|
2017-09-20 16:35:23 +02:00
|
|
|
for func in rna_info.functions:
|
|
|
|
|
func.build()
|
|
|
|
|
for prop in func.args:
|
|
|
|
|
prop.build()
|
|
|
|
|
for prop in func.return_values:
|
|
|
|
|
prop.build()
|
|
|
|
|
done_keys |= new_keys
|
|
|
|
|
new_keys = set(InfoStructRNA.global_lookup.keys()) - done_keys
|
2009-12-26 09:36:50 +00:00
|
|
|
|
2013-09-30 05:50:41 +00:00
|
|
|
# there are too many invalid defaults, unless we intend to fix, leave this off
|
|
|
|
|
if 0:
|
2010-05-04 06:08:00 +00:00
|
|
|
for rna_info in InfoStructRNA.global_lookup.values():
|
|
|
|
|
for prop in rna_info.properties:
|
|
|
|
|
# ERROR CHECK
|
|
|
|
|
default = prop.default
|
2011-08-08 05:21:37 +00:00
|
|
|
if type(default) in {float, int}:
|
2010-05-04 06:08:00 +00:00
|
|
|
if default < prop.min or default > prop.max:
|
2024-04-27 16:06:51 +10:00
|
|
|
print("\t {:s}.{:s}, {:s} not in [{:s} - {:s}]".format(
|
|
|
|
|
rna_info.identifier, prop.identifier, default, prop.min, prop.max,
|
|
|
|
|
))
|
2010-05-04 06:08:00 +00:00
|
|
|
|
2009-12-25 14:42:00 +00:00
|
|
|
# now for operators
|
|
|
|
|
op_mods = dir(bpy.ops)
|
|
|
|
|
|
|
|
|
|
for op_mod_name in sorted(op_mods):
|
2010-08-19 14:43:52 +00:00
|
|
|
if op_mod_name.startswith('__'):
|
2009-12-25 14:42:00 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
op_mod = getattr(bpy.ops, op_mod_name)
|
|
|
|
|
operators = dir(op_mod)
|
|
|
|
|
for op in sorted(operators):
|
|
|
|
|
try:
|
2018-09-13 18:16:06 +10:00
|
|
|
rna_prop = getattr(op_mod, op).get_rna_type()
|
2009-12-25 14:42:00 +00:00
|
|
|
except AttributeError:
|
|
|
|
|
rna_prop = None
|
|
|
|
|
except TypeError:
|
|
|
|
|
rna_prop = None
|
|
|
|
|
|
|
|
|
|
if rna_prop:
|
2018-09-13 18:16:06 +10:00
|
|
|
GetInfoOperatorRNA(rna_prop)
|
2009-12-25 14:42:00 +00:00
|
|
|
|
|
|
|
|
for rna_info in InfoOperatorRNA.global_lookup.values():
|
|
|
|
|
rna_info.build()
|
|
|
|
|
for rna_prop in rna_info.args:
|
|
|
|
|
rna_prop.build()
|
|
|
|
|
|
2016-07-29 21:22:27 +10:00
|
|
|
# for rna_info in InfoStructRNA.global_lookup.values():
|
|
|
|
|
# print(rna_info)
|
2019-01-30 09:03:37 +11:00
|
|
|
return (
|
|
|
|
|
InfoStructRNA.global_lookup,
|
|
|
|
|
InfoFunctionRNA.global_lookup,
|
|
|
|
|
InfoOperatorRNA.global_lookup,
|
|
|
|
|
InfoPropertyRNA.global_lookup,
|
|
|
|
|
)
|
2010-06-15 17:14:21 +00:00
|
|
|
|
|
|
|
|
|
2015-06-07 13:54:13 +10:00
|
|
|
def main():
|
2019-03-15 19:45:21 +11:00
|
|
|
struct = BuildRNAInfo()[0]
|
2010-08-10 06:27:29 +00:00
|
|
|
data = []
|
2018-11-26 09:27:25 +11:00
|
|
|
for _struct_id, v in sorted(struct.items()):
|
2016-07-29 21:22:27 +10:00
|
|
|
struct_id_str = v.identifier # "".join(sid for sid in struct_id if struct_id)
|
2010-08-10 06:27:29 +00:00
|
|
|
|
|
|
|
|
for base in v.get_bases():
|
|
|
|
|
struct_id_str = base.identifier + "|" + struct_id_str
|
2010-09-07 15:17:42 +00:00
|
|
|
|
2010-08-10 06:27:29 +00:00
|
|
|
props = [(prop.identifier, prop) for prop in v.properties]
|
2018-11-26 09:27:25 +11:00
|
|
|
for _prop_id, prop in sorted(props):
|
2012-06-19 22:17:19 +00:00
|
|
|
# if prop.type == "boolean":
|
2010-07-15 11:51:43 +00:00
|
|
|
# continue
|
2010-08-17 02:42:30 +00:00
|
|
|
prop_type = prop.type
|
|
|
|
|
if prop.array_length > 0:
|
2024-04-27 16:06:51 +10:00
|
|
|
prop_type += "[{:d}]".format(prop.array_length)
|
2010-08-17 02:42:30 +00:00
|
|
|
|
2016-07-29 21:22:27 +10:00
|
|
|
data.append(
|
2024-04-27 16:06:51 +10:00
|
|
|
"{:s}.{:s} -> {:s}: {:s}{:s} {:s}".format(
|
|
|
|
|
struct_id_str,
|
|
|
|
|
prop.identifier,
|
|
|
|
|
prop.identifier,
|
|
|
|
|
prop_type,
|
|
|
|
|
", (read-only)" if prop.is_readonly else "", prop.description,
|
|
|
|
|
))
|
2010-08-10 06:27:29 +00:00
|
|
|
data.sort()
|
2010-06-15 17:14:21 +00:00
|
|
|
|
2010-06-19 12:06:34 +00:00
|
|
|
if bpy.app.background:
|
2010-08-10 06:27:29 +00:00
|
|
|
import sys
|
|
|
|
|
sys.stderr.write("\n".join(data))
|
2010-08-19 14:43:52 +00:00
|
|
|
sys.stderr.write("\n\nEOF\n")
|
2010-06-19 12:06:34 +00:00
|
|
|
else:
|
|
|
|
|
text = bpy.data.texts.new(name="api.py")
|
2019-11-16 02:06:20 +11:00
|
|
|
text.from_string("\n".join(data))
|
2015-06-07 13:54:13 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|