Variable amount of properties determined at runtime

Hello,

I’m programming an addon for Blender 3.2 in python.
With my addon I can load a file which creates an object. This object has a variable amount of materials which I only know at runtime.

I now want to include the feature to change the material color in my addon. I would like to implement something like this (this picture is edited, that’s not my actual state):

material_question

The main problem is that I only know at runtime how many materials there are which means that I only know at runtime how many properties I need.

Is it even possible to define a variable amount of properties?

And is there a smart way to update the material colors? I know that you can define an update function for a property as described here. But as I have a variable amount of properties I’d need to have a variable amount of update functions which is not possible as far as I know.

I’d be very thankful if anyone has an idea and could help me out with my problem! :slight_smile:

You need a CollectionProperty:

2 Likes

I agree you should explore bpy.props.CollectionProperty.

Re the update part, you can define variable update functions at runtime although it’s a bit complicated (example), but you can also add extra parameters to let the update method know which property called it and use only one update function, eg

import bpy


def my_update(self, context, prop_name=None):
    print(f"{prop_name=}")
    if prop_name == "prop1":
        foo()
    elif prop_name == "prop2":
        bar()
    else:
        foobar()


class MyPropertyGroup(bpy.types.PropertyGroup):
    my_prop1: bpy.props.FloatProperty(update=lambda self, context: my_update(self, context, "prop1"))
    my_prop2: bpy.props.FloatProperty(update=lambda self, context: my_update(self, context, "prop2"))
    my_prop3: bpy.props.FloatProperty(update=my_update)
1 Like

Perhaps I’m misunderstanding but it sounds as though

the object already has materials applied and you just don’t know how many prior to loading a specific object.

You shouldn’t need additional properties for this. Granted the below is specifically looking for a principled bsdf shader and adjusting the default base color; but the concept of accessing the active objects material properties remains the same.

object material colors

import bpy


class TESTADDON_PT_Main(bpy.types.Panel):
    bl_idname = "TESTADDON_PT_Main"
    bl_label = "Test Addon"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Test"

    def draw(self, context):
        layout = self.layout
        col = layout.column(align=True)
        if context.object and context.object.material_slots:
            obj = context.object
            obj_mats = [m.material for m in obj.material_slots]
            for mat in obj_mats:
                if mat and mat.use_nodes:
                    bsdf = mat.node_tree.nodes.get("Principled BSDF")
                    row = col.row(align=True)
                    row.prop(bsdf.inputs['Base Color'],
                        "default_value",
                        text=mat.name)


def register():
    bpy.utils.register_class(TESTADDON_PT_Main)


def unregister():
    bpy.utils.unregister_class(TESTADDON_PT_Main)

if __name__ == "__main__":
    register()

3 Likes

I got it to work by doing that!
I didn’t know you could also insert existing panels like the base color panel of a bsdf shader directly into an addon. But that’s a lot smarter! So thank you a lot for your answer! :slight_smile:

Yup most properties can be displayed if you have a handle on the holder and its attribute name with a simple layout.prop(obj, "prop_name") no need to reinvent the wheel :slight_smile:

You also have a bunch of templates to display more complicated properties like color ramp nodes. I suggest you read this page for more info https://docs.blender.org/api/current/bpy.types.UILayout.html

2 Likes