Dev:2.5/Py/API/Dynamic Creation of Operators
Introduction
In Blender 2.5 operators are automatically registered (using a metaclass), so Blender can find them. This is normally very convenient, but when you're creating operators dynamically it might cause problems.
Problems and solutions
Example of a normal operator declaration that is automatically registered correctly:
import bpy
class Foobar(bpy.types.Operator):
''''''
bl_idname = "foo.bar"
bl_label = "FooBar!"
def execute(self, context):
print("foobar")
return {'FINISHED'}
When you're dynamically creating operators, you might do something like this (which doesn't work):
import bpy
my_names = ["Foo", "Bar"]
def invoke(self, context, event):
print(self.bl_description)
return{'FINISHED'}
for name in my_names:
my_class = type(name, (bpy.types.Operator,), dict(bl_idname=name, bl_label=name+"!", bl_description=name))
setattr(my_class, 'invoke', invoke)
When you try to run the operator you get the error: "invalid operator call". The reason is that the automatic registering (the metaclass) is done immediately upon creating the class. So right after the line "my_class = type(...)". Adding methods or attributes after that, for example using setattr(), is too late. So the main thing to remember is:
setattr() doesn't work with the automatic registration of operators.
Below are two ways of doing it correctly:
import bpy
my_names = ["Foo", "Bar"]
def my_invoke(self, context, event):
print(self.bl_description)
return{'FINISHED'}
for name in my_names:
class my_class(bpy.types.Operator):
bl_idname = name
bl_label = name+"!"
bl_description = name
invoke = my_invoke
import bpy
my_names = ["Foo", "Bar"]
for name in my_names:
class my_class(bpy.types.Operator):
bl_idname = name
bl_label = name+"!"
bl_description = name
def invoke(self, context, event):
print(self.bl_description)
return{'FINISHED'}
There is one final thing that might stop your script from working, and that's when the script is run as add-on. In your add-on you can't create operators from within the register() function.
Example of code that fails when run as add-on (but works correctly when run from the text-editor):
bl_addon_info = {
"name": "FooBar",
"category": "System"}
import bpy
def create_ops():
my_names = ["Foo", "Bar"]
for name in my_names:
class my_class(bpy.types.Operator):
bl_idname = name
bl_label = name+"!"
bl_description = name
def invoke(self, context, event):
print(self.bl_description)
return{'FINISHED'}
def register():
create_ops()
def unregister():
pass
if __name__ == "__main__":
register()
The solution is to simply create the operator classes in the module itself (this way they are defined when the module is imported), not in a function definition:
bl_addon_info = {
"name": "FooBar",
"category": "System"}
import bpy
my_names = ["Foo", "Bar"]
for name in my_names:
class my_class(bpy.types.Operator):
bl_idname = name
bl_label = name+"!"
bl_description = name
def invoke(self, context, event):
print(self.bl_description)
return{'FINISHED'}
def register():
pass
def unregister():
pass
Summary
- Don't use setattr() to set the poll/invoke/execute functions.
- Don't create operators from within the register() function.
References
Discussion on the mailinglist
Correct link to script mentioned in above discussion