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()
I’ve finally understood, thank you very much.
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…
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…
@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()