Added prop_factory module to create dynamic Blender property groups.
This commit is contained in:
parent
5028f84e28
commit
ad97d29f51
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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 <project>.yaml file?")
|
||||
pass
|
||||
# print("\n(899) filename = ", self.filename)
|
||||
# if 'project_schema' in self.provided_data:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue