1
$\begingroup$

So I was making an addon with a dynamic EnumProperty and apparently ran into a known bug when trying to use Expand = True for this property in the panel. Although everything works fine when not using Expand = True, for my addon, it is essential to have the options expanded and not collapsed in a drop-down menu. The bug can be found in the documentation:

https://docs.blender.org/api/master/bpy.props.html#bpy.props.EnumProperty

enter image description here

This is the most compact example I could think of (add a few objects to the scene to see the bug in effect):

import bpy

def getEnumStuff(self, context):
    return [((obj.name, obj.name, "")) for obj in bpy.context.selectable_objects]

class OBJECT_PT_example_panel(bpy.types.Panel):
    bl_category = "Example"
    bl_label = "Example"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_context = "objectmode"

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.prop(context.scene, "stuff", expand = True)
        
classes = (
    OBJECT_PT_example_panel,)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.stuff = bpy.props.EnumProperty(
        name = "stuff", 
        items = getEnumStuff,
        description = "")

def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()

I tried a lot of things, such as making the list global before returning it from the getEnumStuff function and copying the name strings in order to have them point to a different address than the original object names. Nothing I tried improved the results so far.

The question is, is there even a workaround for this bug? Is there another way to have the options expanded in the panel while still being able to select them (not only printing the name)?

$\endgroup$
1
$\begingroup$

The Warning in the documentation states the EnumProperty should have a permanent handle on the items. One way I see, without resorting to a global variable, is to add an attribute to the getEnumStuff function. See relevant PEP 232. And then "manually" update the values before drawing the col.prop drawing call.

By the way, if you pass the context in a function, you should not use bpy.context afterwards but rather the context parameter because there could be a mismatch between the two, resulting in an incorrect context error.

import bpy


def getEnumStuff(context):
    getEnumStuff.enum_stuffs = [((obj.name, obj.name, "")) for obj in context.selectable_objects]
getEnumStuff.enum_stuffs = []


class OBJECT_PT_example_panel(bpy.types.Panel):
    bl_category = "Example"
    bl_label = "Example"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_context = "objectmode"

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        getEnumStuff(context) # Update enum items before drawing them
        col.prop(context.scene, "stuff", expand = True)
        
        
classes = (
    OBJECT_PT_example_panel,)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.stuff = bpy.props.EnumProperty(
        name = "stuff", 
        items = lambda self, context: getEnumStuff.enum_stuffs,
        description = "")


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()

Result :

enter image description here

$\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.