Enum property with enum flag

Hello, i would like to know how use the get and set functions with an EnumProperty with option ENUM_FLAG.
How understand the value parameter passed on the set function, and what value Blender waits from the get function.

The setter receives a bit field, not a set of strings.

Both, getter’s and setter’s first arguments are the property-owning object, in case of bpy.types.Object, it is the object datablock.

Setter has a second argument with the (former value) | (new value), e.g. first entry is selected and third is clicked, then it would be 001 | 100 = 101. It is called a second time, which can be a bit tricky. If you want to disallow the selection of the second element, you need to use the second argument and do a binary AND with either 101 (1 meaning allowed) or ~010 (inverts to 101) and check whether the result would be 0 and don’t assign that value in the setter to the internal property that stores the state, to not clear all previously selected options.

import bpy


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw_header(self, context):
        layout = self.layout    
        if hasattr(HelloWorldPanel, "val"):
            layout.label(HelloWorldPanel.val)

    def draw(self, context):
        layout = self.layout
        obj = context.object

        layout.prop(obj, "prop")
        

def getter(self):
    print("getter {:03b}".format(self["prop"]))
    return self["prop"]

def setter(self, val):
    print("setter {:03b} {:03b}".format(self["prop"], val))
    v = val & 0b101
    if v:
        self["prop"] = v
    HelloWorldPanel.val = "%i / %i" % (val, v, self["prop"])

def register():
    bpy.utils.register_class(HelloWorldPanel)
    bpy.types.Object.prop =  bpy.props.EnumProperty(items=(("1","One",""),("2","Two",""),("3","Three","")),  options={'ENUM_FLAG'}, default={"1","3"}, get=getter, set=setter)


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)
    del bpy.types.Object.prop

if __name__ == "__main__":
    register()


1 Like

I’ve finally understood, thank you very much. :stuck_out_tongue:

I’ve used this syntax in the setter:

prop = prop & value if value < prop else prop | value

This works with items of 3 indexes, but when the fourth/fifth is an id, many items are selected at a time and the value given to the setter is based on the ids. I don’t know how interpret that. :spin:

interesting, looks like if you supply your own ID integer, it must be in binary bitfield style too, so 1, 2, 4, 8, 16, …

There is a difference.

Test :

  • I use an enum of 4 items, A (id 1), B (2), C (3) and D (4)
  • In the setter i write directly : prop = 1
  • I click on the first item
  • I change the setter with : prop = 2
  • I reload the script
  • I click on the first item etc

Results:

0 (8, 16 etc) : Nothing is selected
1 (9, 17 etc) : A and C are selected
2 : B, C
3 : A, B, C
4 : D
5 : A, C, D
6 : B, C, D
7 : A, B, C, D

8 combinations instead of 16, it’s incomprehensible… :frowning:

As I said, if you use IDs, assign multiples of 2. Not sure what the upper boundary is, possibly 32 elements, here’s an example with 20:

import bpy


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw_header(self, context):
        layout = self.layout    
        if hasattr(HelloWorldPanel, "val"):
            layout.label(HelloWorldPanel.val)

    def draw(self, context):
        layout = self.layout
        obj = context.object

        col = layout.column()
        col.prop(obj, "prop")
        

def getter(self):
    print("getter {:03b}".format(self["prop"]))
    return self["prop"]

def setter(self, val):
    self["prop"] = val
    HelloWorldPanel.val = str(val)

def register():
    bpy.utils.register_class(HelloWorldPanel)
    bpy.types.Object.prop =  bpy.props.EnumProperty(
        items=(
            ("1","One","",1),
            ("2","Two","",2),
            ("3","Three","",4),
            ("4","Four","",8),
            ("5","Five","",16),
            ("6","Six","",32),
            ("7","Seven","",64),
            ("8","Eight","",128),
            ("9","Nine","",256),
            ("10","Ten","",512),
            ("11","Eleven","",1024),
            ("12","Twelve","",2048),
            ("13","Thirteen","",4096),
            ("14","Fourteen","",1<<13),
            ("15","Fifteen","",1<<14),
            ("16","Sixteen","",1<<15),
            ("17","Seventeen","",1<<16),
            ("18","Eighteen","",1<<17),
            ("19","Nineteen","",1<<18),
            ("20","Twenty","",1<<19),
        )
    ,  options={'ENUM_FLAG'}, default={"1","3"}, get=getter, set=setter)


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)
    del bpy.types.Object.prop

if __name__ == "__main__":
    register()

To select the third and the eleventh element, the number is 1028 (4 + 1024).
You could either assign 0b10000000100 or 1<<2 | 1<<10.

I’ve all understood, thank you very much. :RocknRoll:
An entire week… :stuck_out_tongue:

@CoDEmanX, I have too a problem with the enum flag and the enum property, but I dont need a setter and getter function. What I need, though, is to be able to select two ot more buttons of the enum property and it should show different parts of ui(in the following example, a line of text) So, if I enable option_1 and option_3 for example, it should show two lines of text in the panel. Also, I dont understand the numbers in the end. San you help me, please?

import bpy
from bpy.props import EnumProperty, PointerProperty
from bpy.types import Panel

class OBJECTMODE_PG_UI_Properties_TB(bpy.types.PropertyGroup):  
	panel_switch = EnumProperty(items = [ 
	("option_0","Zero","Option Description"),
	("option_1","One","Option Description"),
	("option_2","Two","Option Description"),
	("option_3","Three","Option Description"),
	("option_4","Four","Option Description"),
	("option_5","Five","Option Description"),
	("option_6","Six","Option Description"),
	("option_7","Seven","Option Description"),
	("option_8","Eight","Option Description"),
	("option_9","Nine","Option Description"),
	("option_10","Ten","Option Description"),
	("option_11","Eleven","Option Description"),
	("option_12","Twelve","Option Description"),
	("option_13","Thirteen","Option Description"),
	("option_14","Fourteen","Option Description"),
	("option_15","Fifteen","Option Description"),
	("option_16","Sixteen","Option Description"),
	("option_17","Seventeen","Option Description"),
	("option_18","Eighteen","Option Description"),
	("option_19","Nineteen","Option Description"),
	("option_20","Twenty","Option Description"),
	("option_21","Twenty One","Option Description"),
	("option_22","Twenty Two","Option Description"),
	("option_23","Twenty Three","Option Description"),
	("option_24","Twenty Four","Option Description"),
	("option_25","Twenty Five","Option Description"),  
	("option_26","Twenty Six","Option Description"),
	("option_27","Twenty Seven","Option Description"),
	("option_28","Twenty Eight","Option Description")],  
	name = "Property Name",
	description = "Property Description",
	default= {"option_0", "option_1"},  
	options = {"ENUM_FLAG"}) 

class OBJECTMODE_PT_TEST(bpy.types.Panel):
	bl_space_type = "VIEW_3D"
	bl_region_type = "UI"
	bl_label = "TEST PANEL ENUM_FLAG OPTION"
 
	def draw(self, context):
		layout = self.layout
		col = layout.column(align=True)
		uiprops = context.scene.test_props
		col.prop(uiprops, "panel_switch", text = "s", expand = True)
		if uiprops.panel_switch == "option_0":  		
			col.label(text = "Enum Prop OPTION 0")
		if uiprops.panel_switch == "option_1":  		
			col.label(text = "Enum Prop OPTION 1")
		if uiprops.panel_switch == "option_2":  		
			col.label(text = "Enum Prop OPTION 2")
		if uiprops.panel_switch == "option_3":  		
			col.label(text = "Enum Prop OPTION 3")
		if uiprops.panel_switch == "option_4":  		
			col.label(text = "Enum Prop OPTION 4")
		if uiprops.panel_switch == "option_5":  		
			col.label(text = "Enum Prop OPTION 5")
		if uiprops.panel_switch == "option_6":  		
			col.label(text = "Enum Prop OPTION 6")
		if uiprops.panel_switch == "option_7":  		
			col.label(text = "Enum Prop OPTION 7")
		if uiprops.panel_switch == "option_8":  		
			col.label(text = "Enum Prop OPTION 8")
		if uiprops.panel_switch == "option_9":  		
			col.label(text = "Enum Prop OPTION 9")
		if uiprops.panel_switch == "option_10": 		
			col.label(text = "Enum Prop OPTION 10")
		if uiprops.panel_switch == "option_11": 		
			col.label(text = "Enum Prop OPTION 11")
		if uiprops.panel_switch == "option_12": 		
			col.label(text = "Enum Prop OPTION 12")
		if uiprops.panel_switch == "option_13": 		
			col.label(text = "Enum Prop OPTION 13")
		if uiprops.panel_switch == "option_14": 		
			col.label(text = "Enum Prop OPTION 14")
		if uiprops.panel_switch == "option_15": 		
			col.label(text = "Enum Prop OPTION 15")
		if uiprops.panel_switch == "option_16": 		
			col.label(text = "Enum Prop OPTION 16")
		if uiprops.panel_switch == "option_17": 		
			col.label(text = "Enum Prop OPTION 17")
		if uiprops.panel_switch == "option_18": 		
			col.label(text = "Enum Prop OPTION 18")
		if uiprops.panel_switch == "option_19": 		
			col.label(text = "Enum Prop OPTION 19")
		if uiprops.panel_switch == "option_20": 		
			col.label(text = "Enum Prop OPTION 20")
		if uiprops.panel_switch == "option_21": 		
			col.label(text = "Enum Prop OPTION 21")
		if uiprops.panel_switch == "option_22": 		
			col.label(text = "Enum Prop OPTION 22")
		if uiprops.panel_switch == "option_23": 		
			col.label(text = "Enum Prop OPTION 23")
		if uiprops.panel_switch == "option_24": 		
			col.label(text = "Enum Prop OPTION 24")
		if uiprops.panel_switch == "option_25": 		
			col.label(text = "Enum Prop OPTION 25")
		if uiprops.panel_switch == "option_26": 		
			col.label(text = "Enum Prop OPTION 26")
		if uiprops.panel_switch == "option_27": 		
			col.label(text = "Enum Prop OPTION 27")
		if uiprops.panel_switch == "option_28": 		
			col.label(text = "Enum Prop OPTION 28")

def register():
	bpy.utils.register_class(OBJECTMODE_PG_UI_Properties_TB)	
	bpy.types.Scene.test_props = PointerProperty(type = OBJECTMODE_PG_UI_Properties_TB)
	bpy.utils.register_class(OBJECTMODE_PT_TEST)	

def unregister():
	bpy.utils.unregister_class(OBJECTMODE_PG_UI_Properties_TB)    
	del bpy.types.Scene.test_props
	bpy.utils.unregister_class(OBJECTMODE_PT_TEST)    

if __name__ == "__main__":
	register()