2
$\begingroup$

I'm currently working on the Animation Nodes and I'm trying to read all fcurves of one object and dynamically set the evaluated values on other objects.

data_path = fCurve.data_path
array_index = fCurve.array_index
value = fCurve.evaluate(frame)
targetObject = some object

# How can I know wether to do this:
exec("targetObject.{} = value".format(data_path))

# or this:
exec("targetObject.{}[{}] = value".format(data_path, array_index))

I have a working version using try and except statements but this isn't really nice. I hoped that fcurve.array_index is -1 if it isn't used but it is 0 by default, so I cannot use it.

Just some extra notes:

Ideally the solution is fast but that isn't absolutely necessary because I can cache the result. Based on the decision wether the property at the data path is subscriptable or not, I dynamically create a setAttribute function so that I don't have to use exec for each execution. I talk about > 10.000 executions per second.

cache = {}

def getSetFunction(object, attribute):
    if attribute in cache: return cache[attribute]

    function = createSetFunction(object, attribute)
    cache[attribute] = function
    return function

def createSetFunction(object, attribute):
    data = {}
    try:
        eval("object.{}[0]".format(attribute))
        exec(setAttributeWithIndex.replace("attribute", attribute), data, data)
        return data["setAttributeWithIndex"]
    except:
        try:
            # when the property is not subscriptable 
            eval("object." + attribute)
            exec(setAttributeWithoutIndex.replace("attribute", attribute), data, data)
            return data["setAttributeWithoutIndex"]
        except: pass
    return None

setAttributeWithIndex = '''
def setAttributeWithIndex(object, index, value):
    object.attribute[index] = value
'''

setAttributeWithoutIndex = '''
def setAttributeWithoutIndex(object, index, value):
    object.attribute = value
'''

enter image description here

$\endgroup$
1
  • $\begingroup$ this works for some properties but not when the data path is something like modifiers["Array"].count or other blender specific paths. Also afaik setattr isn't recursive when there are dots in the path. $\endgroup$ Commented Sep 15, 2015 at 22:02

2 Answers 2

2
$\begingroup$

I believe you can do this from the bl_rna array_count property. For instance on the array modifier, count isn't relative_offset_diplace is.

>>> C.object.modifiers['Array'].bl_rna.properties['count'].array_length
0

>>>C.object.modifiers['Array'].bl_rna.properties['relative_offset_displace'].array_length
3

I came across the same prob with searching for drivers and adding to UI sliders, and went for the try-except, which I also try to avoid.

$\endgroup$
1
  • $\begingroup$ Yes that works. Thanks. Too bad that I need (for most reliable results) the original object on which the fcurve has been created.. $\endgroup$ Commented Sep 16, 2015 at 13:38
0
$\begingroup$

The data paths use a known list of preset values, therefore you can test if path is in a list.

indexed_paths = ['location','rotation_euler','scale']
if data_path in indexed_paths:
    exec("targetObject.{}[{}] = value".format(data_path, array_index))
else:
    exec("targetObject.{} = value".format(data_path))
$\endgroup$
3
  • $\begingroup$ But you can add more vector properties via the python api. So I cannot use a static list to check if the array_index is needed. $\endgroup$ Commented Sep 16, 2015 at 20:25
  • $\begingroup$ custom vector properties return a type() of <class 'bpy_prop_array'> though I seem to be missing how to test that. $\endgroup$
    – sambler
    Commented Sep 17, 2015 at 13:26
  • $\begingroup$ Right.. I will use the way described in the other answer. That works quite well :) $\endgroup$ Commented Sep 17, 2015 at 13:28

You must log in to answer this question.

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