A frequent use case of EnumProperties in Blender addons is to display a dropdown with a choice of scene objects. Of course which objects are available in a scene is only known when invoking the addon so a fixed list of choices is of no use here. For this scenario a callback option is provided. The callback can be any function that returns a list of choices. Unfortunately there are a number of severe bugs associated with using a callback: if your python code doesn't keep a reference to the items in the list it returns, Blender may crash. This
bug is documented and a workaround suggested but this doesn't solve the problem completely. Have a look at the code below:
- import bpy
- from bpy.props import EnumProperty
-
- available_objects = []
-
- def availableObjects(self, context):
- available_objects.clear()
- for ob in bpy.data.objects:
- name = ob.name
- available_objects.append((name, name, name))
- return available_objects
-
- class TestCase(bpy.types.Operator):
-
- bl_idname = "object.testcase"
- bl_label = "TestCase"
- bl_options = {'REGISTER', 'UNDO'}
-
- objects = EnumProperty(name="Objects", items = availableObjects)
import bpy
from bpy.props import EnumProperty
available_objects = []
def availableObjects(self, context):
available_objects.clear()
for ob in bpy.data.objects:
name = ob.name
available_objects.append((name, name, name))
return available_objects
class TestCase(bpy.types.Operator):
bl_idname = "object.testcase"
bl_label = "TestCase"
bl_options = {'REGISTER', 'UNDO'}
objects = EnumProperty(name="Objects", items = availableObjects)
if we hover the mouse over the second or third option we see that the description shows garbage, i.e. the description part of another item!

The really annoying part is that even if we make a full copy of the object's name (with
ob.name[:]
) the problem isn't solved. So probably the internal callback code trashes memory even outside its own allocated memory.
- def availableObjects(self, context):
- available_objects.clear()
- for ob in bpy.data.objects:
- name = ob.name[:] # a copy, not just a reference
- available_objects.append((name, name, name))
- return available_objects
def availableObjects(self, context):
available_objects.clear()
for ob in bpy.data.objects:
name = ob.name[:] # a copy, not just a reference
available_objects.append((name, name, name))
return available_objects
A way around this is to let the callback just return a list that is filled with values outside the callback code, for example in the code thst is executed when the user clicks on the menu item that selects the addon:
- available_objects = []
-
- def availableObjectsInit(self, context):
- available_objects.clear()
- for ob in bpy.data.objects:
- name = ob.name[:] # a copy, not just a reference
- available_objects.append((name, name, name))
- return available_objects
-
- class TestCase(bpy.types.Operator):
-
- ... stuff omitted ...
-
- objects = EnumProperty(name="Objects",
- items = lambda self,context: available_objects)
-
-
- def menu_func(self, context):
- availableObjectsInit(self, context)
- self.layout.operator(TestCase.bl_idname,
- text="TestCase",icon='PLUGIN')
-
- def register():
- bpy.utils.register_module(__name__)
- bpy.types.VIEW3D_MT_object.append(menu_func)
available_objects = []
def availableObjectsInit(self, context):
available_objects.clear()
for ob in bpy.data.objects:
name = ob.name[:] # a copy, not just a reference
available_objects.append((name, name, name))
return available_objects
class TestCase(bpy.types.Operator):
... stuff omitted ...
objects = EnumProperty(name="Objects",
items = lambda self,context: available_objects)
def menu_func(self, context):
availableObjectsInit(self, context)
self.layout.operator(TestCase.bl_idname,
text="TestCase",icon='PLUGIN')
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_object.append(menu_func)
This is usable even if the addon changes the number of objects. The only downside is that the code isn't reentrant because in its present form does not guard thread access to the global variable but as far as I know the Blender UI runs as part of a single Python interpreter so there's only one thread, which renders this point moot. The other issue is that it looks ugly, but as long as it works that's a minor issue :-) Note: you might wonder why we need the lambda here but that's because if we would point to just the list here, the list would be empty at the point where the EnumProperty was defined and apparently it makes a copy of that list so it would stay empty.