Let's say I have a python enum:
class UserState(Enum):
OFFLINE = auto()
ONLINE = auto()
BUSY = auto()
I can access the different options with UserState.ONLINE
, UserState.OFFLINE
or UserState.BUSY
.
If I wanted to make this a QEnum so I can use it in QML, I'd need to wrap it inside a QObject
like this:
class UserState(QObject):
@QEnum
class Options(Enum):
OFFLINE = auto()
ONLINE = auto()
BUSY = auto()
In QML I can access this enum now the same way I'd access a normal python enum in python. However if I wanted to access this enum from python, I'd have to write UserState.Options.ONLINE
.
How can I create an enum that will work in python as well as QML using the same syntax?
I have found a solution for this which I will post in the answers section. However it involves nested metaclasses which just doesn't look right. I think the optimal solution would be a class that derives from QObject as well as Enum to have all the functionality for every context.
If anyone can provide a version which works like that, I will make that the accepted answer. Otherwise you can tell me, why my solution actually is a good one.
Here is a class I have written which adds the support I'm asking for in the question:
class CustomEnumMeta(type(Enum)):
def __new__(cls, name, bases, attrs):
# don't change type of base class
if name == "CustomEnum":
return super().__new__(cls, name, bases, attrs)
original_attrs = attrs.copy()
enum = super().__new__(cls, name, bases, attrs)
class WrapperMeta(type(QObject)):
def __new__(cls, wrapper_name, wrapper_bases, wrapper_attrs):
return super().__new__(cls, wrapper_name, wrapper_bases, {**original_attrs, **wrapper_attrs})
class Wrapper(QObject, metaclass=WrapperMeta):
QEnum(enum)
Wrapper.__name__ = name
return Wrapper
class CustomEnum(Enum, metaclass=CustomEnumMeta):
pass
The CustomEnum class can be inherited just like a normal python enum:
class UserState(CustomEnum):
OFFLINE = auto()
ONLINE = auto()
BUSY = auto()
Now you can use UserState
the same way in python as in QML. This is a valid statement for both python and QML: UserState.ONLINE
My implementation works by replacing the original class with a QObject, nesting the original class. And then copying all the attributes of the nested class to the outer class to make them accessible from python.