4
$\begingroup$

I have a property called A

I have another property called ADP that stores the data path of A as a string

How can I use the string data path to set property A?

I could add "= myvar" to the end of the string and use exec on the new string but that's overkill I think.

$\endgroup$
  • $\begingroup$ What's the purpose of storing a data path in a property to another property? $\endgroup$ – CodeManX Apr 18 '14 at 1:00
  • $\begingroup$ I have a UI that the user can paste a data path into, its then stored as a string. like when you add a var to a driver $\endgroup$ – MCHammond Apr 18 '14 at 1:03
  • $\begingroup$ I suppose I should add that the property could be anything that can have a data path: "pose.bones["Bone"].location.x" or "pose.bones["Bone"].constraints["IK"].influence" $\endgroup$ – MCHammond Apr 18 '14 at 1:06
  • $\begingroup$ You could recursively getattr() and stop on e.g. Vectors, otherwise you will end up with a float value, which isn't writeable. Object.path_resolve() has a similar issue, "location.x" will return a value and you can't assign to it. exec() doesn't seem so bad with this in mind... $\endgroup$ – CodeManX Apr 18 '14 at 2:00
  • $\begingroup$ Im looking at using bpy.ops.wm.context_set_float() it works but floods the console with errors if its a vector3 ie rotation_euler.x, any idea? $\endgroup$ – MCHammond Apr 18 '14 at 2:02
4
$\begingroup$

To set a property you only have as a string, you can use Python's setattr()

import bpy
obj = bpy.context.object

# regular method
obj.location = 1, 2, 3

# using setattr
setattr(obj, "location", (1, 2, 3))

Notice setattr takes a string, so this can come from anywhere you like.

For simple properties, this works fine, however, if you want to set a path with multiple elements this becomes more complicated.

eg: modifiers["Subsurf"].levels

In this case there isn't such a clean solution.

There is a blender specific function called bpy_struct.path_resolve. Internally this uses the same method which is used to resolve FCurve and driver paths.

You could define a function for setting the attribute that combines path_resolve and setattr:

def value_set(obj, path, value):

    if "." in path:
        # gives us: ('modifiers["Subsurf"]', 'levels')
        path_prop, path_attr = path.rsplit(".", 1)

        # same as: prop = obj.modifiers["Subsurf"]
        prop = obj.path_resolve(path_prop)
    else:
        prop = obj
        # single attribute such as name, location... etc
        path_attr = path

    # same as: prop.levels = value
    setattr(prop, path_attr, value)

# example usage (requires path_resolve)
value_set(bpy.context.object, 'modifiers["Subsurf"].levels', 2)

# example usage (uses simple setattr)
value_set(bpy.context.object, 'location', (1, 2, 3))

However this is a bit involved, you could just do...

exec("obj" + path + " = value")

Though using exec is not ideal, especially if you don't know where the inputs come from. (security considerations)


Note: even though the example uses bpy.context.object you could use a Scene, Group, Mesh, Modifier... any Blender data really.

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.