update rna_info and rna_rna for better introspection

This commit is contained in:
Campbell Barton 2009-12-25 14:42:00 +00:00
parent 4f3c477a85
commit 4c5a314fef
4 changed files with 215 additions and 29 deletions

@ -42,25 +42,27 @@ class Object(bpy_types.ID):
@property @property
def children(self): def children(self):
"""All the children of this object"""
import bpy import bpy
return [child for child in bpy.data.objects if child.parent == self] return [child for child in bpy.data.objects if child.parent == self]
class _GenericBone: class _GenericBone:
''' """
functions for bones, common between Armature/Pose/Edit bones. functions for bones, common between Armature/Pose/Edit bones.
internal subclassing use only. internal subclassing use only.
''' """
__slots__ = () __slots__ = ()
def translate(self, vec): def translate(self, vec):
"""Utility function to add *vec* to the head and tail of this bone."""
self.head += vec self.head += vec
self.tail += vec self.tail += vec
def parent_index(self, parent_test): def parent_index(self, parent_test):
''' """
The same as 'bone in other_bone.parent_recursive' but saved generating a list. The same as 'bone in other_bone.parent_recursive' but saved generating a list.
''' """
# use the name so different types can be tested. # use the name so different types can be tested.
name = parent_test.name name = parent_test.name
@ -76,11 +78,13 @@ class _GenericBone:
@property @property
def basename(self): def basename(self):
"""The name of this bone before any '.' character"""
#return self.name.rsplit(".", 1)[0] #return self.name.rsplit(".", 1)[0]
return self.name.split(".")[0] return self.name.split(".")[0]
@property @property
def parent_recursive(self): def parent_recursive(self):
"""A list of parents, starting with the immediate parent"""
parent_list = [] parent_list = []
parent = self.parent parent = self.parent
@ -94,23 +98,26 @@ class _GenericBone:
@property @property
def length(self): def length(self):
"""The distance from head to tail, when set the head is moved to fit the length."""
return self.vector.length return self.vector.length
@length.setter @length.setter
def length(self, value): def length(self, value):
"""The distance from head to tail"""
self.tail = self.head + ((self.tail - self.head).normalize() * value) self.tail = self.head + ((self.tail - self.head).normalize() * value)
@property @property
def vector(self): def vector(self):
"""The direction this bone is pointing. Utility function for (tail - head)"""
return (self.tail - self.head) return (self.tail - self.head)
@property @property
def children(self): def children(self):
"""A list of all the bones children."""
return [child for child in self._other_bones if child.parent == self] return [child for child in self._other_bones if child.parent == self]
@property @property
def children_recursive(self): def children_recursive(self):
"""a list of all children from this bone."""
bones_children = [] bones_children = []
for bone in self._other_bones: for bone in self._other_bones:
index = bone.parent_index(self) index = bone.parent_index(self)
@ -123,10 +130,11 @@ class _GenericBone:
@property @property
def children_recursive_basename(self): def children_recursive_basename(self):
''' """
Returns a chain of children with the same base name as this bone Returns a chain of children with the same base name as this bone
Only direct chains are supported, forks caused by multiple children with matching basenames will. Only direct chains are supported, forks caused by multiple children with matching basenames will
''' terminate the function and not be returned.
"""
basename = self.basename basename = self.basename
chain = [] chain = []
@ -177,10 +185,10 @@ class EditBone(StructRNA, _GenericBone):
__slots__ = () __slots__ = ()
def align_orientation(self, other): def align_orientation(self, other):
''' """
Align this bone to another by moving its tail and settings its roll Align this bone to another by moving its tail and settings its roll
the length of the other bone is not used. the length of the other bone is not used.
''' """
vec = other.vector.normalize() * self.length vec = other.vector.normalize() * self.length
self.tail = self.head + vec self.tail = self.head + vec
self.roll = other.roll self.roll = other.roll
@ -196,10 +204,10 @@ class Mesh(bpy_types.ID):
__slots__ = () __slots__ = ()
def from_pydata(self, verts, edges, faces): def from_pydata(self, verts, edges, faces):
''' """
Make a mesh from a list of verts/edges/faces Make a mesh from a list of verts/edges/faces
Until we have a nicer way to make geometry, use this. Until we have a nicer way to make geometry, use this.
''' """
self.add_geometry(len(verts), len(edges), len(faces)) self.add_geometry(len(verts), len(edges), len(faces))
verts_flat = [f for v in verts for f in v] verts_flat = [f for v in verts for f in v]
@ -244,7 +252,7 @@ class Mesh(bpy_types.ID):
return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges] return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges]
def edge_loops(self, faces=None, seams=()): def edge_loops(self, faces=None, seams=()):
''' """
Edge loops defined by faces Edge loops defined by faces
Takes me.faces or a list of faces and returns the edge loops Takes me.faces or a list of faces and returns the edge loops
@ -255,7 +263,7 @@ class Mesh(bpy_types.ID):
[ [(0,1), (4, 8), (3,8)], ...] [ [(0,1), (4, 8), (3,8)], ...]
optionaly, seams are edge keys that will be removed optionaly, seams are edge keys that will be removed
''' """
OTHER_INDEX = 2,3,0,1 # opposite face index OTHER_INDEX = 2,3,0,1 # opposite face index
@ -396,9 +404,9 @@ class Menu(StructRNA):
layout.operator(operator, text=bpy.utils.display_name(f)).path = path layout.operator(operator, text=bpy.utils.display_name(f)).path = path
def draw_preset(self, context): def draw_preset(self, context):
'''Define these on the subclass """Define these on the subclass
- preset_operator - preset_operator
- preset_subdir - preset_subdir
''' """
import bpy import bpy
self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator) self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)

@ -20,6 +20,9 @@
import bpy import bpy
# use to strip python paths
script_paths = bpy.utils.script_paths()
def range_str(val): def range_str(val):
if val < -10000000: return '-inf' if val < -10000000: return '-inf'
if val > 10000000: return 'inf' if val > 10000000: return 'inf'
@ -28,6 +31,12 @@ def range_str(val):
else: else:
return str(val) return str(val)
def float_as_string(f):
val_str = "%g" % f
if '.' not in val_str and '-' not in val_str: # value could be 1e-05
val_str += '.0'
return val_str
class InfoStructRNA: class InfoStructRNA:
global_lookup = {} global_lookup = {}
def __init__(self, rna_type): def __init__(self, rna_type):
@ -73,6 +82,31 @@ class InfoStructRNA:
return ls return ls
def _get_py_visible_attrs(self):
attrs = []
py_class = getattr(bpy.types, self.identifier)
for attr_str in dir(py_class):
if attr_str.startswith("_"):
continue
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
def get_py_functions(self):
import types
functions = []
for identifier, attr in self._get_py_visible_attrs():
if type(attr) is types.FunctionType:
functions.append((identifier, attr))
return functions
def __repr__(self): def __repr__(self):
txt = '' txt = ''
@ -106,6 +140,10 @@ class InfoPropertyRNA:
self.min = getattr(rna_prop, "hard_min", -1) self.min = getattr(rna_prop, "hard_min", -1)
self.max = getattr(rna_prop, "hard_max", -1) self.max = getattr(rna_prop, "hard_max", -1)
self.array_length = getattr(rna_prop, "array_length", 0) self.array_length = getattr(rna_prop, "array_length", 0)
self.collection_type = GetInfoStructRNA(rna_prop.srna)
self.is_required = rna_prop.is_required
self.is_readonly = rna_prop.is_readonly
self.is_never_none = rna_prop.is_never_none
self.type = rna_prop.type.lower() self.type = rna_prop.type.lower()
fixed_type = getattr(rna_prop, "fixed_type", "") fixed_type = getattr(rna_prop, "fixed_type", "")
@ -118,12 +156,43 @@ class InfoPropertyRNA:
self.enum_items[:] = rna_prop.items.keys() self.enum_items[:] = rna_prop.items.keys()
if self.array_length: if self.array_length:
self.default_str = str(getattr(rna_prop, "default_array", "")) self.default = tuple(getattr(rna_prop, "default_array", ()))
self.default_str = ''
# special case for floats
if len(self.default) > 0:
if type(self.default[0]) is float:
self.default_str = "(%s)" % ", ".join([float_as_string(f) for f in self.default])
if not self.default_str:
self.default_str = str(self.default)
else: else:
self.default_str = str(getattr(rna_prop, "default", "")) self.default = getattr(rna_prop, "default", "")
if type(self.default) is float:
self.default_str = float_as_string(self.default)
else:
self.default_str = str(self.default)
self.srna = GetInfoStructRNA(rna_prop.srna) # valid for pointer/collections self.srna = GetInfoStructRNA(rna_prop.srna) # valid for pointer/collections
def get_default_string(self):
# pointer has no default, just set as None
if self.type == "pointer":
return "None"
elif self.type == "string":
return '"' + self.default_str + '"'
elif self.type == "enum":
if self.default_str:
return "'" + self.default_str + "'"
else:
return ""
return self.default_str
def get_arg_default(self, force=True):
default = self.get_default_string()
if default and (force or self.is_required == False):
return "%s=%s" % (self.identifier, default)
return self.identifier
def __repr__(self): def __repr__(self):
txt = '' txt = ''
txt += ' * ' + self.identifier + ': ' + self.description txt += ' * ' + self.identifier + ': ' + self.description
@ -162,6 +231,65 @@ class InfoFunctionRNA:
return txt return txt
class InfoOperatorRNA:
global_lookup = {}
def __init__(self, rna_op):
self.bl_op = rna_op
self.identifier = rna_op.identifier
mod, name = self.identifier.split("_OT_", 1)
self.module_name = mod.lower()
self.func_name = name
# 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
prop = GetInfoPropertyRNA(rna_prop, parent_id)
self.args.append(prop)
def get_location(self):
op_class = getattr(bpy.types, self.identifier)
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]
if source_path[0] in "/\\":
source_path= source_path[1:]
return source_path, op_code.co_firstlineno
else:
return None, None
'''
def __repr__(self):
txt = ''
txt += ' * ' + self.identifier + '('
for arg in self.args:
txt += arg.identifier + ', '
txt += '): ' + self.description
return txt
'''
def _GetInfoRNA(bl_rna, cls, parent_id=''): def _GetInfoRNA(bl_rna, cls, parent_id=''):
if bl_rna == None: if bl_rna == None:
@ -184,6 +312,8 @@ def GetInfoPropertyRNA(bl_rna, parent_id):
def GetInfoFunctionRNA(bl_rna, parent_id): def GetInfoFunctionRNA(bl_rna, parent_id):
return _GetInfoRNA(bl_rna, InfoFunctionRNA, parent_id) return _GetInfoRNA(bl_rna, InfoFunctionRNA, parent_id)
def GetInfoOperatorRNA(bl_rna):
return _GetInfoRNA(bl_rna, InfoOperatorRNA)
def BuildRNAInfo(): def BuildRNAInfo():
# Use for faster lookups # Use for faster lookups
@ -205,7 +335,8 @@ def BuildRNAInfo():
return True return True
if "_PT_" in rna_id: if "_PT_" in rna_id:
return True return True
if "_HT_" in rna_id:
return True
return False return False
def full_rna_struct_path(rna_struct): def full_rna_struct_path(rna_struct):
@ -395,9 +526,34 @@ def BuildRNAInfo():
if func.return_value: if func.return_value:
func.return_value.build() func.return_value.build()
# now for operators
op_mods = dir(bpy.ops)
for op_mod_name in sorted(op_mods):
if op_mod_name.startswith('__') or op_mod_name in ("add", "remove"):
continue
op_mod = getattr(bpy.ops, op_mod_name)
operators = dir(op_mod)
for op in sorted(operators):
try:
rna_prop = getattr(op_mod, op).get_rna()
except AttributeError:
rna_prop = None
except TypeError:
rna_prop = None
if rna_prop:
GetInfoOperatorRNA(rna_prop.bl_rna)
for rna_info in InfoOperatorRNA.global_lookup.values():
rna_info.build()
for rna_prop in rna_info.args:
rna_prop.build()
#for rna_info in InfoStructRNA.global_lookup.values(): #for rna_info in InfoStructRNA.global_lookup.values():
# print(rna_info) # print(rna_info)
return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoPropertyRNA.global_lookup return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup

@ -425,7 +425,7 @@ static int rna_Property_unit_get(PointerRNA *ptr)
return RNA_SUBTYPE_UNIT(prop->subtype); return RNA_SUBTYPE_UNIT(prop->subtype);
} }
static int rna_Property_editable_get(PointerRNA *ptr) static int rna_Property_readonly_get(PointerRNA *ptr)
{ {
PropertyRNA *prop= (PropertyRNA*)ptr->data; PropertyRNA *prop= (PropertyRNA*)ptr->data;
@ -433,7 +433,7 @@ static int rna_Property_editable_get(PointerRNA *ptr)
* data for introspection we only need to know if it can be edited so the * data for introspection we only need to know if it can be edited so the
* flag is better for this */ * flag is better for this */
// return RNA_property_editable(ptr, prop); // return RNA_property_editable(ptr, prop);
return prop->flag & PROP_EDITABLE ? 1:0; return prop->flag & PROP_EDITABLE ? 0:1;
} }
static int rna_Property_use_return_get(PointerRNA *ptr) static int rna_Property_use_return_get(PointerRNA *ptr)
@ -442,6 +442,18 @@ static int rna_Property_use_return_get(PointerRNA *ptr)
return prop->flag & PROP_RETURN ? 1:0; return prop->flag & PROP_RETURN ? 1:0;
} }
static int rna_Property_is_required_get(PointerRNA *ptr)
{
PropertyRNA *prop= (PropertyRNA*)ptr->data;
return prop->flag & PROP_REQUIRED ? 1:0;
}
static int rna_Property_is_never_none_get(PointerRNA *ptr)
{
PropertyRNA *prop= (PropertyRNA*)ptr->data;
return prop->flag & PROP_NEVER_NULL ? 1:0;
}
static int rna_Property_array_length_get(PointerRNA *ptr) static int rna_Property_array_length_get(PointerRNA *ptr)
{ {
PropertyRNA *prop= (PropertyRNA*)ptr->data; PropertyRNA *prop= (PropertyRNA*)ptr->data;
@ -934,15 +946,25 @@ static void rna_def_property(BlenderRNA *brna)
RNA_def_property_enum_funcs(prop, "rna_Property_unit_get", NULL, NULL); RNA_def_property_enum_funcs(prop, "rna_Property_unit_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Unit", "Type of units for this property."); RNA_def_property_ui_text(prop, "Unit", "Type of units for this property.");
prop= RNA_def_property(srna, "editable", PROP_BOOLEAN, PROP_NONE); prop= RNA_def_property(srna, "is_readonly", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Property_editable_get", NULL); RNA_def_property_boolean_funcs(prop, "rna_Property_readonly_get", NULL);
RNA_def_property_ui_text(prop, "Editable", "Property is editable through RNA."); RNA_def_property_ui_text(prop, "Read Only", "Property is editable through RNA.");
prop= RNA_def_property(srna, "is_required", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Property_is_required_get", NULL);
RNA_def_property_ui_text(prop, "Required", "False when this property is an optional argument in an rna function.");
prop= RNA_def_property(srna, "is_never_none", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Property_is_never_none_get", NULL);
RNA_def_property_ui_text(prop, "Never None", "True when this value can't be set to None.");
prop= RNA_def_property(srna, "use_return", PROP_BOOLEAN, PROP_NONE); prop= RNA_def_property(srna, "use_return", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Property_use_return_get", NULL); RNA_def_property_boolean_funcs(prop, "rna_Property_use_return_get", NULL);
RNA_def_property_ui_text(prop, "Return", "True when this property is a return value from an rna function.."); RNA_def_property_ui_text(prop, "Return", "True when this property is a return value from an rna function.");
prop= RNA_def_property(srna, "registered", PROP_BOOLEAN, PROP_NONE); prop= RNA_def_property(srna, "registered", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_clear_flag(prop, PROP_EDITABLE);

@ -397,8 +397,8 @@ def rna2epy(BASEPATH):
array_str = get_array_str(length) array_str = get_array_str(length)
if rna_prop.editable: readonly_str = '' if rna_prop.is_readonly: readonly_str = ' (readonly)'
else: readonly_str = ' (readonly)' else: readonly_str = ''
if rna_prop_ptr: # Use the pointer type if rna_prop_ptr: # Use the pointer type
out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc))