diff --git a/abx/blender_context.py b/abx/blender_context.py deleted file mode 100644 index 1a7d217..0000000 --- a/abx/blender_context.py +++ /dev/null @@ -1,169 +0,0 @@ -# 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]] - -prop_types = { - 'string':{ - '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)}} - } - -class AbxMeta(bpy.types.PropertyGroup): - """ - Metadata property group factory for attachment to Blender object types. - Definitions come from a YAML source (or default defined below). - """ - default_schema = yaml.safe_load(io.StringIO("""\ ---- -blender: - - id: project - type: string - level: project - name: Project Name - desc: Name of the project - maxlen: 32 - - - id: project_title - type: string - level: project - name: Project Title - desc: Full title for the project - maxlen: 64 - - - id: project_description - type: string - level: project - name: Project Description - desc: Brief description of the project - maxlen: 128 - - - id: project_url - type: list string - level: project - name: Project URL - desc: URL for Project home page, or comma-separated list of Project URLs - - - id: level - type: enum - items_from: levels - name: Level - desc: Level of the file in the project hierarchy - -levels: - - project - - series - - episode - - seq - - subseq - - camera - - shot - - element - - frame - -hierarchies: - - library - - episodes - """)) - - def __new__(cls, schema=default_schema): - class CustomPropertyGroup(bpy.types.PropertyGroup): - pass - for definition in schema['blender']: - # Translate and filter parameters - try: - propmap = prop_types[definition['type']] - except KeyError: - # If no 'type' specified or 'type' not found, default to string: - propmap = prop_types['string'] - - filtered = {} - for param in definition: - if 'translate' in propmap and param in propmap['translate']: - filter = propmap['translate'][param][1] - if callable(filter): - # Filtered translation - filtered[propmap['translate'][param][0]] = filter(schema, definition[param]) - else: - # Simple translation - filtered[propmap['translate'][param][0]] = definition[param] - - # Create the Blender Property object - kwargs = dict((key,filtered[key]) for key in propmap['keywords'] if key in filtered) - setattr(CustomPropertyGroup, definition['id'], propmap['property'](**kwargs)) - - bpy.utils.register_class(CustomPropertyGroup) - return(CustomPropertyGroup) - - - -class BlenderContext(RecursiveDict): - """ - Dictionary accumulating data from sources within the currently-open Blender file. - """ - filepath = '' - defaults = {} - - def __init__(self): - self.clear() - - @classmethod - def update(cls): - try: - cls.file_metadata = bpy.data.window_managers['WinMan'].metadata - except AttributeError: - bpy.data.window_managers['WinMan'].new(FileMeta()) - - - def clear(self): - for key in self: - del self[key] - self.update(self.defaults) - - - - - \ No newline at end of file diff --git a/abx/file_context.py b/abx/file_context.py index 4dec9b9..aced7fb 100644 --- a/abx/file_context.py +++ b/abx/file_context.py @@ -1042,7 +1042,7 @@ class FileContext(NameContext): self.namepath_segment = [d['code'] for d in self.provided_data['project_unit']] self.code = self.namepath[-1] except: - print("Errors finding Name Path (is there a 'project_schema' or 'project_unit' defined?") + print("Can't find Name Path. Missing .yaml file?") pass # print("\n(899) filename = ", self.filename) # if 'project_schema' in self.provided_data: diff --git a/abx/prop_factory.py b/abx/prop_factory.py new file mode 100644 index 0000000..01a8eec --- /dev/null +++ b/abx/prop_factory.py @@ -0,0 +1,98 @@ +# 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) + + + + + + \ No newline at end of file diff --git a/tests/__pycache__/test_accumulate.cpython-35.pyc b/tests/__pycache__/test_accumulate.cpython-35.pyc index 0ab3a36..16ba3cc 100644 Binary files a/tests/__pycache__/test_accumulate.cpython-35.pyc and b/tests/__pycache__/test_accumulate.cpython-35.pyc differ diff --git a/tests/__pycache__/test_render_profile.cpython-35.pyc b/tests/__pycache__/test_render_profile.cpython-35.pyc index 723484e..28e84da 100644 Binary files a/tests/__pycache__/test_render_profile.cpython-35.pyc and b/tests/__pycache__/test_render_profile.cpython-35.pyc differ diff --git a/tests/test_render_profile.py b/tests/test_render_profile.py index ee9c794..da9bb28 100644 --- a/tests/test_render_profile.py +++ b/tests/test_render_profile.py @@ -16,11 +16,6 @@ import sys print("__file__ = ", __file__) sys.path.append(os.path.normpath(os.path.join(__file__, '..', '..'))) -TESTDATA = os.path.join(os.path.abspath(__file__), '..', 'testdata') - -TESTPATH = os.path.join(TESTDATA, 'myproject', 'Episodes', 'A.001-Pilot', - 'Seq', 'LP-LastPoint', 'A.001-LP-1-BeginningOfEnd-anim.txt') - import bpy import abx