How to convert a list to a menu enum list?

any simple way using python to convert a list to a menu enum list ?
I got a list of around 300 values
so a bit time consuming to do it by hand !

thanks
happy bl

Do you mean an EnumProperty with 300 items? That’s a lot of options! :joy:

You could write a python script to do this. If each value is in a file stored per line in a file. If the values are already in a list of strings, you only need the second code block.

your_values_list = []
with open("your_values_list_file", 'r') as f:
    your_values_list = f.read().splitlines()

with open("temp_file", 'w') as f:
    for item in your_values_list:
        f.write(f"('{item}', '{item}', ''),\n")

This gives you the list to put in items=()


Or you could use a code editor to edit multiple lines at once.

I started a simple script with a list
but I forgot to convert floats to string for enum so not working !

I will try your method with a file might work

would there be another method for such a long list menu ?
it is a long list of IOR and Material

thanks
happy bl

If you want to ‘abuse’ the identifier of an item in EnumProperty, you can store the numbers as strings. They need to be converted back to numbers during runtime with int() or float().

I think it’s best, if you give a sample to show, how the data is stored and how you would like an item to look like.

I made an enum prop with 314 values
now this is use for a Menu prop in a panel
I get the selected item
but is it possible to get the others values in the enum data

colormenu2 : EnumProperty(items= ( 
	('1', 'Acetone', '1.36'), ('2', 'Actinolite', '1.618'), ('3', 'Agate', '1.544'), ('4', 'Agate, Moss', '1.54'), ('5', 'Air', '1.0002926'), 
	('6', 'Alcohol', '1.329'), ('7', 'Alexandrite', '1.745'), ('8', 'Aluminum', '1.44'), ('9', 'Amber', '1.546'), ('10', 'Amblygonite', '1.611'), 

each enum has 3 values (‘1’, ‘Acetone’, ‘1.36’)
after I selected a value with menu prop
I have the item number from that enum prop menu in an operator class
but is it possible to get the other values

if I select 1 I would like to be able to get the name = acetone and IOR = 1.611

for now I made the addon work by adding another list for the same values
and use that list to get the name and IOR

it would save a lot of lines if it was possible to get the value from the Enum prop instead in then panel
or pass it with a global value to the main panel

my addon is almost completed and mostly working
but not very efficient with a long list !
I can upload if needed tomorrow

thanks
happy bl

Here is an example for EnumProperty.
The data is stored in an dictionary (IOR). From that dict you make the item list with a function (ior_items).
You can access the data (value in IOR) via the selected key of the enum property in the panel.

You don’t need to make arbitrary numbers as identifiers. You can use the names in this case.

import bpy

from bpy.props import EnumProperty
from bpy.types import PropertyGroup


# Data
IOR = {
    "Alcohol": 1.329,
    "Ice": 1.309,
    "Water": 1.333,
}

# retuns the list of items for EnumProperty
def ior_items(scene, context):
    items = []
    for name, ior in IOR.items():
        items.append((name, name, str(ior)))  # name is used as identifier
    return items


class IOR_settings(PropertyGroup):
    ior: EnumProperty(
        name="IOR",
        items=ior_items  # get the list of items from the function
    )


class IOR_PT_Panel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "IOR Enum Test Panel"
    bl_idname = "OBJECT_PT_ior"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        self.layout.prop(bpy.context.scene.ior_settings, "ior")
        self.layout.operator('object.property_example')


class OBJECT_OT_property_example(bpy.types.Operator):
    bl_idname = "object.property_example"
    bl_label = "Property Example"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        name = bpy.context.scene.ior_settings.ior
        ior = IOR[name]
        self.report({'INFO'}, f"{name} {ior}")
        return {'FINISHED'}


def register():
    bpy.utils.register_class(IOR_settings)
    bpy.utils.register_class(IOR_PT_Panel)
    bpy.utils.register_class(OBJECT_OT_property_example)

    bpy.types.Scene.ior_settings = bpy.props.PointerProperty(type=IOR_settings)


def unregister():
    bpy.utils.unregister_class(OBJECT_OT_property_example)
    bpy.utils.unregister_class(IOR_PT_Panel)
    bpy.utils.unregister_class(IOR_settings)

    del bpy.types.Scene.ior_settings


if __name__ == "__main__":
    register()

Here is how you can convert your current item data to a dictionary.

IOR_data = [
    ('1', 'Acetone', '1.36'),
    ('2', 'Actinolite', '1.618'),
    ('3', 'Agate', '1.544'),
    ('4', 'Agate, Moss', '1.54'),
    ('5', 'Air', '1.0002926'),
    ('6', 'Alcohol', '1.329'),
    ('7', 'Alexandrite', '1.745'),
    ('8', 'Aluminum', '1.44'),
    ('9', 'Amber', '1.546'),
    ('10', 'Amblygonite', '1.611'),
]

IOR = {}

for item in IOR_data :
    IOR[item[1]] = float(item[2])
1 Like

will try to understand the new info
and tried to finish a fist version of my addon
and upload inside a few days

thanks
happy bl

very interesting
how is the menu done in the panel with the operator

class OBJECT_OT_property_example(bpy.types.Operator):

bl_idname = "object.property_example"
bl_label = "Property Example"
bl_options = {'REGISTER', 'UNDO'}

def execute(self, context):

    name = bpy.context.scene.ior_settings.ior
    ior = IOR[name]
    self.report({'INFO'}, f"{name} {ior}")
    return {'FINISHED'}

what is the self.report doing ?
I thought this was to show data on top bar

my addon use this menu command

		row .operator_menu_enum('view_3d.my_color2', 'colormenu2', text = "Menu Selection" , icon = 'NONE' )

thanks
happy bl

It’s just used for debug information. Usually it’s used to give feedback to the user in the status bar.

Here is how it looks like with the operator enum menu.
This makes it even more simple. You just need to add the EnumProperty to the operator.

import bpy

from bpy.props import EnumProperty
from bpy.types import PropertyGroup


# Data
IOR = {
    "Alcohol": 1.329,
    "Ice": 1.309,
    "Water": 1.333,
}

# retuns the list of items for EnumProperty
def ior_items(scene, context):
    items = []
    for name, ior in IOR.items():
        items.append((name, name, str(ior)))  # name is used as identifier
    return items


class IOR_PT_Panel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "IOR Enum Test Panel"
    bl_idname = "OBJECT_PT_ior"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        self.layout.operator_menu_enum("object.property_example", "ior_enum_prop", text=OBJECT_OT_property_example.bl_label)


class OBJECT_OT_property_example(bpy.types.Operator):
    bl_idname = "object.property_example"
    bl_label = "Property Example"
    bl_options = {'REGISTER', 'UNDO'}

    ior_enum_prop = bpy.props.EnumProperty(items=ior_items)

    def execute(self, context):
        name = self.ior_enum_prop
        ior = IOR[name]
        self.report({'INFO'}, f"{name} {ior}")
        return {'FINISHED'}


def register():
    bpy.utils.register_class(IOR_PT_Panel)
    bpy.utils.register_class(OBJECT_OT_property_example)


def unregister():
    bpy.utils.unregister_class(OBJECT_OT_property_example)
    bpy.utils.unregister_class(IOR_PT_Panel)


if __name__ == "__main__":
    register()

here is my first version of addon with 2 lists very long and tedious
but it works fine
might need to add modification to adjust nodes set up later on
will see

IOR-SPEC-2.89.blend (164.1 KB)

your method seems a lot shorter have to better understand it and implement a new version

I may have to split the list in 2 parts one for non metallic mat and one for metallic part have to discuss that on another thread
cause calculation is not the same but will see later on

thanks
happy bl

1 Like

in 2.8 do you now how we access the tree nodes for material
I mean now we have only nodes in cycles and EEVEE

so not certain how this is done now in 2.8

thanks
happy bl

Nothing changed… Material nodetrees are stored in bpy.data.materials[‘mat_name’].node_tree, and in there you’ll find the .nodes and the .links collections.

but there must be 2 nodes tree
one for cycles and one for EEVEE !

thanks
happy bl

Nope… most of the nodes are interpreted by both engines, so the same node_tree is used.
The nodes that don’t work in one or other engine will be skipped (muted) when the node_tree gets ‘compiled’.

is there 2 ways to compile it ?
how does it knows which nodes have to be muted when being compiled?

is there some parameter to set up for cycles or EEVEE before compiling it ?

thanks
happy bl

Yes. Each renderer works differently, so the node tree is ‘scanned’ and turned into a sequence of commands each render can execute.
I don’t know the details so well.

The Material Output node has the option to have separated nodetrees for each engine, and each engine scans the nodetree by starting from the Material Output node that targets the engine (the default is both), and going through all nodes that are connected to it, and so on, to build the shader code.