8
$\begingroup$

How do you generate Blender Properties dynamically, and assign them on the fly?

Generating properties dynamically is inherently useful when parsing semi-structured data from an external source such as in an XML or JSON document, but there's isn't really a straightforward way to accomplish this.

$\endgroup$

1 Answer 1

7
$\begingroup$

see related here: https://blenderartists.org/forum/archive/index.php/t-266184.html

To solve this we're going to make use of blender's bpy.utils.register_class() method and the type() method. First, we need a method to generate our dynamic properties. In this case, you can use a decorator function to make things a bit easier:

def dynamic(func):
    """
    @:keyword: dynamic - Function Generator that creates properties for Blender data types on the fly
    :param func: Function to generate
    :return: dynamic_property(): returns decorated function
    """
    def dynamic_property(*args, **kwargs):
        """

        :param args: first argument is always *prop_dict (from func in outer scope)
        :parameter *prop_dict: One Blender Property Mapped to a dict: e.g. prop_dict = {'mapname': bpy.props.StringProperty(default="some_path_to_map")}
        :param kwargs: specific keywords needed by enclosed methods
        :return: Pointer to newly registered/assigned property
        """
        Prop = type(str("Parameters"), (bpy.types.PropertyGroup,), func(*args))
        bpy.utils.register_class(Prop)
        PropPointer = bpy.props.PointerProperty(name=func(kwargs.get('param_type')), type=Prop)
        setattr(func(kwargs.get('blender_type')), func(kwargs.get('param_type')), PropPointer)
        return PropPointer
    return dynamic_property

This decorated function generates a function that returns a pointer to a new class instance called Prop, which subclasses bpy.types.PropertyGroup. func(*args) is always a 1 item dictionary. (Further details on the dict contents below)

I also found it useful to create a class to hold the variables I need to pass to the decorated function. (To extend functionality, just create a subclass and override the blender_type variable with whatever type you need + any other variables needed for your implementation)

from bpy.types import Material

    class BlenderProperty(object):
    def __init__(self, **kwargs):
        self.name = kwargs.setdefault("name", None)
        self.prop_dict = {}
        self.blender_type = Material
        self.param_type = "TestParameters"
        self._prop = None

    @property
    def prop(self):
        prop_ptr = self.create_property(self.prop_dict, blender_type=self.blender_type, param_type= self.param_type)
        self._prop = prop_ptr
        return self._prop

    @staticmethod
    @dynamic
    def create_property(prop_dict, **kwargs):
        return prop_dict

For testing purposes, let's use a fake dictionary and fill it with some stuff. Then assign this dictionary to a BlenderProperty.prop_dict

fake_dict = {}
prop_list = ['Prop1', 'prop2', 'prop3']
for prop in prop_list:
    fake_dict[prop] = {'mapname': bpy.props.StringProperty(default="some_path_to_map")}
    new_blender_prop = BlenderProperty(name=str(prop))
    new_blender_prop.prop_dict = fake_dict
print(new_blender_prop.prop)

Calling new_blender_prop.prop calls the decorated getter function for this class's private variable prop, which just so happens to be a pointer to the dynamically created property inside of blender.

$\endgroup$
1
  • 2
    $\begingroup$ Im kinda confused, I understand how this works.. but could you explain how I could add a property to the bpy.types.Object class ? $\endgroup$
    – Skylumz
    Jun 8, 2018 at 7:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .