Copy all nodes from one material to another?

I have two materials named Source and Target. I want to delete all the current nodes in the Target material, and then copy all the nodes (and their values) from the Source material. The context is that I have another script that will be assigning parameters to the Source material, and will go in a loop that iterators over a list of target material names.

I see that this can be done in the UI by using the Copy Material and Paste Material options in the properties panel down-arrow menu. This uses bpy.ops.material.copy() and bpy.ops.material.paste(). But I cannot figure out how to target these commands by name in a material script. (And given that they are bpy.ops, I assume these commands are mode/context sensitive or something? I’d like this script to work under any circumstances.)

EDIT: I also see that bpy.ops.node.clipboard_copy() and paste can be used to copy selected nodes and paste to active material, but this still has the bpy.ops problems.

Is there another way to copy the node setup? Or override one material’s node tree data with another? Or can anyone confirm that I have to use the bpy.ops route, and how I would go about targeting it to the correct material? I’ve never used bpy.ops commands before, only read about them.

You can try to make a copy of the Source material, then replace all object slots that have Target material with the Copied material. Then delete Target material and rename Copied material to Target material

import bpy
context = bpy.context

# get materials
source_mat = bpy.data.materials["Source"]
target_mat = bpy.data.materials["Target"]

# make copy
copied_mat = source_mat.copy()

# replace material
for ob in context.scene.objects:
    if ob.type in ('MESH', 'CURVE'):
        if ob.data.materials:
            for slot in ob.material_slots:
                if slot.material.name == "Target":
                    slot.material = copied_mat


# delete initial target material
def purge_materials():
    """Remove materials with 0 users from scene."""
    for me in bpy.data.meshes:
        if not me.users:
            me.materials.clear()

    for material in bpy.data.materials:
        if not material.users:
            bpy.data.materials.remove(material)
            
purge_materials()

# rename copy 
copied_mat.name = "Target"
1 Like