3
$\begingroup$

I am trying to execute a line of code, such as: bpy.ops.object.modifier_add(type='SUBSURF') from a string of text.

My code is:

import bpy
import math

print("~~~Separator~~~")

obj_list = []
task_list = []

class ExportSomeData(bpy.types.Operator):
"""Test exporter which just writes hello world"""
bl_idname = "export.some_data"
bl_label = "Export Some Data"

filepath = bpy.props.StringProperty(subtype="FILE_PATH")

@classmethod
def poll(cls, context):
    if not obj_list:
        return False
    else:
        return True

def callMethod(o, name):
    getattr(o, name)()

def execute(self, context):
    global task_list
    file = open(self.filepath, 'r+')
    for line in file:
        task_list.append(line)
        callMethod("I don't know what to put here", line)
    print(str(task_list))
    file.close()

    return {'FINISHED'}

def invoke(self, context, event):
    context.window_manager.fileselect_add(self)
    return {'RUNNING_MODAL'}

class SelectOperator(bpy.types.Operator):
"""Adds """
bl_idname = "scene.select_option"
bl_label = "Multi Select Operator"

@classmethod
def poll(cls, context):
    if not bpy.context.selected_objects:
        return False
    else:
        return True

def execute(self, context):
    global obj_list
    obj_list = []
    for obj in bpy.context.selected_objects:
        obj_list.append(obj)
        print(str(obj))
    print("Objects in the active list: " + str(obj_list))
    return {'FINISHED'}

class TaskOperator(bpy.types.Operator):
"""Subdivide and scale a mesh."""
bl_idname = "scene.task_option"
bl_label = "Multi Task Operator"

@classmethod
def poll(cls, context):
    global obj_list
    if not obj_list:
        return False
    else:
        return True

def execute(self, context):
    global obj_list

    return {'FINISHED'}

class SelectionPanel(bpy.types.Panel):

bl_label = "Multi Selection Options"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'

def draw(self, context):
    layout = self.layout

    row = layout.row()

    split = layout.split()

    col = split.column(align=True)

    'row.operator("mesh.primitive_uv_sphere_add")'

    col.operator("scene.select_option", text = "Set Objects", icon = "MESH_CUBE")

    'row.operator("scene.task_option", text = "Do Task", icon = "LONGDISPLAY")'

    col.operator("export.some_data", text = "Open And Do Task", icon = "FILE")
def register():
bpy.utils.register_class(SelectionPanel)
bpy.utils.register_class(SelectOperator)
bpy.utils.register_class(TaskOperator)
bpy.utils.register_class(ExportSomeData)

def unregister():
bpy.utils.unregister_class(SelectionPanel)
bpy.utils.unregister_class(SelectOperator)
bpy.utils.unregister_class(TaskOperator)
bpy.utils.unregister_class(ExportSomeData)

if __name__ == "__main__":
register()

Below is what is causing me trouble:

def callMethod(o, name):
    getattr(o, name)()

def execute(self, context):
    global task_list
    file = open(self.filepath, 'r+')
    for line in file:
        task_list.append(line)
        callMethod("I don't know what to put here", line)
    print(str(task_list))
    file.close()

    return {'FINISHED'}

line is bpy.ops.object.modifier_add(type='SUBSURF'), but I don't know the object to put for the first parameter. If I am going the wrong direction with this, please correct me.

$\endgroup$

1 Answer 1

4
$\begingroup$

It looks like you are passing a whole function call to the getattr() function as a string.

getattr() takes an object as first argument and a string a second argument.

What you should do is isolate the object, the function and the params

Once you isolated your object string (ie. str="bpy.ops.object") you can isolate the object with a quick but unsafe myObj = eval(str) or use a safer

import sys

def str_to_class(str):
    return getattr(sys.modules[__name__], str)

myObj = str_to_class("bpy.ops.object") #line added from original code

(Code from https://stackoverflow.com/questions/1176136/convert-string-to-python-class-object)

After that you should call getattr in this fashion:
getattr(myObj, 'myFunction')(params)

In the specific case you presented it would look something like this in explicit form
getattr(bpy.ops.object, "modifier_add")(type='SUBSURF')

Note: eval() is a very unsafe to use if there's some kind of user input, since the user could inject malicious code in eval()'s parameter string.

Edit: if security is not a concern, for any reason, you could also just eval() the whole string. Just make sure not to share your code without a proper warning.
If security is a concern, in both of the cases described above, you might want to apply a filter on the strings you pass to the converting function. Something like if str[:3]=="bpy": str_to_class(str) #or eval(). It makes things a little safer.

$\endgroup$
4
  • $\begingroup$ I apologize, but I am still having an error. getattr(bpy.ops.object, "modifier_add")(type='SUBSURF') works fine. However, import sys def str_to_class(str): return getattr(sys.modules[__name__], str) myObj = str_to_class("bpy.ops.object") #line added from original code gives an error of "AttributeError: 'module' object has no attribute 'bpy.ops.object'" $\endgroup$
    – Tritofic
    Aug 31, 2015 at 14:17
  • $\begingroup$ Yup: Didn't test the code coming from the page I linked, assuming it was working, but it isn't for me either. I've been playing around a bit and worked out something like this: m = __import__("bpy") \ ops = getattr(m, "ops") \ object = getattr(ops, "object") You can easily loop that process with the aid of "my.test.string".split(".") Give a shout out if you need any more help :) $\endgroup$
    – torels
    Sep 1, 2015 at 15:33
  • $\begingroup$ Maybe m = locals().get("bpy", None) , fair to assume bpy would be in locals for a blender script. $\endgroup$
    – batFINGER
    Jan 1, 2017 at 11:41
  • $\begingroup$ Nice Insight! It is amazing that we can use the parenthesis after getattr to call a method by a string. getattr(myObj, 'myMethodAsString')(Params). $\endgroup$ Dec 13, 2019 at 12:31

You must log in to answer this question.

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