maybe instead of copying the whole driver at once, I need to copy all the component of the driver, how do I get the variable in the driver and how do I copy the f-curve?
It is possible, I’m sure and I think that I’d be able to do it, but I’m afraid it’s not a one-liner solution.
The operator you mentioned won’t work in this case.
I think it’s necessary to store all properties of the driver, then use them in newly created driver.
I will try to create a code for this, but I need to try something. Give me some time, I’ll try to figure it out.
in speaker_tools I was running an operator from a button by overriding the view docs operator. The copy_driver_button operator may be usable by working out the context from the button and overriding in a call.
Here is some code to copy a driver.
import bpy
def copy_driver(from_fcurve, target_fcurve):
td = target_fcurve.driver
fd = from_fcurve.driver
td.type = fd.type
td.expression = fd.expression
for var in fd.variables:
v = td.variables.new()
v.name = var.name
v.type = var.type
for i,target in var.targets.items():
v.targets[i].id = target.id_data
bpy.app.driver_namespace["copydriver"] = copy_driver
added the method to the driver namespace to test in console
Here I add a driver to location.y of the context.object which is a copy of the first driver fcurve (C.object.drivers[0])
The new driver fcurve is created, then the method is used to copy the attributes of an existing driver.
ok, I’m almost there. I’m able to find all info about the f-curve and store the keyframes info… my only problem now is to create the f-curve (adding keyframe) for the driver.
bpy.data.objects[‘Rene_obj’].animation_data.drivers[0].keyframe_insert(‘location’, index=0, frame=0, group="")
doesn’t work, even for the driver for the X location… can’t find the location
and if I do
bpy.data.objects[‘Rene_obj’].keyframe_insert(‘location’, index=0, frame=0, group="")
it creates a keyframe on the transfrom x loc but not on the driver
so it seems that the data_path for the f-curve drivers are a bit hidden.
I’m confused as to what you are after here. Drivers don’t have keyframes. Some or all of the target variables may be keyframed.
If the fcurve has keyframes it is an action.
Here is the console output for a driver on the location.x fcurve of default Cube.
If you are trying to “copy” the driver as an fcurve
In speaker_tools I use this bit of code to “bake” an action from a driver, that is it changes the property from being driven by a driver to being keyframed as an action.
(from eqmenu.py in speaker_tools
driver is the fcurve part of a driver eg driven_fcurve above
which, in this case, is being driven by an action that is baked from a sound file.)
while frame <= frame_end:
scene.frame_set(frame)
# quick fix try array, then without
#print(type(driver.id_data))
try:
driver.id_data.keyframe_insert(driver.data_path,
index=driver.array_index)
except:
driver.id_data.keyframe_insert(driver.data_path)
finally:
frame = frame + 1
The keyframed action takes precedence in blender over the driver so the driver doesn’t need to be removed. Removing the action fcurve will re-expose the driver.
I attached an example with the driver and the curve I’m speaking about. And the script I have so far (with your help batFINGER). you can see the curve in the driver section, but when I try to create the same curve for the new driver, I can’t find the data_path to insert the keyframe. As it is, the script create the keyframe directly in the location.
is this created from old IPO curves, anyhow it’s a new one on me. Can you explain what the result is? it appears to be a combination of the driver fcurve and the variable input.
To copy the keyframe_points try
new_fcurve_driver.keyframe_points.add(numberofpointsinoriginal)
for point in driver.keyframe_points:
point.co = originalpoint.co
batFINGER you are a genius! thanks it works, with this code, it recreate exactly the same curve:
if len(fc.keyframe_points)>0:
dest_anim.drivers[0].keyframe_points.add(count=len(fc.keyframe_points))
i=0
for key in dest_anim.drivers[0].keyframe_points:
key.co = fc.keyframe_points[i].co
key.interpolation = fc.keyframe_points[i].interpolation
key.handle_left = fc.keyframe_points[i].handle_left
key.handle_left_type = fc.keyframe_points[i].handle_left_type
key.handle_right = fc.keyframe_points[i].handle_right
key.handle_right_type = fc.keyframe_points[i].handle_right_type
i+=1
the purpose is to have a bit more control on how the object react to it’s driver. For example if you want to connect a keyshape of a bicep contracting when you bend the arm, if you have a linear driver, it won’t look good. with a curve, you can create some ease in and ease out so that the bicep won’t contract too much at the beginning of the bending but the more you get closer to the 180deg and the more you can have the bicep get bigger.
yes, I was looking at it and tried to use it. It was working fine when it was a driver for transform, but I got error when it was a driver for shapekey… I don’t know why, but I’m going to look at it!
is there a way to simplify the code the same way than the variables for the modifiers?
here is the variables script that you gave me batFINGER:
for var in fd.variables:
v = td.variables.new()
v.name = var.name
v.type = var.type
for i,target in var.targets.items():
v.targets[i].id = target.id_data
Is there a way to do the same targets.items() for modifiers
### copy modifier from original
for mod in fc.modifiers:
m = destination.animation_data.drivers[num_dr-1].modifiers.new(mod.type)
and for the keyframe info to replace all of these lines
I think I was confused with the items function. I thought it was looking at each parameters of a structure instead of needing a line for each one of them, but now I understand it’s just looking into an array.
tc.keyframe_points.add(count=len(fc.keyframe_points))
for i,key in tc.keyframe_points.items():
for j, parameter in key[i].parameters:
parameter = tc.keyframe_points[i].parameters[j]
I’m wondering whether it may be simpler to copy the original object with its animation_data (there is a flag to turn this on or off I cant remember where off the top of my head,) and then change the underlying data. A quick test in the console and it appears this could be feasible, either using bpy.ops.object.duplicate() or object.copy().
Another suggestion is to use setattr
>>> setattr(
setattr(object, name, value)
Set a named attribute on an object; setattr(x, 'y', v) is equivalent to
``x.y = v''.
and the property list (the properties for an envelope modifier)
For the modifiers you might require a switch statement or something, since different mod types have different props, also some features may not be available via the API, like envelope modifier control points add and remove… ( I’ve just finished a patch for as I need it in speaker_tools)