# blender_context.py """ Contextual metadata acquired from internal values in a Blender file. This module must be invoked from within Blender to work, as it relies on the bpy Blender API module and the currently-open Blender file's data graph in order to work. It collects data about scenes, objects, groups, and other datablocks in the Blender file, as well as data encoded in text blocks in different formats. Overall file data is incorporated into a PropertyGroup attached to the "WindowManager" object identified as 'WinMan' (normally, it appears there is only ever one of these in a Blender file, but if there is more than one, this is the one that will be used). """ import io import bpy, bpy.app, bpy.props, bpy.utils from bpy.app.handlers import persistent from .accumulate import UnionList, RecursiveDict import yaml def EnumFromList(schema, listname): return [(e, e.capitalize(), e.capitalize()) for e in schema[listname]] class PropertyGroupFactory(bpy.types.PropertyGroup): """ Metadata property group factory for attachment to Blender object types. Definitions come from a YAML source (or default defined below). """ # These values mirror the Blender documentation for the bpy.props types: prop_types = { 'str':{ 'property': bpy.props.StringProperty, 'keywords': { 'name', 'description', 'default', 'maxlen', 'options', 'subtype'}, 'translate': { 'desc': ('description', None)}}, 'enum': { 'property': bpy.props.EnumProperty, 'keywords': { 'items', 'name', 'description', 'default', 'options'}, 'translate': { 'desc': ('description', None), 'items_from': ('items', EnumFromList)}}, 'int': { 'property': bpy.props.IntProperty, 'keywords': { 'name', 'description', 'default', 'min', 'max', 'soft_min', 'soft_max', 'step', 'options', 'subtype'}, 'translate': { 'desc': ('description', None)}}, 'float': { 'property': bpy.props.FloatProperty, 'keywords': { 'name', 'description', 'default', 'min', 'max', 'soft_min', 'soft_max', 'step', 'options', 'subtype', 'precision', 'unit'}, 'translate': { 'desc': ('description', None)}}, 'bool': { 'property': bpy.props.BoolProperty, 'keywords': { 'name', 'description', 'default', 'options', 'subtype'}, 'translate': { 'desc': ('description', None)}} } def __new__(cls, name, schema): class CustomPropertyGroup(bpy.types.PropertyGroup): pass for definition in schema[name]: # Translate and filter parameters try: propmap = cls.prop_types[definition['type']] except KeyError: # If no 'type' specified or 'type' not found, default to string: propmap = cls.prop_types['str'] filtered = {} for param in definition: if 'translate' in propmap and param in propmap['translate']: translator = propmap['translate'][param][1] if callable(translator): # Filtered translation filtered[propmap['translate'][param][0]] = translator(schema, definition[param]) else: # Simple translation filtered[propmap['translate'][param][0]] = definition[param] else: filtered[param] = definition[param] # Create the Blender Property object kwargs = dict((key,filtered[key]) for key in propmap['keywords'] if key in filtered) setattr(CustomPropertyGroup, definition['code'], propmap['property'](**kwargs)) bpy.utils.register_class(CustomPropertyGroup) return(CustomPropertyGroup)