1

I want to make an interaction with an ipywidgets Dropdown which goes on to create a barchart. The bars of the barchart can be ordered in different ways.

I have an enum of the types of sorting I wish to give as options:

class SORT_TYPE(Enum):
    ALPHABETICAL = 1
    ASCENDING = 2
    DESCENDING = 3
    UNSORTED = 4

I have a dropdown widget where the values use the enum above

ordering_dropdown = widgets.Dropdown(
    options={'Alphabetical': SORT_TYPE.ALPHABETICAL, \
             'Ascending': SORT_TYPE.ASCENDING, \
             'Descending': SORT_TYPE.DESCENDING, \
             'Unsorted': SORT_TYPE.UNSORTED},
    value=SORT_TYPE.ALPHABETICAL,
    description='Ordering: ',
)

But when I go to use it in an interaction

interactive(my_func, p1=p1, p2=p2, ordering=ordering_dropdown)

I get

ValueError: <SORT_TYPE.ALPHABETICAL: 1> cannot be transformed to a widget

Do you know what I'm doing wrong?

Thanks in advance.

Lnr
  • 187
  • 1
  • 7
  • I think your original code will work if you change the value you pass to `options` from a `dict[str, Enum]` to a `list[tuple[str, Enum]]`. In other words, from `{'Alphabetical': SORT_TYPE.ALPHABETICAL, ...}` to `[('Alphabetical', SORT_TYPE.ALPHABETICAL), ...]`. See my answer for more details on why, and for how you might use a `StrEnum` for this. – posita Oct 17 '22 at 14:51

2 Answers2

1

While you can define your own mappings and treat them as constants, IntEnum (standard library) and StrEnum (third party package, but in the standard library as of Python 3.11) are closer to your original intent:

In [1]: from enum import IntEnum

In [2]: class MyEnum(IntEnum):
   ...:     ONE = 1
   ...:     TWO = 2
   ...:

In [3]: MyEnum.TWO
Out[3]: <MyEnum.TWO: 2>

In [4]: MyEnum.TWO.value  # <-- deflate via the value property
Out[4]: 2

In [5]: MyEnum(2)  # <-- reinflate by passing the int value to the constructor
Out[5]: <MyEnum.TWO: 2>

If you don't have access to StrEnum, then you can use a trick identified by the aforementioned SE answer to approximate something similar. We can exploit a certain property of selection widgets (e.g., Dropdown, RadioButtons, etc.) to afford automated translation between the strings and the enums. From the docs:

You can specify the enumeration of selectable options by passing a list (options are either (label, value) pairs, or simply values for which the labels are derived by calling str).

The affords enum semantics with a tighter relationship between strings and their corresponding enum values.

class SORT_TYPE(str, Enum):  # <-- or just StrEnum, if you have it
    ALPHABETICAL = "Alphabetical"
    ASCENDING = "Descending"
    DESCENDING = "Descending"
    UNSORTED = "Unsorted"

ordering_dropdown = widgets.Dropdown(
    options=[
        (
            sort_type.value,  # the label (a str)
            sort_type,  # the value (an enum)
        ) for sort_type in SORT_TYPE
    ],
    value=SORT_TYPE.ALPHABETICAL,  # probably must be in options
    description='Ordering: ',
)

Retrieving the value back from your widget will result in the enum:

assert isinstance(ordering_dropdown.value, SORT_TYPE)  # should work
posita
  • 864
  • 1
  • 9
  • 17
0

Answering myself. Figure the error is because enums aren't a fixed type. Have changed the enum to

SORT_TYPE={'ALPHABETICAL': 1,\
       'ASCENDING': 2,\
       'DESCENDING': 3,\
       'UNSORTED': 4}

and the Dropdown to

ordering_dropdown = widgets.Dropdown(
       options={'Alphabetical': SORT_TYPE['ALPHABETICAL'], \
                'Ascending': SORT_TYPE['ASCENDING'], \
                'Descending': SORT_TYPE['DESCENDING'], \
                'Unsorted': SORT_TYPE['UNSORTED']},
       value=SORT_TYPE['ALPHABETICAL'],
       description='Ordering: ',
    )

It's behaving a bit better now.

Lnr
  • 187
  • 1
  • 7