0
$\begingroup$

I’m currently developing an addon for Blender. For that I wanted to be able to change material node values in the sidebar. Here is what I did in the draw method:

    if 'Math_a01' in nodes:
        col.prop(nodes['Math_a01'].inputs[1], "default_value", text = 'Strength')    
        
    if 'MixColor_a01' in nodes:
        col.prop(nodes['MixColor_a01'].inputs[7], "default_value", text = 'Color') 
        
    if 'MixColor_a02' in nodes:
        col.prop(nodes['MixColor_a02'].inputs[0], "default_value", text = 'Center Strength')

This works fine.

I now want to add some sort of reset button behind every setting, so that you can reset the value to the original one. I’m appending the material in the beginning, so the default value I want it to be reset to, isn’t necessarily the one you get, when you create such a node. But rather the one I appended the material with. I also want an overall reset button, not just for the single values.

What would be the best way to do that?

Thanks!

$\endgroup$

1 Answer 1

3
$\begingroup$

Custom Properties

bpy.types.NodeSocket supports Custom Properties at least since 2.79.


import bpy

nodes = bpy.data.materials[1].node_tree.nodes

input_socket = nodes[0].inputs[0]

input_socket['my_default_value'] = 42

print(dict(input_socket.items()))
# {'my_default_value': 42}

assert input_socket['my_default_value'] == 42

bpy.props

Also you can use a bpy.props property: https://docs.blender.org/api/current/bpy.props.html which allows more functionality.

The original ones.

the original one [...] the one you get, when you create such a node

This is actually not easy to retrieve.

This does not work: bpy.context.object.active_material.node_tree.nodes['Principled BSDF'].inputs['IOR'].bl_rna.properties['default_value'].default

The problem with the wrong defaults is unsolved https://projects.blender.org/blender/blender/issues/80164.

As a work around you can collect the defaults from newly created nodes. Similar to how Node Wrangler's Reset Nodes works.

Reset Buttons

enter image description here

bl_info = {
    "name": "test_reset_socket_value",
    "author": "unwave",
    "version": (0, 0, 1),
    "blender": (3, 0, 0),
    "location": "",
    "description": "A test addon to pass a shader socket to an operator",
    "warning": "",
    "doc_url": "https://blender.stackexchange.com/a/293700/76897",
    "category": "Generic",
}

import bpy

def get_library_path(library_name: str):

    library = bpy.data.libraries.get(library_name)
    if not library:
        return None

    return library.filepath

class TESTADDON_OT_reset_socket_value(bpy.types.Operator):
    bl_idname = "node.ta_reset_socket_value"
    bl_label = "Reset Value"
    bl_description = "Set the default value to 42"
    bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
    origin_name: bpy.props.StringProperty(name='Name')
    library_name: bpy.props.StringProperty(name='Library')
    property_path: bpy.props.StringProperty(name='Path')
    is_node_group: bpy.props.BoolProperty(name='Is Node Group')

    def draw(self, context):
        layout = self.layout
        if self.is_node_group:
            layout.prop_search(self, 'origin_name', bpy.data,  "node_groups")
        else:
            layout.prop_search(self, 'origin_name', bpy.data,  "materials")
        layout.prop_search(self, 'library_name', bpy.data, "libraries")
        layout.prop(self, 'property_path')
        layout.prop(self, 'is_node_group')

    def execute(self, context):
        
        if self.is_node_group:
            node_tree = bpy.data.node_groups[self.origin_name, get_library_path(self.library_name)]
        else:
            node_tree = bpy.data.materials[self.origin_name, get_library_path(self.library_name)].node_tree
        
        input = node_tree.path_resolve(self.property_path)
        
        default_value = input.default_value
        if isinstance(default_value, float): # int or float
            if input.default_value > 0:
                input.default_value = -42
            else:
                input.default_value = 42
        elif len(input.default_value) == 3:
            input.default_value = (42, 42, 42) # vector
        elif len(input.default_value) == 4:
            input.default_value = (42, 42, 42, 42) # color
        else:
            raise NotImplementedError(f'Not supported value: {default_value} of type {type(default_value)}')
            
        return {'FINISHED'}
        
        
class TESTADDON_PT_reset_socket_value(bpy.types.Panel):
    bl_idname = "TESTADDON_PT_reset_socket_value"
    bl_label = "TEST RESET"
    bl_space_type = 'NODE_EDITOR'
    bl_region_type = "UI"
    bl_category = "TEST"
    
    def draw(self, context):
        
        layout = self.layout
        
        selected_nodes = context.selected_nodes
        if not selected_nodes:
            layout.label(text = "Select a node")
            return
        
        tree = context.space_data.edit_tree
        
        layout.label(text = f"{tree.is_embedded_data=}")
        layout.label(text = f"{tree=}")
        layout.label(text = f"{type(tree)=}")
        layout.label(text = f"{context.space_data.id=}")
        
        origin = context.space_data.id
        if origin.node_tree != context.space_data.edit_tree: # Node Group
            origin = context.space_data.edit_tree
            is_node_group = True
        else: # Material
            is_node_group = False
        
        layout.separator()
        layout.label(text = "INPUTS:")
        
        layout = layout.box()
        
        for node in selected_nodes:
            for input in node.inputs:
                
                if isinstance(input, bpy.types.NodeSocketVirtual):
                    continue
                
                layout.label(text = "Path: " + str(input.path_from_id()))
                layout.label(text = "ID Name: " + origin.name)
                layout.label(text = "Library: " + (origin.library.name if origin.library else ''))
                
                op = layout.operator("node.ta_reset_socket_value", text = input.name + ' | ' + input.identifier)
                op.origin_name = origin.name
                op.library_name = origin.library.name if origin.library else ''
                op.is_node_group = is_node_group
                op.property_path = input.path_from_id()

def register():
    bpy.utils.register_class(TESTADDON_OT_reset_socket_value)
    bpy.utils.register_class(TESTADDON_PT_reset_socket_value)
    
def unregister():
    bpy.utils.unregister_class(TESTADDON_PT_reset_socket_value)
    bpy.utils.unregister_class(TESTADDON_OT_reset_socket_value)

if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

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