|
42 | 42 | import types |
43 | 43 | import shutil |
44 | 44 | import importlib |
| 45 | +import json |
45 | 46 |
|
46 | 47 |
|
47 | | -__version__ = "1.2.3" |
| 48 | +__version__ = "1.3.2" |
48 | 49 |
|
49 | 50 | # Enable support for `from Qt import *` |
50 | 51 | __all__ = [] |
51 | 52 |
|
52 | 53 | # Flags from environment variables |
53 | 54 | QT_VERBOSE = bool(os.getenv("QT_VERBOSE")) |
| 55 | +QT_PREFERRED_BINDING_JSON = os.getenv("QT_PREFERRED_BINDING_JSON", "") |
54 | 56 | QT_PREFERRED_BINDING = os.getenv("QT_PREFERRED_BINDING", "") |
55 | 57 | QT_SIP_API_HINT = os.getenv("QT_SIP_API_HINT") |
56 | 58 |
|
@@ -777,19 +779,25 @@ def _wrapinstance(ptr, base=None): |
777 | 779 | raise AttributeError("'module' has no attribute 'wrapInstance'") |
778 | 780 |
|
779 | 781 | if base is None: |
780 | | - q_object = func(long(ptr), Qt.QtCore.QObject) |
781 | | - meta_object = q_object.metaObject() |
782 | | - class_name = meta_object.className() |
783 | | - super_class_name = meta_object.superClass().className() |
| 782 | + if Qt.IsPyQt4 or Qt.IsPyQt5: |
| 783 | + base = Qt.QtCore.QObject |
| 784 | + else: |
| 785 | + q_object = func(long(ptr), Qt.QtCore.QObject) |
| 786 | + meta_object = q_object.metaObject() |
784 | 787 |
|
785 | | - if hasattr(Qt.QtWidgets, class_name): |
786 | | - base = getattr(Qt.QtWidgets, class_name) |
| 788 | + while True: |
| 789 | + class_name = meta_object.className() |
787 | 790 |
|
788 | | - elif hasattr(Qt.QtWidgets, super_class_name): |
789 | | - base = getattr(Qt.QtWidgets, super_class_name) |
| 791 | + try: |
| 792 | + base = getattr(Qt.QtWidgets, class_name) |
| 793 | + except AttributeError: |
| 794 | + try: |
| 795 | + base = getattr(Qt.QtCore, class_name) |
| 796 | + except AttributeError: |
| 797 | + meta_object = meta_object.superClass() |
| 798 | + continue |
790 | 799 |
|
791 | | - else: |
792 | | - base = Qt.QtCore.QObject |
| 800 | + break |
793 | 801 |
|
794 | 802 | return func(long(ptr), base) |
795 | 803 |
|
@@ -969,7 +977,7 @@ def createWidget(self, class_name, parent=None, name=""): |
969 | 977 | parent, |
970 | 978 | name) |
971 | 979 | elif class_name in self.custom_widgets: |
972 | | - widget = self.custom_widgets[class_name](parent) |
| 980 | + widget = self.custom_widgets[class_name](parent=parent) |
973 | 981 | else: |
974 | 982 | raise Exception("Custom widget '%s' not supported" |
975 | 983 | % class_name) |
@@ -1251,16 +1259,24 @@ def _setup(module, extras): |
1251 | 1259 |
|
1252 | 1260 | Qt.__binding__ = module.__name__ |
1253 | 1261 |
|
| 1262 | + def _warn_import_error(exc, module): |
| 1263 | + msg = str(exc) |
| 1264 | + if "No module named" in msg: |
| 1265 | + return |
| 1266 | + _warn("ImportError(%s): %s" % (module, msg)) |
| 1267 | + |
1254 | 1268 | for name in list(_common_members) + extras: |
1255 | 1269 | try: |
1256 | 1270 | submodule = _import_sub_module( |
1257 | 1271 | module, name) |
1258 | | - except ImportError: |
| 1272 | + except ImportError as e: |
1259 | 1273 | try: |
1260 | 1274 | # For extra modules like sip and shiboken that may not be |
1261 | 1275 | # children of the binding. |
1262 | 1276 | submodule = __import__(name) |
1263 | | - except ImportError: |
| 1277 | + except ImportError as e2: |
| 1278 | + _warn_import_error(e, name) |
| 1279 | + _warn_import_error(e2, name) |
1264 | 1280 | continue |
1265 | 1281 |
|
1266 | 1282 | setattr(Qt, "_" + name, submodule) |
@@ -1508,13 +1524,13 @@ def _pyqt5(): |
1508 | 1524 | extras = ["uic"] |
1509 | 1525 |
|
1510 | 1526 | try: |
1511 | | - import sip |
| 1527 | + # Relevant to PyQt5 5.11 and above |
| 1528 | + from PyQt5 import sip |
1512 | 1529 | extras += ["sip"] |
1513 | 1530 | except ImportError: |
1514 | 1531 |
|
1515 | | - # Relevant to PyQt5 5.11 and above |
1516 | 1532 | try: |
1517 | | - from PyQt5 import sip |
| 1533 | + import sip |
1518 | 1534 | extras += ["sip"] |
1519 | 1535 | except ImportError: |
1520 | 1536 | sip = None |
@@ -1662,7 +1678,11 @@ def _none(): |
1662 | 1678 |
|
1663 | 1679 | def _log(text): |
1664 | 1680 | if QT_VERBOSE: |
1665 | | - sys.stdout.write(text + "\n") |
| 1681 | + sys.stdout.write("Qt.py [info]: %s\n" % text) |
| 1682 | + |
| 1683 | + |
| 1684 | +def _warn(text): |
| 1685 | + sys.stderr.write("Qt.py [warning]: %s\n" % text) |
1666 | 1686 |
|
1667 | 1687 |
|
1668 | 1688 | def _convert(lines): |
@@ -1781,11 +1801,36 @@ def __call__(self, *a, **kw): |
1781 | 1801 |
|
1782 | 1802 |
|
1783 | 1803 | def _install(): |
1784 | | - # Default order (customise order and content via QT_PREFERRED_BINDING) |
| 1804 | + # Default order (customize order and content via QT_PREFERRED_BINDING) |
1785 | 1805 | default_order = ("PySide2", "PyQt5", "PySide", "PyQt4") |
1786 | | - preferred_order = list( |
1787 | | - b for b in QT_PREFERRED_BINDING.split(os.pathsep) if b |
1788 | | - ) |
| 1806 | + preferred_order = None |
| 1807 | + if QT_PREFERRED_BINDING_JSON: |
| 1808 | + # A per-vendor preferred binding customization was defined |
| 1809 | + # This should be a dictionary of the full Qt.py module namespace to |
| 1810 | + # apply binding settings to. The "default" key can be used to apply |
| 1811 | + # custom bindings to all modules not explicitly defined. If the json |
| 1812 | + # data is invalid this will raise a exception. |
| 1813 | + # Example: |
| 1814 | + # {"mylibrary.vendor.Qt": ["PySide2"], "default":["PyQt5","PyQt4"]} |
| 1815 | + try: |
| 1816 | + preferred_bindings = json.loads(QT_PREFERRED_BINDING_JSON) |
| 1817 | + except ValueError: |
| 1818 | + # Python 2 raises ValueError, Python 3 raises json.JSONDecodeError |
| 1819 | + # a subclass of ValueError |
| 1820 | + _warn("Failed to parse QT_PREFERRED_BINDING_JSON='%s'" |
| 1821 | + % QT_PREFERRED_BINDING_JSON) |
| 1822 | + _warn("Falling back to default preferred order") |
| 1823 | + else: |
| 1824 | + preferred_order = preferred_bindings.get(__name__) |
| 1825 | + # If no matching binding was used, optionally apply a default. |
| 1826 | + if preferred_order is None: |
| 1827 | + preferred_order = preferred_bindings.get("default", None) |
| 1828 | + if preferred_order is None: |
| 1829 | + # If a json preferred binding was not used use, respect the |
| 1830 | + # QT_PREFERRED_BINDING environment variable if defined. |
| 1831 | + preferred_order = list( |
| 1832 | + b for b in QT_PREFERRED_BINDING.split(os.pathsep) if b |
| 1833 | + ) |
1789 | 1834 |
|
1790 | 1835 | order = preferred_order or default_order |
1791 | 1836 |
|
|
0 commit comments