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. This will require obtaining a reference to the namespace object where the enumeration is defined, so that its __dict__ can be searched for all the relevant enumerators. In general, this namespace object won't always be available locally, and its parent module very often won't even have been imported yet, but importlib can be used to resolve this issue efficiently. In addition, some namespaces are multi-level (e.g. QtGui.QTouchEvent.TouchPoint.InfoFlag), so that needs special handling as well.

    A basic implementation might therefore look something like this:

    from importlib import import_module
    
    def enum_mapping(enum):
        ns = import_module(enum.__module__)
        for name in enum.__qualname__.split('.')[:-1]:
            ns = getattr(ns, name)
        mapping = {}
        for key, value in ns.__dict__.items():
            if isinstance(value, enum):
                mapping[key] = value
        return mapping
    
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    
    for enum in (
        QTouchEvent.TouchPoint.InfoFlag,
        QTableWidget.EditTrigger,
        QMessageBox.StandardButton,
        ):
        print(f'{enum.__qualname__}:')
        for key, value in enum_mapping(enum).items():
            print(f'  {key} = {value}')
        print()
    

    Output:

    QTouchEvent.TouchPoint.InfoFlag:
      Pen = 1
      Token = 2
    
    QAbstractItemView.EditTrigger:
      AllEditTriggers = 31
      AnyKeyPressed = 16
      CurrentChanged = 1
      DoubleClicked = 2
      EditKeyPressed = 8
      NoEditTriggers = 0
      SelectedClicked = 4
    
    QMessageBox.StandardButton:
      Abort = 262144
      Apply = 33554432
      ButtonMask = -769
      Cancel = 4194304
      Close = 2097152
      Default = 256
      Discard = 8388608
      Escape = 512
      FirstButton = 1024
      FlagMask = 768
      Help = 16777216
      Ignore = 1048576
      LastButton = 134217728
      No = 65536
      NoAll = 131072
      NoButton = 0
      NoToAll = 131072
      Ok = 1024
      Open = 8192
      Reset = 67108864
      RestoreDefaults = 134217728
      Retry = 524288
      Save = 2048
      SaveAll = 4096
      Yes = 16384
      YesAll = 32768
      YesToAll = 32768