Search code examples
pythonenumspyqtpysiderepr

Iterate over PyQt enums and get string representation


In PySide I can get the dictionary with possible/allowed enumerator values and their string representations by using values attribute. For example: QtWidgets.QMessageBox.StandardButton.values.items(). How to achieve the same in PyQt4/PyQt5? Is that even possible? I have found nothing about this in the docs.


Solution

  • PySide/PySide2 have a built-in enum type (Shiboken.EnumType) which supports iteration over the names/values. It also supports a name attribute, which you can use to get the enumerator name directly from its value.

    Unfortunately, all versions of PyQt earlier than PyQt6 do not provide similar support. PyQt6 enums are subclasses of Python's IntEnum (so do support the above features), but PyQt5 enums are just plain subclasses of int with no additional features. This means you will have to roll your own solution to work around this limitation. It's tempting to try to use the Meta-Object System for this, but many classes (such as the Qt namespace) don't provide access to the necessary staticMetaObject, so that approach could never lead to a workable solution.

    A much simpler and more general solution would be to leverage Python's introspection support to build a two-way mapping, like this:

    def enum_mapping(cls, enum):
        mapping = {}
        for key in dir(cls):
            value = getattr(cls, key)
            if isinstance(value, enum):
                mapping[key] = value
                mapping[value] = key
        return mapping
    
    enum = enum_mapping(QMessageBox, QMessageBox.StandardButton)
    
    print('Ok = %s' % enum['Ok'])
    print('QMessageBox.Ok = %s' % enum[QMessageBox.Ok])
    print('1024 = %s' % enum[1024])
    print()
    

    Output:

    Ok = 1024
    QMessageBox.Ok = Ok
    1024 = Ok
    

    Alternatively, you could dynamically create an IntEnum subclass, like this:

    def wrap_enum(cls, enum):
        members = {}
        for key in dir(cls):
            value = getattr(cls, key)
            if isinstance(value, enum):
                members[key] = value
        return IntEnum(enum.__name__, members)
    
    e = wrap_enum(QMessageBox, QMessageBox.StandardButton)
    
    for member in e:
        print(repr(member))
    

    Output:

    <StandardButton.Abort: 262144>
    <StandardButton.Apply: 33554432>
    <StandardButton.ButtonMask: -769>
    <StandardButton.Cancel: 4194304>
    <StandardButton.Close: 2097152>
    <StandardButton.Default: 256>
    <StandardButton.Discard: 8388608>
    <StandardButton.Escape: 512>
    <StandardButton.FirstButton: 1024>
    <StandardButton.FlagMask: 768>
    <StandardButton.Help: 16777216>
    <StandardButton.Ignore: 1048576>
    <StandardButton.LastButton: 134217728>
    <StandardButton.No: 65536>
    <StandardButton.NoAll: 131072>
    <StandardButton.NoButton: 0>
    <StandardButton.Open: 8192>
    <StandardButton.Reset: 67108864>
    <StandardButton.Retry: 524288>
    <StandardButton.Save: 2048>
    <StandardButton.SaveAll: 4096>
    <StandardButton.Yes: 16384>
    <StandardButton.YesAll: 32768>