diff --git a/Qt.py b/Qt.py index 0bff5f52..643b6582 100644 --- a/Qt.py +++ b/Qt.py @@ -37,6 +37,7 @@ """ +import inspect import os import sys import types @@ -1800,6 +1801,35 @@ def __call__(self, *a, **kw): raise NotImplementedError(self.__err) +def _pyqt_enums_patch(): + """Patch ``PyQt*`` Qt namespace enums to behave like ``PySide*``. + + i.e. ``PyQt*`` only has Qt.Checked and Qt.CheckState, but not ``PySide*`` + Qt.CheckState.Checked attribute or Qt.CheckState.values dictionary. + """ + qt_attrs = { + name: getattr(Qt.QtCore.Qt, name) + for name in dir(Qt.QtCore.Qt) + if not name.startswith("_") + } + + known_enums = {} + for class_name, enum_class in qt_attrs.items(): + if inspect.isclass(enum_class) and issubclass(enum_class, int): + class EnumFlags(enum_class): + values = {} + + known_enums[enum_class] = class_name + setattr(Qt.QtCompat, class_name, EnumFlags) + + for attr_name, attr in qt_attrs.items(): + class_name = known_enums.get(attr.__class__) + if isinstance(attr, int) and class_name is not None: + enum_class = getattr(Qt.QtCompat, class_name) + enum_class.values[attr_name] = attr + setattr(enum_class, attr_name, attr) + + def _install(): # Default order (customize order and content via QT_PREFERRED_BINDING) default_order = ("PySide2", "PyQt5", "PySide", "PyQt4") @@ -1915,6 +1945,12 @@ def _install(): if hasattr(Qt.QtCompat, 'loadUi'): Qt.QtCompat.load_ui = Qt.QtCompat.loadUi + if Qt.__binding__.startswith("PyQt"): + if getattr(getattr(Qt, "QtCore", None), "Qt", None) is None: + _warn("Qt.QtCore.Qt not available. Not applying enums patch.") + else: + _pyqt_enums_patch() + _install() diff --git a/tests.py b/tests.py index a60e50e0..ba4d949b 100644 --- a/tests.py +++ b/tests.py @@ -1159,3 +1159,42 @@ def test_multiple_preferred(): assert Qt.__binding__ == "PyQt4", ( "PyQt4 should have been picked, " "instead got %s" % Qt.__binding__) + + def test_pyqt_enums(): + """Check some known enums/flags mappings.""" + from Qt import QtCompat + + class_attributes = { # Version agnostic flags and enums + "CheckState": ["Checked", "Unchecked", "PartiallyChecked"], + "Orientation": ["Horizontal", "Vertical"], + } + for class_name, attributes in class_attributes.items(): + qt_class = getattr(QtCompat, class_name) + + values_path = "QtCompat.%s.%s" % (class_name, "values") + assert hasattr(qt_class, "values"), ( + "%s should exist, but is missing" % values_path) + + cls_values = getattr(qt_class, "values", None) + assert cls_values and isinstance(cls_values, dict), ( + "%s should be a non-empty dictionary, not %s" + % (values_path, cls_values)) + + for attr_name in attributes: + attr_path = "QtCompat.%s.%s" % (class_name, attr_name) + + assert hasattr(qt_class, attr_name), ( + "%s should exist, but is missing" % attr_path) + + value = getattr(qt_class, attr_name, None) + assert value is not None, "%s shouldn't be None" % attr_path + + assert attr_name in cls_values, ( + "%s key should exist in %s dict" % + (attr_name, values_path)) + + cls_value = cls_values[attr_name] + assert value == cls_values[attr_name], ( + "(%s) %r != %r (%s[%s])" % + (attr_path, value, cls_value, values_path, attr_name) + )