# CollectionProperty with inherited types

Is it possible to store different types (with a shared base class) in a CollectionProperty?

I want each object to have a set of "Components". Each of these components will have its own PropertyGroup that contains various different settings.

e.g. something like the following:

class Property_Component(bpy.types.PropertyGroup):
type = bpy.props.EnumProperty(items=component_types_array)

class Property_ComponentList(bpy.types.PropertyGroup):
components = bpy.props.CollectionProperty(type=Property_Component)

class Property_Component_Water(Property_Component):
wave_height = bpy.props.StringProperty(...)
color = bpy.props.FloatProperty(...)

class Property_Component_Terrain(Property_Component):
max_height = bpy.props.FloatProperty(...)

# other component types...


However, I'm having trouble actually creating the different types. I know what type to add to the collection, but calling component_list.components.add() just creates an object of type Property_Component, instead of the derived type, and I don't seem to be able to assign an object of the correct type afterwards.

Is there a way to do this? or some way to work around this?

Something magical with PointerProperty's? Or maybe just give each object an instance of every component (ick), with a boolean to show if it's actually used or not?

• Related Feb 13 '18 at 14:02

This is not possible in Blender. Blender's API is build on defining exactly what type is in a collection. It is not possible to add other types (even if they are subtypes) to that collection.

The reason for this that blender does not store the type of the item with the item. It always uses the class that has been given by the type parameter of the CollectionProperty as the class. If your addon will rename your class Blender will still be able to fill the list as it is defined by the CollectionProperty.

Best way to solve this is to do the reverse and add use a 'type' enum to your PropertyComponent. I assume that your type was already created for this.

class Property_Component_Water(object):
wave_height = bpy.props.StringProperty(...)
color = bpy.props.FloatProperty(...)

class Property_Component_Terrain(object):
max_height = bpy.props.FloatProperty(...)

class Property_Component(bpy.types.PropertyGroup, Property_Component_Water, Property_Component_Terrain):
type = bpy.props.EnumProperty(items=component_types_array)

class Property_ComponentList(bpy.types.PropertyGroup):
components = bpy.props.CollectionProperty(type=Property_Component)


Note: You need to be aware of not redefining attributes with the same name multiple times.

I tried using a PointerProperty to allow storing different types in the collection, but that doesn't work either:

class Property_ComponentNull(bpy.types.PropertyGroup):
pass

class Property_ComponentWrapper(bpy.types.PropertyGroup):
component = bpy.props.PointerProperty(type=Property_ComponentNull)

class Property_ComponentList(bpy.types.PropertyGroup):
component_list = bpy.props.CollectionProperty(type=Property_ComponentWrapper)

...

wrapper.component = bpy.props.PointerProperty(type=get_type_from_id(self.type)) # ERROR! wrapper.component is read_only


Apparently the pointer property in the wrapper is readonly for some reason. TT

So for now I'm assuming this isn't possible, and just adding an instance of every type to the component list, with a boolean to indicate whether it's actually enabled or not.

As a separate issue, it looks like you have to use mixin classes for property group inheritance, something like this:

class Property_Component: # does not derive from bpy.types.PropertyGroup
type = bpy.props.EnumProperty(...)

class Property_Component_Water(bpy.types.PropertyGroup, Property_Component):
...


Doing it the way I originally tried resulted in the base Property_Component properties being inaccessible to layout.prop().