Skip to content

PyQt enums patch #352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Qt.py
Original file line number Diff line number Diff line change
@@ -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("_")
}
Comment on lines +1810 to +1814
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one for example could (and likely is) pick up enums other than the 2 being fixed by this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it was the original intention to be able to capture all cases found, not just CheckState only. The description in the PR could probably do with a better wording to highlight it.


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()

39 changes: 39 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
@@ -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)
)