How to put together a driver with python I'm trying to write a script to create the beginnings of a music visualizer but I'm having a hard time creating drivers to do it. My basic game plan is this:


Calculate frequencies and step sizes
For each frequency step, create a cube.
Create an empty to go along with it.
Do some transformations on the cube.
Add a driver to the cube.
Link the cube's Z scale with the empty's scale using drivers.
Create a material that also uses drivers to hook into the empty's scale and use that to animate stuff.
Bake sound to empty's scale.
Rinse and repeat until all cubes/empties/materials are created.


The reason I want to use drivers is that my previous approach was to bake the sound to the cube's Z scale then I had another script that would read that information for each frame and then keyframe the cube's material. This led to pretty big file sizes. Larger than it needed to be, anyway. So this time around I figured I'd get around that by using drivers. I've been playing around in the console trying to figure it out and this is what I've got so far:

bpy.ops.mesh.primitive_cube_add(location=(.5, -.5, 0)) # Create a cube
currentcube = bpy.context.active_object # Make sure we can keep track of this cube while we're working on it.
currentcube.name="cube.001"
bpy.ops.object.empty_add(location=(.5, -.5, -1)) # Add our control-freak empty.
currentempty=bpy.context.active_object # Make sure we can keep track of this empty while we're working on it.
currentempty.name="empty.001"
currentcube.dimensions = (1, 1, 5) # Do some transformations.
bpy.ops.object.transform_apply(scale=True) # Apply the transformations.
fcurve = currentcube.driver_add("scale", 2) # Add a driver to the scaling on the Z axis.
fcurve.driver.type="AVERAGE" # Default is SCRIPTED for some reason.
drivervar = fcurve.driver.variables.new() # Create a new variable on the driver.


And it's at this point that I'm stuck. dir() and autocomplete got me this far but now it's failed me and I'm having a hard time working my way through the docs.
I know drivervar is a DriverVariable. From the docs it looks like I need to add a new DriverTarget into targets, but it's read-only.

But that's okay because while organizing my thoughts and the random code bits I had been trying in the console I found out that there's already a blank target by default at drivervar.targets[0] and it's not just an empty array like I had assumed. I would like to thank everyone here for the wonderful help and expertise I'm sure you would have provided if I hadn't discovered this on my own.
This is a general function I use in many of my scripts for adding drivers. You're welcome to implement it in your own script. It's pretty general and supports inverse relationships between properties (if one property goes up, the driven property goes down by the same amount), and also adding functions to the scripted expression (such as sin, cos, max, round, radians, etc).

def add_driver(
        source, target, prop, dataPath,
        index = -1, negative = False, func = ''
    ):
    ''' Add driver to source prop (at index), driven by target dataPath '''

    if index != -1:
        d = source.driver_add( prop, index ).driver
    else:
        d = source.driver_add( prop ).driver

    v = d.variables.new()
    v.name                 = prop
    v.targets[0].id        = target
    v.targets[0].data_path = dataPath

    d.expression = func + "(" + v.name + ")" if func else v.name
    d.expression = d.expression if not negative else "-1 * " + d.expression


You can then use it to link you empty and cube's Z scale this way for instance:

cube  = bpy.context.scene.objects['Cube']
empty = bpy.context.scene.objects['Empty']
add_driver( cube, empty, 'scale', 'scale.z', 2 )


I do have to say, though, that if you want to link the empty and the cube's scale, all you need to do is parent the cube to the empty, no driver is required.

cube.parent = empty