Add/remove EnumProperty items?

I want to use enum property in my addon to have dropdown menu with angle values + I want to have add/remove buttons with operators which will add/remove values from this enum.

I have already read this documentation and I don’t understant how to do this. Absolutely. Getter/setter, of course … but I watch videos about getter setter and it all about pure python. Then I look at example in documentation and it looks for me like just some piece of bullshit.

def get_enum(self):
    import random
    return random.randint(1, 4)


def set_enum(self, value):
    print("setting value", value)


bpy.types.Scene.test_enum = bpy.props.EnumProperty(items=test_items, get=get_enum, set=set_enum)

Getting enum to return random??? Setting enum by printing some value??? How it could help me?

I already spent hours of googling any example and there is no such across the internet. Not tutors, no videos, no nothing… only this piece of documentstion I don’t understand. I already asked on Stack Exchange and only ansver I got look more like “go f*** with your self”… Not too helpfull.

Looks like everybody in entire world know how to do this except me.

Can anyone just show me some 5 or something rows of example working code, please? With some EnumProperty and some simplest operator which can change this “items” value somehow.

I wrote an Example for you, while keeping it as simple as i can

To Achieve the effect that you describe, first you will need a Property Group and Collection Property

First thing you need is Property Group to Store the Number, this is something to use to store multiple properties in blender, in our case right now, we only need to store a number

This class will need to be registered in the register function below

class Test_Number(bpy.types.PropertyGroup):
    number: bpy.props.IntProperty()

Now you have the property type, however, you cannot use this yet, to access this, you can use either pointerproperty, or collectionproperty, in our case, we need collectionproperty, because we need multiple of it to create the enum

bpy.types.Scene.test_collection = bpy.props.CollectionProperty(type=Test_Number)

The Collection type will use the property group class we created earlier, so now, you have a list of that object type you created.

Lets create our Enum Property

To create a Dynamic Property, we creates a function that returns the items that we want.
the items that we need to return is in this format

[(identifier, name, description),(identifier, name, description)]

This code will grab the collectionproperty, and loop through it like a list, each of the item are the propertygroup that we defined, and as what I did previously, it have number property

im not too sure, but I remember enum can only accept string, so I convert them into string and just feed identifier, name and description with the number, of course, you can give it anything, but when you grab the data later for use, it will use the identifier

def test_items(self, context):
    
    Enum_items = []
    
    for test_number in context.scene.test_collection:
        
        data = str(test_number.number)
        item = (data, data, data)
        
        Enum_items.append(item)
        
    return Enum_items


######################
###REGISTER############

bpy.types.Scene.test_number = bpy.props.EnumProperty(items=test_items)

To Add new Object, just simply run the add function in the collection property and set the number to what you want, you can just put this in your operator

Test_Number = context.scene.test_collection.add()
Test_Number.number = random.randint(1, 4)

Below are the full code of the example, I hope it helps

import bpy
import random

class Example_Add_Operator(bpy.types.Operator):

    bl_idname = "example.add"
    bl_label = "Add"


    def execute(self, context):
        
        Test_Number = context.scene.test_collection.add()
        Test_Number.number = random.randint(1, 4)

        return {'FINISHED'}
    
class Example_Remove_Operator(bpy.types.Operator):

    bl_idname = "example.remove"
    bl_label = "Remove"


    def execute(self, context):
        
        if len(context.scene.test_collection) > 0:
            context.scene.test_collection.remove(0)


        return {'FINISHED'}
    

class EXAMPLE_Panel(bpy.types.Panel):

    bl_label = "Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Example"

    def draw(self, context):
        layout = self.layout
        
        row = layout.row(align=True)
        row.prop(context.scene, "test_number", text="")
        row.operator("example.add", text="", icon="ADD")
        row.operator("example.remove", text="", icon="REMOVE")
        
def test_items(self, context):
    
    Enum_items = []
    
    for test_number in context.scene.test_collection:
        
        data = str(test_number.number)
        item = (data, data, data)
        
        Enum_items.append(item)
        
    return Enum_items

class Test_Number(bpy.types.PropertyGroup):
    number: bpy.props.IntProperty()
 
    
def register():
    
    bpy.utils.register_class(EXAMPLE_Panel)
    bpy.utils.register_class(Example_Add_Operator)
    bpy.utils.register_class(Example_Remove_Operator)
    
    bpy.utils.register_class(Test_Number)
    
    bpy.types.Scene.test_number = bpy.props.EnumProperty(items=test_items)
    bpy.types.Scene.test_collection = bpy.props.CollectionProperty(type=Test_Number)
    
    



def unregister():
    
    del bpy.types.Scene.test_number
    del bpy.types.Scene.test_collection
    
    bpy.utils.unregister_class(EXAMPLE_Panel)
    bpy.utils.unregister_class(Example_Add_Operator)
    bpy.utils.unregister_class(Example_Remove_Operator)
    bpy.utils.unregister_class(Test_Number)

if __name__ == "__main__":
    register()

Example.py (2.2 KB)

2 Likes

Thank you for reply. At least someone tried to explain. But what you show not exactly what I seek for (or is it?).

Here my addon code with panel where I have this enum, add/remove operators buttons and those operators/property group (I removed everything else).

bl_info = {
	"name": "FRP",
	"author": "",
	"version": (1, 0),
	"blender": (2, 83, 0),
	"location": "Viev3D > N panel > FG Tools > FRP",
	"description": "",
	"warning": "",
	"wiki_url": "",
	"category": "",
}

import bpy,math

class FRP_PT_Panel(bpy.types.Panel):
	bl_label = 'FRP'
	bl_idname = 'FRP_PT_Panel'
	bl_space_type = 'VIEW_3D'
	bl_region_type = 'UI'
	bl_category = 'FG_Tools'

	def draw(self,context):
		layout = self.layout
		row = layout.row(align=True)

		sc_frp = context.scene.frp_props

		row_a = layout.row(align=True)
		row_a.prop(sc_frp, 'frp_angle_a', text = '')
		row_a.operator('fgt.frp_angle_a_add', text='', icon='ADD')
		row_a.operator('fgt.frp_angle_a_rem', text='', icon='REMOVE')

class FRP_OT_Angle_A_Add(bpy.types.Operator):
	bl_idname = 'fgt.frp_angle_a_add'
	bl_label = 'Add custom angle.'

	angle = bpy.props.FloatProperty(
		name = 'Angel A', 
		default = 0.0, 
		min= 0.0, 
		max= 360.0
	)

	def execute(self, context):

		return{'FINISHED'}

	def invoke(self, context, event):

		return context.window_manager.invoke_props_dialog(self)

class FRP_OT_Angle_A_Rem(bpy.types.Operator):
	bl_idname = 'fgt.frp_angle_a_rem'
	bl_label = 'Remove custom angle.'

	def execute(self, context):
		print(context.scene.frp_props.frp_angle_a)
		return{'FINISHED'}


class FRP_Settings_Props(bpy.types.PropertyGroup):

	frp_angle_a: bpy.props.EnumProperty(
		name = 'Angle_A',
		description= 'Something here',
		items = [('a1', '30', '', 1),('a2', '10', '', 2)]
	)

CTR = [
	FRP_PT_Panel,
	FRP_OT_Angle_A_Add,
	FRP_OT_Angle_A_Rem,
	FRP_Settings_Props,
]

FRP_Keymap = []

def register():
	for cls in CTR:
		bpy.utils.register_class(cls)

	bpy.types.Scene.frp_props = bpy.props.PointerProperty(type=FRP_Settings_Props)

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

	del bpy.types.Scene.frp_props


In your code you like create it from list and then register, that’s all. You use collection property to manage it and then convert it to enum property.

Instead of what you show I create this enum right from start with some default values. Then I want to get existing enum property by operator, get whats’s inside (this “items” tuple) and add some angle value to existing values (using popup menu), or remove current picked angle value.

In ADD case operator I guess I have to “get” this enum tuple > append to it > “set” it back to enum. It also have to live inside ADD operator execut to be executed from popup “OK” button as I understand how it work (I may be wrong).

In REMOVE case operator I guess I have to check current picked enum value (context.scene.frp_props.frp_angle_a) > save this value somewhere > get access to enum tuple > find this value value > remove it > “set” this tuple back to my enum


Just context.scene.frp_props.frp_angle_a give (of course) just current picked enum identificator. (on GIF when I click on " - " a1, a2 )
context.scene.frp_props.frp_angle_a() give error “TypeError: ‘str’ object is not callable”
context.scene.frp_props.frp_angle_a.items give error “AttributeError: ‘str’ object has no attribute ‘items’”

So there is no easy way to get this tuple inside enum? In documentation EnumProperty has getter/setter definitions so as I understand it, by this definitions I can call and change whenever I want.


So I guess I have to use getter/setter somehov to manage it. But when I watch some video in youtube about python getter/setter (like his one) it’s like about something one and then I look at API Property definitions page and it’s about something comletely different (or maybe I’m the most stupid guy in the world, at least I feel so after days looking for how to do this).

I can’t find any example of such enum property using case code… Here where I need help. How to make those getter/setter and use it (example in API documentation give me “0” clues).

How did you solve it? Can you share it?

I was wrong

What was my mistake from start - wrong understanding of what is class lead me to wrong expectations from what enum update/get/set does.

Those is just functions to be called when something happen (actually as it described in description). This is not functions used set this enum property items value.


After more practicing/learning what is class and how it work, I gues some things. Right now for my addon I use solution proposed by BlenderBoi2018. I guess you, same as me back in the days, have issues with understanding this logic.

Shortly (clearer):

  • you have some iterable (or any else) variable (or CollectionProperty) to store some data used to generate enum items base on this data
  • you have some function which use this data to generate enum items and return them
  • you put this function name in to enum property items parameter

Then code will run this function when you howering addon panel (Blender go through addon code to draw addon UI) and regenerate enum property from variable by using function.


Here example from my addon:

Variable pr_values used to store dictionary. I use it then in function upd_preset_enum to generate enum items. It return output similar as if I set such items manually in enum property parameters. On next image I do for loop for each pr_values dictionary item, generate tuple new_pr and each time append this tuple to list pr_enum which will return then as function output.

Some description for what function return.

If you check again API documentation - it has to return list of tuples each has to contain (identifier(string, has to be different for different enum items), name(string, what shown in UI, can be same as identifier, same for all items or what ever), description (string, what ever you want, shown as a hint whe mouse over enum item), icon(optional, may not be included), number(integer, different for different enum items)).

  • Identifier - what you will get from python API. My addon example bpy.context.scene.sot_props.presets (sot_props - my properties collection, presets - enum property name) will return some 'Preset.002' (currently selected enum item string identifier). You can use it to identify which enum item is selected.
  • Number - not same as identifier, has to be some different integer numbers (1,2,3,4,5) Bledner will use them to manage enum items.

Here - name of this function used as “items” in enum property. Return of this function become value of “items” of my enum property.

As you noticed (in my case) variable pr_values empty from start. It’s OK, I use add/remove operators (on next image) for add/remove interface buttons to manage pr_values dictionary and, as result, manage my enum property items.

Here code of panel where I use this enum property (selected enum property, add and remove operators buttons). As you see I use same pr_values variable also to manage UI, change “remove” button icon, show info about selected preset.

Here how it look in addon UI panel.

Thank you very much for your reply,But the function I want seems different from yours。
I want to continuously register enumproperty or customize enumproperty in the for loop。I don’t know if you can understand what I mean, but it is such a function. Please forgive me. I’m a novice learning blender