Show More
@@ -33,10 +33,10 b' import sys' | |||
|
33 | 33 | |
|
34 | 34 | from IPython.utils.version import check_version |
|
35 | 35 | from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE, |
|
36 | QT_API_PYQT, QT_API_PYQT5, | |
|
36 | QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, | |
|
37 | 37 | QT_API_PYQTv1, QT_API_PYQT_DEFAULT) |
|
38 | 38 | |
|
39 | _qt_apis = (QT_API_PYSIDE, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1, | |
|
39 | _qt_apis = (QT_API_PYSIDE, QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1, | |
|
40 | 40 | QT_API_PYQT_DEFAULT) |
|
41 | 41 | |
|
42 | 42 | #Constraints placed on an imported matplotlib |
@@ -83,7 +83,8 b' def get_options():' | |||
|
83 | 83 | qt_api = os.environ.get('QT_API', None) |
|
84 | 84 | if qt_api is None: |
|
85 | 85 | #no ETS variable. Ask mpl, then use default fallback path |
|
86 |
return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE, |
|
|
86 | return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE, | |
|
87 | QT_API_PYQT5, QT_API_PYSIDE2] | |
|
87 | 88 | elif qt_api not in _qt_apis: |
|
88 | 89 | raise RuntimeError("Invalid Qt API %r, valid values are: %r" % |
|
89 | 90 | (qt_api, ', '.join(_qt_apis))) |
@@ -20,6 +20,15 b" QT_API_PYQT5 = 'pyqt5'" | |||
|
20 | 20 | QT_API_PYQTv1 = 'pyqtv1' # Force version 2 |
|
21 | 21 | QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2 |
|
22 | 22 | QT_API_PYSIDE = 'pyside' |
|
23 | QT_API_PYSIDE2 = 'pyside2' | |
|
24 | ||
|
25 | api_to_module = {QT_API_PYSIDE2: 'PySide2', | |
|
26 | QT_API_PYSIDE: 'PySide', | |
|
27 | QT_API_PYQT: 'PyQt4', | |
|
28 | QT_API_PYQTv1: 'PyQt4', | |
|
29 | QT_API_PYQT5: 'PyQt5', | |
|
30 | QT_API_PYQT_DEFAULT: 'PyQt4', | |
|
31 | } | |
|
23 | 32 | |
|
24 | 33 | |
|
25 | 34 | class ImportDenier(object): |
@@ -54,14 +63,21 b' def commit_api(api):' | |||
|
54 | 63 | """Commit to a particular API, and trigger ImportErrors on subsequent |
|
55 | 64 | dangerous imports""" |
|
56 | 65 | |
|
66 | if api == QT_API_PYSIDE2: | |
|
67 | ID.forbid('PySide') | |
|
68 | ID.forbid('PyQt4') | |
|
69 | ID.forbid('PyQt5') | |
|
57 | 70 | if api == QT_API_PYSIDE: |
|
71 | ID.forbid('PySide2') | |
|
58 | 72 | ID.forbid('PyQt4') |
|
59 | 73 | ID.forbid('PyQt5') |
|
60 | 74 | elif api == QT_API_PYQT5: |
|
75 | ID.forbid('PySide2') | |
|
61 | 76 | ID.forbid('PySide') |
|
62 | 77 | ID.forbid('PyQt4') |
|
63 | 78 | else: # There are three other possibilities, all representing PyQt4 |
|
64 | 79 | ID.forbid('PyQt5') |
|
80 | ID.forbid('PySide2') | |
|
65 | 81 | ID.forbid('PySide') |
|
66 | 82 | |
|
67 | 83 | |
@@ -73,7 +89,7 b' def loaded_api():' | |||
|
73 | 89 | |
|
74 | 90 | Returns |
|
75 | 91 | ------- |
|
76 | None, 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1' | |
|
92 | None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1' | |
|
77 | 93 | """ |
|
78 | 94 | if 'PyQt4.QtCore' in sys.modules: |
|
79 | 95 | if qtapi_version() == 2: |
@@ -82,18 +98,21 b' def loaded_api():' | |||
|
82 | 98 | return QT_API_PYQTv1 |
|
83 | 99 | elif 'PySide.QtCore' in sys.modules: |
|
84 | 100 | return QT_API_PYSIDE |
|
101 | elif 'PySide2.QtCore' in sys.modules: | |
|
102 | return QT_API_PYSIDE2 | |
|
85 | 103 | elif 'PyQt5.QtCore' in sys.modules: |
|
86 | 104 | return QT_API_PYQT5 |
|
87 | 105 | return None |
|
88 | 106 | |
|
89 | 107 | |
|
90 | 108 | def has_binding(api): |
|
91 | """Safely check for PyQt4/5 or PySide, without importing | |
|
92 | submodules | |
|
109 | """Safely check for PyQt4/5, PySide or PySide2, without importing submodules | |
|
110 | ||
|
111 | Supports Python <= 3.3 | |
|
93 | 112 | |
|
94 | 113 | Parameters |
|
95 | 114 | ---------- |
|
96 | api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault'] | |
|
115 | api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault'] | |
|
97 | 116 | Which module to check for |
|
98 | 117 | |
|
99 | 118 | Returns |
@@ -103,12 +122,7 b' def has_binding(api):' | |||
|
103 | 122 | # we can't import an incomplete pyside and pyqt4 |
|
104 | 123 | # this will cause a crash in sip (#1431) |
|
105 | 124 | # check for complete presence before importing |
|
106 | module_name = {QT_API_PYSIDE: 'PySide', | |
|
107 | QT_API_PYQT: 'PyQt4', | |
|
108 | QT_API_PYQTv1: 'PyQt4', | |
|
109 | QT_API_PYQT5: 'PyQt5', | |
|
110 | QT_API_PYQT_DEFAULT: 'PyQt4'} | |
|
111 | module_name = module_name[api] | |
|
125 | module_name = api_to_module[api] | |
|
112 | 126 | |
|
113 | 127 | import imp |
|
114 | 128 | try: |
@@ -118,7 +132,7 b' def has_binding(api):' | |||
|
118 | 132 | imp.find_module('QtCore', mod.__path__) |
|
119 | 133 | imp.find_module('QtGui', mod.__path__) |
|
120 | 134 | imp.find_module('QtSvg', mod.__path__) |
|
121 |
if api |
|
|
135 | if api in (QT_API_PYQT5, QT_API_PYSIDE2): | |
|
122 | 136 | # QT5 requires QtWidgets too |
|
123 | 137 | imp.find_module('QtWidgets', mod.__path__) |
|
124 | 138 | |
@@ -130,6 +144,48 b' def has_binding(api):' | |||
|
130 | 144 | except ImportError: |
|
131 | 145 | return False |
|
132 | 146 | |
|
147 | def has_binding_new(api): | |
|
148 | """Safely check for PyQt4/5, PySide or PySide2, without importing submodules | |
|
149 | ||
|
150 | Supports Python >= 3.4 | |
|
151 | ||
|
152 | Parameters | |
|
153 | ---------- | |
|
154 | api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault'] | |
|
155 | Which module to check for | |
|
156 | ||
|
157 | Returns | |
|
158 | ------- | |
|
159 | True if the relevant module appears to be importable | |
|
160 | """ | |
|
161 | module_name = api_to_module[api] | |
|
162 | from importlib.util import find_spec | |
|
163 | ||
|
164 | required = ['QtCore', 'QtGui', 'QtSvg'] | |
|
165 | if api in (QT_API_PYQT5, QT_API_PYSIDE2): | |
|
166 | # QT5 requires QtWidgets too | |
|
167 | required.append('QtWidgets') | |
|
168 | ||
|
169 | for submod in required: | |
|
170 | try: | |
|
171 | spec = find_spec('%s.%s' % (module_name, submod)) | |
|
172 | except ImportError: | |
|
173 | # Package (e.g. PyQt5) not found | |
|
174 | return False | |
|
175 | else: | |
|
176 | if spec is None: | |
|
177 | # Submodule (e.g. PyQt5.QtCore) not found | |
|
178 | return False | |
|
179 | ||
|
180 | if api == QT_API_PYSIDE: | |
|
181 | # We can also safely check PySide version | |
|
182 | import PySide | |
|
183 | return check_version(PySide.__version__, '1.0.3') | |
|
184 | ||
|
185 | return True | |
|
186 | ||
|
187 | if sys.version_info >= (3, 4): | |
|
188 | has_binding = has_binding_new | |
|
133 | 189 | |
|
134 | 190 | def qtapi_version(): |
|
135 | 191 | """Return which QString API has been set, if any |
@@ -229,6 +285,22 b' def import_pyside():' | |||
|
229 | 285 | from PySide import QtGui, QtCore, QtSvg |
|
230 | 286 | return QtCore, QtGui, QtSvg, QT_API_PYSIDE |
|
231 | 287 | |
|
288 | def import_pyside2(): | |
|
289 | """ | |
|
290 | Import PySide2 | |
|
291 | ||
|
292 | ImportErrors raised within this function are non-recoverable | |
|
293 | """ | |
|
294 | from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport | |
|
295 | ||
|
296 | # Join QtGui and QtWidgets for Qt4 compatibility. | |
|
297 | QtGuiCompat = types.ModuleType('QtGuiCompat') | |
|
298 | QtGuiCompat.__dict__.update(QtGui.__dict__) | |
|
299 | QtGuiCompat.__dict__.update(QtWidgets.__dict__) | |
|
300 | QtGuiCompat.__dict__.update(QtPrintSupport.__dict__) | |
|
301 | ||
|
302 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2 | |
|
303 | ||
|
232 | 304 | |
|
233 | 305 | def load_qt(api_options): |
|
234 | 306 | """ |
@@ -240,7 +312,7 b' def load_qt(api_options):' | |||
|
240 | 312 | Parameters |
|
241 | 313 | ---------- |
|
242 | 314 | api_options: List of strings |
|
243 | The order of APIs to try. Valid items are 'pyside', | |
|
315 | The order of APIs to try. Valid items are 'pyside', 'pyside2', | |
|
244 | 316 | 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault' |
|
245 | 317 | |
|
246 | 318 | Returns |
@@ -256,7 +328,9 b' def load_qt(api_options):' | |||
|
256 | 328 | bindings (either becaues they aren't installed, or because |
|
257 | 329 | an incompatible library has already been installed) |
|
258 | 330 | """ |
|
259 | loaders = {QT_API_PYSIDE: import_pyside, | |
|
331 | loaders = { | |
|
332 | QT_API_PYSIDE2: import_pyside2, | |
|
333 | QT_API_PYSIDE: import_pyside, | |
|
260 | 334 | QT_API_PYQT: import_pyqt4, |
|
261 | 335 | QT_API_PYQT5: import_pyqt5, |
|
262 | 336 | QT_API_PYQTv1: partial(import_pyqt4, version=1), |
@@ -281,16 +355,18 b' def load_qt(api_options):' | |||
|
281 | 355 | else: |
|
282 | 356 | raise ImportError(""" |
|
283 | 357 | Could not load requested Qt binding. Please ensure that |
|
284 |
PyQt4 >= 4.7, PyQt5 |
|
|
358 | PyQt4 >= 4.7, PyQt5, PySide >= 1.0.3 or PySide2 is available, | |
|
285 | 359 | and only one is imported per session. |
|
286 | 360 | |
|
287 | 361 | Currently-imported Qt library: %r |
|
288 | 362 | PyQt4 available (requires QtCore, QtGui, QtSvg): %s |
|
289 | 363 | PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s |
|
290 | 364 | PySide >= 1.0.3 installed: %s |
|
365 | PySide2 installed: %s | |
|
291 | 366 | Tried to load: %r |
|
292 | 367 | """ % (loaded_api(), |
|
293 | 368 | has_binding(QT_API_PYQT), |
|
294 | 369 | has_binding(QT_API_PYQT5), |
|
295 | 370 | has_binding(QT_API_PYSIDE), |
|
371 | has_binding(QT_API_PYSIDE2), | |
|
296 | 372 | api_options)) |
General Comments 0
You need to be logged in to leave comments.
Login now