# Using Python to add driver to cycles material node properly

I've been working with an audio-driven animation and discovered that you cannot apply a driver directly to an input field on a material node. Rather you need to navigate through the Datablock to the object and drill down until you find the node tree with the target value. Right-Click on the row (not the input field) and hit D to add a driver. The input field does not turn purple but the driver will show up in the Graphs window under drivers.

That method works but is so tedious so why not write a python script right?

My script works, it can find the active material, the node, the value and create a driver and the driver shows up but turns purple so that method is creating the driver on the material.

In the Graph window the proper driver shows up under the Object. The incorrect one shows up under the Material.

Hovering over the input field shows the correct path:

bpy.data.node_groups["Shader Nodetree"].nodes["Value"].outputs[0].default_value


If you drill into the Data-blocks and find the target node, change the value the console outputs:

bpy.data.node_groups["Shader Nodetree"].nodes["Value"].outputs[0].default_value = 1


If you add the Driver by selecting the row and hitting D it outputs:

bpy.ops.outliner.drivers_add_selected()


Which isn't very helpful as that is based on the selected row in the active context. For my tool, I just want to click on the node making it active and obviously this method is parsing the node for the material outputs and binds the driver to the material not the object.

for n in context.active_node.outputs:
if( n.name == "Value" ):
var = newdriver.driver.variables.new()
var.name='audDrv'
var.type='SINGLE_PROP'
var.targets[0].id_type = 'WORLD'
var.targets[0].id = bpy.data.worlds["World"]
var.targets[0].data_path = '["TestCurve"]'
newdriver.driver.expression = var.name


Rather, when I click on the node and apply the driver it needs to reference the object the material is active on. Find the animation data, the driver and bind the node path.

So back the drawing board. I was able to reproduce the Path that Blender shows when you hover over the input field of the targeted node value in the Data-Block tree.

# active_mat_name = bpy.context.object.active_material.name
active_node_tree = bpy.context.object.active_material.node_tree.name
active_node_name = context.active_node.name
print( "Active Material: %s" % bpy.context.object.active_material.name )
print( "Active Node Tree: %s" % active_node_tree )
print( "Active Node: %s" % context.active_node.name )

# This may work using format to produce the datapath string required.
# Ref: https://docs.python.org/3/library/string.html#format-string-syntaxt
dpath = "bpy.context.scene.node_groups[\"{0}\"].nodes[\"{1}\"].outputs[0].default_value".format(active_node_tree,active_node_name)
bpy.context.object.active_material.node_tree.nodes[active_node_name].outputs[0].default_value

print( "Path: %s" % dpath)

# Now for the Active Object
object = bpy.context.active_object
print( "Active Object: %s" % object )

#newdriver = bpy.context.object.active_material.node_tree.nodes[active_node_name].outputs[0].default_value
newdriver.data_path = bpy.context.object.active_material.node_tree.nodes[active_node_name].outputs[0].default_value
# create a variable
var = newdriver.driver.variables.new()
var.name='audDrv'
# set the type first (default is 'SINGLE_PROP')
var.type='SINGLE_PROP' # you can use 'SINGLE_PROP', 'LOC_DIFF' or 'ROTATION_DIFF'
var.targets[0].id_type = 'WORLD'
var.targets[0].id = bpy.data.worlds["World"]
var.targets[0].data_path = '["TargetProp"]'
# now set the expression on the driver
newdriver.driver.expression = var.name


Now the drive appears under the cube but still doesn't work changing the material value. I've tried many methods but the end result are all end up either putting the driver on the material turning the field purple or simply not working at all.

It's very ambiguous as to what specifically the difference is between adding a driver directly to the material node or adding it to the node in the data-block tree again on the row selected specifically and not the value field.

Thought I had it figured out but now I need to turn to hopefully someone here who might have already gone through this process.

• I don't understand. You can add a driver to a material input field. – cmomoney Nov 10 '17 at 22:24
• You can, but it doesn't update from the F-Curve that is being used to drive the animation in 2.79. It has to be bound in the data-block tree and not on the input rather the row the input is on. Do that, it works great. Now how to program for that because navigating the tree to find the node is very tedious. – Ian Wilson Nov 10 '17 at 22:30