What causes EnumProperty to do this? UI BUG?

Why can I not expand a dynamic EnumProperty in UI. How do I solve this issue?

def my_items(scene, context):

    my_folders = []
    items = []

    for folders in bpy.context.scene.objects:
        if folders.type == 'MESH':
    for i, name in enumerate(my_folders):
        #Uppercase for enum identifier
        name_upper = name.upper()
        #Remove underscore and capitalize for enum name
        remove_underscore = name_upper.replace("_", " ")
        cap_words = string.capwords(remove_underscore)
        #Insert items                                                          
        items.insert(i, (name_upper, cap_words, ""))
    return items

Prints in console. This is what I want.

[('SUZANNE_MONKEY', 'Suzanne Monkey', ''), ('CUBE', 'Cube', ''), ('CYLINDER', 'Cylinder', '')]

You can see the UI goes nuts when trying to correct the “identifier” and “name” for EnumProperty and drawing like this layout.prop(mytool, "transform", expand=True).

If doing layout.prop(mytool, "transform", text='') it’s correct and causes no issues.

First of all, your object names list can be done like this:

objs_names = [obj.name for obj in bpy.context.view_layer.objects if obj.type == "MESH"]

then you can do this:

import string
items = []
for name in objs_names:
    items.append((name.upper(), string.capwords(name).replace("_"," ") ,""))

This then prints like this:

[('CUBE', 'Cube', ''), ('CYLINDER', 'Cylinder', ''), ('SUZANNE_MONKEY', 'Suzanne Monkey', '')]

then you do this:

items_enum : EnumProperty(items=items, name="Objects", description="Mesh Objects")

Then call the items_enum prop in your UI layout. I save all these variables in my Add-ons into a PropertyGroup and use a PorpertyPointer to access them.

Hope this helps!


If you define these things in you __init__.py file they all get automatically updated…

1 Like

Can you post a example?

Here’s an extract from my __init__.py file for Precision Drawing Tools:

class PDTSceneProperties(PropertyGroup):
    """Contains all PDT related properties."""

    pdt_library_path: StringProperty(
        description="Parts Library File",

    object_search_string: StringProperty(name="Search", default="", description=PDT_DES_LIBSER)
    collection_search_string: StringProperty(name="Search", default="", description=PDT_DES_LIBSER)
    material_search_string: StringProperty(name="Search", default="", description=PDT_DES_LIBSER)

    cartesian_coords: FloatVectorProperty(
        name="Coords", default=(0.0, 0.0, 0.0), subtype="XYZ", description=PDT_DES_COORDS
    distance: FloatProperty(
        name="Distance", default=0.0, precision=5, description=PDT_DES_OFFDIS, unit="LENGTH"
    angle: FloatProperty(
        name="Angle", min=-180, max=180, default=0.0, precision=5, description=PDT_DES_OFFANG
    percent: FloatProperty(name="Percent", default=0.0, precision=5, description=PDT_DES_OFFPER)

This class is then registered thus:

def register():
    """Register Classes and Create Scene Variables.

    Operates on the classes list defined above.

    from bpy.utils import register_class

    for cls in classes:

    # OpenGL flag
    window_manager = WindowManager
    # Register Internal OpenGL Property
    window_manager.pdt_run_opengl = BoolProperty(default=False)

    Scene.pdt_pg = PointerProperty(type=PDTSceneProperties)

All specific PDT variables are then accessed through the pdt_pg PointerProperty, so to use distance as defined in the class, you simply call:

pg = scene.pdt_pg

delta_distance = pg.distance * 0.0254

as an example.

The full __init__.py file can be found here. This approach keeps all your variables in one place and does not stand the chance of them interfering with anything else in Blender.

Cheers, Clock. :wink: