How to make UI bool props mutually exclusive?

Hi,
I started looking into some UI example code today, and I was wondering whether there’s a way to make it so that when I enable one of the bool properties, it will cause the rest to be unselected.
Untitled

Here’s the main code behind it

class MyProperties(PropertyGroup):

    my_bool1: BoolProperty(
        name="Layer 1",
        description="A bool property",
        default = False
        )
    
    my_bool2: BoolProperty(
        name="Layer 2",
        description="A bool property",
        default = False
        )

class OBJECT_PT_CustomPanel(Panel):
    ...

    @classmethod
    def poll(self,context):
        return context.object is not None

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        mytool = scene.my_tool

        layout.prop(mytool, "my_bool1",toggle=True)
        layout.prop(mytool, "my_bool2",toggle=True)

I tried doing something like this to see if I could overwrite the values but it was giving me an error in the console.

@classmethod
    def poll(self,context):
        scene = context.scene
        mytool = scene.my_tool
        if mytool.my_bool2:
            mytool.my_bool2 = False
        return context.object is not None

Is there an easy way to get the UI to behave this way, or if not how can I modify property values through code?
Thanks

1 Like

Use an update callback function on your bool properties? Alternatively based on the limited example shown you could just use an enumerated list and use the expand option in the layout like:

Untitled

layout.prop(rd, "hair_type", text="Shape", expand=True)

Which would only allow 1 selection.

Sample panel including use of both booleans with call back and enumerated list.

import bpy


def test_bool1_cb(self, context):
    self.test_bool2 = not(self.test_bool1)
    return None


def test_bool2_cb(self, context):
    if self.test_bool1 + self.test_bool2 == 1:
        return None
    self.test_bool1 = not(self.test_bool2)

class TEST_PG_props(bpy.types.PropertyGroup):
    test_bool1: bpy.props.BoolProperty(
        name="test_bool1",
        description="test_bool1",
        default=True,
        update=test_bool1_cb,
    )
    test_bool2: bpy.props.BoolProperty(
        name="test_bool2",
        description="test_bool2",
        default=False,
        update=test_bool2_cb,
    )
    test_enum: bpy.props.EnumProperty(
        name="test_enum",
        items=(
            ("opt1", "Option 1", "desc for opt1"),
            ("opt2", "Option 2", "desc for opt2"),
        ),
        description="",
        default="opt1",
    )


class VIEW3D_PT_test():
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Test Panel"


class TEST_PT_sub_01(VIEW3D_PT_test, bpy.types.Panel):
    bl_idname = "VIEW3D_PT_test_panel_1"
    bl_label = "Test Panel 1"


    def draw(self, context):
        props = context.window_manager.test_pg_props
        layout = self.layout
        box = layout.box()
        col = box.column(align=True)
        col.label(text="Sub Panel label")
        col.label(text="booleans")
        row = col.row()
        row.prop(props, "test_bool1")
        row.prop(props, "test_bool2")
        row = col.row()
        row.label(text="enum list")
        row = col.row()
        row.prop(props, "test_enum", expand=True)

classes = [
    TEST_PG_props,
    TEST_PT_sub_01,
    ]

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.WindowManager.test_pg_props = bpy.props.PointerProperty(
        type=TEST_PG_props)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
    del bpy.types.WindowManager.test_pg_props

if __name__ == "__main__":
    register()
3 Likes

Wouldn’t an enumproperty be a better fit for this?

4 Likes

I agree, the enum property lets you do this natively without callbacks.

1 Like

Thank you for providing a sample Nezumi, and Testure for the enum propety suggestion.
Enum property seems to be what I was looking for, after adding the “expand=True” parameter when drawing.